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