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