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