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