Fixed bug #15254: Extension Manager allows to edit arbitrary files if noEdit flag...
[Packages/TYPO3.CMS.git] / typo3 / mod / tools / em / class.em_index.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2008 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * (c) 2005-2008 Karsten Dambekalns <karsten@typo3.org>
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 * A copy is found in the textfile GPL.txt and important notices to the license
18 * from the author is found in LICENSE.txt distributed with these scripts.
19 *
20 *
21 * This script is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * This copyright notice MUST APPEAR in all copies of the script!
27 ***************************************************************/
28 /**
29 * Module: Extension manager
30 *
31 * $Id$
32 *
33 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
34 * @author Karsten Dambekalns <karsten@typo3.org>
35 */
36 /**
37 * [CLASS/FUNCTION INDEX of SCRIPT]
38 *
39 *
40 *
41 * 194: class SC_mod_tools_em_index extends t3lib_SCbase
42 *
43 * SECTION: Standard module initialization
44 * 337: function init()
45 * 417: function handleExternalFunctionValue($MM_key='function', $MS_value=NULL)
46 * 431: function menuConfig()
47 * 508: function main()
48 * 584: function printContent()
49 *
50 * SECTION: Function Menu Applications
51 * 609: function extensionList_loaded()
52 * 664: function extensionList_installed()
53 * 736: function extensionList_import()
54 * 903: function alterSettings()
55 *
56 * SECTION: Command Applications (triggered by GET var)
57 * 1005: function importExtInfo($extKey, $version='')
58 * 1062: function fetchMetaData($metaType)
59 * 1125: function getMirrorURL()
60 * 1158: function installExtension($extKey, $version=null, $mode=EM_INSTALL_VERSION_MIN)
61 * 1279: function importExtFromRep($extKey,$version,$loc,$uploadFlag=0,$dontDelete=0,$directInput='')
62 * 1425: function showExtDetails($extKey)
63 *
64 * SECTION: Application Sub-functions (HTML parts)
65 * 1737: function updatesForm($extKey,$extInfo,$notSilent=0,$script='',$addFields='')
66 * 1768: function extDumpTables($extKey,$extInfo)
67 * 1835: function getFileListOfExtension($extKey,$conf)
68 * 1889: function extDelete($extKey,$extInfo)
69 * 1920: function extUpdateEMCONF($extKey,$extInfo)
70 * 1940: function extBackup($extKey,$extInfo)
71 * 1987: function extBackup_dumpDataTablesLine($tablesArray,$extKey)
72 * 2015: function extInformationArray($extKey,$extInfo,$remote=0)
73 * 2097: function extInformationArray_dbReq($techInfo,$tableHeader=0)
74 * 2110: function extInformationArray_dbInst($dbInst,$current)
75 * 2129: function getRepositoryUploadForm($extKey,$extInfo)
76 *
77 * SECTION: Extension list rendering
78 * 2190: function extensionListRowHeader($trAttrib,$cells,$import=0)
79 * 2251: function extensionListRow($extKey,$extInfo,$cells,$bgColorClass='',$inst_list=array(),$import=0,$altLinkUrl='')
80 *
81 * SECTION: Output helper functions
82 * 2367: function wrapEmail($str,$email)
83 * 2380: function helpCol($key)
84 * 2396: function labelInfo($str)
85 * 2408: function extensionTitleIconHeader($extKey,$extInfo,$align='top')
86 * 2423: function removeButton()
87 * 2432: function installButton()
88 * 2441: function noImportMsg()
89 * 2454: function depToString($dep,$type='depends')
90 * 2473: function stringToDep($dep)
91 *
92 * SECTION: Read information about all available extensions
93 * 2503: function getInstalledExtensions()
94 * 2530: function getInstExtList($path,&$list,&$cat,$type)
95 * 2561: function fixEMCONF($emConf)
96 * 2600: function splitVersionRange($ver)
97 * 2616: function prepareImportExtList()
98 * 2660: function setCat(&$cat,$listArrayPart,$extKey)
99 *
100 * SECTION: Extension analyzing (detailed information)
101 * 2710: function makeDetailedExtensionAnalysis($extKey,$extInfo,$validity=0)
102 * 2892: function getClassIndexLocallangFiles($absPath,$table_class_prefix,$extKey)
103 * 2962: function modConfFileAnalysis($confFilePath)
104 * 2990: function serverExtensionMD5Array($extKey,$conf)
105 * 3015: function findMD5ArrayDiff($current,$past)
106 *
107 * SECTION: File system operations
108 * 3047: function createDirsInPath($dirs,$extDirPath)
109 * 3065: function removeExtDirectory($removePath,$removeContentOnly=0)
110 * 3128: function clearAndMakeExtensionDir($importedData,$type,$dontDelete=0)
111 * 3182: function removeCacheFiles()
112 * 3192: function extractDirsFromFileList($files)
113 * 3218: function getExtPath($extKey,$type)
114 *
115 * SECTION: Writing to "conf.php" and "localconf.php" files
116 * 3252: function writeTYPO3_MOD_PATH($confFilePath,$type,$mP)
117 * 3289: function writeNewExtensionList($newExtList)
118 * 3312: function writeTsStyleConfig($extKey,$arr)
119 * 3334: function updateLocalEM_CONF($extKey,$extInfo)
120 *
121 * SECTION: Compiling upload information, emconf-file etc.
122 * 3376: function construct_ext_emconf_file($extKey,$EM_CONF)
123 * 3407: function arrayToCode($array, $level=0)
124 * 3433: function makeUploadArray($extKey,$conf)
125 * 3502: function getSerializedLocalLang($file,$content)
126 *
127 * SECTION: Managing dependencies, conflicts, priorities, load order of extension keys
128 * 3538: function addExtToList($extKey,$instExtInfo)
129 * 3569: function checkDependencies($extKey, $conf, $instExtInfo)
130 * 3709: function removeExtFromList($extKey,$instExtInfo)
131 * 3746: function removeRequiredExtFromListArr($listArr)
132 * 3761: function managesPriorities($listArr,$instExtInfo)
133 *
134 * SECTION: System Update functions (based on extension requirements)
135 * 3813: function checkClearCache($extInfo)
136 * 3840: function checkUploadFolder($extKey,$extInfo)
137 * 3925: function checkDBupdates($extKey,$extInfo,$infoOnly=0)
138 * 4022: function forceDBupdates($extKey, $extInfo)
139 * 4080: function tsStyleConfigForm($extKey,$extInfo,$output=0,$script='',$addFields='')
140 *
141 * SECTION: Dumping database (MySQL compliant)
142 * 4175: function dumpTableAndFieldStructure($arr)
143 * 4200: function dumpStaticTables($tableList)
144 * 4229: function dumpHeader()
145 * 4246: function dumpTableHeader($table,$fieldKeyInfo,$dropTableIfExists=0)
146 * 4288: function dumpTableContent($table,$fieldStructure)
147 * 4323: function getTableAndFieldStructure($parts)
148 *
149 * SECTION: TER Communication functions
150 * 4373: function uploadExtensionToTER($em)
151 *
152 * SECTION: Various helper functions
153 * 4411: function listOrderTitle($listOrder,$key)
154 * 4436: function makeVersion($v,$mode)
155 * 4448: function renderVersion($v,$raise='')
156 * 4485: function ulFolder($extKey)
157 * 4494: function importAtAll()
158 * 4505: function importAsType($type,$lockType='')
159 * 4527: function deleteAsType($type)
160 * 4548: function versionDifference($v1,$v2,$div=1)
161 * 4560: function first_in_array($str,$array,$caseInsensitive=FALSE)
162 * 4578: function includeEMCONF($path,$_EXTKEY)
163 * 4593: function searchExtension($extKey,$row)
164 *
165 * TOTAL FUNCTIONS: 90
166 * (This index is automatically created/updated by the extension "extdeveval")
167 *
168 */
169
170 // Include classes needed:
171 require_once(PATH_t3lib.'class.t3lib_tcemain.php');
172 require_once(PATH_t3lib.'class.t3lib_install.php');
173 require_once(PATH_t3lib.'class.t3lib_tsstyleconfig.php');
174 require_once(PATH_t3lib.'class.t3lib_scbase.php');
175
176 require_once('class.em_xmlhandler.php');
177 require_once('class.em_terconnection.php');
178 require_once('class.em_unzip.php');
179
180 // from tx_ter by Robert Lemke
181 define('TX_TER_RESULT_EXTENSIONSUCCESSFULLYUPLOADED', '10504');
182
183 define('EM_INSTALL_VERSION_MIN', 1);
184 define('EM_INSTALL_VERSION_MAX', 2);
185 define('EM_INSTALL_VERSION_STRICT', 3);
186
187 /**
188 * Module: Extension manager
189 *
190 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
191 * @author Karsten Dambekalns <karsten@typo3.org>
192 * @package TYPO3
193 * @subpackage core
194 */
195 class SC_mod_tools_em_index extends t3lib_SCbase {
196
197 // Internal, static:
198 var $versionDiffFactor = 1; // This means that version difference testing for import is detected for sub-versions only, not dev-versions. Default: 1000
199 var $systemInstall = 0; // If "1" then installs in the sysext directory is allowed. Default: 0
200 var $requiredExt = ''; // List of required extension (from TYPO3_CONF_VARS)
201 var $maxUploadSize = 31457280; // Max size in bytes of extension upload to repository
202 var $kbMax = 500; // Max size in kilobytes for files to be edited.
203 var $doPrintContent = true; // If set (default), the function printContent() will echo the content which was collected in $this->content. You can set this to FALSE in order to echo content from elsewhere, fx. when using outbut buffering
204 var $listingLimit = 500; // List that many extension maximally at one time (fixing memory problems)
205 var $listingLimitAuthor = 250; // List that many extension maximally at one time (fixing memory problems)
206
207 /**
208 * Internal variable loaded with extension categories (for display/listing). Should reflect $categories above
209 * Dynamic var.
210 */
211 var $defaultCategories = Array(
212 'cat' => Array (
213 'be' => array(),
214 'module' => array(),
215 'fe' => array(),
216 'plugin' => array(),
217 'misc' => array(),
218 'services' => array(),
219 'templates' => array(),
220 'example' => array(),
221 'doc' => array()
222 )
223 );
224
225 /**
226 * Extension Categories (static var)
227 * Content must be redundant with the same internal variable as in class.tx_extrep.php!
228 */
229 var $categories = Array(
230 'be' => 'Backend',
231 'module' => 'Backend Modules',
232 'fe' => 'Frontend',
233 'plugin' => 'Frontend Plugins',
234 'misc' => 'Miscellaneous',
235 'services' => 'Services',
236 'templates' => 'Templates',
237 'example' => 'Examples',
238 'doc' => 'Documentation'
239 );
240
241 /**
242 * Extension States
243 * Content must be redundant with the same internal variable as in class.tx_extrep.php!
244 */
245 var $states = Array (
246 'alpha' => 'Alpha',
247 'beta' => 'Beta',
248 'stable' => 'Stable',
249 'experimental' => 'Experimental',
250 'test' => 'Test',
251 'obsolete' => 'Obsolete',
252 );
253
254 /**
255 * Colors for extension states
256 */
257 var $stateColors = Array (
258 'alpha' => '#d12438',
259 'beta' => '#97b17e',
260 'stable' => '#3bb65c',
261 'experimental' => '#007eba',
262 'test' => '#979797',
263 'obsolete' => '#000000',
264 );
265
266 /**
267 * "TYPE" information; labels, paths, description etc.
268 */
269 var $typeLabels = Array (
270 'S' => 'System',
271 'G' => 'Global',
272 'L' => 'Local',
273 );
274 var $typeDescr = Array (
275 'S' => 'System extension (typo3/sysext/) - Always distributed with source code (Static).',
276 'G' => 'Global extensions (typo3/ext/) - Available for shared source on server (Dynamic).',
277 'L' => 'Local extensions (typo3conf/ext/) - Local for this TYPO3 installation only (Dynamic).',
278 );
279 var $typePaths = Array(); // Also static, set in init()
280 var $typeBackPaths = Array(); // Also static, set in init()
281
282 var $typeRelPaths = Array (
283 'S' => 'sysext/',
284 'G' => 'ext/',
285 'L' => '../typo3conf/ext/',
286 );
287
288 var $detailCols = Array (
289 0 => 2,
290 1 => 5,
291 2 => 6,
292 3 => 6,
293 4 => 4,
294 5 => 1
295 );
296
297 var $fe_user = array(
298 'username' => '',
299 'password' => '',
300 );
301
302 var $privacyNotice = 'When you interact with the online repository, server information may be sent and stored in the repository for statistics.';
303 var $securityHint = '<strong>Found a security problem? Please get in touch with us!</strong><br />If you think you have found a security issue in TYPO3 or an extension, please contact the <a href="http://typo3.org/teams/security/" target="_blank">TYPO3 security team</a>! Thank you!';
304 var $editTextExtensions = 'html,htm,txt,css,tmpl,inc,php,sql,conf,cnf,pl,pm,sh,xml,ChangeLog';
305 var $nameSpaceExceptions = 'beuser_tracking,design_components,impexp,static_file_edit,cms,freesite,quickhelp,classic_welcome,indexed_search,sys_action,sys_workflows,sys_todos,sys_messages,direct_mail,sys_stat,tt_address,tt_board,tt_calender,tt_guest,tt_links,tt_news,tt_poll,tt_rating,tt_products,setup,taskcenter,tsconfig_help,context_help,sys_note,tstemplate,lowlevel,install,belog,beuser,phpmyadmin,aboutmodules,imagelist,setup,taskcenter,sys_notepad,viewpage,adodb';
306
307
308
309
310
311 // Default variables for backend modules
312 var $MCONF = array(); // Module configuration
313 var $MOD_MENU = array(); // Module menu items
314 var $MOD_SETTINGS = array(); // Module session settings
315 /**
316 * Document Template Object
317 *
318 * @var noDoc
319 */
320 var $doc;
321 var $content; // Accumulated content
322
323 var $inst_keys = array(); // Storage of installed extensions
324 var $gzcompress = 0; // Is set true, if system support compression.
325
326 /**
327 * instance of TER connection handler
328 *
329 * @var SC_mod_tools_em_terconnection
330 */
331 var $terConnection;
332
333 /**
334 * XML handling class for the TYPO3 Extension Manager
335 *
336 * @var SC_mod_tools_em_xmlhandler
337 */
338 var $xmlhandler;
339 var $JScode; // JavaScript code to be forwared to $this->doc->JScode
340
341 // GPvars:
342 var $CMD = array(); // CMD array
343 var $listRemote; // If set, connects to remote repository
344 var $lookUpStr; // Search string when listing local extensions
345
346
347
348
349 /*********************************
350 *
351 * Standard module initialization
352 *
353 *********************************/
354
355 /**
356 * Standard init function of a module.
357 *
358 * @return void
359 */
360 function init() {
361 global $BE_USER,$LANG,$BACK_PATH,$TYPO3_CONF_VARS;
362
363 // Setting paths of install scopes:
364 $this->typePaths = Array (
365 'S' => TYPO3_mainDir.'sysext/',
366 'G' => TYPO3_mainDir.'ext/',
367 'L' => 'typo3conf/ext/'
368 );
369 $this->typeBackPaths = Array (
370 'S' => '../../../',
371 'G' => '../../../',
372 'L' => '../../../../'.TYPO3_mainDir
373 );
374
375 $this->excludeForPackaging = $GLOBALS['TYPO3_CONF_VARS']['EXT']['excludeForPackaging'];
376
377 // Setting module configuration:
378 $this->MCONF = $GLOBALS['MCONF'];
379
380 // Setting GPvars:
381 $this->CMD = is_array(t3lib_div::_GP('CMD')) ? t3lib_div::_GP('CMD') : array();
382 $this->lookUpStr = trim(t3lib_div::_GP('_lookUp'));
383 $this->listRemote = t3lib_div::_GP('ter_connect');
384 $this->listRemote_search = trim(t3lib_div::_GP('ter_search'));
385
386
387 // Configure menu
388 $this->menuConfig();
389
390 // Setting internal static:
391 if ($TYPO3_CONF_VARS['EXT']['allowSystemInstall']) $this->systemInstall = 1;
392 $this->requiredExt = t3lib_div::trimExplode(',',$TYPO3_CONF_VARS['EXT']['requiredExt'],1);
393
394
395 // Initialize helper object
396 $this->terConnection = t3lib_div::makeInstance('SC_mod_tools_em_terconnection');
397 $this->terConnection->emObj =& $this;
398 $this->terConnection->wsdlURL = $TYPO3_CONF_VARS['EXT']['em_wsdlURL'];
399 $this->xmlhandler = t3lib_div::makeInstance('SC_mod_tools_em_xmlhandler');
400 $this->xmlhandler->emObj =& $this;
401 $this->xmlhandler->useUnchecked = $this->MOD_SETTINGS['display_unchecked'];
402 $this->xmlhandler->useObsolete = $this->MOD_SETTINGS['display_obsolete'];
403
404 // Initialize Document Template object:
405 $this->doc = t3lib_div::makeInstance('template');
406 $this->doc->backPath = $BACK_PATH;
407 $this->doc->setModuleTemplate('templates/em_index.html');
408 $this->doc->docType='xhtml_trans';
409
410 // JavaScript
411 $this->doc->JScode = $this->doc->wrapScriptTags('
412 script_ended = 0;
413 function jumpToUrl(URL) { //
414 window.location.href = URL;
415 }
416 ');
417
418 // Reload left frame menu
419 if ($this->CMD['refreshMenu']) {
420 $this->doc->JScode .= $this->doc->wrapScriptTags('
421 if(top.refreshMenu) {
422 top.refreshMenu();
423 } else {
424 top.TYPO3ModuleMenu.refreshMenu();
425 }
426 ');
427 }
428
429
430 // Descriptions:
431 $this->descrTable = '_MOD_'.$this->MCONF['name'];
432 if ($BE_USER->uc['edit_showFieldHelp']) {
433 $LANG->loadSingleTableDescription($this->descrTable);
434 }
435
436 // Setting username/password etc. for upload-user:
437 $this->fe_user['username'] = $this->MOD_SETTINGS['fe_u'];
438 $this->fe_user['password'] = $this->MOD_SETTINGS['fe_p'];
439 parent::init();
440 $this->handleExternalFunctionValue('singleDetails');
441 }
442
443 /**
444 * This function is a copy of the same function in t3lib_SCbase with one modification:
445 * In contrast to t3lib_SCbase::handleExternalFunctionValue() this function merges the $this->extClassConf array
446 * instead of overwriting it. That was necessary for including the Kickstarter as a submodule into the 'singleDetails'
447 * selectorbox as well as in the main 'function' selectorbox.
448 *
449 * @param string Mod-setting array key
450 * @param string Mod setting value, overriding the one in the key
451 * @return void
452 * @see t3lib_SCbase::handleExternalFunctionValue()
453 */
454 function handleExternalFunctionValue($MM_key='function', $MS_value=NULL) {
455 $MS_value = is_null($MS_value) ? $this->MOD_SETTINGS[$MM_key] : $MS_value;
456 $externalItems = $this->getExternalItemConfig($this->MCONF['name'],$MM_key,$MS_value);
457 if (is_array($externalItems)) $this->extClassConf = array_merge($externalItems,is_array($this->extClassConf)?$this->extClassConf:array());
458 if (is_array($this->extClassConf) && $this->extClassConf['path']) {
459 $this->include_once[]=$this->extClassConf['path'];
460 }
461 }
462
463 /**
464 * Configuration of which mod-menu items can be used
465 *
466 * @return void
467 */
468 function menuConfig() {
469 global $BE_USER, $TYPO3_CONF_VARS;
470
471 // MENU-ITEMS:
472 $this->MOD_MENU = array(
473 'function' => array(
474 0 => 'Loaded extensions',
475 1 => 'Install extensions',
476 2 => 'Import extensions',
477 4 => 'Translation handling',
478 3 => 'Settings',
479 5 => 'Check for extension updates',
480 ),
481 'listOrder' => array(
482 'cat' => 'Category',
483 'author_company' => 'Author',
484 'state' => 'State',
485 'type' => 'Type'
486 ),
487 'display_details' => array(
488 1 => 'Details',
489 0 => 'Description',
490 2 => 'More details',
491
492 3 => 'Technical (takes time!)',
493 4 => 'Validating (takes time!)',
494 5 => 'Changed? (takes time!)',
495 ),
496 'display_shy' => '',
497 'display_own' => '',
498 'display_unchecked' => '',
499 'display_obsolete' => '',
500 'display_installed' => '',
501 'display_files' => '',
502
503
504 'singleDetails' => array(
505 'info' => 'Information',
506 'edit' => 'Edit files',
507 'backup' => 'Backup/Delete',
508 'dump' => 'Dump DB',
509 'upload' => 'Upload to TER',
510 'updateModule' => 'UPDATE!',
511 ),
512 'fe_u' => '',
513 'fe_p' => '',
514
515 'mirrorListURL' => '',
516 'rep_url' => '',
517 'extMirrors' => '',
518 'selectedMirror' => '',
519
520 'selectedLanguages' => ''
521 );
522
523 $this->MOD_MENU['singleDetails'] = $this->mergeExternalItems($this->MCONF['name'],'singleDetails',$this->MOD_MENU['singleDetails']);
524
525 // page/be_user TSconfig settings and blinding of menu-items
526 if (!$BE_USER->getTSConfigVal('mod.'.$this->MCONF['name'].'.allowTVlisting')) {
527 unset($this->MOD_MENU['display_details'][3]);
528 unset($this->MOD_MENU['display_details'][4]);
529 unset($this->MOD_MENU['display_details'][5]);
530 }
531
532 // CLEANSE SETTINGS
533 $this->MOD_SETTINGS = t3lib_BEfunc::getModuleData($this->MOD_MENU, t3lib_div::_GP('SET'), $this->MCONF['name']);
534
535 if ($this->MOD_SETTINGS['function']==2) {
536 // If listing from online repository, certain items are removed though:
537 unset($this->MOD_MENU['listOrder']['type']);
538 unset($this->MOD_MENU['display_details'][2]);
539 unset($this->MOD_MENU['display_details'][3]);
540 unset($this->MOD_MENU['display_details'][4]);
541 unset($this->MOD_MENU['display_details'][5]);
542 $this->MOD_SETTINGS = t3lib_BEfunc::getModuleData($this->MOD_MENU, t3lib_div::_GP('SET'), $this->MCONF['name']);
543 }
544 parent::menuConfig();
545 }
546
547 /**
548 * Main function for Extension Manager module.
549 *
550 * @return void
551 */
552 function main() {
553 global $BE_USER,$LANG,$TYPO3_CONF_VARS;
554
555 if (empty($this->MOD_SETTINGS['mirrorListURL'])) $this->MOD_SETTINGS['mirrorListURL'] = $TYPO3_CONF_VARS['EXT']['em_mirrorListURL'];
556
557 // Starting page:
558 $this->content.=$this->doc->header('Extension Manager');
559 $this->content.=$this->doc->spacer(5);
560
561 // Commands given which is executed regardless of main menu setting:
562 if ($this->CMD['showExt']) { // Show details for a single extension
563 $this->showExtDetails($this->CMD['showExt']);
564 } elseif ($this->CMD['requestInstallExtensions']) { // Show details for a single extension
565 $this->requestInstallExtensions($this->CMD['requestInstallExtensions']);
566 } elseif ($this->CMD['importExt'] || $this->CMD['uploadExt']) { // Imports an extension from online rep.
567 $err = $this->importExtFromRep($this->CMD['importExt'],$this->CMD['extVersion'],$this->CMD['loc'],$this->CMD['uploadExt']);
568 if ($err) {
569 $this->content.=$this->doc->section('',$GLOBALS['TBE_TEMPLATE']->rfw($err));
570 }
571 if(!$err && $this->CMD['importExt']) {
572 $this->installTranslationsForExtension($this->CMD['importExt'], $this->getMirrorURL());
573 }
574 } elseif ($this->CMD['importExtInfo']) { // Gets detailed information of an extension from online rep.
575 $this->importExtInfo($this->CMD['importExtInfo'],$this->CMD['extVersion']);
576 } else { // No command - we show what the menu setting tells us:
577 if (t3lib_div::inList('0,1,2',$this->MOD_SETTINGS['function'])) {
578 $menu.='&nbsp;Group by:&nbsp;'.t3lib_BEfunc::getFuncMenu(0,'SET[listOrder]',$this->MOD_SETTINGS['listOrder'],$this->MOD_MENU['listOrder']).
579 '&nbsp;&nbsp;Show:&nbsp;'.t3lib_BEfunc::getFuncMenu(0,'SET[display_details]',$this->MOD_SETTINGS['display_details'],$this->MOD_MENU['display_details']).'<br />';
580 }
581 if (t3lib_div::inList('0,1,5',$this->MOD_SETTINGS['function'])) {
582 $menu.='<label for="checkDisplayShy">Display shy extensions:</label>&nbsp;&nbsp;'.t3lib_BEfunc::getFuncCheck(0,'SET[display_shy]',$this->MOD_SETTINGS['display_shy'],'','','id="checkDisplayShy"');
583 }
584 if (t3lib_div::inList('2',$this->MOD_SETTINGS['function']) && strlen($this->fe_user['username'])) {
585 $menu.='<label for="checkDisplayOwn">Only my extensions:</label>&nbsp;&nbsp;'.t3lib_BEfunc::getFuncCheck(0,'SET[display_own]',$this->MOD_SETTINGS['display_own'],'','','id="checkDisplayOwn"');
586 }
587 if (t3lib_div::inList('0,1,2',$this->MOD_SETTINGS['function'])) {
588 $menu.='&nbsp;&nbsp;<label for="checkDisplayObsolete">Show obsolete:</label>&nbsp;&nbsp;'.t3lib_BEfunc::getFuncCheck(0,'SET[display_obsolete]',$this->MOD_SETTINGS['display_obsolete'],'','','id="checkDisplayObsolete"');
589 }
590
591 $this->content.=$this->doc->section('','<form action="index.php" method="post" name="pageform"><span class="nobr">'.$menu.'</span></form>');
592 $this->content.=$this->doc->spacer(10);
593
594 switch((string)$this->MOD_SETTINGS['function']) {
595 case '0':
596 // Lists loaded (installed) extensions
597 $this->extensionList_loaded();
598 break;
599 case '1':
600 // Lists the installed (available) extensions
601 $this->extensionList_installed();
602 break;
603 case '2':
604 // Lists the extensions available from online rep.
605 $this->extensionList_import();
606 break;
607 case '3':
608 // Shows the settings screen
609 $this->alterSettings();
610 break;
611 case '4':
612 // Allows to set the translation preferences and check the status
613 $this->translationHandling();
614 break;
615 case '5':
616 // Shows a list of extensions with updates in TER
617 $this->checkForUpdates();
618 break;
619 default:
620 $this->extObjContent();
621 break;
622 }
623 }
624
625 // closing any form?
626 $formTags = substr_count($this->content, '<form') + substr_count($this->content, '</form');
627 if ($formTags % 2 > 0) {
628 $this->content .= '</form>';
629 }
630
631 // Setting up the buttons and markers for docheader
632 $docHeaderButtons = $this->getButtons();
633 $markers = array(
634 'CSH' => $docHeaderButtons['csh'],
635 'FUNC_MENU' => $this->getFuncMenu(),
636 'CONTENT' => $this->content
637 );
638
639 // Build the <body> for the module
640 $this->content = $this->doc->startPage('Extension Manager');
641 $this->content.= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
642 $this->content.= $this->doc->endPage();
643 $this->content = $this->doc->insertStylesAndJS($this->content);
644 }
645
646 /**
647 * Print module content. Called as last thing in the global scope.
648 *
649 * @return void
650 */
651 function printContent() {
652 if ($this->doPrintContent) {
653 echo $this->content;
654 }
655 }
656
657 /**
658 * Create the function menu
659 *
660 * @return string HTML of the function menu
661 */
662 protected function getFuncMenu() {
663 $funcMenu = '';
664 if(!$this->CMD['showExt'] && !$this->CMD['requestInstallExtensions'] && !$this->CMD['importExt'] && !$this->CMD['uploadExt'] && !$this->CMD['importExtInfo']) {
665 $funcMenu = t3lib_BEfunc::getFuncMenu(0, 'SET[function]', $this->MOD_SETTINGS['function'], $this->MOD_MENU['function']);
666 } elseif($this->CMD['showExt'] && (!$this->CMD['standAlone'] && !t3lib_div::_GP('standAlone'))) {
667 $funcMenu = t3lib_BEfunc::getFuncMenu(0, 'SET[singleDetails]', $this->MOD_SETTINGS['singleDetails'], $this->MOD_MENU['singleDetails'], '', '&CMD[showExt]=' . $this->CMD['showExt']);
668 }
669 return $funcMenu;
670 }
671
672 /**
673 * Create the panel of buttons for submitting the form or otherwise perform operations.
674 *
675 * @return array all available buttons as an assoc. array
676 */
677 protected function getButtons() {
678
679 $buttons = array(
680 'csh' => '',
681 'back' => '',
682 'shortcut' => ''
683 );
684 // CSH
685 //$buttons['csh'] = t3lib_BEfunc::cshItem('_MOD_web_func', '', $GLOBALS['BACK_PATH']);
686
687 // Shortcut
688 if ($GLOBALS['BE_USER']->mayMakeShortcut()) {
689 $buttons['shortcut'] = $this->doc->makeShortcutIcon('CMD','function',$this->MCONF['name']);
690 }
691 // Back
692 if(($this->CMD['showExt'] && (!$this->CMD['standAlone'] && !t3lib_div::_GP('standAlone'))) || ($this->CMD['importExt'] || $this->CMD['uploadExt'] && (!$this->CMD['standAlone'])) || $this->CMD['importExtInfo']) {
693 $buttons['back'] = '<a href="index.php" class="typo3-goBack"><img' . t3lib_iconWorks::skinImg($this->doc->backPath, 'gfx/goback.gif') . ' title="Go back" class="absmiddle" alt="" /></a>';
694 }
695
696 return $buttons;
697 }
698
699
700
701
702
703
704
705
706 /*********************************
707 *
708 * Function Menu Applications
709 *
710 *********************************/
711
712 /**
713 * Listing of loaded (installed) extensions
714 *
715 * @return void
716 */
717 function extensionList_loaded() {
718 global $TYPO3_LOADED_EXT;
719
720 list($list,$cat) = $this->getInstalledExtensions();
721
722 // Loaded extensions
723 $content = '';
724 $lines = array();
725
726 // Available extensions
727 if (is_array($cat[$this->MOD_SETTINGS['listOrder']])) {
728 $content='';
729 $lines=array();
730 $lines[] = $this->extensionListRowHeader(' class="bgColor5"',array('<td><img src="clear.gif" width="1" height="1" alt="" /></td>'));
731
732 foreach($cat[$this->MOD_SETTINGS['listOrder']] as $catName => $extEkeys) {
733 natcasesort($extEkeys);
734 reset($extEkeys);
735 $extensions = array();
736 while(list($extKey)=each($extEkeys)) {
737 if (array_key_exists($extKey,$TYPO3_LOADED_EXT) && ($this->MOD_SETTINGS['display_shy'] || !$list[$extKey]['EM_CONF']['shy']) && $this->searchExtension($extKey,$list[$extKey])) {
738 if (in_array($extKey, $this->requiredExt)) {
739 $loadUnloadLink = '<strong>'.$GLOBALS['TBE_TEMPLATE']->rfw('Rq').'</strong>';
740 } else {
741 $loadUnloadLink = '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1').'">'.$this->removeButton().'</a>';
742 }
743
744 $extensions[] = $this->extensionListRow($extKey,$list[$extKey],array('<td class="bgColor">'.$loadUnloadLink.'</td>'));
745 }
746 }
747 if(count($extensions)) {
748 $lines[]='<tr><td colspan="'.(3+$this->detailCols[$this->MOD_SETTINGS['display_details']]).'"><br /></td></tr>';
749 $lines[]='<tr><td colspan="'.(3+$this->detailCols[$this->MOD_SETTINGS['display_details']]).'"><img '.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/i/sysf.gif"', 'width="18" height="16"').' align="top" alt="" /><strong>'.$this->listOrderTitle($this->MOD_SETTINGS['listOrder'],$catName).'</strong></td></tr>';
750 $lines[] = implode(chr(10),$extensions);
751 }
752 }
753 }
754
755 $content.= t3lib_BEfunc::cshItem('_MOD_tools_em', 'loaded', $GLOBALS['BACK_PATH'],'');
756 $content.= '<form action="index.php" method="post" name="lookupform">';
757 $content.= '<label for="_lookUp">Look up:</label> <input type="text" id="_lookUp" name="_lookUp" value="'.htmlspecialchars($this->lookUpStr).'" /><input type="submit" value="Search"/><br/><br/>';
758
759 $content.= '</form>
760
761 <!-- Loaded Extensions List -->
762 <table border="0" cellpadding="2" cellspacing="1">'.implode('',$lines).'</table>';
763
764 $this->content.=$this->doc->section('Loaded Extensions',$content,0,1);
765 }
766
767 /**
768 * Listing of available (installed) extensions
769 *
770 * @return void
771 */
772 function extensionList_installed() {
773 global $TYPO3_LOADED_EXT;
774
775 list($list,$cat)=$this->getInstalledExtensions();
776
777 // Available extensions
778 if (is_array($cat[$this->MOD_SETTINGS['listOrder']])) {
779 $content='';
780 $lines=array();
781 $lines[]=$this->extensionListRowHeader(' class="bgColor5"',array('<td><img src="clear.gif" width="18" height="1" alt="" /></td>'));
782
783 $allKeys=array();
784 foreach($cat[$this->MOD_SETTINGS['listOrder']] as $catName => $extEkeys) {
785 if(!$this->MOD_SETTINGS['display_obsolete'] && $catName=='obsolete') continue;
786
787 $allKeys[]='';
788 $allKeys[]='TYPE: '.$catName;
789
790 natcasesort($extEkeys);
791 reset($extEkeys);
792 $extensions = array();
793 while(list($extKey)=each($extEkeys)) {
794 $allKeys[]=$extKey;
795 if ((!$list[$extKey]['EM_CONF']['shy'] || $this->MOD_SETTINGS['display_shy']) &&
796 ($list[$extKey]['EM_CONF']['state']!='obsolete' || $this->MOD_SETTINGS['display_obsolete'])
797 && $this->searchExtension($extKey,$list[$extKey])) {
798 $loadUnloadLink = t3lib_extMgm::isLoaded($extKey)?
799 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->removeButton().'</a>':
800 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[load]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->installButton().'</a>';
801 if (in_array($extKey,$this->requiredExt)) {
802 $loadUnloadLink='<strong>'.$GLOBALS['TBE_TEMPLATE']->rfw('Rq').'</strong>';
803 }
804 $theRowClass = t3lib_extMgm::isLoaded($extKey)? 'em-listbg1' : 'em-listbg2';
805 $extensions[]=$this->extensionListRow($extKey,$list[$extKey],array('<td class="bgColor">'.$loadUnloadLink.'</td>'),$theRowClass);
806 }
807 }
808 if(count($extensions)) {
809 $lines[]='<tr><td colspan="'.(3+$this->detailCols[$this->MOD_SETTINGS['display_details']]).'"><br /></td></tr>';
810 $lines[]='<tr><td colspan="'.(3+$this->detailCols[$this->MOD_SETTINGS['display_details']]).'"><img '.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/i/sysf.gif"', 'width="18" height="16"').'align="top" alt="" /><strong>'.$this->listOrderTitle($this->MOD_SETTINGS['listOrder'],$catName).'</strong></td></tr>';
811 $lines[] = implode(chr(10),$extensions);
812 }
813 }
814
815 $content.='
816
817
818 <!--
819 EXTENSION KEYS:
820
821 '.trim(implode(chr(10),$allKeys)).'
822
823 -->
824
825 ';
826
827 $content.= t3lib_BEfunc::cshItem('_MOD_tools_em', 'avail', $GLOBALS['BACK_PATH'],'|<br/>');
828 $content.= 'If you want to use an extension in TYPO3, you should simply click the "plus" button '.$this->installButton().' . <br />
829 Installed extensions can also be removed again - just click the remove button '.$this->removeButton().' .<br /><br />';
830 $content .= '<form action="index.php" method="post" name="lookupform">';
831 $content.= '<label for="_lookUp">Look up:</label> <input type="text" id="_lookUp" name="_lookUp" value="'.htmlspecialchars($this->lookUpStr).'" /><input type="submit" value="Search"/></form><br/><br/>';
832 $content.= $this->securityHint.'<br /><br />';
833
834 $content.= '<table border="0" cellpadding="2" cellspacing="1">'.implode('',$lines).'</table>';
835
836 $this->content.=$this->doc->section('Available Extensions - Grouped by: '.$this->MOD_MENU['listOrder'][$this->MOD_SETTINGS['listOrder']],$content,0,1);
837 }
838 }
839
840 /**
841 * Listing remote extensions from online repository
842 *
843 * @return void
844 */
845 function extensionList_import() {
846 global $TYPO3_LOADED_EXT;
847 $content='';
848
849 // Listing from online repository:
850 if ($this->listRemote) {
851 list($inst_list,) = $this->getInstalledExtensions();
852 $this->inst_keys = array_flip(array_keys($inst_list));
853
854 $this->detailCols[1]+=6;
855
856 // see if we have an extensionlist at all
857 $this->extensionCount = $this->xmlhandler->countExtensions();
858 if (!$this->extensionCount) {
859 $content .= $this->fetchMetaData('extensions');
860 }
861
862 if($this->MOD_SETTINGS['listOrder']=='author_company') {
863 $this->listingLimit = $this->listingLimitAuthor;
864 }
865
866 $this->pointer = intval(t3lib_div::_GP('pointer'));
867 $offset = $this->listingLimit*$this->pointer;
868
869 if($this->MOD_SETTINGS['display_own'] && strlen($this->fe_user['username'])) {
870 $this->xmlhandler->searchExtensionsXML($this->listRemote_search, $this->fe_user['username'], $this->MOD_SETTINGS['listOrder']);
871 } else {
872 $this->xmlhandler->searchExtensionsXML($this->listRemote_search, '', $this->MOD_SETTINGS['listOrder'], false, false, $offset, $this->listingLimit);
873 }
874 if (count($this->xmlhandler->extensionsXML)) {
875 list($list,$cat) = $this->prepareImportExtList(true);
876
877 // Available extensions
878 if (is_array($cat[$this->MOD_SETTINGS['listOrder']])) {
879 $lines=array();
880 $lines[]=$this->extensionListRowHeader(' class="bgColor5"',array('<td><img src="clear.gif" width="18" height="1" alt="" /></td>'),1);
881
882 foreach($cat[$this->MOD_SETTINGS['listOrder']] as $catName => $extEkeys) {
883 if (count($extEkeys)) {
884 $lines[]='<tr><td colspan="'.(3+$this->detailCols[$this->MOD_SETTINGS['display_details']]).'"><br /></td></tr>';
885 $lines[]='<tr><td colspan="'.(3+$this->detailCols[$this->MOD_SETTINGS['display_details']]).'"><img '.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/i/sysf.gif"', 'width="18" height="16"').'align="top" alt="" /><strong>'.$this->listOrderTitle($this->MOD_SETTINGS['listOrder'],$catName).'</strong></td></tr>';
886
887 natcasesort($extEkeys);
888 reset($extEkeys);
889 while(list($extKey)=each($extEkeys)) {
890 $version = array_keys($list[$extKey]['versions']);
891 $version = end($version);
892 $ext = $list[$extKey]['versions'][$version];
893 $ext['downloadcounter_all'] = $list[$extKey]['downloadcounter'];
894 $ext['_ICON'] = $list[$extKey]['_ICON'];
895 $loadUnloadLink='';
896 if ($inst_list[$extKey]['type']!='S' && (!isset($inst_list[$extKey]) || $this->versionDifference($version,$inst_list[$extKey]['EM_CONF']['version'],$this->versionDiffFactor))) {
897 if (isset($inst_list[$extKey])) {
898 // update
899 $loc= ($inst_list[$extKey]['type']=='G'?'G':'L');
900 $aUrl = 'index.php?CMD[importExt]='.$extKey.'&CMD[extVersion]='.$version.'&CMD[loc]='.$loc;
901 $loadUnloadLink.= '<a href="'.htmlspecialchars($aUrl).'"><img src="'.$GLOBALS['BACK_PATH'].'gfx/import_update.gif" width="12" height="12" title="Update the extension in \''.($loc=='G'?'global':'local').'\' from online repository to server" alt="" /></a>';
902 } else {
903 // import
904 $aUrl = 'index.php?CMD[importExt]='.$extKey.'&CMD[extVersion]='.$version.'&CMD[loc]=L';
905 $loadUnloadLink.= '<a href="'.htmlspecialchars($aUrl).'"><img src="'.$GLOBALS['BACK_PATH'].'gfx/import.gif" width="12" height="12" title="Import this extension to \'local\' dir typo3conf/ext/ from online repository." alt="" /></a>';
906 }
907 } else {
908 $loadUnloadLink = '&nbsp;';
909 }
910
911 if (isset($inst_list[$extKey])) {
912 $theRowClass = t3lib_extMgm::isLoaded($extKey) ? 'em-listbg1' : 'em-listbg2';
913 } else {
914 $theRowClass = 'em-listbg3';
915 }
916
917 $lines[]=$this->extensionListRow($extKey,$ext,array('<td class="bgColor">'.$loadUnloadLink.'</td>'),$theRowClass,$inst_list,1,'index.php?CMD[importExtInfo]='.rawurlencode($extKey));
918 unset($list[$extKey]);
919 }
920 }
921 }
922 unset($list);
923
924 // CSH:
925 $content.= t3lib_BEfunc::cshItem('_MOD_tools_em', 'import_ter', $GLOBALS['BACK_PATH'],'|<br/>');
926 $onsubmit = "window.location.href='index.php?ter_connect=1&ter_search='+escape(this.elements['_lookUp'].value);return false;";
927 $content.= '<form action="index.php" method="post" onsubmit="'.htmlspecialchars($onsubmit).'"><label for="_lookUp">List or look up <strong'.($this->MOD_SETTINGS['display_unchecked']?' style="color:#900;">all':' style="color:#090;">reviewed').'</strong> extensions</label><br />
928 <input type="text" id="_lookUp" name="_lookUp" value="'.htmlspecialchars($this->listRemote_search).'" /> <input type="submit" value="Look up" /></form><br /><br />';
929
930 $content .= $this->browseLinks();
931
932 $content.= '
933
934 <!-- TER Extensions list -->
935 <table border="0" cellpadding="2" cellspacing="1">'.implode(chr(10),$lines).'</table>';
936 $content .= '<br />'.$this->browseLinks();
937 $content.= '<br /><br />'.$this->securityHint;
938 $content.= '<br /><br /><strong>PRIVACY NOTICE:</strong><br /> '.$this->privacyNotice;
939
940 $this->content.=$this->doc->section('Extensions in TYPO3 Extension Repository (online) - Grouped by: '.$this->MOD_MENU['listOrder'][$this->MOD_SETTINGS['listOrder']],$content,0,1);
941
942 // Plugins which are NOT uploaded to repository but present on this server.
943 $content='';
944 $lines=array();
945 if (count($this->inst_keys)) {
946 reset($this->inst_keys);
947 while(list($extKey)=each($this->inst_keys)) {
948 $this->xmlhandler->searchExtensionsXML($extKey, '', '', true);
949 if((strlen($this->listRemote_search) && !stristr($extKey,$this->listRemote_search)) || isset($this->xmlhandler->extensionsXML[$extKey])) continue;
950
951 $loadUnloadLink = t3lib_extMgm::isLoaded($extKey)?
952 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->removeButton().'</a>':
953 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[load]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->installButton().'</a>';
954 if (in_array($extKey,$this->requiredExt)) $loadUnloadLink='<strong>'.$GLOBALS['TBE_TEMPLATE']->rfw('Rq').'</strong>';
955 $lines[]=$this->extensionListRow($extKey,$inst_list[$extKey],array('<td class="bgColor">'.$loadUnloadLink.'</td>'),t3lib_extMgm::isLoaded($extKey)?'em-listbg1':'em-listbg2');
956 }
957 }
958 if(count($lines)) {
959 $content.= 'This is the list of extensions which are available locally, but not in the repository.<br />They might be user-defined and should be prepended user_ then.<br /><br />';
960 $content.= '<table border="0" cellpadding="2" cellspacing="1">'.
961 $this->extensionListRowHeader(' class="bgColor5"',array('<td><img src="clear.gif" width="18" height="1" alt="" /></td>')).
962 implode('',$lines).'</table>';
963 $this->content.=$this->doc->spacer(20);
964 $this->content.=$this->doc->section('Extensions found only on this server',$content,0,1);
965 }
966 }
967 } else {
968 $content.= t3lib_BEfunc::cshItem('_MOD_tools_em', 'import_ter', $GLOBALS['BACK_PATH'],'|<br/>');
969 $onsubmit = "window.location.href='index.php?ter_connect=1&ter_search='+escape(this.elements['_lookUp'].value);return false;";
970 $content.= '<form action="index.php" method="post" onsubmit="'.htmlspecialchars($onsubmit).'"><label for="_lookUp">List or look up <strong'.($this->MOD_SETTINGS['display_unchecked']?' style="color:#900;">all':' style="color:#090;">reviewed').'</strong> extensions</label><br />
971 <input type="text" id="_lookUp" name="_lookUp" value="'.htmlspecialchars($this->listRemote_search).'" /> <input type="submit" value="Look up" /></form><br /><br />';
972
973 $content.= '<p><strong>No matching extensions found.</strong></p>';
974
975 $content.= '<br /><br /><strong>PRIVACY NOTICE:</strong><br /> '.$this->privacyNotice;
976 $this->content.=$this->doc->section('Extensions in TYPO3 Extension Repository (online) - Grouped by: '.$this->MOD_MENU['listOrder'][$this->MOD_SETTINGS['listOrder']],$content,0,1);
977 }
978 } else {
979 // CSH
980 $content.= t3lib_BEfunc::cshItem('_MOD_tools_em', 'import', $GLOBALS['BACK_PATH'],'|<br/>');
981
982 $onsubmit = "window.location.href='index.php?ter_connect=1&ter_search='+escape(this.elements['_lookUp'].value);return false;";
983 $content.= '<form action="index.php" method="post" onsubmit="'.htmlspecialchars($onsubmit).'"><label for="_lookUp">List or look up <strong'.($this->MOD_SETTINGS['display_unchecked']?' style="color:#900;">all':' style="color:#090;">reviewed').'</strong> extensions</label><br />
984 <input type="text" id="_lookUp" name="_lookUp" value="" /> <input type="submit" value="Look up" /><br /><br />';
985
986 if ($this->CMD['fetchMetaData']) { // fetches mirror/extension data from online rep.
987 $content .= $this->fetchMetaData($this->CMD['fetchMetaData']);
988 } else {
989 $onCLick = "window.location.href='index.php?CMD[fetchMetaData]=extensions';return false;";
990 $content.= 'Connect to the current mirror and retrieve the current list of available plugins from the TYPO3 Extension Repository.<br />
991 <input type="submit" value="Retrieve/Update" onclick="'.htmlspecialchars($onCLick).'" />';
992 if (is_file(PATH_site.'typo3temp/extensions.xml.gz')) {
993 $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
994 $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
995 $content.= ' (last update: '.date($dateFormat.' '.$timeFormat,filemtime(PATH_site.'typo3temp/extensions.xml.gz')).')';
996 }
997 }
998 $content.= '</form><br /><br />'.$this->securityHint;
999 $content.= '<br /><br /><strong>PRIVACY NOTICE:</strong><br />'.$this->privacyNotice;
1000
1001 $this->content.=$this->doc->section('Extensions in TYPO3 Extension Repository',$content,0,1);
1002 }
1003
1004 // Upload:
1005 if ($this->importAtAll()) {
1006 $content= '<form action="index.php" enctype="'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'].'" method="post">
1007 <label for="upload_ext_file">Upload extension file (.t3x):</label><br />
1008 <input type="file" size="60" id="upload_ext_file" name="upload_ext_file" /><br />
1009 ... to location:<br />
1010 <select name="CMD[loc]">';
1011 if ($this->importAsType('L')) $content.='<option value="L">Local (../typo3conf/ext/)</option>';
1012 if ($this->importAsType('G')) $content.='<option value="G">Global (typo3/ext/)</option>';
1013 if ($this->importAsType('S')) $content.='<option value="S">System (typo3/sysext/)</option>';
1014 $content.='</select><br />
1015 <input type="checkbox" value="1" name="CMD[uploadOverwrite]" id="checkUploadOverwrite" /> <label for="checkUploadOverwrite">Overwrite any existing extension!</label><br />
1016 <input type="submit" name="CMD[uploadExt]" value="Upload extension file" /></form><br />
1017 ';
1018 } else $content=$this->noImportMsg();
1019
1020 $this->content.=$this->doc->spacer(20);
1021 $this->content.=$this->doc->section('Upload extension file directly (.t3x):',$content,0,1);
1022 }
1023
1024 /**
1025 * Generates a link to the next page of extensions
1026 *
1027 * @return void
1028 */
1029 function browseLinks() {
1030 $content = '';
1031 if ($this->pointer) {
1032 $content .= '<a href="'.t3lib_div::linkThisScript(array('pointer' => $this->pointer-1)).'" class="typo3-prevPage"><img'.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/pilleft_n.gif','width="14" height="14"').' alt="Prev page" /> Prev page</a>';
1033 }
1034 if ($content) $content .= '&nbsp;&nbsp;&nbsp;';
1035 if (intval($this->xmlhandler->matchingCount/$this->listingLimit)>$this->pointer) {
1036 $content .= '<a href="'.t3lib_div::linkThisScript(array('pointer' => $this->pointer+1)).'" class="typo3-nextPage"><img'.t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],'gfx/pilright_n.gif','width="14" height="14"').' alt="Next page" /> Next page</a>';
1037 }
1038 $upper = (($this->pointer+1)*$this->listingLimit);
1039 if ($upper>$this->xmlhandler->matchingCount) {
1040 $upper = $this->xmlhandler->matchingCount;
1041 }
1042 if ($content) $content .= '<br /><br />Showing extensions <strong>'.($this->pointer*$this->listingLimit+1).'</strong> to <strong>'.$upper.'</strong>';
1043 if ($content) $content .= '<br /><br />';
1044 return $content;
1045 }
1046
1047 /**
1048 * Allows changing of settings
1049 *
1050 * @return void
1051 */
1052 function alterSettings() {
1053
1054 // Prepare the HTML output:
1055 $content.= '
1056 '.t3lib_BEfunc::cshItem('_MOD_tools_em', 'settings', $GLOBALS['BACK_PATH'],'|<br/>').'
1057 <form action="index.php" method="post" name="altersettings">
1058 <fieldset><legend>Security Settings</legend>
1059 <table border="0" cellpadding="2" cellspacing="2">
1060 <tr class="bgColor4">
1061 <td><label for="display_unchecked">Enable extensions without review (basic security check):</label></td>
1062 <td>'.t3lib_BEfunc::getFuncCheck(0,'SET[display_unchecked]',$this->MOD_SETTINGS['display_unchecked'],'','','id="display_unchecked"').'</td>
1063 </tr>
1064 </table>
1065 <strong>Notice:</strong> Make sure you know what consequences enabling this checkbox might have. Check the <a href="http://typo3.org/extensions/what-are-reviews/" target="_blank">information on typo3.org about security reviewing</a>!
1066 </fieldset>
1067 <br />
1068 <br />
1069 <fieldset><legend>User Settings</legend>
1070 <table border="0" cellpadding="2" cellspacing="2">
1071 <tr class="bgColor4">
1072 <td><label for="set_fe_u">Enter repository username:</label></td>
1073 <td><input type="text" id="set_fe_u" name="SET[fe_u]" value="'.htmlspecialchars($this->MOD_SETTINGS['fe_u']).'" /></td>
1074 </tr>
1075 <tr class="bgColor4">
1076 <td><label for="set_fe_p">Enter repository password:</label></td>
1077 <td><input type="password" id="set_fe_p" name="SET[fe_p]" value="'.htmlspecialchars($this->MOD_SETTINGS['fe_p']).'" /></td>
1078 </tr>
1079 </table>
1080 <strong>Notice:</strong> This is <em>not</em> your password to the TYPO3 backend! This user information is what is needed to log in at typo3.org with your account there!
1081 </fieldset>
1082 <br />
1083 <br />
1084 <fieldset><legend>Mirror selection</legend>
1085 <table border="0" cellpadding="2" cellspacing="2">
1086 <tr class="bgColor4">
1087 <td><label for="set_mirror_list_url">Enter mirror list URL:</label></a></td>
1088 <td><input type="text" size="50" id="set_mirror_list_url" name="SET[mirrorListURL]" value="'.htmlspecialchars($this->MOD_SETTINGS['mirrorListURL']).'" /></td>
1089 </tr>
1090 </table>
1091 <br />
1092 <p>Select a mirror from below. This list is built from the online mirror list retrieved from the URL above.<br /><br /></p>
1093 <fieldset><legend>Mirror list</legend>';
1094 if(!empty($this->MOD_SETTINGS['mirrorListURL'])) {
1095 if ($this->CMD['fetchMetaData']) { // fetches mirror/extension data from online rep.
1096 $content .= $this->fetchMetaData($this->CMD['fetchMetaData']);
1097 } else {
1098 $content.= '<a href="index.php?CMD[fetchMetaData]=mirrors">Click here to reload the list.</a>';
1099 }
1100 }
1101 $content .= '<br />
1102 <table cellspacing="4" style="text-align:left; vertical-alignment:top;">
1103 <tr><td>Use</td><td>Name</td><td>URL</td><td>Country</td><td>Sponsored by</td></tr>
1104 ';
1105
1106 if (!strlen($this->MOD_SETTINGS['extMirrors'])) $this->fetchMetaData('mirrors');
1107 $extMirrors = unserialize($this->MOD_SETTINGS['extMirrors']);
1108 $extMirrors[''] = array('title'=>'Random (recommended!)');
1109 ksort($extMirrors);
1110 if(is_array($extMirrors)) {
1111 foreach($extMirrors as $k => $v) {
1112 if(isset($v['sponsor'])) {
1113 $sponsor = '<a href="'.htmlspecialchars($v['sponsor']['link']).'" target="_new"><img src="'.$v['sponsor']['logo'].'" title="'.htmlspecialchars($v['sponsor']['name']).'" alt="'.htmlspecialchars($v['sponsor']['name']).'" /></a>';
1114 }
1115 $selected = ($this->MOD_SETTINGS['selectedMirror']==$k) ? 'checked="checked"' : '';
1116 $content.='<tr class="bgColor4">
1117 <td><input type="radio" name="SET[selectedMirror]" id="selectedMirror'.$k.'" value="'.$k.'" '.$selected.'/></td><td><label for="selectedMirror'.$k.'">'.htmlspecialchars($v['title']).'</label></td><td>'.htmlspecialchars($v['host'].$v['path']).'</td><td>'.$v['country'].'</td><td>'.$sponsor.'</td></tr>';
1118 }
1119 }
1120 $content.= '
1121 </table>
1122 </fieldset>
1123 <br />
1124 <table border="0" cellpadding="2" cellspacing="2">
1125 <tr class="bgColor4">
1126 <td><label for="set_rep_url">Enter repository URL:</label></td>
1127 <td><input type="text" size="50" id="set_rep_url" name="SET[rep_url]" value="'.htmlspecialchars($this->MOD_SETTINGS['rep_url']).'" /></td>
1128 </tr>
1129 </table>
1130
1131 If you set a repository URL, this overrides the use of a mirror. Use this to select a specific (private) repository.<br />
1132 </fieldset>
1133 <br />
1134 <input type="submit" value="Update" />
1135 </form>
1136 ';
1137
1138 $this->content.=$this->doc->section('Repository settings',$content,0,1);
1139 }
1140
1141 /**
1142 * Allows to set the translation preferences and check the status
1143 *
1144 * @return void
1145 */
1146 function translationHandling() {
1147 global $LANG, $TYPO3_LOADED_EXT;
1148 $LANG->includeLLFile('EXT:setup/mod/locallang.xml');
1149
1150 //prepare docheader
1151 $docHeaderButtons = $this->getButtons();
1152 $markers = array(
1153 'CSH' => $docHeaderButtons['csh'],
1154 'FUNC_MENU' => $this->getFuncMenu(),
1155 );
1156
1157
1158 $incoming = t3lib_div::_POST('SET');
1159 if(isset($incoming['selectedLanguages']) && is_array($incoming['selectedLanguages'])) {
1160 t3lib_BEfunc::getModuleData($this->MOD_MENU, array('selectedLanguages' => serialize($incoming['selectedLanguages'])), $this->MCONF['name'], '', 'selectedLanguages');
1161 $this->MOD_SETTINGS['selectedLanguages'] = serialize($incoming['selectedLanguages']);
1162 }
1163
1164 $selectedLanguages = unserialize($this->MOD_SETTINGS['selectedLanguages']);
1165 if(count($selectedLanguages)==1 && empty($selectedLanguages[0])) $selectedLanguages = array();
1166 $theLanguages = t3lib_div::trimExplode('|',TYPO3_languages);
1167 foreach($theLanguages as $val) {
1168 if ($val!='default') {
1169 $localLabel = ' - ['.htmlspecialchars($GLOBALS['LOCAL_LANG']['default']['lang_'.$val]).']';
1170 $selected = (is_array($selectedLanguages) && in_array($val, $selectedLanguages)) ? ' selected="selected"' : '';
1171 $opt[$GLOBALS['LOCAL_LANG']['default']['lang_'.$val].'--'.$val]='
1172 <option value="'.$val.'"'.$selected.'>'.$LANG->getLL('lang_'.$val,1).$localLabel.'</option>';
1173 }
1174 }
1175 ksort($opt);
1176
1177 // Prepare the HTML output:
1178 $content.= '
1179 '.t3lib_BEfunc::cshItem('_MOD_tools_em', 'translation', $GLOBALS['BACK_PATH'],'|<br/>').'
1180 <form action="index.php" method="post" name="translationform">
1181 <fieldset><legend>Translation Settings</legend>
1182 <table border="0" cellpadding="2" cellspacing="2">
1183 <tr class="bgColor4">
1184 <td>Languages to fetch:</td>
1185 <td>
1186 <select name="SET[selectedLanguages][]" multiple="multiple" size="10">
1187 <option></option>'.
1188 implode('',$opt).'
1189 </select>
1190 </td>
1191 </tr>
1192 </table>
1193 <br />
1194 <p>For the selected languages the EM tries to download and install translation files if available, whenever an extension is installed. (This replaces the <code>csh_*</code> extensions that were used to install core translations before TYPO3 version 4!)<br />
1195 <br />To request an update/install for already loaded extensions, see below.</p>
1196 </fieldset>
1197 <br />
1198 <input type="submit" value="Save selection" />
1199 <br />
1200 </fieldset>
1201 </form>';
1202
1203 $this->content.=$this->doc->section('Translation settings',$content,0,1);
1204
1205 if(count($selectedLanguages)>0) {
1206 $mirrorURL = $this->getMirrorURL();
1207 $content = '<input type="button" value="Check status against repository" onclick="document.location.href=\''.t3lib_div::linkThisScript(array('l10n'=>'check')).'\'" />&nbsp;<input type="button" value="Update from repository" onclick="document.location.href=\''.t3lib_div::linkThisScript(array('l10n'=>'update')).'\'" />';
1208
1209 // as this page loads dynamically, quit output buffering caused by ob_gzhandler
1210 $this->quitOutputBuffering();
1211
1212 if(t3lib_div::_GET('l10n') == 'check') {
1213 $loadedExtensions = array_keys($TYPO3_LOADED_EXT);
1214 $loadedExtensions = array_diff($loadedExtensions,array('_CACHEFILE'));
1215
1216 // Override content output - we now do that ourself:
1217 $this->content .= $this->doc->section('Translation status',$content,0,1);
1218 // Setting up the buttons and markers for docheader
1219 $content = $this->doc->startPage('Extension Manager');
1220 $content.= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
1221 $contentParts=explode('###CONTENT###',$content);
1222
1223 echo $contentParts[0].$this->content;
1224
1225 $this->doPrintContent = FALSE;
1226 flush();
1227
1228 echo '
1229 <br />
1230 <br />
1231 <p id="progress-message">
1232 Checking translation status, please wait ...
1233 </p>
1234 <br />
1235 <div style="width:100%; height:20px; border: 1px solid black;">
1236 <div id="progress-bar" style="float: left; width: 0%; height: 20px; background-color:green;">&nbsp;</div>
1237 <div id="transparent-bar" style="float: left; width: 100%; height: 20px; background-color:'.$this->doc->bgColor2.';">&nbsp;</div>
1238 </div>
1239 <br />
1240 <br /><p>This table shows the status of the loaded extension\'s translations.</p><br />
1241 <table border="0" cellpadding="2" cellspacing="2">
1242 <tr class="bgColor2"><td>Extension key</td>
1243 ';
1244
1245 foreach($selectedLanguages as $lang) {
1246 echo ('<td>'.$LANG->getLL('lang_'.$lang,1).'</td>');
1247 }
1248 echo ('</tr>');
1249
1250 $counter = 1;
1251 foreach($loadedExtensions as $extKey) {
1252
1253 $percentDone = intval (($counter / count($loadedExtensions)) * 100);
1254 echo ('
1255 <script>
1256 document.getElementById("progress-bar").style.width = "'.$percentDone.'%";
1257 document.getElementById("transparent-bar").style.width = "'.(100-$percentDone).'%";
1258 document.getElementById("progress-message").firstChild.data="Checking translation status for extension \"'.$extKey.'\" ...";
1259 </script>
1260 ');
1261
1262 flush();
1263 $translationStatusArr = $this->terConnection->fetchTranslationStatus($extKey,$mirrorURL);
1264
1265 echo ('<tr class="bgColor4"><td>'.$extKey.'</td>');
1266 foreach($selectedLanguages as $lang) {
1267 // remote unknown -> keine l10n
1268 if(!isset($translationStatusArr[$lang])) {
1269 echo ('<td title="No translation available">N/A</td>');
1270 continue;
1271 }
1272 // determine local md5 from zip
1273 if(is_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip')) {
1274 $localmd5 = md5_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip');
1275 } else {
1276 echo ('<td title="Not installed / Unknown" style="background-color:#ff0">???</td>');
1277 continue;
1278 }
1279 // local!=remote -> needs update
1280 if($localmd5 != $translationStatusArr[$lang]['md5']) {
1281 echo ('<td title="Needs update" style="background-color:#ff0">UPD</td>');
1282 continue;
1283 }
1284 echo ('<td title="Is up to date" style="background-color:#69a550">OK</td>');
1285 }
1286 echo ('</tr>');
1287
1288 $counter ++;
1289 }
1290 echo '</table>
1291 <script>
1292 document.getElementById("progress-message").firstChild.data="Check done.";
1293 </script>
1294 ';
1295 echo $contentParts[1] . $this->doc->endPage();
1296 exit;
1297
1298 } elseif(t3lib_div::_GET('l10n') == 'update') {
1299 $loadedExtensions = array_keys($TYPO3_LOADED_EXT);
1300 $loadedExtensions = array_diff($loadedExtensions,array('_CACHEFILE'));
1301
1302 // Override content output - we now do that ourself:
1303 $this->content .= $this->doc->section('Translation status',$content,0,1);
1304 // Setting up the buttons and markers for docheader
1305 $content = $this->doc->startPage('Extension Manager');
1306 $content.= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
1307 $contentParts=explode('###CONTENT###',$content);
1308
1309 echo $contentParts[0].$this->content;
1310
1311 $this->doPrintContent = FALSE;
1312 flush();
1313
1314 echo ('
1315 <br />
1316 <br />
1317 <p id="progress-message">
1318 Updating translations, please wait ...
1319 </p>
1320 <br />
1321 <div style="width:100%; height:20px; border: 1px solid black;">
1322 <div id="progress-bar" style="float: left; width: 0%; height: 20px; background-color:green;">&nbsp;</div>
1323 <div id="transparent-bar" style="float: left; width: 100%; height: 20px; background-color:'.$this->doc->bgColor2.';">&nbsp;</div>
1324 </div>
1325 <br />
1326 <br /><p>This table shows the update results of the loaded extension\'s translations.<br />
1327 <em>If you want to force a full check/update, delete the l10n zip-files from the typo3temp folder.</em></p><br />
1328 <table border="0" cellpadding="2" cellspacing="2">
1329 <tr class="bgColor2"><td>Extension key</td>
1330 ');
1331
1332 foreach($selectedLanguages as $lang) {
1333 echo '<td>'.$LANG->getLL('lang_'.$lang,1).'</td>';
1334 }
1335 echo '</tr>';
1336
1337 $counter = 1;
1338 foreach($loadedExtensions as $extKey) {
1339 $percentDone = intval (($counter / count($loadedExtensions)) * 100);
1340 echo ('
1341 <script>
1342 document.getElementById("progress-bar").style.width = "'.$percentDone.'%";
1343 document.getElementById("transparent-bar").style.width = "'.(100-$percentDone).'%";
1344 document.getElementById("progress-message").firstChild.data="Updating translation for extension \"'.$extKey.'\" ...";
1345 </script>
1346 ');
1347
1348 flush();
1349 $translationStatusArr = $this->terConnection->fetchTranslationStatus($extKey,$mirrorURL);
1350
1351 echo ('<tr class="bgColor4"><td>'.$extKey.'</td>');
1352 if(is_array($translationStatusArr)) {
1353 foreach($selectedLanguages as $lang) {
1354 // remote unknown -> no l10n available
1355 if(!isset($translationStatusArr[$lang])) {
1356 echo ('<td title="No translation available">N/A</td>');
1357 continue;
1358 }
1359 // determine local md5 from zip
1360 if(is_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip')) {
1361 $localmd5 = md5_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip');
1362 } else {
1363 $localmd5 = 'zzz';
1364 }
1365 // local!=remote or not installed -> needs update
1366 if($localmd5 != $translationStatusArr[$lang]['md5']) {
1367 $ret = $this->updateTranslation($extKey, $lang, $mirrorURL);
1368 if($ret === true) {
1369 echo ('<td title="Has been updated" style="background-color:#69a550">UPD</td>');
1370 } else {
1371 echo ('<td title="'.htmlspecialchars($ret).'" style="background-color:#cb3352">ERR</td>');
1372 }
1373 continue;
1374 }
1375 echo ('<td title="Is up to date" style="background-color:#69a550">OK</td>');
1376 }
1377 } else {
1378 echo ('<td colspan="'.count($selectedLanguages).'" title="Possible reasons: network problems, allow_url_fopen off, curl not enabled in Install tool.">Could not fetch translation status</td>');
1379 }
1380 echo ('</tr>');
1381 $counter++;
1382 }
1383 echo '</table>
1384 <script>
1385 document.getElementById("progress-message").firstChild.data="Update done.";
1386 </script>
1387 ';
1388 echo $contentParts[1] . $this->doc->endPage();
1389 exit;
1390 }
1391
1392 $this->content.=$this->doc->section('Translation status',$content,0,1);
1393 }
1394 }
1395
1396 /**
1397 * Install translations for all selected languages for an extension
1398 *
1399 * @param string $extKey The extension key to install the translations for
1400 * @param string $lang Language code of translation to fetch
1401 * @param string $mirrorURL Mirror URL to fetch data from
1402 * @return mixed true on success, error string on fauilure
1403 */
1404 function updateTranslation($extKey, $lang, $mirrorURL) {
1405 $l10n = $this->terConnection->fetchTranslation($extKey, $lang, $mirrorURL);
1406 if(is_array($l10n)) {
1407 $file = PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip';
1408 $path = 'l10n/'.$lang.'/';
1409 if(!is_dir(PATH_typo3conf.$path)) t3lib_div::mkdir_deep(PATH_typo3conf,$path);
1410 t3lib_div::writeFile($file, $l10n[0]);
1411 if($this->unzip($file, PATH_typo3conf.$path)) {
1412 return true;
1413 } else {
1414 return 'Unpacking the language pack failed!';
1415 }
1416 } else {
1417 return $l10n;
1418 }
1419 }
1420
1421 /**
1422 * Install translations for all selected languages for an extension
1423 *
1424 * @param string $extKey The extension key to install the translations for
1425 * @param string $mirrorURL Mirror URL to fetch data from
1426 * @return mixed true on success, error string on fauilure
1427 */
1428 function installTranslationsForExtension($extKey, $mirrorURL) {
1429 $selectedLanguages = unserialize($this->MOD_SETTINGS['selectedLanguages']);
1430 if(!is_array($selectedLanguages)) $selectedLanguages = array();
1431 foreach($selectedLanguages as $lang) {
1432 $l10n = $this->terConnection->fetchTranslation($extKey, $lang, $mirrorURL);
1433 if(is_array($l10n)) {
1434 $file = PATH_typo3conf.'l10n/'.$extKey.'-l10n-'.$lang.'.zip';
1435 $path = 'l10n/'.$lang.'/'.$extKey;
1436 t3lib_div::writeFile($file, $l10n[0]);
1437 if(!is_dir(PATH_typo3conf.$path)) t3lib_div::mkdir_deep(PATH_typo3conf,$path);
1438 if($this->unzip($file, PATH_typo3conf.$path)) {
1439 return true;
1440 } else {
1441 return 'Unpacking the language pack failed!';
1442 }
1443 } else {
1444 return $l10n;
1445 }
1446 }
1447 }
1448
1449 /**
1450 * Unzips a zip file in the given path.
1451 *
1452 * Uses unzip binary if available, otherwise a pure PHP unzip is used.
1453 *
1454 * @param string $file Full path to zip file
1455 * @param string $path Path to change to before extracting
1456 * @return boolean True on success, false in failure
1457 */
1458 function unzip($file, $path) {
1459 if(strlen($GLOBALS['TYPO3_CONF_VARS']['BE']['unzip_path'])) {
1460 chdir($path);
1461 $cmd = $GLOBALS['TYPO3_CONF_VARS']['BE']['unzip_path'].' -o '.escapeshellarg($file);
1462 exec($cmd, $list, $ret);
1463 return ($ret === 0);
1464 } else {
1465 // we use a pure PHP unzip
1466 $unzip = new em_unzip($file);
1467 $ret = $unzip->extract(array('add_path'=>$path));
1468 return (is_array($ret));
1469 }
1470 }
1471
1472
1473
1474 /*********************************
1475 *
1476 * Command Applications (triggered by GET var)
1477 *
1478 *********************************/
1479
1480 /**
1481 * Returns detailed info about an extension in the online repository
1482 *
1483 * @param string Extension repository uid + optional "private key": [uid]-[key].
1484 * @param [type] $version: ...
1485 * @return void
1486 */
1487 function importExtInfo($extKey, $version='') {
1488
1489 $content = '<form action="index.php" method="post" name="pageform">';
1490
1491 // Fetch remote data:
1492 $this->xmlhandler->searchExtensionsXML($extKey, '', '', true, true);
1493 list($fetchData,) = $this->prepareImportExtList(true);
1494
1495 $versions = array_keys($fetchData[$extKey]['versions']);
1496 $version = ($version == '') ? end($versions) : $version;
1497
1498 $opt = array();
1499 foreach(array_keys($fetchData[$extKey]['versions']) as $ver) {
1500 $opt[]='<option value="'.$ver.'"'.(($version == $ver) ? ' selected="selected"' : '').'>'.$ver.'</option>';
1501 }
1502
1503 // "Select version" box:
1504 $onClick = 'window.location.href=\'index.php?CMD[importExtInfo]='.$extKey.'&CMD[extVersion]=\'+document.pageform.extVersion.options[document.pageform.extVersion.selectedIndex].value; return false;';
1505 $select='<select name="extVersion">'.implode('',$opt).'</select> <input type="submit" value="Load details" onclick="'.htmlspecialchars($onClick).'" /> or<br /><br />';
1506
1507 if ($this->importAtAll()) {
1508 $onClick = '
1509 window.location.href=\'index.php?CMD[importExt]='.$extKey.'\'
1510 +\'&CMD[extVersion]=\'+document.pageform.extVersion.options[document.pageform.extVersion.selectedIndex].value
1511 +\'&CMD[loc]=\'+document.pageform.loc.options[document.pageform.loc.selectedIndex].value;
1512 return false;';
1513 $select.='
1514 <input type="submit" value="Import/Update" onclick="'.htmlspecialchars($onClick).'"> to:
1515 <select name="loc">'.
1516 ($this->importAsType('G',$fetchData['emconf_lockType'])?'<option value="G">Global: '.$this->typePaths['G'].$extKey.'/'.(@is_dir(PATH_site.$this->typePaths['G'].$extKey)?' (OVERWRITE)':' (empty)').'</option>':'').
1517 ($this->importAsType('L',$fetchData['emconf_lockType'])?'<option value="L">Local: '.$this->typePaths['L'].$extKey.'/'.(@is_dir(PATH_site.$this->typePaths['L'].$extKey)?' (OVERWRITE)':' (empty)').'</option>':'').
1518 ($this->importAsType('S',$fetchData['emconf_lockType'])?'<option value="S">System: '.$this->typePaths['S'].$extKey.'/'.(@is_dir(PATH_site.$this->typePaths['S'].$extKey)?' (OVERWRITE)':' (empty)').'</option>':'').
1519 '</select>
1520 </form>';
1521 } else $select.= $this->noImportMsg();
1522 $content.= $select;
1523 $this->content.= $this->doc->section('Select command',$content,0,1);
1524
1525 // Details:
1526 $eInfo = $fetchData[$extKey]['versions'][$version];
1527 $content='<strong>'.$fetchData[$extKey]['_ICON'].' &nbsp;'.$eInfo['EM_CONF']['title'].' ('.$extKey.', '.$version.')</strong><br /><br />';
1528 $content.=$this->extInformationArray($extKey,$eInfo,1);
1529 $this->content.=$this->doc->spacer(10);
1530 $this->content.=$this->doc->section('Remote Extension Details',$content,0,1);
1531 }
1532
1533 /**
1534 * Fetches metadata and stores it to the corresponding place. This includes the mirror list,
1535 * extension XML files.
1536 *
1537 * @param string Type of data to fetch: (mirrors)
1538 * @param boolean If true the method doesn't produce any output
1539 * @return void
1540 */
1541 function fetchMetaData($metaType) {
1542 global $TYPO3_CONF_VARS;
1543
1544 switch($metaType) {
1545 case 'mirrors':
1546 $mfile = t3lib_div::tempnam('mirrors');
1547 $mirrorsFile = t3lib_div::getURL($this->MOD_SETTINGS['mirrorListURL'], 0, array(TYPO3_user_agent));
1548 if($mirrorsFile===false) {
1549 t3lib_div::unlink_tempfile($mfile);
1550 $content = '<p>The mirror list was not updated, it could not be fetched from '.$this->MOD_SETTINGS['mirrorListURL'].'. Possible reasons: network problems, allow_url_fopen is off, curl is not enabled in Install tool.</p>';
1551 } else {
1552 t3lib_div::writeFile($mfile, $mirrorsFile);
1553 $mirrors = implode('',gzfile($mfile));
1554 t3lib_div::unlink_tempfile($mfile);
1555
1556 $mirrors = $this->xmlhandler->parseMirrorsXML($mirrors);
1557 if(is_array($mirrors) && count($mirrors)) {
1558 t3lib_BEfunc::getModuleData($this->MOD_MENU, array('extMirrors' => serialize($mirrors)), $this->MCONF['name'], '', 'extMirrors');
1559 $this->MOD_SETTINGS['extMirrors'] = serialize($mirrors);
1560 $content = '<p>The mirror list has been updated and now contains '.count($mirrors).' entries.</p>';
1561 }
1562 else {
1563 $content = '<p>'.$mirrors.'<br />The mirror list was not updated as it contained no entries.</p>';
1564 }
1565 }
1566 break;
1567 case 'extensions':
1568 $this->fetchMetaData('mirrors'); // if we fetch the extensions anyway, we can as well keep this up-to-date
1569
1570 $mirror = $this->getMirrorURL();
1571 $extfile = $mirror.'extensions.xml.gz';
1572 $extmd5 = t3lib_div::getURL($mirror.'extensions.md5', 0, array(TYPO3_user_agent));
1573 if (is_file(PATH_site.'typo3temp/extensions.xml.gz')) {
1574 $localmd5 = md5_file(PATH_site.'typo3temp/extensions.xml.gz');
1575 }
1576
1577 if($extmd5 === false) {
1578 $content .= '<p>Error: The extension MD5 sum could not be fetched from '.$mirror.'extensions.md5. Possible reasons: network problems, allow_url_fopen is off, curl is not enabled in Install tool.</p>';
1579 } elseif($extmd5 == $localmd5) {
1580 $content .= '<p>The extension list has not changed remotely, it has thus not been fetched.</p>';
1581 } else {
1582 $extXML = t3lib_div::getURL($extfile, 0, array(TYPO3_user_agent));
1583 if($extXML === false) {
1584 $content .= '<p>Error: The extension list could not be fetched from '.$extfile.'. Possible reasons: network problems, allow_url_fopen is off, curl is not enabled in Install tool.</p>';
1585 } else {
1586 t3lib_div::writeFile(PATH_site.'typo3temp/extensions.xml.gz', $extXML);
1587 $content .= $this->xmlhandler->parseExtensionsXML(PATH_site.'typo3temp/extensions.xml.gz');
1588 }
1589 }
1590 break;
1591 }
1592
1593 return $content;
1594 }
1595
1596 /**
1597 * Returns the base URL for the slected or a random mirror.
1598 *
1599 * @return string The URL for the selected or a random mirror
1600 */
1601 function getMirrorURL() {
1602 if(strlen($this->MOD_SETTINGS['rep_url'])) return $this->MOD_SETTINGS['rep_url'];
1603
1604 $mirrors = unserialize($this->MOD_SETTINGS['extMirrors']);
1605 if(!is_array($mirrors)) {
1606 $this->fetchMetaData('mirrors');
1607 $mirrors = unserialize($this->MOD_SETTINGS['extMirrors']);
1608 if(!is_array($mirrors)) return false;
1609 }
1610 if($this->MOD_SETTINGS['selectedMirror']=='') {
1611 srand((float) microtime() * 10000000); // not needed after PHP 4.2.0...
1612 $rand = array_rand($mirrors);
1613 $url = 'http://'.$mirrors[$rand]['host'].$mirrors[$rand]['path'];
1614 }
1615 else {
1616 $url = 'http://'.$mirrors[$this->MOD_SETTINGS['selectedMirror']]['host'].$mirrors[$this->MOD_SETTINGS['selectedMirror']]['path'];
1617 }
1618
1619 return $url;
1620 }
1621
1622
1623
1624 /**
1625 * Installs (activates) an extension
1626 *
1627 * For $mode use the three constants EM_INSTALL_VERSION_MIN, EM_INSTALL_VERSION_MAX, EM_INSTALL_VERSION_STRICT
1628 *
1629 * If an extension is loaded or imported already and the version requirement is matched, it will not be
1630 * fetched from the repository. This means, if you use EM_INSTALL_VERSION_MIN, you will not always get the latest
1631 * version of an extension!
1632 *
1633 * @param string $extKey The extension key to install
1634 * @param string $version A version number that should be installed
1635 * @param int $mode If a version is requested, this determines if it is the min, max or strict version requested
1636 * @return [type] ...
1637 * @todo Make the method able to handle needed interaction somehow (unmatched dependencies)
1638 */
1639 function installExtension($extKey, $version=null, $mode=EM_INSTALL_VERSION_MIN) {
1640 list($inst_list,) = $this->getInstalledExtensions();
1641
1642 // check if it is already installed and loaded with sufficient version
1643 if(isset($inst_list[$extKey])) {
1644 $currentVersion = $inst_list[$extKey]['EM_CONF']['version'];
1645
1646 if(t3lib_extMgm::isLoaded($extKey)) {
1647 if($version===null) {
1648 return array(true, 'Extension already installed and loaded.');
1649 } else {
1650 switch($mode) {
1651 case EM_INSTALL_VERSION_STRICT:
1652 if ($currentVersion == $version) {
1653 return array(true, 'Extension already installed and loaded.');
1654 }
1655 break;
1656 case EM_INSTALL_VERSION_MIN:
1657 if (version_compare($currentVersion, $version, '>=')) {
1658 return array(true, 'Extension already installed and loaded.');
1659 }
1660 break;
1661 case EM_INSTALL_VERSION_MAX:
1662 if (version_compare($currentVersion, $version, '<=')) {
1663 return array(true, 'Extension already installed and loaded.');
1664 }
1665 break;
1666 }
1667 }
1668 } else {
1669 if (!t3lib_extMgm::isLocalconfWritable()) {
1670 return array(false, 'localconf.php is not writable!');
1671 }
1672 $newExtList = -1;
1673 switch($mode) {
1674 case EM_INSTALL_VERSION_STRICT:
1675 if ($currentVersion == $version) {
1676 $newExtList = $this->addExtToList($extKey, $inst_list);
1677 }
1678 break;
1679 case EM_INSTALL_VERSION_MIN:
1680 if (version_compare($currentVersion, $version, '>=')) {
1681 $newExtList = $this->addExtToList($extKey, $inst_list);
1682 }
1683 break;
1684 case EM_INSTALL_VERSION_MAX:
1685 if (version_compare($currentVersion, $version, '<=')) {
1686 $newExtList = $this->addExtToList($extKey, $inst_list);
1687 }
1688 break;
1689 }
1690 if ($newExtList!=-1) {
1691 $this->writeNewExtensionList($newExtList);
1692 $this->refreshGlobalExtList();
1693 $this->forceDBupdates($extKey, $inst_list[$extKey]);
1694 return array(true, 'Extension was already installed, it has been loaded.');
1695 }
1696 }
1697 }
1698
1699 // at this point we know we need to import (a matching version of) the extension from TER2
1700
1701 // see if we have an extensionlist at all
1702 if (!$this->xmlhandler->countExtensions()) {
1703 $this->fetchMetaData('extensions');
1704 }
1705 $this->xmlhandler->searchExtensionsXML($extKey, '', '', true);
1706
1707 // check if extension can be fetched
1708 if(isset($this->xmlhandler->extensionsXML[$extKey])) {
1709 $versions = array_keys($this->xmlhandler->extensionsXML[$extKey]['versions']);
1710 $latestVersion = end($versions);
1711 switch($mode) {
1712 case EM_INSTALL_VERSION_STRICT:
1713 if(!isset($this->xmlhandler->extensionsXML[$extKey]['versions'][$version])) {
1714 return array(false, 'Extension not available in matching version');
1715 }
1716 break;
1717 case EM_INSTALL_VERSION_MIN:
1718 if (version_compare($latestVersion, $version, '>=')) {
1719 $version = $latestVersion;
1720 } else {
1721 return array(false, 'Extension not available in matching version');
1722 }
1723 break;
1724 case EM_INSTALL_VERSION_MAX:
1725 while (($v = array_pop($versions)) && version_compare($v, $version, '>=')) {
1726 // Loop until a version is found
1727 }
1728
1729 if ($v !== null && version_compare($v, $version, '<=')) {
1730 $version = $v;
1731 } else {
1732 return array(false, 'Extension not available in matching version');
1733 }
1734 break;
1735 }
1736 $this->importExtFromRep($extKey, $version, 'L');
1737 $newExtList = $this->addExtToList($extKey, $inst_list);
1738 if ($newExtList!=-1) {
1739 $this->writeNewExtensionList($newExtList);
1740 $this->refreshGlobalExtList();
1741 $this->forceDBupdates($extKey, $inst_list[$extKey]);
1742 $this->installTranslationsForExtension($extKey, $this->getMirrorURL());
1743 return array(true, 'Extension has been imported from repository and loaded.');
1744 } else {
1745 return array(false, 'Extension is in repository, but could not be loaded.');
1746 }
1747 } else {
1748 return array(false, 'Extension not available in repository');
1749 }
1750 }
1751
1752 function refreshGlobalExtList() {
1753 global $TYPO3_LOADED_EXT;
1754
1755 $TYPO3_LOADED_EXT = t3lib_extMgm::typo3_loadExtensions();
1756 if ($TYPO3_LOADED_EXT['_CACHEFILE']) {
1757 require(PATH_typo3conf.$TYPO3_LOADED_EXT['_CACHEFILE'].'_ext_localconf.php');
1758 }
1759 return;
1760
1761 $GLOBALS['TYPO3_LOADED_EXT'] = t3lib_extMgm::typo3_loadExtensions();
1762 if ($TYPO3_LOADED_EXT['_CACHEFILE']) {
1763 require(PATH_typo3conf.$TYPO3_LOADED_EXT['_CACHEFILE'].'_ext_localconf.php');
1764 } else {
1765 $temp_TYPO3_LOADED_EXT = $TYPO3_LOADED_EXT;
1766 reset($temp_TYPO3_LOADED_EXT);
1767 while(list($_EXTKEY,$temp_lEDat)=each($temp_TYPO3_LOADED_EXT)) {
1768 if (is_array($temp_lEDat) && $temp_lEDat['ext_localconf.php']) {
1769 $_EXTCONF = $TYPO3_CONF_VARS['EXT']['extConf'][$_EXTKEY];
1770 require($temp_lEDat['ext_localconf.php']);
1771 }
1772 }
1773 }
1774 }
1775
1776
1777 /**
1778 * Imports an extensions from the online repository
1779 * NOTICE: in version 4.0 this changed from "importExtFromRep_old($extRepUid,$loc,$uploadFlag=0,$directInput='',$recentTranslations=0,$incManual=0,$dontDelete=0)"
1780 *
1781 * @param string Extension key
1782 * @param string Version
1783 * @param string Install scope: "L" or "G" or "S"
1784 * @param boolean If true, extension is uploaded as file
1785 * @param boolean If true, extension directory+files will not be deleted before writing the new ones. That way custom files stored in the extension folder will be kept.
1786 * @param array Direct input array (like from kickstarter)
1787 * @return string Return false on success, returns error message if error.
1788 */
1789 function importExtFromRep($extKey,$version,$loc,$uploadFlag=0,$dontDelete=0,$directInput='') {
1790
1791 $uploadSucceed = false;
1792 $uploadedTempFile = '';
1793 if (is_array($directInput)) {
1794 $fetchData = array($directInput,'');
1795 $loc = ($loc==='G'||$loc==='S') ? $loc : 'L';
1796 } elseif ($uploadFlag) {
1797 if (($uploadedTempFile = $this->CMD['alreadyUploaded']) || $_FILES['upload_ext_file']['tmp_name']) {
1798
1799 // Read uploaded file:
1800 if (!$uploadedTempFile) {
1801 if (!is_uploaded_file($_FILES['upload_ext_file']['tmp_name'])) {
1802 t3lib_div::sysLog('Possible file upload attack: '.$_FILES['upload_ext_file']['tmp_name'], 'Extension Manager', 3);
1803
1804 return 'File was not uploaded?!?';
1805 }
1806
1807 $uploadedTempFile = t3lib_div::upload_to_tempfile($_FILES['upload_ext_file']['tmp_name']);
1808 }
1809 $fileContent = t3lib_div::getUrl($uploadedTempFile);
1810
1811 if (!$fileContent) return 'File is empty!';
1812
1813 // Decode file data:
1814 $fetchData = $this->terConnection->decodeExchangeData($fileContent);
1815
1816 if (is_array($fetchData)) {
1817 $extKey = $fetchData[0]['extKey'];
1818 if ($extKey) {
1819 if (!$this->CMD['uploadOverwrite']) {
1820 $loc = ($loc==='G'||$loc==='S') ? $loc : 'L';
1821 $comingExtPath = PATH_site.$this->typePaths[$loc].$extKey.'/';
1822 if (@is_dir($comingExtPath)) {
1823 return 'Extension was already present in "'.$comingExtPath.'" - and the overwrite flag was not set! So nothing done...';
1824 } // ... else go on, install...
1825 } // ... else go on, install...
1826 } else return 'No extension key in file. Strange...';
1827 } else return 'Wrong file format. No data recognized, '.$fetchData;
1828 } else return 'No file uploaded! Probably the file was too large for PHPs internal limit for uploadable files.';
1829 } else {
1830 $this->xmlhandler->searchExtensionsXML($extKey, '', '', true, true);
1831
1832 // Fetch extension from TER:
1833 if(!strlen($version)) {
1834 $versions = array_keys($this->xmlhandler->extensionsXML[$extKey]['versions']);
1835 $version = end($versions);
1836 }
1837 $fetchData = $this->terConnection->fetchExtension($extKey, $version, $this->xmlhandler->extensionsXML[$extKey]['versions'][$version]['t3xfilemd5'], $this->getMirrorURL());
1838 }
1839
1840 // At this point the extension data should be present; so we want to write it to disc:
1841 if ($this->importAsType($loc)) {
1842 if (is_array($fetchData)) { // There was some data successfully transferred
1843 if ($fetchData[0]['extKey'] && is_array($fetchData[0]['FILES'])) {
1844 $extKey = $fetchData[0]['extKey'];
1845 if(!isset($fetchData[0]['EM_CONF']['constraints'])) $fetchData[0]['EM_CONF']['constraints'] = $this->xmlhandler->extensionsXML[$extKey]['versions'][$version]['dependencies'];
1846 $EM_CONF = $this->fixEMCONF($fetchData[0]['EM_CONF']);
1847 if (!$EM_CONF['lockType'] || !strcmp($EM_CONF['lockType'],$loc)) {
1848 // check dependencies, act accordingly if ext is loaded
1849 list($instExtInfo,)=$this->getInstalledExtensions();
1850 $depStatus = $this->checkDependencies($extKey, $EM_CONF, $instExtInfo);
1851 if(t3lib_extMgm::isLoaded($extKey) && !$depStatus['returnCode']) {
1852 $this->content .= $depStatus['html'];
1853 if ($uploadedTempFile) {
1854 $this->content .= '<input type="hidden" name="CMD[alreadyUploaded]" value="'.$uploadedTempFile.'" />';
1855 }
1856 } else {
1857 $res = $this->clearAndMakeExtensionDir($fetchData[0],$loc,$dontDelete);
1858 if (is_array($res)) {
1859 $extDirPath = trim($res[0]);
1860 if ($extDirPath && @is_dir($extDirPath) && substr($extDirPath,-1)=='/') {
1861
1862 $emConfFile = $this->construct_ext_emconf_file($extKey,$EM_CONF);
1863 $dirs = $this->extractDirsFromFileList(array_keys($fetchData[0]['FILES']));
1864
1865 $res = $this->createDirsInPath($dirs,$extDirPath);
1866 if (!$res) {
1867 $writeFiles = $fetchData[0]['FILES'];
1868 $writeFiles['ext_emconf.php']['content'] = $emConfFile;
1869 $writeFiles['ext_emconf.php']['content_md5'] = md5($emConfFile);
1870
1871 // Write files:
1872 foreach($writeFiles as $theFile => $fileData) {
1873 t3lib_div::writeFile($extDirPath.$theFile,$fileData['content']);
1874 if (!@is_file($extDirPath.$theFile)) {
1875 $content.='Error: File "'.$extDirPath.$theFile.'" could not be created!!!<br />';
1876 } elseif (md5(t3lib_div::getUrl($extDirPath.$theFile)) != $fileData['content_md5']) {
1877 $content.='Error: File "'.$extDirPath.$theFile.'" MD5 was different from the original files MD5 - so the file is corrupted!<br />';
1878 }
1879 }
1880
1881 // No content, no errors. Create success output here:
1882 if (!$content) {
1883 $content='SUCCESS: '.$extDirPath.'<br />';
1884
1885 $uploadSucceed = true;
1886
1887 // Fix TYPO3_MOD_PATH for backend modules in extension:
1888 $modules = t3lib_div::trimExplode(',',$EM_CONF['module'],1);
1889 if (count($modules)) {
1890 foreach($modules as $mD) {
1891 $confFileName = $extDirPath.$mD.'/conf.php';
1892 if (@is_file($confFileName)) {
1893 $content.= $this->writeTYPO3_MOD_PATH($confFileName,$loc,$extKey.'/'.$mD.'/').'<br />';
1894 } else $content.='Error: Couldn\'t find "'.$confFileName.'"<br />';
1895 }
1896 }
1897 // NOTICE: I used two hours trying to find out why a script, ext_emconf.php, written twice and in between included by PHP did not update correct the second time. Probably something with PHP-A cache and mtime-stamps.
1898 // But this order of the code works.... (using the empty Array with type, EMCONF and files hereunder).
1899
1900 // Writing to ext_emconf.php:
1901 $sEMD5A = $this->serverExtensionMD5Array($extKey,array('type' => $loc, 'EM_CONF' => array(), 'files' => array()));
1902 $EM_CONF['_md5_values_when_last_written'] = serialize($sEMD5A);
1903 $emConfFile = $this->construct_ext_emconf_file($extKey,$EM_CONF);
1904 t3lib_div::writeFile($extDirPath.'ext_emconf.php',$emConfFile);
1905
1906 $content.='ext_emconf.php: '.$extDirPath.'ext_emconf.php<br />';
1907 $content.='Type: '.$loc.'<br />';
1908
1909 // Remove cache files:
1910 if (t3lib_extMgm::isLoaded($extKey)) {
1911 if ($this->removeCacheFiles()) {
1912 $content.='Cache-files are removed and will be re-written upon next hit<br />';
1913 }
1914
1915 list($new_list)=$this->getInstalledExtensions();
1916 $content.=$this->updatesForm($extKey,$new_list[$extKey],1,'index.php?CMD[showExt]='.$extKey.'&SET[singleDetails]=info');
1917 }
1918
1919 // Install / Uninstall:
1920 if(!$this->CMD['standAlone']) {
1921 $content.='<h3>Install / Uninstall Extension:</h3>';
1922 $content.= $new_list[$extKey] ?
1923 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->removeButton().' Uninstall extension</a>' :
1924 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[load]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->installButton().' Install extension</a>';
1925 } else {
1926 $content = 'Extension has been imported.<br /><br /><a href="javascript:opener.top.content.document.forms[0].submit();window.close();">Close window and recheck dependencies</a>';
1927 }
1928
1929 }
1930 } else $content = $res;
1931 } else $content = 'Error: The extension path "'.$extDirPath.'" was different than expected...';
1932 } else $content = $res;
1933 }
1934 } else $content = 'Error: The extension can only be installed in the path '.$this->typePaths[$EM_CONF['lockType']].' (lockType='.$EM_CONF['lockType'].')';
1935 } else $content = 'Error: No extension key!!! Why? - nobody knows... (Or no files in the file-array...)';
1936 } else $content = 'Error: The datatransfer did not succeed. '.$fetchData;
1937 } else $content = 'Error: Installation is not allowed in this path ('.$this->typePaths[$loc].')';
1938
1939 $this->content.=$this->doc->section('Extension import results',$content,0,1);
1940
1941 if ($uploadSucceed && $uploadedTempFile) {
1942 t3lib_div::unlink_tempfile($uploadedTempFile);
1943 }
1944
1945 return false;
1946 }
1947
1948 /**
1949 * Display extensions details.
1950 *
1951 * @param string Extension key
1952 * @return void Writes content to $this->content
1953 */
1954 function showExtDetails($extKey) {
1955 global $TYPO3_LOADED_EXT;
1956
1957 list($list,)=$this->getInstalledExtensions();
1958 $absPath = $this->getExtPath($extKey,$list[$extKey]['type']);
1959
1960 // Check updateModule:
1961 if (isset($list[$extKey]) && @is_file($absPath.'class.ext_update.php')) {
1962 require_once($absPath.'class.ext_update.php');
1963 $updateObj = new ext_update;
1964 if (!$updateObj->access()) {
1965 unset($this->MOD_MENU['singleDetails']['updateModule']);
1966 }
1967 } else {
1968 unset($this->MOD_MENU['singleDetails']['updateModule']);
1969 }
1970
1971 if($this->CMD['doDelete']) {
1972 $this->MOD_MENU['singleDetails'] = array();
1973 }
1974
1975 // Function menu here:
1976 if(!$this->CMD['standAlone'] && !t3lib_div::_GP('standAlone')) {
1977 $content = 'Extension:&nbsp;<strong>' . $this->extensionTitleIconHeader($extKey, $list[$extKey]) . '</strong> (' . htmlspecialchars($extKey) . ')';
1978 $this->content.= $this->doc->section('', $content);
1979 }
1980
1981 // Show extension details:
1982 if ($list[$extKey]) {
1983
1984 // Checking if a command for install/uninstall is executed:
1985 if (($this->CMD['remove'] || $this->CMD['load']) && !in_array($extKey,$this->requiredExt)) {
1986
1987 // Install / Uninstall extension here:
1988 if (t3lib_extMgm::isLocalconfWritable()) {
1989 // Check dependencies:
1990 $depStatus = $this->checkDependencies($extKey, $list[$extKey]['EM_CONF'], $list);
1991 if(!$this->CMD['remove'] && !$depStatus['returnCode']) {
1992 $this->content .= $depStatus['html'];
1993 $newExtList = -1;
1994 } elseif ($this->CMD['remove']) {
1995 $newExtList = $this->removeExtFromList($extKey,$list);
1996 } else {
1997 $newExtList = $this->addExtToList($extKey,$list);
1998 }
1999
2000 // Success-installation:
2001 if ($newExtList!=-1) {
2002 $updates = '';
2003 if ($this->CMD['load']) {
2004 if($_SERVER['REQUEST_METHOD'] == 'POST') {
2005 $script = t3lib_div::linkThisScript(array('CMD[showExt]' => $extKey, 'CMD[load]' => 1, 'CMD[clrCmd]' => $this->CMD['clrCmd'], 'SET[singleDetails]' => 'info'));
2006 } else {
2007 $script = '';
2008 }
2009 if($this->CMD['standAlone']) {
2010 $standaloneUpdates = '<input type="hidden" name="standAlone" value="1" />';
2011 }
2012 $depsolver = t3lib_div::_POST('depsolver');
2013 if(is_array($depsolver['ignore'])) {
2014 foreach($depsolver['ignore'] as $depK => $depV) {
2015 $dependencyUpdates .= '<input type="hidden" name="depsolver[ignore]['.$depK.']" value="1" />';
2016 }
2017 }
2018 $updatesForm = $this->updatesForm($extKey,$list[$extKey],1,$script, $dependencyUpdates.$standaloneUpdates.'<input type="hidden" name="_do_install" value="1" /><input type="hidden" name="_clrCmd" value="'.$this->CMD['clrCmd'].'" />');
2019 if ($updatesForm) {
2020 $updates = 'Before the extension can be installed the database needs to be updated with new tables or fields. Please select which operations to perform:'.$updatesForm;
2021 $this->content.=$this->doc->section('Installing '.$this->extensionTitleIconHeader($extKey,$list[$extKey]).strtoupper(': Database needs to be updated'),$updates,1,1,1,1);
2022 }
2023 } elseif ($this->CMD['remove']) {
2024 $updates.= $this->checkClearCache($list[$extKey]);
2025 if ($updates) {
2026 $updates = '
2027 <form action="'.t3lib_div::linkThisScript().'" method="post">'.$updates.'
2028 <br /><input type="submit" name="write" value="Remove extension" />
2029 <input type="hidden" name="_do_install" value="1" />
2030 <input type="hidden" name="_clrCmd" value="'.$this->CMD['clrCmd'].'" />
2031 <input type="hidden" name="standAlone" value="'.$this->CMD['standAlone'].'" />
2032 </form>';
2033 $this->content.=$this->doc->section('Removing '.$this->extensionTitleIconHeader($extKey,$list[$extKey]).strtoupper(': Database needs to be updated'),$updates,1,1,1,1);
2034 }
2035 }
2036 if (!$updates || t3lib_div::_GP('_do_install')) {
2037 $this->writeNewExtensionList($newExtList);
2038 $GLOBALS['BE_USER']->writelog(5,1,0,0,'Extension list has been changed, extension %s has been %s',array($extKey,($this->CMD['load']?'installed':'removed')));
2039 if ($this->CMD['clrCmd'] || t3lib_div::_GP('_clrCmd')) {
2040 if ($this->CMD['load'] && @is_file($absPath.'ext_conf_template.txt')) {
2041 $vA = array('CMD'=>Array('showExt'=>$extKey));
2042 } else {
2043 $vA = array('CMD'=>'');
2044 }
2045 } else {
2046 $vA = array('CMD'=>Array('showExt'=>$extKey));
2047 }
2048 if($this->CMD['standAlone'] || t3lib_div::_GP('standAlone')) {
2049 $this->content .= 'Extension has been '.($this->CMD['load'] ? 'installed' : 'removed').'.<br /><br /><a href="javascript:opener.top.content.document.forms[0].submit();window.close();">Close window and recheck dependencies</a>';
2050 } else {
2051 // Determine if new modules were installed:
2052 $techInfo = $this->makeDetailedExtensionAnalysis($extKey, $list[$extKey]);
2053 if (($this->CMD['load'] || $this->CMD['remove']) && is_array($techInfo['flags']) && in_array('Module', $techInfo['flags'], true)) {
2054 $vA['CMD']['refreshMenu'] = 1;
2055 }
2056 header('Location: '.t3lib_div::linkThisScript($vA));
2057 }
2058 }
2059 }
2060 } else {
2061 $this->content.=$this->doc->section('Installing '.$this->extensionTitleIconHeader($extKey,$list[$extKey]).strtoupper(': Write access error'),'typo3conf/localconf.php seems not to be writable, so the extension cannot be installed automatically!',1,1,2,1);
2062 }
2063
2064 } elseif ($this->CMD['downloadFile'] && !in_array($extKey,$this->requiredExt)) {
2065
2066 // Link for downloading extension has been clicked - deliver content stream:
2067 $dlFile = $this->CMD['downloadFile'];
2068 if (t3lib_div::isFirstPartOfStr($dlFile,PATH_site) && t3lib_div::isFirstPartOfStr($dlFile,$absPath) && @is_file($dlFile)) {
2069 $mimeType = 'application/octet-stream';
2070 Header('Content-Type: '.$mimeType);
2071 Header('Content-Disposition: attachment; filename='.basename($dlFile));
2072 echo t3lib_div::getUrl($dlFile);
2073 exit;
2074 } else die('Error while trying to download extension file...');
2075
2076 } elseif ($this->CMD['editFile'] && !in_array($extKey,$this->requiredExt)) {
2077
2078 // Editing extension file:
2079 $editFile = $this->CMD['editFile'];
2080 if (t3lib_div::isAllowedAbsPath($editFile) && t3lib_div::isFirstPartOfStr($editFile, $absPath)) {
2081
2082 $fI = t3lib_div::split_fileref($editFile);
2083 if (@is_file($editFile) && t3lib_div::inList($this->editTextExtensions,($fI['fileext']?$fI['fileext']:$fI['filebody']))) {
2084 if (filesize($editFile)<($this->kbMax*1024)) {
2085 $outCode = '<form action="index.php" method="post" name="editfileform">';
2086 $info = '';
2087 $submittedContent = t3lib_div::_POST('edit');
2088 $saveFlag = 0;
2089
2090 if(isset($submittedContent['file']) && !$GLOBALS['TYPO3_CONF_VARS']['EXT']['noEdit']) { // Check referer here?
2091 $oldFileContent = t3lib_div::getUrl($editFile);
2092 if($oldFileContent != $submittedContent['file']) {
2093 $oldMD5 = md5(str_replace(chr(13),'',$oldFileContent));
2094 $info.= 'MD5: <b>'.$oldMD5.'</b> (Previous File)<br />';
2095 t3lib_div::writeFile($editFile,$submittedContent['file']);
2096 $saveFlag = 1;
2097 } else {
2098 $info .= 'No changes to the file detected!<br />';
2099 }
2100 }
2101
2102 $fileContent = t3lib_div::getUrl($editFile);
2103
2104 $outCode.= 'File: <b>'.substr($editFile,strlen($absPath)).'</b> ('.t3lib_div::formatSize(filesize($editFile)).')<br />';
2105 $fileMD5 = md5(str_replace(chr(13),'',$fileContent));
2106 $info.= 'MD5: <b>'.$fileMD5.'</b> (Current File)<br />';
2107 if($saveFlag) {
2108 $saveMD5 = md5(str_replace(chr(13),'',$submittedContent['file']));
2109 $info.= 'MD5: <b>'.$saveMD5.'</b> (Submitted)<br />';
2110 if($fileMD5!=$saveMD5) $info .= $GLOBALS['TBE_TEMPLATE']->rfw('<br /><strong>Saving failed, the content was not correctly written to disk. Changes have been lost!</strong>').'<br />';
2111 else $info.= $GLOBALS['TBE_TEMPLATE']->rfw('<br /><strong>File saved.</strong>').'<br />';
2112 }
2113
2114 $outCode.= '<textarea name="edit[file]" rows="35" wrap="off"'.$this->doc->formWidthText(48,'width:98%;height:70%','off').' class="fixed-font enable-tab">'.t3lib_div::formatForTextarea($fileContent).'</textarea>';
2115 $outCode.= '<input type="hidden" name="edit[filename]" value="'.$editFile.'" />';
2116 $outCode.= '<input type="hidden" name="CMD[editFile]" value="'.htmlspecialchars($editFile).'" />';
2117 $outCode.= '<input type="hidden" name="CMD[showExt]" value="'.$extKey.'" />';
2118 $outCode.= $info;
2119
2120 if (!$GLOBALS['TYPO3_CONF_VARS']['EXT']['noEdit']) {
2121 $outCode.='<br /><input type="submit" name="save_file" value="Save file" />';
2122 } else $outCode.=$GLOBALS['TBE_TEMPLATE']->rfw('<br />[SAVING IS DISABLED - can be enabled by the $TYPO3_CONF_VARS[\'EXT\'][\'noEdit\']-flag] ');
2123
2124 $onClick = 'window.location.href=\'index.php?CMD[showExt]='.$extKey.'\';return false;';
2125 $outCode.='<input type="submit" name="cancel" value="Cancel" onclick="'.htmlspecialchars($onClick).'" /></form>';
2126
2127 $theOutput.=$this->doc->spacer(15);
2128 $theOutput.=$this->doc->section('Edit file:','',0,1);
2129 $theOutput.=$this->doc->sectionEnd().$outCode;
2130 $this->content.=$theOutput;
2131 } else {
2132 $theOutput.=$this->doc->spacer(15);
2133 $theOutput.=$this->doc->section('Filesize exceeded '.$this->kbMax.' Kbytes','Files larger than '.$this->kbMax.' KBytes are not allowed to be edited.');
2134 }
2135 }
2136 } else die('Fatal Edit error: File "' . htmlspecialchars($editFile) . '" was not inside the correct path of the TYPO3 Extension!');
2137 } else {
2138
2139 // MAIN:
2140 switch((string)$this->MOD_SETTINGS['singleDetails']) {
2141 case 'info':
2142 // Loaded / Not loaded:
2143 if (!in_array($extKey,$this->requiredExt)) {
2144 if ($TYPO3_LOADED_EXT[$extKey]) {
2145 $content = '<strong>The extension is installed (loaded and running)!</strong><br />'.
2146 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1').'">Click here to remove the extension: '.$this->removeButton().'</a>';
2147 } else {
2148 $content = 'The extension is <strong>not</strong> installed yet.<br />'.
2149 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[load]=1').'">Click here to install the extension: '.$this->installButton().'</a>';
2150 }
2151 } else {
2152 $content = 'This extension is entered in the TYPO3_CONF_VARS[SYS][requiredExt] list and is therefore always loaded.';
2153 }
2154 $this->content.=$this->doc->spacer(10);
2155 $this->content.=$this->doc->section('Active status:',$content,0,1);
2156
2157 if (t3lib_extMgm::isLoaded($extKey)) {
2158 $updates=$this->updatesForm($extKey,$list[$extKey]);
2159 if ($updates) {
2160 $this->content.=$this->doc->spacer(10);
2161 $this->content.=$this->doc->section('Update needed:',$updates.'<br /><br />Notice: "Static data" may not <em>need</em> to be updated. You will only have to import static data each time you upgrade the extension.',0,1);
2162 }
2163 }
2164
2165 // Config:
2166 if (@is_file($absPath.'ext_conf_template.txt')) {
2167 $this->content.=$this->doc->spacer(10);
2168 $this->content.=$this->doc->section('Configuration:','(<em>Notice: You may need to clear the cache after configuration of the extension. This is required if the extension adds TypoScript depending on these settings.</em>)<br /><br />',0,1);
2169
2170 $this->tsStyleConfigForm($extKey, $list[$extKey]);
2171 }
2172
2173 // Show details:
2174 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'info', $GLOBALS['BACK_PATH'],'|<br/>');
2175 $content.= $this->extInformationArray($extKey,$list[$extKey]);
2176
2177 $this->content.=$this->doc->spacer(10);
2178 $this->content.=$this->doc->section('Details:',$content,0,1);
2179 break;
2180 case 'upload':
2181 $em = t3lib_div::_POST('em');
2182 if($em['action'] == 'doUpload') {
2183 $em['extKey'] = $extKey;
2184 $em['extInfo'] = $list[$extKey];
2185 $content = $this->uploadExtensionToTER($em);
2186 $content .= $this->doc->spacer(10);
2187 // Must reload this, because EM_CONF information has been updated!
2188 list($list,)=$this->getInstalledExtensions();
2189 } else {
2190 // CSH:
2191 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'upload', $GLOBALS['BACK_PATH'],'|<br/>');
2192
2193 // Upload:
2194 if (substr($extKey,0,5)!='user_') {
2195 $content.= $this->getRepositoryUploadForm($extKey,$list[$extKey]);
2196 $eC=0;
2197 } else {
2198 $content.='The extensions has an extension key prefixed "user_" which indicates that it is a user-defined extension with no official unique identification. Therefore it cannot be uploaded.';
2199 $eC=2;
2200 }
2201 if (!$this->fe_user['username']) {
2202 $content.= '<br /><br /><img src="'.$GLOBALS['BACK_PATH'].'gfx/icon_note.gif" width="18" height="16" align="top" alt="" />You have not configured a default username/password yet. <a href="index.php?SET[function]=3">Go to "Settings"</a> if you want to do that.<br />';
2203 }
2204 }
2205 $this->content.=$this->doc->section('Upload extension to repository',$content,0,1,$eC);
2206 break;
2207 case 'backup':
2208 if($this->CMD['doDelete']) {
2209 $content = $this->extDelete($extKey,$list[$extKey]);
2210 $this->content.=$this->doc->section('Delete',$content,0,1);
2211 } else {
2212 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'backup_delete', $GLOBALS['BACK_PATH'],'|<br/>');
2213 $content.= $this->extBackup($extKey,$list[$extKey]);
2214 $this->content.=$this->doc->section('Backup',$content,0,1);
2215
2216 $content = $this->extDelete($extKey,$list[$extKey]);
2217 $this->content.=$this->doc->section('Delete',$content,0,1);
2218
2219 $content = $this->extUpdateEMCONF($extKey,$list[$extKey]);
2220 $this->content.=$this->doc->section('Update EM_CONF',$content,0,1);
2221 }
2222 break;
2223 case 'dump':
2224 $this->extDumpTables($extKey,$list[$extKey]);
2225 break;
2226 case 'edit':
2227 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'editfiles', $GLOBALS['BACK_PATH'],'|<br/>');
2228 $content.= $this->getFileListOfExtension($extKey,$list[$extKey]);
2229
2230 $this->content.=$this->doc->section('Extension files',$content,0,1);
2231 break;
2232 case 'updateModule':
2233 $this->content.=$this->doc->section('Update:',is_object($updateObj) ? $updateObj->main() : 'No update object',0,1);
2234 break;
2235 default:
2236 $this->extObjContent();
2237 break;
2238 }
2239 }
2240 }
2241 }
2242
2243 /**
2244 * Outputs a screen from where you can install multiple extensions in one go
2245 * This can be called from external modules with "...index.php?CMD[requestInstallExtensions]=
2246 *
2247 * @param string Comma list of extension keys to install. Renders a screen with checkboxes for all extensions not already imported or installed
2248 * @return void
2249 */
2250 function requestInstallExtensions($extList) {
2251
2252 // Return URL:
2253 $returnUrl = t3lib_div::_GP('returnUrl');
2254 $installOrImportExtension = t3lib_div::_POST('installOrImportExtension');
2255
2256 // Extension List:
2257 $extArray = explode(',',$extList);
2258 $outputRow = array();
2259 $outputRow[] = '
2260 <tr class="bgColor5 tableheader">
2261 <td>Install/Import:</td>
2262 <td>Extension Key:</td>
2263 </tr>
2264 ';
2265
2266 foreach($extArray as $extKey) {
2267
2268 // Check for the request:
2269 if ($installOrImportExtension[$extKey]) {
2270 $this->installExtension($extKey);
2271 }
2272
2273 // Display:
2274 if (!t3lib_extMgm::isLoaded($extKey)) {
2275 $outputRow[] = '
2276 <tr class="bgColor4">
2277 <td><input type="checkbox" name="'.htmlspecialchars('installOrImportExtension['.$extKey.']').'" value="1" checked="checked" id="check_'.$extKey.'" /></td>
2278 <td><label for="check_'.$extKey.'">'.htmlspecialchars($extKey).'</label></td>
2279 </tr>
2280 ';
2281 }
2282 }
2283
2284 if (count($outputRow)>1 || !$returnUrl) {
2285 $content = '
2286 <!-- ending page form ... -->
2287 <form action="'.htmlspecialchars(t3lib_div::getIndpEnv('REQUEST_URI')).'" method="post">
2288 <table border="0" cellpadding="1" cellspacing="1">'.implode('',$outputRow).'</table>
2289 <input type="submit" name="_" value="Import and Install selected" />
2290 </form>';
2291
2292 if ($returnUrl) {
2293 $content.= '
2294 <br/>
2295 <br/>
2296 <a href="'.htmlspecialchars($returnUrl).'">Return</a>
2297 ';
2298 }
2299
2300 $this->content.= $this->doc->section('Import/Install Extensions:',$content,0,1);
2301 } else {
2302 header('Location: '.t3lib_div::locationHeaderUrl($returnUrl));
2303 }
2304 }
2305
2306
2307
2308
2309
2310
2311
2312
2313 /***********************************
2314 *
2315 * Application Sub-functions (HTML parts)
2316 *
2317 **********************************/
2318
2319 /**
2320 * Creates a form for an extension which contains all options for configuration, updates of database, clearing of cache etc.
2321 * This form is shown when
2322 *
2323 * @param string Extension key
2324 * @param array Extension information array
2325 * @param boolean If set, the form will ONLY show if fields/tables should be updated (suppressing forms like general configuration and cache clearing).
2326 * @param string Alternative action=""-script
2327 * @param string HTML: Additional form fields
2328 * @return string HTML
2329 */
2330 function updatesForm($extKey,$extInfo,$notSilent=0,$script='',$addFields='') {
2331 $script = $script ? $script : t3lib_div::linkThisScript();
2332 $updates.= $this->checkDBupdates($extKey,$extInfo);
2333 $uCache = $this->checkClearCache($extInfo);
2334 if ($notSilent) $updates.= $uCache;
2335 $updates.= $this->checkUploadFolder($extKey,$extInfo);
2336
2337 $absPath = $this->getExtPath($extKey, $extInfo['type']);
2338 if ($notSilent && @is_file($absPath.'ext_conf_template.txt')) {
2339 $configForm = $this->tsStyleConfigForm($extKey, $extInfo, 1, $script, $updates.$addFields.'<br />');
2340 }
2341
2342 if ($updates || $configForm) {
2343 if ($configForm) {
2344 $updates = '</form>'.$configForm.'<form>';
2345 } else {
2346 $updates = '</form><form action="'.htmlspecialchars($script).'" method="post">'.$updates.$addFields.'
2347 <br /><input type="submit" name="write" value="Make updates" />
2348 ';
2349 }
2350 }
2351
2352 return $updates;
2353 }
2354
2355 /**
2356 * Creates view for dumping static tables and table/fields structures...
2357 *
2358 * @param string Extension key
2359 * @param array Extension information array
2360 * @return void
2361 */
2362 function extDumpTables($extKey,$extInfo) {
2363
2364 // Get dbInfo which holds the structure known from the tables.sql file
2365 $techInfo = $this->makeDetailedExtensionAnalysis($extKey,$extInfo);
2366 $absPath = $this->getExtPath($extKey,$extInfo['type']);
2367
2368 // Static tables:
2369 if (is_array($techInfo['static'])) {
2370 if ($this->CMD['writeSTATICdump']) { // Writing static dump:
2371 $writeFile = $absPath.'ext_tables_static+adt.sql';
2372 if (@is_file($writeFile)) {
2373 $dump_static = $this->dumpStaticTables(implode(',',$techInfo['static']));
2374 t3lib_div::writeFile($writeFile,$dump_static);
2375 $this->content.=$this->doc->section('Table and field structure required',t3lib_div::formatSize(strlen($dump_static)).'bytes written to '.substr($writeFile,strlen(PATH_site)),0,1);
2376 }
2377 } else { // Showing info about what tables to dump - and giving the link to execute it.
2378 $msg = 'Dumping table content for static tables:<br />';
2379 $msg.= '<br />'.implode('<br />',$techInfo['static']).'<br />';
2380
2381 // ... then feed that to this function which will make new CREATE statements of the same fields but based on the current database content.
2382 $this->content.=$this->doc->section('Static tables',$msg.'<hr /><strong><a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[writeSTATICdump]=1').'">Write current static table contents to ext_tables_static+adt.sql now!</a></strong>',0,1);
2383 $this->content.=$this->doc->spacer(20);
2384 }
2385 }
2386
2387 // Table and field definitions:
2388 if (is_array($techInfo['dump_tf'])) {
2389 $dump_tf_array = $this->getTableAndFieldStructure($techInfo['dump_tf']);
2390 $dump_tf = $this->dumpTableAndFieldStructure($dump_tf_array);
2391 if ($this->CMD['writeTFdump']) {
2392 $writeFile = $absPath.'ext_tables.sql';
2393 if (@is_file($writeFile)) {
2394 t3lib_div::writeFile($writeFile,$dump_tf);
2395 $this->content.=$this->doc->section('Table and field structure required',t3lib_div::formatSize(strlen($dump_tf)).'bytes written to '.substr($writeFile,strlen(PATH_site)),0,1);
2396 }
2397 } else {
2398 $msg = 'Dumping current database structure for:<br />';
2399 if (is_array($techInfo['tables'])) {
2400 $msg.= '<br /><strong>Tables:</strong><br />'.implode('<br />',$techInfo['tables']).'<br />';
2401 }
2402 if (is_array($techInfo['fields'])) {
2403 $msg.= '<br /><strong>Solo-fields:</strong><br />'.implode('<br />',$techInfo['fields']).'<br />';
2404 }
2405
2406 // ... then feed that to this function which will make new CREATE statements of the same fields but based on the current database content.
2407 $this->content.=$this->doc->section('Table and field structure required',$msg.'<hr /><strong><a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[writeTFdump]=1').'">Write this dump to ext_tables.sql now!</a></strong><hr />
2408 <pre>'.htmlspecialchars($dump_tf).'</pre>',0,1);
2409
2410
2411 $details = ' This dump is based on two factors:<br />
2412 <ul>
2413 <li>1) All tablenames in ext_tables.sql which are <em>not</em> found in the "modify_tables" list in ext_emconf.php are dumped with the current database structure.</li>
2414 <li>2) For any tablenames which <em>are</em> listed in "modify_tables" all fields and keys found for the table in ext_tables.sql will be re-dumped with the fresh equalents from the database.</li>
2415 </ul>
2416 Bottomline is: Whole tables are dumped from database with no regard to which fields and keys are defined in ext_tables.sql. But for tables which are only modified, any NEW fields added to the database must in some form or the other exist in the ext_tables.sql file as well.<br />';
2417 $this->content.=$this->doc->section('',$details);
2418 }
2419 }
2420 }
2421
2422 /**
2423 * Returns file-listing of an extension
2424 *
2425 * @param string Extension key
2426 * @param array Extension information array
2427 * @return string HTML table.
2428 */
2429 function getFileListOfExtension($extKey,$conf) {
2430 $content = '';
2431 $extPath = $this->getExtPath($extKey,$conf['type']);
2432
2433 if ($extPath) {
2434 // Read files:
2435 $fileArr = array();
2436 $fileArr = t3lib_div::getAllFilesAndFoldersInPath($fileArr,$extPath,'',0,99,$this->excludeForPackaging);
2437
2438 // Start table:
2439 $lines = array();
2440 $totalSize = 0;
2441
2442 // Header:
2443 $lines[] = '
2444 <tr class="bgColor5">
2445 <td>File:</td>
2446 <td>Size:</td>
2447 <td>Edit:</td>
2448 </tr>';
2449
2450 foreach($fileArr as $file) {
2451 $fI = t3lib_div::split_fileref($file);
2452 $lines[] = '
2453 <tr class="bgColor4">
2454 <td><a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[downloadFile]='.rawurlencode($file)).'" title="Download...">'.substr($file,strlen($extPath)).'</a></td>
2455 <td>'.t3lib_div::formatSize(filesize($file)).'</td>
2456 <td>'.(!in_array($extKey,$this->requiredExt)&&t3lib_div::inList($this->editTextExtensions,($fI['fileext']?$fI['fileext']:$fI['filebody']))?'<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[editFile]='.rawurlencode($file)).'">Edit file</a>':'').'</td>
2457 </tr>';
2458 $totalSize+=filesize($file);
2459 }
2460
2461 $lines[] = '
2462 <tr class="bgColor6">
2463 <td><strong>Total:</strong></td>
2464 <td><strong>'.t3lib_div::formatSize($totalSize).'</strong></td>
2465 <td>&nbsp;</td>
2466 </tr>';
2467
2468 $content = '
2469 Path: '.$extPath.'<br /><br />
2470 <table border="0" cellpadding="1" cellspacing="2">'.implode('',$lines).'</table>';
2471 }
2472
2473 return $content;
2474 }
2475
2476 /**
2477 * Delete extension from the file system
2478 *
2479 * @param string Extension key
2480 * @param array Extension info array
2481 * @return string Returns message string about the status of the operation
2482 */
2483 function extDelete($extKey,$extInfo) {
2484 $absPath = $this->getExtPath($extKey,$extInfo['type']);
2485 if (t3lib_extMgm::isLoaded($extKey)) {
2486 return 'This extension is currently installed (loaded and active) and so cannot be deleted!';
2487 } elseif (!$this->deleteAsType($extInfo['type'])) {
2488 return 'You cannot delete (and install/update) extensions in the '.$this->typeLabels[$extInfo['type']].' scope.';
2489 } elseif (t3lib_div::inList('G,L',$extInfo['type'])) {
2490 if ($this->CMD['doDelete'] && !strcmp($absPath,$this->CMD['absPath'])) {
2491 $res = $this->removeExtDirectory($absPath);
2492 if ($res) {
2493 return 'ERROR: Could not remove extension directory "'.$absPath.'". Had the following errors:<br /><br />'.
2494 nl2br($res);
2495 } else {
2496 return 'Removed extension in path "'.$absPath.'"!';
2497 }
2498 } else {
2499 $onClick = "if (confirm('Are you sure you want to delete this extension from the server?')) {window.location.href='index.php?CMD[showExt]=".$extKey.'&CMD[doDelete]=1&CMD[absPath]='.rawurlencode($absPath)."';}";
2500 $content.= '<a href="#" onclick="'.htmlspecialchars($onClick).' return false;"><strong>DELETE EXTENSION FROM SERVER</strong> (in the "'.$this->typeLabels[$extInfo['type']].'" location "'.substr($absPath,strlen(PATH_site)).'")!</a>';
2501 $content.= '<br /><br />(Maybe you should make a backup first, see above.)';
2502 return $content;
2503 }
2504 } else return 'Extension is not a global or local extension and cannot be removed.';
2505 }
2506
2507 /**
2508 * Update extension EM_CONF...
2509 *
2510 * @param string Extension key
2511 * @param array Extension information array
2512 * @return string HTML content.
2513 */
2514 function extUpdateEMCONF($extKey,$extInfo) {
2515 $absPath = $this->getExtPath($extKey,$extInfo['type']);
2516 if ($this->CMD['doUpdateEMCONF']) {
2517 return $this->updateLocalEM_CONF($extKey,$extInfo);
2518 } else {
2519 $onClick = "if (confirm('Are you sure you want to update EM_CONF?')) {window.location.href='index.php?CMD[showExt]=".$extKey."&CMD[doUpdateEMCONF]=1';}";
2520 $content.= '<a href="#" onclick="'.htmlspecialchars($onClick).' return false;"><strong>Update extension EM_CONF file</strong> (in the "'.$this->typeLabels[$extInfo['type']].'" location "'.substr($absPath,strlen(PATH_site)).'")!</a>';
2521 $content.= '<br /><br />If files are changed, added or removed to an extension this is normally detected and displayed so you know that this extension has been locally altered and may need to be uploaded or at least not overridden.<br />
2522 Updating this file will first of all reset this registration.';
2523 return $content;
2524 }
2525 }
2526
2527 /**
2528 * Download extension as file / make backup
2529 *
2530 * @param string Extension key
2531 * @param array Extension information array
2532 * @return string HTML content
2533 */
2534 function extBackup($extKey,$extInfo) {
2535 $uArr = $this->makeUploadArray($extKey,$extInfo);
2536 if (is_array($uArr)) {
2537 $backUpData = $this->terConnection->makeUploadDataFromArray($uArr);
2538 $filename = 'T3X_'.$extKey.'-'.str_replace('.','_',$extInfo['EM_CONF']['version']).'-z-'.date('YmdHi').'.t3x';
2539 if (intval($this->CMD['doBackup'])==1) {
2540 header('Content-Type: application/octet-stream');
2541 header('Content-Disposition: attachment; filename='.$filename);
2542 echo $backUpData;
2543 exit;
2544 } elseif ($this->CMD['dumpTables']) {
2545 $filename='T3X_'.$extKey;
2546 $cTables = count(explode(',',$this->CMD['dumpTables']));
2547 if ($cTables>1) {
2548 $filename.='-'.$cTables.'tables';
2549 } else {
2550 $filename.='-'.$this->CMD['dumpTables'];
2551 }
2552 $filename.='+adt.sql';
2553
2554 header('Content-Type: application/octet-stream');
2555 header('Content-Disposition: attachment; filename='.$filename);
2556 echo $this->dumpStaticTables($this->CMD['dumpTables']);
2557 exit;
2558 } else {
2559 $techInfo = $this->makeDetailedExtensionAnalysis($extKey,$extInfo);
2560 $lines=array();
2561 $lines[]='<tr class="bgColor5"><td colspan="2"><strong>Make selection:</strong></td></tr>';
2562 $lines[]='<tr class="bgColor4"><td><strong>Extension files:</strong></td><td>'.
2563 '<a href="'.htmlspecialchars('index.php?CMD[doBackup]=1&CMD[showExt]='.$extKey).'">Download extension "'.$extKey.'" as a file</a><br />('.$filename.', '.t3lib_div::formatSize(strlen($backUpData)).', MD5: '.md5($backUpData).')<br /></td></tr>';
2564
2565 if (is_array($techInfo['tables'])) { $lines[]='<tr class="bgColor4"><td><strong>Data tables:</strong></td><td>'.$this->extBackup_dumpDataTablesLine($techInfo['tables'],$extKey).'</td></tr>'; }
2566 if (is_array($techInfo['static'])) { $lines[]='<tr class="bgColor4"><td><strong>Static tables:</strong></td><td>'.$this->extBackup_dumpDataTablesLine($techInfo['static'],$extKey).'</td></tr>'; }
2567
2568 $content = '<table border="0" cellpadding="2" cellspacing="2">'.implode('',$lines).'</table>';
2569 return $content;
2570 }
2571 } else die('Error...');
2572 }
2573
2574 /**
2575 * Link to dump of database tables
2576 *
2577 * @param string Extension key
2578 * @param array Extension information array
2579 * @return string HTML
2580 */
2581 function extBackup_dumpDataTablesLine($tablesArray,$extKey) {
2582 $tables = array();
2583 $tablesNA = array();
2584
2585 foreach($tablesArray as $tN) {
2586 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $tN, '');
2587 if (!$GLOBALS['TYPO3_DB']->sql_error()) {
2588 $row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
2589 $tables[$tN]='<tr><td>&nbsp;</td><td><a href="'.htmlspecialchars('index.php?CMD[dumpTables]='.rawurlencode($tN).'&CMD[showExt]='.$extKey).'" title="Dump table \''.$tN.'\'">'.$tN.'</a></td><td>&nbsp;&nbsp;&nbsp;</td><td>'.$row[0].' records</td></tr>';
2590 } else {
2591 $tablesNA[$tN]='<tr><td>&nbsp;</td><td>'.$tN.'</td><td>&nbsp;</td><td>Did not exist.</td></tr>';
2592 }
2593 }
2594 $label = '<table border="0" cellpadding="0" cellspacing="0">'.implode('',array_merge($tables,$tablesNA)).'</table>';// Candidate for t3lib_div::array_merge() if integer-keys will some day make trouble...
2595 if (count($tables)) {
2596 $label = '<a href="'.htmlspecialchars('index.php?CMD[dumpTables]='.rawurlencode(implode(',',array_keys($tables))).'&CMD[showExt]='.$extKey).'" title="Dump all existing tables.">Download all data from:</a><br /><br />'.$label;
2597 } else $label = 'Nothing to dump...<br /><br />'.$label;
2598 return $label;
2599 }
2600
2601 /**
2602 * Prints a table with extension information in it.
2603 *
2604 * @param string Extension key
2605 * @param array Extension information array
2606 * @param boolean If set, the information array shows information for a remote extension in TER, not a local one.
2607 * @return string HTML content.
2608 */
2609 function extInformationArray($extKey,$extInfo,$remote=0) {
2610 $lines=array();
2611 $lines[]='<tr class="bgColor5"><td colspan="2"><strong>General information:</strong></td>'.$this->helpCol('').'</tr>';
2612 $lines[]='<tr class="bgColor4"><td>Title:</td><td>'.$extInfo['EM_CONF']['_icon'].$extInfo['EM_CONF']['title'].'</td>'.$this->helpCol('title').'</tr>';
2613 $lines[]='<tr class="bgColor4"><td>Description:</td><td>'.nl2br(htmlspecialchars($extInfo['EM_CONF']['description'])).'</td>'.$this->helpCol('description').'</tr>';
2614 $lines[]='<tr class="bgColor4"><td>Author:</td><td>'.$this->wrapEmail($extInfo['EM_CONF']['author'].($extInfo['EM_CONF']['author_email'] ? ' <'.$extInfo['EM_CONF']['author_email'].'>' : ''),$extInfo['EM_CONF']['author_email']).($extInfo['EM_CONF']['author_company']?', '.$extInfo['EM_CONF']['author_company']:'').
2615 '</td>'.$this->helpCol('author').'</tr>';
2616
2617 $lines[]='<tr class="bgColor4"><td>Version:</td><td>'.$extInfo['EM_CONF']['version'].'</td>'.$this->helpCol('version').'</tr>';
2618 $lines[]='<tr class="bgColor4"><td>Category:</td><td>'.$this->categories[$extInfo['EM_CONF']['category']].'</td>'.$this->helpCol('category').'</tr>';
2619 $lines[]='<tr class="bgColor4"><td>State:</td><td>'.$this->states[$extInfo['EM_CONF']['state']].'</td>'.$this->helpCol('state').'</tr>';
2620 $lines[]='<tr class="bgColor4"><td>Shy?</td><td>'.($extInfo['EM_CONF']['shy']?'Yes':'').'</td>'.$this->helpCol('shy').'</tr>';
2621 $lines[]='<tr class="bgColor4"><td>Internal?</td><td>'.($extInfo['EM_CONF']['internal']?'Yes':'').'</td>'.$this->helpCol('internal').'</tr>';
2622
2623 $lines[]='<tr class="bgColor4"><td>Depends on:</td><td>'.$this->depToString($extInfo['EM_CONF']['constraints']).'</td>'.$this->helpCol('dependencies').'</tr>';
2624 $lines[]='<tr class="bgColor4"><td>Conflicts with:</td><td>'.$this->depToString($extInfo['EM_CONF']['constraints'],'conflicts').'</td>'.$this->helpCol('conflicts').'</tr>';
2625 $lines[]='<tr class="bgColor4"><td>Suggests:</td><td>'.$this->depToString($extInfo['EM_CONF']['constraints'],'suggests').'</td>'.$this->helpCol('suggests').'</tr>';
2626 if (!$remote) {
2627 $lines[]='<tr class="bgColor4"><td>Priority:</td><td>'.$extInfo['EM_CONF']['priority'].'</td>'.$this->helpCol('priority').'</tr>';
2628 $lines[]='<tr class="bgColor4"><td>Clear cache?</td><td>'.($extInfo['EM_CONF']['clearCacheOnLoad']?'Yes':'').'</td>'.$this->helpCol('clearCacheOnLoad').'</tr>';
2629 $lines[]='<tr class="bgColor4"><td>Includes modules:</td><td>'.$extInfo['EM_CONF']['module'].'</td>'.$this->helpCol('module').'</tr>';
2630 $lines[]='<tr class="bgColor4"><td>Lock Type?</td><td>'.($extInfo['EM_CONF']['lockType']?$extInfo['EM_CONF']['lockType']:'').'</td>'.$this->helpCol('lockType').'</tr>';
2631 $lines[]='<tr class="bgColor4"><td>Modifies tables:</td><td>'.$extInfo['EM_CONF']['modify_tables'].'</td>'.$this->helpCol('modify_tables').'</tr>';
2632
2633 // Installation status:
2634 $techInfo = $this->makeDetailedExtensionAnalysis($extKey,$extInfo,1);
2635 $lines[]='<tr><td>&nbsp;</td><td></td>'.$this->helpCol('').'</tr>';
2636 $lines[]='<tr class="bgColor5"><td colspan="2"><strong>Installation status:</strong></td>'.$this->helpCol('').'</tr>';
2637 $lines[]='<tr class="bgColor4"><td>Type of install:</td><td>'.$this->typeLabels[$extInfo['type']].' - <em>'.$this->typeDescr[$extInfo['type']].'</em></td>'.$this->helpCol('type').'</tr>';
2638 $lines[]='<tr class="bgColor4"><td>Double installs?</td><td>'.$this->extInformationArray_dbInst($extInfo['doubleInstall'],$extInfo['type']).'</td>'.$this->helpCol('doubleInstall').'</tr>';
2639 if (is_array($extInfo['files'])) {
2640 sort($extInfo['files']);
2641 $lines[]='<tr class="bgColor4"><td>Root files:</td><td>'.implode('<br />',$extInfo['files']).'</td>'.$this->helpCol('rootfiles').'</tr>';
2642 }
2643
2644 if ($techInfo['tables']||$techInfo['static']||$techInfo['fields']) {
2645 if (!$remote && t3lib_extMgm::isLoaded($extKey)) {
2646 $tableStatus = $GLOBALS['TBE_TEMPLATE']->rfw(($techInfo['tables_error']?'<strong>Table error!</strong><br />Probably one or more required fields/tables are missing in the database!':'').
2647 ($techInfo['static_error']?'<strong>Static table error!</strong><br />The static tables are missing or empty!':''));
2648 } else {
2649 $tableStatus = $techInfo['tables_error']||$techInfo['static_error'] ? 'The database will need to be updated when this extension is installed.' : 'All required tables are already in the database!';
2650 }
2651 }
2652
2653 $lines[]='<tr class="bgColor4"><td>Database requirements:</td><td>'.$this->extInformationArray_dbReq($techInfo,1).'</td>'.$this->helpCol('dbReq').'</tr>';
2654 $lines[]='<tr class="bgColor4"><td>Database status:</td><td>'.$tableStatus.'</td>'.$this->helpCol('dbStatus').'</tr>';
2655 $lines[]='<tr class="bgColor4"><td>Flags:</td><td>'.(is_array($techInfo['flags'])?implode('<br />',$techInfo['flags']):'').'</td>'.$this->helpCol('flags').'</tr>';
2656 $lines[]='<tr class="bgColor4"><td>Conf