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