Fixed bug #11905: Extension Manager is not translatable (part 3) (thanks to Christoph...
[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="' . sprintf($GLOBALS['LANG']->getLL('do_update'), ($loc == 'G' ? $GLOBALS['LANG']->getLL('global') : $GLOBALS['LANG']->getLL('local'))) . '" 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="' . $GLOBALS['LANG']->getLL('excluded_from_updates') . '" 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="' . $GLOBALS['LANG']->getLL('import_to_local_dir') . '" 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) .
947 '"><label for="_lookUp">' .
948 sprintf($GLOBALS['LANG']->getLL('list_or_look_up'),
949 ($this->MOD_SETTINGS['display_unchecked'] ?
950 '<strong style="color:#900;">' . $GLOBALS['LANG']->getLL('list_or_look_up_all') . '</strong>'
951 : '<strong style="color:#090;">' . $GLOBALS['LANG']->getLL('list_or_look_up_reviewed') . '</strong>'
952 )
953 ) .
954 '</label><br />
955 <input type="text" id="_lookUp" name="_lookUp" value="' . htmlspecialchars($this->listRemote_search) .
956 '" /> <input type="submit" value="' . $GLOBALS['LANG']->getLL('look_up_button') . '" /></form><br /><br />';
957
958 $content .= $this->browseLinks();
959
960 $content.= '
961
962 <!-- TER Extensions list -->
963 <table border="0" cellpadding="2" cellspacing="1">'.implode(chr(10),$lines).'</table>';
964 $content .= '<br />'.$this->browseLinks();
965 $content.= '<br /><br />'.$this->securityHint;
966 $content .= '<br /><br /><strong>' . $GLOBALS['LANG']->getLL('privacy_notice_header') .
967 '</strong><br /> ' . $this->privacyNotice;
968
969 $this->content .= $this->doc->section($GLOBALS['LANG']->getLL('extensions_repository_group_by') . ' ' .
970 $this->MOD_MENU['listOrder'][$this->MOD_SETTINGS['listOrder']], $content, 0, 1);
971
972 // Plugins which are NOT uploaded to repository but present on this server.
973 $content='';
974 $lines=array();
975 if (count($this->inst_keys)) {
976 reset($this->inst_keys);
977 while(list($extKey)=each($this->inst_keys)) {
978 $this->xmlhandler->searchExtensionsXMLExact($extKey, '', '', true);
979 if((strlen($this->listRemote_search) && !stristr($extKey,$this->listRemote_search)) || isset($this->xmlhandler->extensionsXML[$extKey])) continue;
980
981 $loadUnloadLink = t3lib_extMgm::isLoaded($extKey)?
982 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->removeButton().'</a>':
983 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[load]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->installButton().'</a>';
984 if (in_array($extKey,$this->requiredExt)) $loadUnloadLink='<strong>'.$GLOBALS['TBE_TEMPLATE']->rfw('Rq').'</strong>';
985 $lines[]=$this->extensionListRow($extKey,$inst_list[$extKey],array('<td class="bgColor">'.$loadUnloadLink.'</td>'),t3lib_extMgm::isLoaded($extKey)?'em-listbg1':'em-listbg2');
986 }
987 }
988 if(count($lines)) {
989 $content .= $GLOBALS['LANG']->getLL('list_of_local_extensions') .
990 '<br />' . $GLOBALS['LANG']->getLL('might_be_user_defined') . '<br /><br />';
991 $content.= '<table border="0" cellpadding="2" cellspacing="1">'.
992 $this->extensionListRowHeader(' class="bgColor5"',array('<td><img src="clear.gif" width="18" height="1" alt="" /></td>')).
993 implode('',$lines).'</table>';
994 $this->content.=$this->doc->spacer(20);
995 $this->content.=$this->doc->section($GLOBALS['LANG']->getLL('only_on_this_server'), $content, 0, 1);
996 }
997 }
998 } else {
999 $content .= t3lib_BEfunc::cshItem('_MOD_tools_em', 'import_ter', $GLOBALS['BACK_PATH'], '|<br />');
1000 $onsubmit = "window.location.href='index.php?ter_connect=1&ter_search='+escape(this.elements['_lookUp'].value);return false;";
1001 $content .= '<form action="index.php" method="post" onsubmit="' . htmlspecialchars($onsubmit) .
1002 '"><label for="_lookUp">' .
1003 sprintf($GLOBALS['LANG']->getLL('list_or_look_up'),
1004 ($this->MOD_SETTINGS['display_unchecked'] ?
1005 '<strong style="color:#900;">' . $GLOBALS['LANG']->getLL('list_or_look_up_all') . '</strong>'
1006 : '<strong style="color:#090;">' . $GLOBALS['LANG']->getLL('list_or_look_up_reviewed') . '</strong>'
1007 )
1008 ) .
1009 '</label><br />
1010 <input type="text" id="_lookUp" name="_lookUp" value="' . htmlspecialchars($this->listRemote_search) .
1011 '" /> <input type="submit" value="' . $GLOBALS['LANG']->getLL('look_up_button') . '" /></form><br /><br />';
1012
1013 $content .= '<p><strong>' . $GLOBALS['LANG']->getLL('no_matching_extensions') . '</strong></p>';
1014
1015 $content .= '<br /><br /><strong>' . $GLOBALS['LANG']->getLL('privacy_notice_header') .
1016 '</strong><br /> ' . $this->privacyNotice;
1017 $this->content.=$this->doc->section($GLOBALS['LANG']->getLL('extensions_repository_group_by') . ' ' .
1018 $this->MOD_MENU['listOrder'][$this->MOD_SETTINGS['listOrder']], $content, 0, 1);
1019 }
1020 } else {
1021 // CSH
1022 $content .= t3lib_BEfunc::cshItem('_MOD_tools_em', 'import', $GLOBALS['BACK_PATH'], '|<br />');
1023
1024 $onsubmit = "window.location.href='index.php?ter_connect=1&ter_search='+escape(this.elements['_lookUp'].value);return false;";
1025 $content .= '<form action="index.php" method="post" onsubmit="' . htmlspecialchars($onsubmit) .
1026 '"><label for="_lookUp">' .
1027 sprintf($GLOBALS['LANG']->getLL('list_or_look_up'),
1028 ($this->MOD_SETTINGS['display_unchecked'] ?
1029 '<strong style="color:#900;">' . $GLOBALS['LANG']->getLL('list_or_look_up_all') . '</strong>'
1030 : '<strong style="color:#090;">' . $GLOBALS['LANG']->getLL('list_or_look_up_reviewed') . '</strong>'
1031 )
1032 ) .
1033 '</label><br />
1034 <input type="text" id="_lookUp" name="_lookUp" value="" /> <input type="submit" value="' .
1035 $GLOBALS['LANG']->getLL('look_up_button') . '" /><br /><br />';
1036
1037 if ($this->CMD['fetchMetaData']) { // fetches mirror/extension data from online rep.
1038 $content .= $this->fetchMetaData($this->CMD['fetchMetaData']);
1039 } else {
1040 $onCLick = "window.location.href='index.php?CMD[fetchMetaData]=extensions';return false;";
1041 $content .= $GLOBALS['LANG']->getLL('connect_to_ter') . '<br />
1042 <input type="submit" value="' . $GLOBALS['LANG']->getLL('retrieve_update') .
1043 '" onclick="' . htmlspecialchars($onCLick) . '" />';
1044 if (is_file(PATH_site.'typo3temp/extensions.xml.gz')) {
1045 $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
1046 $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
1047 $content .= ' ' . sprintf($GLOBALS['LANG']->getLL('ext_list_last_updated') . ' ',
1048 date(
1049 $dateFormat . ', ' . $timeFormat,
1050 filemtime(PATH_site . 'typo3temp/extensions.xml.gz')
1051 )
1052 );
1053 }
1054 }
1055 $content.= '</form><br /><br />'.$this->securityHint;
1056 $content .= '<br /><br /><strong>' . $GLOBALS['LANG']->getLL('privacy_notice_header') .
1057 '</strong><br />' . $this->privacyNotice;
1058
1059 $this->content.=$this->doc->section($GLOBALS['LANG']->getLL('in_repository'), $content, 0, 1);
1060 }
1061
1062 // Upload:
1063 if ($this->importAtAll()) {
1064 $content= '<form action="index.php" enctype="'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['form_enctype'].'" method="post">
1065 <label for="upload_ext_file">' . $GLOBALS['LANG']->getLL('upload_t3x') . '</label><br />
1066 <input type="file" size="60" id="upload_ext_file" name="upload_ext_file" /><br />' .
1067 $GLOBALS['LANG']->getLL('upload_to_location') . '<br />
1068 <select name="CMD[loc]">';
1069 if ($this->importAsType('L')) $content .= '<option value="L">' . $GLOBALS['LANG']->getLL('local_folder') . '</option>';
1070 if ($this->importAsType('G')) $content .= '<option value="G">' . $GLOBALS['LANG']->getLL('global_folder') . '</option>';
1071 if ($this->importAsType('S')) $content .= '<option value="S">' . $GLOBALS['LANG']->getLL('system_folder') . '</option>';
1072 $content.='</select><br />
1073 <input type="checkbox" value="1" name="CMD[uploadOverwrite]" id="checkUploadOverwrite" /> <label for="checkUploadOverwrite">' .
1074 $GLOBALS['LANG']->getLL('overwrite_ext') . '</label><br />
1075 <input type="submit" name="CMD[uploadExt]" value="' . $GLOBALS['LANG']->getLL('upload_ext_file') . '" /></form><br />
1076 ';
1077 } else $content=$this->noImportMsg();
1078
1079 $this->content.=$this->doc->spacer(20);
1080 $this->content .= $this->doc->section($GLOBALS['LANG']->getLL('upload_ext_directly'), $content, 0, 1);
1081 }
1082
1083 /**
1084 * Generates a link to the next page of extensions
1085 *
1086 * @return void
1087 */
1088 function browseLinks() {
1089 $content = '';
1090 if ($this->pointer) {
1091 $content .= '<a href="' . t3lib_div::linkThisScript(array('pointer' => $this->pointer-1)) .
1092 '" class="typo3-prevPage"><img' . t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],
1093 'gfx/pilleft_n.gif', 'width="14" height="14"') .
1094 ' alt="' . $GLOBALS['LANG']->getLL('previous_page') . '" /> ' .
1095 $GLOBALS['LANG']->getLL('previous_page') . '</a>';
1096 }
1097 if ($content) $content .= '&nbsp;&nbsp;&nbsp;';
1098 if (intval($this->xmlhandler->matchingCount/$this->listingLimit)>$this->pointer) {
1099 $content .= '<a href="' . t3lib_div::linkThisScript(array('pointer' => $this->pointer+1)) .
1100 '" class="typo3-nextPage"><img' . t3lib_iconWorks::skinImg($GLOBALS['BACK_PATH'],
1101 'gfx/pilright_n.gif', 'width="14" height="14"') .
1102 ' alt="' . $GLOBALS['LANG']->getLL('next_page') . '" /> ' .
1103 $GLOBALS['LANG']->getLL('next_page') . '</a>';
1104 }
1105 $upper = (($this->pointer+1)*$this->listingLimit);
1106 if ($upper>$this->xmlhandler->matchingCount) {
1107 $upper = $this->xmlhandler->matchingCount;
1108 }
1109 if ($content) $content .= '<br /><br />' .
1110 sprintf($GLOBALS['LANG']->getLL('showing_extensions_from_to'),
1111 '<strong>' . ($this->pointer*$this->listingLimit+1) . '</strong>',
1112 '<strong>' . $upper . '</strong>'
1113 );
1114 if ($content) $content .= '<br /><br />';
1115 return $content;
1116 }
1117
1118 /**
1119 * Allows changing of settings
1120 *
1121 * @return void
1122 */
1123 function alterSettings() {
1124
1125 // Prepare the HTML output:
1126 $content.= '
1127 ' . t3lib_BEfunc::cshItem('_MOD_tools_em', 'settings', $GLOBALS['BACK_PATH'], '|<br />') . '
1128 <form action="index.php" method="post" name="altersettings">
1129 <fieldset><legend>' . $GLOBALS['LANG']->getLL('security_settings') . '</legend>
1130 <table border="0" cellpadding="2" cellspacing="2">
1131 <tr class="bgColor4">
1132 <td><label for="display_unchecked">' . $GLOBALS['LANG']->getLL('show_exts_without_security_check') . '</label></td>
1133 <td>'.t3lib_BEfunc::getFuncCheck(0,'SET[display_unchecked]',$this->MOD_SETTINGS['display_unchecked'],'','','id="display_unchecked"').'</td>
1134 </tr>
1135 </table>
1136 <strong>' . $GLOBALS['LANG']->getLL('notice') . '</strong> ' .
1137 sprintf($GLOBALS['LANG']->getLL('security_notice'),
1138 '<a href="http://typo3.org/extensions/what-are-reviews/" target="_blank">', '</a>'
1139 ) .
1140 '</fieldset>
1141 <br />
1142 <br />
1143 <fieldset><legend>' . $GLOBALS['LANG']->getLL('user_settings') . '</legend>
1144 <table border="0" cellpadding="2" cellspacing="2">
1145 <tr class="bgColor4">
1146 <td><label for="set_fe_u">' . $GLOBALS['LANG']->getLL('enter_repository_username') . '</label></td>
1147 <td><input type="text" id="set_fe_u" name="SET[fe_u]" value="'.htmlspecialchars($this->MOD_SETTINGS['fe_u']).'" /></td>
1148 </tr>
1149 <tr class="bgColor4">
1150 <td><label for="set_fe_p">' . $GLOBALS['LANG']->getLL('enter_repository_password') . '</label></td>
1151 <td><input type="password" id="set_fe_p" name="SET[fe_p]" value="'.htmlspecialchars($this->MOD_SETTINGS['fe_p']).'" /></td>
1152 </tr>
1153 </table>
1154 <strong>' . $GLOBALS['LANG']->getLL('notice') . '</strong> ' .
1155 $GLOBALS['LANG']->getLL('repository_password_info') . '
1156 </fieldset>
1157 <br />
1158 <br />
1159 <fieldset><legend>' . $GLOBALS['LANG']->getLL('mirror_selection') . '</legend>
1160 <table border="0" cellpadding="2" cellspacing="2">
1161 <tr class="bgColor4">
1162 <td><label for="set_mirror_list_url">' . $GLOBALS['LANG']->getLL('mirror_list_url') . '</label></a></td>
1163 <td><input type="text" size="50" id="set_mirror_list_url" name="SET[mirrorListURL]" value="'.htmlspecialchars($this->MOD_SETTINGS['mirrorListURL']).'" /></td>
1164 </tr>
1165 </table>
1166 <br />
1167 <p>' . $GLOBALS['LANG']->getLL('mirror_select') . '<br /><br /></p>
1168 <fieldset><legend>' . $GLOBALS['LANG']->getLL('mirror_list') . '</legend>';
1169 if(!empty($this->MOD_SETTINGS['mirrorListURL'])) {
1170 if ($this->CMD['fetchMetaData']) { // fetches mirror/extension data from online rep.
1171 $content .= $this->fetchMetaData($this->CMD['fetchMetaData']);
1172 } else {
1173 $content .= '<a href="index.php?CMD[fetchMetaData]=mirrors">' . $GLOBALS['LANG']->getLL('mirror_list_reload') . '</a>';
1174 }
1175 }
1176 $content .= '<br />
1177 <table cellspacing="4" style="text-align:left; vertical-alignment:top;">
1178 <tr>
1179 <td>' . $GLOBALS['LANG']->getLL('mirror_use') . '</td>
1180 <td>' . $GLOBALS['LANG']->getLL('mirror_name') . '</td>
1181 <td>' . $GLOBALS['LANG']->getLL('mirror_url') . '</td>
1182 <td>' . $GLOBALS['LANG']->getLL('mirror_country') . '</td>
1183 <td>' . $GLOBALS['LANG']->getLL('mirror_sponsored_by') . '</td>
1184 </tr>
1185 ';
1186
1187 if (!strlen($this->MOD_SETTINGS['extMirrors'])) $this->fetchMetaData('mirrors');
1188 $extMirrors = unserialize($this->MOD_SETTINGS['extMirrors']);
1189 $extMirrors[''] = array('title'=>$GLOBALS['LANG']->getLL('mirror_use_random'));
1190 ksort($extMirrors);
1191 if(is_array($extMirrors)) {
1192 foreach($extMirrors as $k => $v) {
1193 if(isset($v['sponsor'])) {
1194 $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>';
1195 }
1196 $selected = ($this->MOD_SETTINGS['selectedMirror']==$k) ? 'checked="checked"' : '';
1197 $content.='<tr class="bgColor4">
1198 <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>';
1199 }
1200 }
1201 $content.= '
1202 </table>
1203 </fieldset>
1204 <br />
1205 <table border="0" cellpadding="2" cellspacing="2">
1206 <tr class="bgColor4">
1207 <td><label for="set_rep_url">' . $GLOBALS['LANG']->getLL('enter_repository_url') . '</label></td>
1208 <td><input type="text" size="50" id="set_rep_url" name="SET[rep_url]" value="'.htmlspecialchars($this->MOD_SETTINGS['rep_url']).'" /></td>
1209 </tr>
1210 </table>
1211
1212 ' . $GLOBALS['LANG']->getLL('repository_url_hint') . '<br />
1213 </fieldset>
1214 <br />
1215 <input type="submit" value="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_tsfe.xml:update') . '" />
1216 </form>
1217 ';
1218
1219 $this->content .= $this->doc->section($GLOBALS['LANG']->getLL('repository_settings'), $content, 0, 1);
1220 }
1221
1222 /**
1223 * Allows to set the translation preferences and check the status
1224 *
1225 * @return void
1226 */
1227 function translationHandling() {
1228 global $LANG, $TYPO3_LOADED_EXT;
1229 $LANG->includeLLFile('EXT:setup/mod/locallang.xml');
1230
1231 //prepare docheader
1232 $docHeaderButtons = $this->getButtons();
1233 $markers = array(
1234 'CSH' => $docHeaderButtons['csh'],
1235 'FUNC_MENU' => $this->getFuncMenu(),
1236 );
1237
1238
1239 $incoming = t3lib_div::_POST('SET');
1240 if(isset($incoming['selectedLanguages']) && is_array($incoming['selectedLanguages'])) {
1241 t3lib_BEfunc::getModuleData($this->MOD_MENU, array('selectedLanguages' => serialize($incoming['selectedLanguages'])), $this->MCONF['name'], '', 'selectedLanguages');
1242 $this->MOD_SETTINGS['selectedLanguages'] = serialize($incoming['selectedLanguages']);
1243 }
1244
1245 $selectedLanguages = unserialize($this->MOD_SETTINGS['selectedLanguages']);
1246 if(count($selectedLanguages)==1 && empty($selectedLanguages[0])) $selectedLanguages = array();
1247 $theLanguages = t3lib_div::trimExplode('|',TYPO3_languages);
1248 foreach($theLanguages as $val) {
1249 if ($val!='default') {
1250 $localLabel = ' - ['.htmlspecialchars($GLOBALS['LOCAL_LANG']['default']['lang_'.$val]).']';
1251 $selected = (is_array($selectedLanguages) && in_array($val, $selectedLanguages)) ? ' selected="selected"' : '';
1252 $opt[$GLOBALS['LOCAL_LANG']['default']['lang_'.$val].'--'.$val]='
1253 <option value="'.$val.'"'.$selected.'>'.$LANG->getLL('lang_'.$val,1).$localLabel.'</option>';
1254 }
1255 }
1256 ksort($opt);
1257
1258 // Prepare the HTML output:
1259 $content.= '
1260 ' . t3lib_BEfunc::cshItem('_MOD_tools_em', 'translation', $GLOBALS['BACK_PATH'], '|<br />') . '
1261 <form action="index.php" method="post" name="translationform">
1262 <fieldset><legend>Translation Settings</legend>
1263 <table border="0" cellpadding="2" cellspacing="2">
1264 <tr class="bgColor4">
1265 <td>Languages to fetch:</td>
1266 <td>
1267 <select name="SET[selectedLanguages][]" multiple="multiple" size="10">
1268 <option></option>'.
1269 implode('',$opt).'
1270 </select>
1271 </td>
1272 </tr>
1273 </table>
1274 <br />
1275 <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 />
1276 <br />To request an update/install for already loaded extensions, see below.</p>
1277 </fieldset>
1278 <br />
1279 <input type="submit" value="Save selection" />
1280 <br />
1281 </fieldset>
1282 </form>';
1283
1284 $this->content.=$this->doc->section('Translation settings',$content,0,1);
1285
1286 if(count($selectedLanguages)>0) {
1287 $mirrorURL = $this->getMirrorURL();
1288 $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')).'\'" />';
1289
1290 if(t3lib_div::_GET('l10n') == 'check') {
1291 $loadedExtensions = array_keys($TYPO3_LOADED_EXT);
1292 $loadedExtensions = array_diff($loadedExtensions,array('_CACHEFILE'));
1293
1294 // Override content output - we now do that ourself:
1295 $this->content .= $this->doc->section('Translation status',$content,0,1);
1296 // Setting up the buttons and markers for docheader
1297 $content = $this->doc->startPage('Extension Manager');
1298 $content.= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
1299 $contentParts=explode('###CONTENT###',$content);
1300 echo $contentParts[0].$this->content;
1301
1302 $this->doPrintContent = FALSE;
1303 flush();
1304
1305 echo '
1306 <br />
1307 <br />
1308 <p id="progress-message">
1309 Checking translation status, please wait ...
1310 </p>
1311 <br />
1312 <div style="width:100%; height:20px; border: 1px solid black;">
1313 <div id="progress-bar" style="float: left; width: 0%; height: 20px; background-color:green;">&nbsp;</div>
1314 <div id="transparent-bar" style="float: left; width: 100%; height: 20px; background-color:'.$this->doc->bgColor2.';">&nbsp;</div>
1315 </div>
1316 <br />
1317 <br /><p>This table shows the status of the loaded extension\'s translations.</p><br />
1318 <table border="0" cellpadding="2" cellspacing="2">
1319 <tr class="bgColor2"><td>Extension key</td>
1320 ';
1321
1322 foreach($selectedLanguages as $lang) {
1323 echo ('<td>'.$LANG->getLL('lang_'.$lang,1).'</td>');
1324 }
1325 echo ('</tr>');
1326
1327 $counter = 1;
1328 foreach($loadedExtensions as $extKey) {
1329
1330 $percentDone = intval (($counter / count($loadedExtensions)) * 100);
1331 echo ('
1332 <script>
1333 document.getElementById("progress-bar").style.width = "'.$percentDone.'%";
1334 document.getElementById("transparent-bar").style.width = "'.(100-$percentDone).'%";
1335 document.getElementById("progress-message").firstChild.data="Checking translation status for extension \"'.$extKey.'\" ...";
1336 </script>
1337 ');
1338
1339 flush();
1340 $translationStatusArr = $this->terConnection->fetchTranslationStatus($extKey,$mirrorURL);
1341
1342 echo ('<tr class="bgColor4"><td>'.$extKey.'</td>');
1343 foreach($selectedLanguages as $lang) {
1344 // remote unknown -> keine l10n
1345 if(!isset($translationStatusArr[$lang])) {
1346 echo ('<td title="No translation available">N/A</td>');
1347 continue;
1348 }
1349 // determine local md5 from zip
1350 if(is_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip')) {
1351 $localmd5 = md5_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip');
1352 } else {
1353 echo ('<td title="Not installed / Unknown" style="background-color:#ff0">???</td>');
1354 continue;
1355 }
1356 // local!=remote -> needs update
1357 if($localmd5 != $translationStatusArr[$lang]['md5']) {
1358 echo ('<td title="Needs update" style="background-color:#ff0">UPD</td>');
1359 continue;
1360 }
1361 echo ('<td title="Is up to date" style="background-color:#69a550">OK</td>');
1362 }
1363 echo ('</tr>');
1364
1365 $counter ++;
1366 }
1367 echo '</table>
1368 <script>
1369 document.getElementById("progress-message").firstChild.data="Check done.";
1370 </script>
1371 ';
1372 echo $contentParts[1] . $this->doc->endPage();
1373 exit;
1374
1375 } elseif(t3lib_div::_GET('l10n') == 'update') {
1376 $loadedExtensions = array_keys($TYPO3_LOADED_EXT);
1377 $loadedExtensions = array_diff($loadedExtensions,array('_CACHEFILE'));
1378
1379 // Override content output - we now do that ourself:
1380 $this->content .= $this->doc->section('Translation status',$content,0,1);
1381 // Setting up the buttons and markers for docheader
1382 $content = $this->doc->startPage('Extension Manager');
1383 $content.= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
1384 $contentParts=explode('###CONTENT###',$content);
1385 echo $contentParts[0].$this->content;
1386
1387 $this->doPrintContent = FALSE;
1388 flush();
1389
1390 echo ('
1391 <br />
1392 <br />
1393 <p id="progress-message">
1394 Updating translations, please wait ...
1395 </p>
1396 <br />
1397 <div style="width:100%; height:20px; border: 1px solid black;">
1398 <div id="progress-bar" style="float: left; width: 0%; height: 20px; background-color:green;">&nbsp;</div>
1399 <div id="transparent-bar" style="float: left; width: 100%; height: 20px; background-color:'.$this->doc->bgColor2.';">&nbsp;</div>
1400 </div>
1401 <br />
1402 <br /><p>This table shows the update results of the loaded extension\'s translations.<br />
1403 <em>If you want to force a full check/update, delete the l10n zip-files from the typo3temp folder.</em></p><br />
1404 <table border="0" cellpadding="2" cellspacing="2">
1405 <tr class="bgColor2"><td>Extension key</td>
1406 ');
1407
1408 foreach($selectedLanguages as $lang) {
1409 echo '<td>'.$LANG->getLL('lang_'.$lang,1).'</td>';
1410 }
1411 echo '</tr>';
1412
1413 $counter = 1;
1414 foreach($loadedExtensions as $extKey) {
1415 $percentDone = intval (($counter / count($loadedExtensions)) * 100);
1416 echo ('
1417 <script>
1418 document.getElementById("progress-bar").style.width = "'.$percentDone.'%";
1419 document.getElementById("transparent-bar").style.width = "'.(100-$percentDone).'%";
1420 document.getElementById("progress-message").firstChild.data="Updating translation for extension \"'.$extKey.'\" ...";
1421 </script>
1422 ');
1423
1424 flush();
1425 $translationStatusArr = $this->terConnection->fetchTranslationStatus($extKey,$mirrorURL);
1426
1427 echo ('<tr class="bgColor4"><td>'.$extKey.'</td>');
1428 if(is_array($translationStatusArr)) {
1429 foreach($selectedLanguages as $lang) {
1430 // remote unknown -> no l10n available
1431 if(!isset($translationStatusArr[$lang])) {
1432 echo ('<td title="No translation available">N/A</td>');
1433 continue;
1434 }
1435 // determine local md5 from zip
1436 if(is_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip')) {
1437 $localmd5 = md5_file(PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip');
1438 } else {
1439 $localmd5 = 'zzz';
1440 }
1441 // local!=remote or not installed -> needs update
1442 if($localmd5 != $translationStatusArr[$lang]['md5']) {
1443 $ret = $this->updateTranslation($extKey, $lang, $mirrorURL);
1444 if($ret === true) {
1445 echo ('<td title="Has been updated" style="background-color:#69a550">UPD</td>');
1446 } else {
1447 echo ('<td title="'.htmlspecialchars($ret).'" style="background-color:#cb3352">ERR</td>');
1448 }
1449 continue;
1450 }
1451 echo ('<td title="Is up to date" style="background-color:#69a550">OK</td>');
1452 }
1453 } else {
1454 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>');
1455 }
1456 echo ('</tr>');
1457 $counter++;
1458 }
1459 echo '</table>
1460 <script>
1461 document.getElementById("progress-message").firstChild.data="Update done.";
1462 </script>
1463 ';
1464 echo $contentParts[1] . $this->doc->endPage();
1465 exit;
1466 }
1467
1468 $this->content.=$this->doc->section('Translation status',$content,0,1);
1469 }
1470 }
1471
1472 /**
1473 * Install translations for all selected languages for an extension
1474 *
1475 * @param string $extKey The extension key to install the translations for
1476 * @param string $lang Language code of translation to fetch
1477 * @param string $mirrorURL Mirror URL to fetch data from
1478 * @return mixed true on success, error string on fauilure
1479 */
1480 function updateTranslation($extKey, $lang, $mirrorURL) {
1481 $l10n = $this->terConnection->fetchTranslation($extKey, $lang, $mirrorURL);
1482 if(is_array($l10n)) {
1483 $file = PATH_site.'typo3temp/'.$extKey.'-l10n-'.$lang.'.zip';
1484 $path = 'l10n/'.$lang.'/';
1485 if(!is_dir(PATH_typo3conf.$path)) t3lib_div::mkdir_deep(PATH_typo3conf,$path);
1486 t3lib_div::writeFile($file, $l10n[0]);
1487 if($this->unzip($file, PATH_typo3conf.$path)) {
1488 return true;
1489 } else {
1490 return 'Unpacking the language pack failed!';
1491 }
1492 } else {
1493 return $l10n;
1494 }
1495 }
1496
1497 /**
1498 * Install translations for all selected languages for an extension
1499 *
1500 * @param string $extKey The extension key to install the translations for
1501 * @param string $mirrorURL Mirror URL to fetch data from
1502 * @return mixed true on success, error string on fauilure
1503 */
1504 function installTranslationsForExtension($extKey, $mirrorURL) {
1505 $selectedLanguages = unserialize($this->MOD_SETTINGS['selectedLanguages']);
1506 if(!is_array($selectedLanguages)) $selectedLanguages = array();
1507 foreach($selectedLanguages as $lang) {
1508 $l10n = $this->terConnection->fetchTranslation($extKey, $lang, $mirrorURL);
1509 if(is_array($l10n)) {
1510 $file = PATH_typo3conf.'l10n/'.$extKey.'-l10n-'.$lang.'.zip';
1511 $path = 'l10n/'.$lang.'/'.$extKey;
1512 t3lib_div::writeFile($file, $l10n[0]);
1513 if(!is_dir(PATH_typo3conf.$path)) t3lib_div::mkdir_deep(PATH_typo3conf,$path);
1514 if($this->unzip($file, PATH_typo3conf.$path)) {
1515 return true;
1516 } else {
1517 return 'Unpacking the language pack failed!';
1518 }
1519 } else {
1520 return $l10n;
1521 }
1522 }
1523 }
1524
1525 /**
1526 * Unzips a zip file in the given path.
1527 *
1528 * Uses unzip binary if available, otherwise a pure PHP unzip is used.
1529 *
1530 * @param string $file Full path to zip file
1531 * @param string $path Path to change to before extracting
1532 * @return boolean True on success, false in failure
1533 */
1534 function unzip($file, $path) {
1535 if(strlen($GLOBALS['TYPO3_CONF_VARS']['BE']['unzip_path'])) {
1536 chdir($path);
1537 $cmd = $GLOBALS['TYPO3_CONF_VARS']['BE']['unzip_path'].' -o '.escapeshellarg($file);
1538 exec($cmd, $list, $ret);
1539 return ($ret === 0);
1540 } else {
1541 // we use a pure PHP unzip
1542 $unzip = new em_unzip($file);
1543 $ret = $unzip->extract(array('add_path'=>$path));
1544 return (is_array($ret));
1545 }
1546 }
1547
1548
1549
1550 /*********************************
1551 *
1552 * Command Applications (triggered by GET var)
1553 *
1554 *********************************/
1555
1556 /**
1557 * Returns detailed info about an extension in the online repository
1558 *
1559 * @param string Extension repository uid + optional "private key": [uid]-[key].
1560 * @param [type] $version: ...
1561 * @return void
1562 */
1563 function importExtInfo($extKey, $version='') {
1564
1565 $content = '<form action="index.php" method="post" name="pageform">';
1566
1567 // Fetch remote data:
1568 $this->xmlhandler->searchExtensionsXMLExact($extKey, '', '', true, true);
1569 list($fetchData,) = $this->prepareImportExtList(true);
1570
1571 $versions = array_keys($fetchData[$extKey]['versions']);
1572 $version = ($version == '') ? end($versions) : $version;
1573
1574 $opt = array();
1575 foreach(array_keys($fetchData[$extKey]['versions']) as $ver) {
1576 $opt[]='<option value="'.$ver.'"'.(($version == $ver) ? ' selected="selected"' : '').'>'.$ver.'</option>';
1577 }
1578
1579 // "Select version" box:
1580 $onClick = 'window.location.href=\'index.php?CMD[importExtInfo]='.$extKey.'&CMD[extVersion]=\'+document.pageform.extVersion.options[document.pageform.extVersion.selectedIndex].value; return false;';
1581 $select='<select name="extVersion">'.implode('',$opt).'</select> <input type="submit" value="Load details" onclick="'.htmlspecialchars($onClick).'" />';
1582
1583 if ($this->importAtAll()) {
1584 // Check for write-protected extension
1585 list($inst_list,) = $this->getInstalledExtensions();
1586 if ($inst_list[$extKey]['EM_CONF']['state'] != 'excludeFromUpdates') {
1587 $onClick = '
1588 window.location.href=\'index.php?CMD[importExt]='.$extKey.'\'
1589 +\'&CMD[extVersion]=\'+document.pageform.extVersion.options[document.pageform.extVersion.selectedIndex].value
1590 +\'&CMD[loc]=\'+document.pageform.loc.options[document.pageform.loc.selectedIndex].value;
1591 return false;';
1592 $select .= ' or<br /><br />
1593 <input type="submit" value="Import/Update" onclick="'.htmlspecialchars($onClick).'"> to:
1594 <select name="loc">'.
1595 ($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>':'').
1596 ($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>':'').
1597 ($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>':'').
1598 '</select>
1599 </form>';
1600 } else {
1601 $select .= '<br /><br />This extension is excluded from Updates! You can change this in the extensions\' ext_emconf.php file.';
1602 }
1603 } else {
1604 $select .= '<br /><br />' . $this->noImportMsg();
1605 }
1606 $content.= $select;
1607 $this->content.= $this->doc->section('Select command',$content,0,1);
1608
1609 // Details:
1610 $eInfo = $fetchData[$extKey]['versions'][$version];
1611 $content='<strong>'.$fetchData[$extKey]['_ICON'].' &nbsp;'.$eInfo['EM_CONF']['title'].' ('.$extKey.', '.$version.')</strong><br /><br />';
1612 $content.=$this->extInformationArray($extKey,$eInfo,1);
1613 $this->content.=$this->doc->spacer(10);
1614 $this->content.=$this->doc->section('Remote Extension Details',$content,0,1);
1615 }
1616
1617 /**
1618 * Fetches metadata and stores it to the corresponding place. This includes the mirror list,
1619 * extension XML files.
1620 *
1621 * @param string Type of data to fetch: (mirrors)
1622 * @param boolean If true the method doesn't produce any output
1623 * @return void
1624 */
1625 function fetchMetaData($metaType) {
1626 global $TYPO3_CONF_VARS;
1627
1628 switch($metaType) {
1629 case 'mirrors':
1630 $mfile = t3lib_div::tempnam('mirrors');
1631 $mirrorsFile = t3lib_div::getURL($this->MOD_SETTINGS['mirrorListURL'], 0, array(TYPO3_user_agent));
1632 if($mirrorsFile===false) {
1633 t3lib_div::unlink_tempfile($mfile);
1634 $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>';
1635 } else {
1636 t3lib_div::writeFile($mfile, $mirrorsFile);
1637 $mirrors = implode('',gzfile($mfile));
1638 t3lib_div::unlink_tempfile($mfile);
1639
1640 $mirrors = $this->xmlhandler->parseMirrorsXML($mirrors);
1641 if(is_array($mirrors) && count($mirrors)) {
1642 t3lib_BEfunc::getModuleData($this->MOD_MENU, array('extMirrors' => serialize($mirrors)), $this->MCONF['name'], '', 'extMirrors');
1643 $this->MOD_SETTINGS['extMirrors'] = serialize($mirrors);
1644 $content = '<p>The mirror list has been updated and now contains '.count($mirrors).' entries.</p>';
1645 }
1646 else {
1647 $content = '<p>'.$mirrors.'<br />The mirror list was not updated as it contained no entries.</p>';
1648 }
1649 }
1650 break;
1651 case 'extensions':
1652 $this->fetchMetaData('mirrors'); // if we fetch the extensions anyway, we can as well keep this up-to-date
1653
1654 $mirror = $this->getMirrorURL();
1655 $extfile = $mirror.'extensions.xml.gz';
1656 $extmd5 = t3lib_div::getURL($mirror.'extensions.md5', 0, array(TYPO3_user_agent));
1657 if (is_file(PATH_site.'typo3temp/extensions.xml.gz')) {
1658 $localmd5 = md5_file(PATH_site.'typo3temp/extensions.xml.gz');
1659 }
1660
1661 if($extmd5 === false) {
1662 $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>';
1663 } elseif($extmd5 == $localmd5) {
1664 $content .= '<p>The extension list has not changed remotely, it has thus not been fetched.</p>';
1665 } else {
1666 $extXML = t3lib_div::getURL($extfile, 0, array(TYPO3_user_agent));
1667 if($extXML === false) {
1668 $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>';
1669 } else {
1670 t3lib_div::writeFile(PATH_site.'typo3temp/extensions.xml.gz', $extXML);
1671 $content .= $this->xmlhandler->parseExtensionsXML(PATH_site.'typo3temp/extensions.xml.gz');
1672 }
1673 }
1674 break;
1675 }
1676
1677 return $content;
1678 }
1679
1680 /**
1681 * Returns the base URL for the slected or a random mirror.
1682 *
1683 * @return string The URL for the selected or a random mirror
1684 */
1685 function getMirrorURL() {
1686 if(strlen($this->MOD_SETTINGS['rep_url'])) return $this->MOD_SETTINGS['rep_url'];
1687
1688 $mirrors = unserialize($this->MOD_SETTINGS['extMirrors']);
1689 if(!is_array($mirrors)) {
1690 $this->fetchMetaData('mirrors');
1691 $mirrors = unserialize($this->MOD_SETTINGS['extMirrors']);
1692 if(!is_array($mirrors)) return false;
1693 }
1694 if($this->MOD_SETTINGS['selectedMirror']=='') {
1695 $rand = array_rand($mirrors);
1696 $url = 'http://'.$mirrors[$rand]['host'].$mirrors[$rand]['path'];
1697 }
1698 else {
1699 $url = 'http://'.$mirrors[$this->MOD_SETTINGS['selectedMirror']]['host'].$mirrors[$this->MOD_SETTINGS['selectedMirror']]['path'];
1700 }
1701
1702 return $url;
1703 }
1704
1705
1706
1707 /**
1708 * Installs (activates) an extension
1709 *
1710 * For $mode use the three constants EM_INSTALL_VERSION_MIN, EM_INSTALL_VERSION_MAX, EM_INSTALL_VERSION_STRICT
1711 *
1712 * If an extension is loaded or imported already and the version requirement is matched, it will not be
1713 * fetched from the repository. This means, if you use EM_INSTALL_VERSION_MIN, you will not always get the latest
1714 * version of an extension!
1715 *
1716 * @param string $extKey The extension key to install
1717 * @param string $version A version number that should be installed
1718 * @param int $mode If a version is requested, this determines if it is the min, max or strict version requested
1719 * @return [type] ...
1720 * @todo Make the method able to handle needed interaction somehow (unmatched dependencies)
1721 */
1722 function installExtension($extKey, $version=null, $mode=EM_INSTALL_VERSION_MIN) {
1723 list($inst_list,) = $this->getInstalledExtensions();
1724
1725 // check if it is already installed and loaded with sufficient version
1726 if(isset($inst_list[$extKey])) {
1727 $currentVersion = $inst_list[$extKey]['EM_CONF']['version'];
1728
1729 if(t3lib_extMgm::isLoaded($extKey)) {
1730 if($version===null) {
1731 return array(true, 'Extension already installed and loaded.');
1732 } else {
1733 switch($mode) {
1734 case EM_INSTALL_VERSION_STRICT:
1735 if ($currentVersion == $version) {
1736 return array(true, 'Extension already installed and loaded.');
1737 }
1738 break;
1739 case EM_INSTALL_VERSION_MIN:
1740 if (version_compare($currentVersion, $version, '>=')) {
1741 return array(true, 'Extension already installed and loaded.');
1742 }
1743 break;
1744 case EM_INSTALL_VERSION_MAX:
1745 if (version_compare($currentVersion, $version, '<=')) {
1746 return array(true, 'Extension already installed and loaded.');
1747 }
1748 break;
1749 }
1750 }
1751 } else {
1752 if (!t3lib_extMgm::isLocalconfWritable()) {
1753 return array(false, 'localconf.php is not writable!');
1754 }
1755 $newExtList = -1;
1756 switch($mode) {
1757 case EM_INSTALL_VERSION_STRICT:
1758 if ($currentVersion == $version) {
1759 $newExtList = $this->addExtToList($extKey, $inst_list);
1760 }
1761 break;
1762 case EM_INSTALL_VERSION_MIN:
1763 if (version_compare($currentVersion, $version, '>=')) {
1764 $newExtList = $this->addExtToList($extKey, $inst_list);
1765 }
1766 break;
1767 case EM_INSTALL_VERSION_MAX:
1768 if (version_compare($currentVersion, $version, '<=')) {
1769 $newExtList = $this->addExtToList($extKey, $inst_list);
1770 }
1771 break;
1772 }
1773 if ($newExtList!=-1) {
1774 $this->writeNewExtensionList($newExtList);
1775 $this->refreshGlobalExtList();
1776 $this->forceDBupdates($extKey, $inst_list[$extKey]);
1777 return array(true, 'Extension was already installed, it has been loaded.');
1778 }
1779 }
1780 }
1781
1782 // at this point we know we need to import (a matching version of) the extension from TER2
1783
1784 // see if we have an extensionlist at all
1785 if (!$this->xmlhandler->countExtensions()) {
1786 $this->fetchMetaData('extensions');
1787 }
1788 $this->xmlhandler->searchExtensionsXMLExact($extKey, '', '', true);
1789
1790 // check if extension can be fetched
1791 if(isset($this->xmlhandler->extensionsXML[$extKey])) {
1792 $versions = array_keys($this->xmlhandler->extensionsXML[$extKey]['versions']);
1793 $latestVersion = end($versions);
1794 switch($mode) {
1795 case EM_INSTALL_VERSION_STRICT:
1796 if(!isset($this->xmlhandler->extensionsXML[$extKey]['versions'][$version])) {
1797 return array(false, 'Extension not available in matching version');
1798 }
1799 break;
1800 case EM_INSTALL_VERSION_MIN:
1801 if (version_compare($latestVersion, $version, '>=')) {
1802 $version = $latestVersion;
1803 } else {
1804 return array(false, 'Extension not available in matching version');
1805 }
1806 break;
1807 case EM_INSTALL_VERSION_MAX:
1808 while (($v = array_pop($versions)) && version_compare($v, $version, '>=')) {
1809 // Loop until a version is found
1810 }
1811
1812 if ($v !== null && version_compare($v, $version, '<=')) {
1813 $version = $v;
1814 } else {
1815 return array(false, 'Extension not available in matching version');
1816 }
1817 break;
1818 }
1819 $this->importExtFromRep($extKey, $version, 'L');
1820 $newExtList = $this->addExtToList($extKey, $inst_list);
1821 if ($newExtList!=-1) {
1822 $this->writeNewExtensionList($newExtList);
1823 $this->refreshGlobalExtList();
1824 $this->forceDBupdates($extKey, $inst_list[$extKey]);
1825 $this->installTranslationsForExtension($extKey, $this->getMirrorURL());
1826 return array(true, 'Extension has been imported from repository and loaded.');
1827 } else {
1828 return array(false, 'Extension is in repository, but could not be loaded.');
1829 }
1830 } else {
1831 return array(false, 'Extension not available in repository');
1832 }
1833 }
1834
1835 function refreshGlobalExtList() {
1836 global $TYPO3_LOADED_EXT;
1837
1838 $TYPO3_LOADED_EXT = t3lib_extMgm::typo3_loadExtensions();
1839 if ($TYPO3_LOADED_EXT['_CACHEFILE']) {
1840 require(PATH_typo3conf.$TYPO3_LOADED_EXT['_CACHEFILE'].'_ext_localconf.php');
1841 }
1842 return;
1843
1844 $GLOBALS['TYPO3_LOADED_EXT'] = t3lib_extMgm::typo3_loadExtensions();
1845 if ($TYPO3_LOADED_EXT['_CACHEFILE']) {
1846 require(PATH_typo3conf.$TYPO3_LOADED_EXT['_CACHEFILE'].'_ext_localconf.php');
1847 } else {
1848 $temp_TYPO3_LOADED_EXT = $TYPO3_LOADED_EXT;
1849 reset($temp_TYPO3_LOADED_EXT);
1850 while(list($_EXTKEY,$temp_lEDat)=each($temp_TYPO3_LOADED_EXT)) {
1851 if (is_array($temp_lEDat) && $temp_lEDat['ext_localconf.php']) {
1852 $_EXTCONF = $TYPO3_CONF_VARS['EXT']['extConf'][$_EXTKEY];
1853 require($temp_lEDat['ext_localconf.php']);
1854 }
1855 }
1856 }
1857 }
1858
1859
1860 /**
1861 * Imports an extensions from the online repository
1862 * NOTICE: in version 4.0 this changed from "importExtFromRep_old($extRepUid,$loc,$uploadFlag=0,$directInput='',$recentTranslations=0,$incManual=0,$dontDelete=0)"
1863 *
1864 * @param string Extension key
1865 * @param string Version
1866 * @param string Install scope: "L" or "G" or "S"
1867 * @param boolean If true, extension is uploaded as file
1868 * @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.
1869 * @param array Direct input array (like from kickstarter)
1870 * @return string Return false on success, returns error message if error.
1871 */
1872 function importExtFromRep($extKey,$version,$loc,$uploadFlag=0,$dontDelete=0,$directInput='') {
1873
1874 $uploadSucceed = false;
1875 $uploadedTempFile = '';
1876 if (is_array($directInput)) {
1877 $fetchData = array($directInput,'');
1878 $loc = ($loc==='G'||$loc==='S') ? $loc : 'L';
1879 } elseif ($uploadFlag) {
1880 if (($uploadedTempFile = $this->CMD['alreadyUploaded']) || $_FILES['upload_ext_file']['tmp_name']) {
1881
1882 // Read uploaded file:
1883 if (!$uploadedTempFile) {
1884 if (!is_uploaded_file($_FILES['upload_ext_file']['tmp_name'])) {
1885 t3lib_div::sysLog('Possible file upload attack: '.$_FILES['upload_ext_file']['tmp_name'], 'Extension Manager', 3);
1886
1887 return 'File was not uploaded?!?';
1888 }
1889
1890 $uploadedTempFile = t3lib_div::upload_to_tempfile($_FILES['upload_ext_file']['tmp_name']);
1891 }
1892 $fileContent = t3lib_div::getUrl($uploadedTempFile);
1893
1894 if (!$fileContent) return 'File is empty!';
1895
1896 // Decode file data:
1897 $fetchData = $this->terConnection->decodeExchangeData($fileContent);
1898
1899 if (is_array($fetchData)) {
1900 $extKey = $fetchData[0]['extKey'];
1901 if ($extKey) {
1902 if (!$this->CMD['uploadOverwrite']) {
1903 $loc = ($loc==='G'||$loc==='S') ? $loc : 'L';
1904 $comingExtPath = PATH_site.$this->typePaths[$loc].$extKey.'/';
1905 if (@is_dir($comingExtPath)) {
1906 return 'Extension was already present in "'.$comingExtPath.'" - and the overwrite flag was not set! So nothing done...';
1907 } // ... else go on, install...
1908 } // ... else go on, install...
1909 } else return 'No extension key in file. Strange...';
1910 } else return 'Wrong file format. No data recognized, '.$fetchData;
1911 } else return 'No file uploaded! Probably the file was too large for PHPs internal limit for uploadable files.';
1912 } else {
1913 $this->xmlhandler->searchExtensionsXMLExact($extKey, '', '', true, true);
1914
1915 // Fetch extension from TER:
1916 if(!strlen($version)) {
1917 $versions = array_keys($this->xmlhandler->extensionsXML[$extKey]['versions']);
1918 $version = end($versions);
1919 }
1920 $fetchData = $this->terConnection->fetchExtension($extKey, $version, $this->xmlhandler->extensionsXML[$extKey]['versions'][$version]['t3xfilemd5'], $this->getMirrorURL());
1921 }
1922
1923 // At this point the extension data should be present; so we want to write it to disc:
1924 if ($this->importAsType($loc)) {
1925 if (is_array($fetchData)) { // There was some data successfully transferred
1926 if ($fetchData[0]['extKey'] && is_array($fetchData[0]['FILES'])) {
1927 $extKey = $fetchData[0]['extKey'];
1928 if(!isset($fetchData[0]['EM_CONF']['constraints'])) $fetchData[0]['EM_CONF']['constraints'] = $this->xmlhandler->extensionsXML[$extKey]['versions'][$version]['dependencies'];
1929 $EM_CONF = $this->fixEMCONF($fetchData[0]['EM_CONF']);
1930 if (!$EM_CONF['lockType'] || !strcmp($EM_CONF['lockType'],$loc)) {
1931 // check dependencies, act accordingly if ext is loaded
1932 list($instExtInfo,)=$this->getInstalledExtensions();
1933 $depStatus = $this->checkDependencies($extKey, $EM_CONF, $instExtInfo);
1934 if(t3lib_extMgm::isLoaded($extKey) && !$depStatus['returnCode']) {
1935 $this->content .= $depStatus['html'];
1936 if ($uploadedTempFile) {
1937 $this->content .= '<input type="hidden" name="CMD[alreadyUploaded]" value="'.$uploadedTempFile.'" />';
1938 }
1939 } else {
1940 $res = $this->clearAndMakeExtensionDir($fetchData[0],$loc,$dontDelete);
1941 if (is_array($res)) {
1942 $extDirPath = trim($res[0]);
1943 if ($extDirPath && @is_dir($extDirPath) && substr($extDirPath,-1)=='/') {
1944
1945 $emConfFile = $this->construct_ext_emconf_file($extKey,$EM_CONF);
1946 $dirs = $this->extractDirsFromFileList(array_keys($fetchData[0]['FILES']));
1947
1948 $res = $this->createDirsInPath($dirs,$extDirPath);
1949 if (!$res) {
1950 $writeFiles = $fetchData[0]['FILES'];
1951 $writeFiles['ext_emconf.php']['content'] = $emConfFile;
1952 $writeFiles['ext_emconf.php']['content_md5'] = md5($emConfFile);
1953
1954 // Write files:
1955 foreach($writeFiles as $theFile => $fileData) {
1956 t3lib_div::writeFile($extDirPath.$theFile,$fileData['content']);
1957 if (!@is_file($extDirPath.$theFile)) {
1958 $content.='Error: File "'.$extDirPath.$theFile.'" could not be created!!!<br />';
1959 } elseif (md5(t3lib_div::getUrl($extDirPath.$theFile)) != $fileData['content_md5']) {
1960 $content.='Error: File "'.$extDirPath.$theFile.'" MD5 was different from the original files MD5 - so the file is corrupted!<br />';
1961 }
1962 }
1963
1964 // No content, no errors. Create success output here:
1965 if (!$content) {
1966 $content='SUCCESS: '.$extDirPath.'<br />';
1967
1968 $uploadSucceed = true;
1969
1970 // Fix TYPO3_MOD_PATH for backend modules in extension:
1971 $modules = t3lib_div::trimExplode(',',$EM_CONF['module'],1);
1972 if (count($modules)) {
1973 foreach($modules as $mD) {
1974 $confFileName = $extDirPath.$mD.'/conf.php';
1975 if (@is_file($confFileName)) {
1976 $content.= $this->writeTYPO3_MOD_PATH($confFileName,$loc,$extKey.'/'.$mD.'/').'<br />';
1977 } else $content.='Error: Couldn\'t find "'.$confFileName.'"<br />';
1978 }
1979 }
1980 // 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.
1981 // But this order of the code works.... (using the empty Array with type, EMCONF and files hereunder).
1982
1983 // Writing to ext_emconf.php:
1984 $sEMD5A = $this->serverExtensionMD5Array($extKey,array('type' => $loc, 'EM_CONF' => array(), 'files' => array()));
1985 $EM_CONF['_md5_values_when_last_written'] = serialize($sEMD5A);
1986 $emConfFile = $this->construct_ext_emconf_file($extKey,$EM_CONF);
1987 t3lib_div::writeFile($extDirPath.'ext_emconf.php',$emConfFile);
1988
1989 $content.='ext_emconf.php: '.$extDirPath.'ext_emconf.php<br />';
1990 $content.='Type: '.$loc.'<br />';
1991
1992 // Remove cache files:
1993 if (t3lib_extMgm::isLoaded($extKey)) {
1994 if ($this->removeCacheFiles()) {
1995 $content.='Cache-files are removed and will be re-written upon next hit<br />';
1996 }
1997
1998 list($new_list)=$this->getInstalledExtensions();
1999 $content.=$this->updatesForm($extKey,$new_list[$extKey],1,'index.php?CMD[showExt]='.$extKey.'&SET[singleDetails]=info');
2000 }
2001
2002 // Install / Uninstall:
2003 if(!$this->CMD['standAlone']) {
2004 $content.='<h3>Install / Uninstall Extension:</h3>';
2005 $content.= $new_list[$extKey] ?
2006 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->removeButton().' Uninstall extension</a>' :
2007 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[load]=1&CMD[clrCmd]=1&SET[singleDetails]=info').'">'.$this->installButton().' Install extension</a>';
2008 } else {
2009 $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>';
2010 }
2011
2012 }
2013 } else $content = $res;
2014 } else $content = 'Error: The extension path "'.$extDirPath.'" was different than expected...';
2015 } else $content = $res;
2016 }
2017 } else $content = 'Error: The extension can only be installed in the path '.$this->typePaths[$EM_CONF['lockType']].' (lockType='.$EM_CONF['lockType'].')';
2018 } else $content = 'Error: No extension key!!! Why? - nobody knows... (Or no files in the file-array...)';
2019 } else $content = 'Error: The datatransfer did not succeed. '.$fetchData;
2020 } else $content = 'Error: Installation is not allowed in this path ('.$this->typePaths[$loc].')';
2021
2022 $this->content.=$this->doc->section('Extension import results',$content,0,1);
2023
2024 if ($uploadSucceed && $uploadedTempFile) {
2025 t3lib_div::unlink_tempfile($uploadedTempFile);
2026 }
2027
2028 return false;
2029 }
2030
2031 /**
2032 * Display extensions details.
2033 *
2034 * @param string Extension key
2035 * @return void Writes content to $this->content
2036 */
2037 function showExtDetails($extKey) {
2038 global $TYPO3_LOADED_EXT;
2039
2040 list($list,)=$this->getInstalledExtensions();
2041 $absPath = $this->getExtPath($extKey,$list[$extKey]['type']);
2042
2043 // Check updateModule:
2044 if (isset($list[$extKey]) && @is_file($absPath.'class.ext_update.php')) {
2045 require_once($absPath.'class.ext_update.php');
2046 $updateObj = new ext_update;
2047 if (!$updateObj->access()) {
2048 unset($this->MOD_MENU['singleDetails']['updateModule']);
2049 }
2050 } else {
2051 unset($this->MOD_MENU['singleDetails']['updateModule']);
2052 }
2053
2054 if($this->CMD['doDelete']) {
2055 $this->MOD_MENU['singleDetails'] = array();
2056 }
2057
2058 // Function menu here:
2059 if(!$this->CMD['standAlone'] && !t3lib_div::_GP('standAlone')) {
2060 $content = 'Extension:&nbsp;<strong>' . $this->extensionTitleIconHeader($extKey, $list[$extKey]) . '</strong> (' . $extKey . ')';
2061 $this->content.= $this->doc->section('', $content);
2062 }
2063
2064 // Show extension details:
2065 if ($list[$extKey]) {
2066
2067 // Checking if a command for install/uninstall is executed:
2068 if (($this->CMD['remove'] || $this->CMD['load']) && !in_array($extKey,$this->requiredExt)) {
2069
2070 // Install / Uninstall extension here:
2071 if (t3lib_extMgm::isLocalconfWritable()) {
2072 // Check dependencies:
2073 $depStatus = $this->checkDependencies($extKey, $list[$extKey]['EM_CONF'], $list);
2074 if(!$this->CMD['remove'] && !$depStatus['returnCode']) {
2075 $this->content .= $depStatus['html'];
2076 $newExtList = -1;
2077 } elseif ($this->CMD['remove']) {
2078 $newExtList = $this->removeExtFromList($extKey,$list);
2079 } else {
2080 $newExtList = $this->addExtToList($extKey,$list);
2081 }
2082
2083 // Success-installation:
2084 if ($newExtList!=-1) {
2085 $updates = '';
2086 if ($this->CMD['load']) {
2087 if($_SERVER['REQUEST_METHOD'] == 'POST') {
2088 $script = t3lib_div::linkThisScript(array('CMD[showExt]' => $extKey, 'CMD[load]' => 1, 'CMD[clrCmd]' => $this->CMD['clrCmd'], 'SET[singleDetails]' => 'info'));
2089 } else {
2090 $script = '';
2091 }
2092 if($this->CMD['standAlone']) {
2093 $standaloneUpdates = '<input type="hidden" name="standAlone" value="1" />';
2094 }
2095 $depsolver = t3lib_div::_POST('depsolver');
2096 if(is_array($depsolver['ignore'])) {
2097 foreach($depsolver['ignore'] as $depK => $depV) {
2098 $dependencyUpdates .= '<input type="hidden" name="depsolver[ignore]['.$depK.']" value="1" />';
2099 }
2100 }
2101 $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'].'" />');
2102 if ($updatesForm) {
2103 $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;
2104 $this->content.=$this->doc->section('Installing '.$this->extensionTitleIconHeader($extKey,$list[$extKey]).strtoupper(': Database needs to be updated'),$updates,1,1,1,1);
2105 }
2106 } elseif ($this->CMD['remove']) {
2107 $updates.= $this->checkClearCache($list[$extKey]);
2108 if ($updates) {
2109 $updates = '
2110 <form action="'.t3lib_div::linkThisScript().'" method="post">'.$updates.'
2111 <br /><input type="submit" name="write" value="Remove extension" />
2112 <input type="hidden" name="_do_install" value="1" />
2113 <input type="hidden" name="_clrCmd" value="'.$this->CMD['clrCmd'].'" />
2114 <input type="hidden" name="standAlone" value="'.$this->CMD['standAlone'].'" />
2115 </form>';
2116 $this->content.=$this->doc->section('Removing '.$this->extensionTitleIconHeader($extKey,$list[$extKey]).strtoupper(': Database needs to be updated'),$updates,1,1,1,1);
2117 }
2118 }
2119 if (!$updates || t3lib_div::_GP('_do_install')) {
2120 $this->writeNewExtensionList($newExtList);
2121 $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')));
2122 if ($this->CMD['clrCmd'] || t3lib_div::_GP('_clrCmd')) {
2123 if ($this->CMD['load'] && @is_file($absPath.'ext_conf_template.txt')) {
2124 $vA = array('CMD'=>Array('showExt'=>$extKey));
2125 } else {
2126 $vA = array('CMD'=>'');
2127 }
2128 } else {
2129 $vA = array('CMD'=>Array('showExt'=>$extKey));
2130 }
2131 if($this->CMD['standAlone'] || t3lib_div::_GP('standAlone')) {
2132 $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>';
2133 } else {
2134 // Determine if new modules were installed:
2135 $techInfo = $this->makeDetailedExtensionAnalysis($extKey, $list[$extKey]);
2136 if (($this->CMD['load'] || $this->CMD['remove']) && is_array($techInfo['flags']) && in_array('Module', $techInfo['flags'], true)) {
2137 $vA['CMD']['refreshMenu'] = 1;
2138 }
2139 header('Location: '.t3lib_div::linkThisScript($vA));
2140 }
2141 }
2142 }
2143 } else {
2144 $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);
2145 }
2146
2147 } elseif ($this->CMD['downloadFile'] && !in_array($extKey,$this->requiredExt)) {
2148
2149 // Link for downloading extension has been clicked - deliver content stream:
2150 $dlFile = $this->CMD['downloadFile'];
2151 if (t3lib_div::isFirstPartOfStr($dlFile,PATH_site) && t3lib_div::isFirstPartOfStr($dlFile,$absPath) && @is_file($dlFile)) {
2152 $mimeType = 'application/octet-stream';
2153 Header('Content-Type: '.$mimeType);
2154 Header('Content-Disposition: attachment; filename='.basename($dlFile));
2155 echo t3lib_div::getUrl($dlFile);
2156 exit;
2157 } else die('Error while trying to download extension file...');
2158
2159 } elseif ($this->CMD['editFile'] && !in_array($extKey,$this->requiredExt)) {
2160
2161 // Editing extension file:
2162 $editFile = $this->CMD['editFile'];
2163 if (t3lib_div::isFirstPartOfStr($editFile,PATH_site) && t3lib_div::isFirstPartOfStr($editFile,$absPath)) { // Paranoia...
2164
2165 $fI = t3lib_div::split_fileref($editFile);
2166 if (@is_file($editFile) && t3lib_div::inList($this->editTextExtensions,($fI['fileext']?$fI['fileext']:$fI['filebody']))) {
2167 if (filesize($editFile)<($this->kbMax*1024)) {
2168 $outCode = '<form action="index.php" method="post" name="editfileform">';
2169 $info = '';
2170 $submittedContent = t3lib_div::_POST('edit');
2171 $saveFlag = 0;
2172
2173 if(isset($submittedContent['file']) && !$GLOBALS['TYPO3_CONF_VARS']['EXT']['noEdit']) { // Check referer here?
2174 $oldFileContent = t3lib_div::getUrl($editFile);
2175 if($oldFileContent != $submittedContent['file']) {
2176 $oldMD5 = md5(str_replace(chr(13),'',$oldFileContent));
2177 $info.= 'MD5: <b>'.$oldMD5.'</b> (Previous File)<br />';
2178 t3lib_div::writeFile($editFile,$submittedContent['file']);
2179 $saveFlag = 1;
2180 } else {
2181 $info .= 'No changes to the file detected!<br />';
2182 }
2183 }
2184
2185 $fileContent = t3lib_div::getUrl($editFile);
2186
2187 $outCode.= 'File: <b>'.substr($editFile,strlen($absPath)).'</b> ('.t3lib_div::formatSize(filesize($editFile)).')<br />';
2188 $fileMD5 = md5(str_replace(chr(13),'',$fileContent));
2189 $info.= 'MD5: <b>'.$fileMD5.'</b> (Current File)<br />';
2190 if($saveFlag) {
2191 $saveMD5 = md5(str_replace(chr(13),'',$submittedContent['file']));
2192 $info.= 'MD5: <b>'.$saveMD5.'</b> (Submitted)<br />';
2193 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 />';
2194 else $info.= $GLOBALS['TBE_TEMPLATE']->rfw('<br /><strong>File saved.</strong>').'<br />';
2195 }
2196
2197 $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>';
2198 $outCode.= '<input type="hidden" name="edit[filename]" value="'.$editFile.'" />';
2199 $outCode.= '<input type="hidden" name="CMD[editFile]" value="'.htmlspecialchars($editFile).'" />';
2200 $outCode.= '<input type="hidden" name="CMD[showExt]" value="'.$extKey.'" />';
2201 $outCode.= $info;
2202
2203 if (!$GLOBALS['TYPO3_CONF_VARS']['EXT']['noEdit']) {
2204 $outCode.='<br /><input type="submit" name="save_file" value="Save file" />';
2205 } else $outCode.=$GLOBALS['TBE_TEMPLATE']->rfw('<br />[SAVING IS DISABLED - can be enabled by the $TYPO3_CONF_VARS[\'EXT\'][\'noEdit\']-flag] ');
2206
2207 $onClick = 'window.location.href=\'index.php?CMD[showExt]='.$extKey.'\';return false;';
2208 $outCode.='<input type="submit" name="cancel" value="Cancel" onclick="'.htmlspecialchars($onClick).'" /></form>';
2209
2210 $theOutput.=$this->doc->spacer(15);
2211 $theOutput.=$this->doc->section('Edit file:','',0,1);
2212 $theOutput.=$this->doc->sectionEnd().$outCode;
2213 $this->content.=$theOutput;
2214 } else {
2215 $theOutput.=$this->doc->spacer(15);
2216 $theOutput.=$this->doc->section('Filesize exceeded '.$this->kbMax.' Kbytes','Files larger than '.$this->kbMax.' KBytes are not allowed to be edited.');
2217 }
2218 }
2219 } else die('Fatal Edit error: File "'.$editFile.'" was not inside the correct path of the TYPO3 Extension!');
2220 } else {
2221
2222 // MAIN:
2223 switch((string)$this->MOD_SETTINGS['singleDetails']) {
2224 case 'info':
2225 // Loaded / Not loaded:
2226 if (!in_array($extKey,$this->requiredExt)) {
2227 if ($TYPO3_LOADED_EXT[$extKey]) {
2228 $content = '<strong>The extension is installed (loaded and running)!</strong><br />'.
2229 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[remove]=1').'">Click here to remove the extension: '.$this->removeButton().'</a>';
2230 } else {
2231 $content = 'The extension is <strong>not</strong> installed yet.<br />'.
2232 '<a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[load]=1').'">Click here to install the extension: '.$this->installButton().'</a>';
2233 }
2234 } else {
2235 $content = 'This extension is entered in the TYPO3_CONF_VARS[SYS][requiredExt] list and is therefore always loaded.';
2236 }
2237 $this->content.=$this->doc->spacer(10);
2238 $this->content.=$this->doc->section('Active status:',$content,0,1);
2239
2240 if (t3lib_extMgm::isLoaded($extKey)) {
2241 $updates=$this->updatesForm($extKey,$list[$extKey]);
2242 if ($updates) {
2243 $this->content.=$this->doc->spacer(10);
2244 $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);
2245 }
2246 }
2247
2248 // Config:
2249 if (@is_file($absPath.'ext_conf_template.txt')) {
2250 $this->content.=$this->doc->spacer(10);
2251 $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);
2252
2253 $this->tsStyleConfigForm($extKey, $list[$extKey]);
2254 }
2255
2256 // Show details:
2257 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'info', $GLOBALS['BACK_PATH'], '|<br />');
2258 $content.= $this->extInformationArray($extKey,$list[$extKey]);
2259
2260 $this->content.=$this->doc->spacer(10);
2261 $this->content.=$this->doc->section('Details:',$content,0,1);
2262 break;
2263 case 'upload':
2264 $em = t3lib_div::_POST('em');
2265 if($em['action'] == 'doUpload') {
2266 $em['extKey'] = $extKey;
2267 $em['extInfo'] = $list[$extKey];
2268 $content = $this->uploadExtensionToTER($em);
2269 $content .= $this->doc->spacer(10);
2270 // Must reload this, because EM_CONF information has been updated!
2271 list($list,)=$this->getInstalledExtensions();
2272 } else {
2273 // CSH:
2274 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'upload', $GLOBALS['BACK_PATH'], '|<br />');
2275
2276 // Upload:
2277 if (substr($extKey,0,5)!='user_') {
2278 $content.= $this->getRepositoryUploadForm($extKey,$list[$extKey]);
2279 $eC=0;
2280 } else {
2281 $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.';
2282 $eC=2;
2283 }
2284 if (!$this->fe_user['username']) {
2285 $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 />';
2286 }
2287 }
2288 $this->content.=$this->doc->section('Upload extension to repository',$content,0,1,$eC);
2289 break;
2290 case 'backup':
2291 if($this->CMD['doDelete']) {
2292 $content = $this->extDelete($extKey,$list[$extKey]);
2293 $this->content.=$this->doc->section('Delete',$content,0,1);
2294 } else {
2295 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'backup_delete', $GLOBALS['BACK_PATH'], '|<br />');
2296 $content.= $this->extBackup($extKey,$list[$extKey]);
2297 $this->content.=$this->doc->section('Backup',$content,0,1);
2298
2299 $content = $this->extDelete($extKey,$list[$extKey]);
2300 $this->content.=$this->doc->section('Delete',$content,0,1);
2301
2302 $content = $this->extUpdateEMCONF($extKey,$list[$extKey]);
2303 $this->content.=$this->doc->section('Update EM_CONF',$content,0,1);
2304 }
2305 break;
2306 case 'dump':
2307 $this->extDumpTables($extKey,$list[$extKey]);
2308 break;
2309 case 'edit':
2310 $content = t3lib_BEfunc::cshItem('_MOD_tools_em', 'editfiles', $GLOBALS['BACK_PATH'], '|<br />');
2311 $content.= $this->getFileListOfExtension($extKey,$list[$extKey]);
2312
2313 $this->content.=$this->doc->section('Extension files',$content,0,1);
2314 break;
2315 case 'updateModule':
2316 $this->content.=$this->doc->section('Update:',is_object($updateObj) ? $updateObj->main() : 'No update object',0,1);
2317 break;
2318 default:
2319 $this->extObjContent();
2320 break;
2321 }
2322 }
2323 }
2324 }
2325
2326 /**
2327 * Outputs a screen from where you can install multiple extensions in one go
2328 * This can be called from external modules with "...index.php?CMD[requestInstallExtensions]=
2329 *
2330 * @param string Comma list of extension keys to install. Renders a screen with checkboxes for all extensions not already imported or installed
2331 * @return void
2332 */
2333 function requestInstallExtensions($extList) {
2334
2335 // Return URL:
2336 $returnUrl = t3lib_div::_GP('returnUrl');
2337 $installOrImportExtension = t3lib_div::_POST('installOrImportExtension');
2338
2339 // Extension List:
2340 $extArray = explode(',',$extList);
2341 $outputRow = array();
2342 $outputRow[] = '
2343 <tr class="bgColor5 tableheader">
2344 <td>Install/Import:</td>
2345 <td>Extension Key:</td>
2346 </tr>
2347 ';
2348
2349 foreach($extArray as $extKey) {
2350
2351 // Check for the request:
2352 if ($installOrImportExtension[$extKey]) {
2353 $this->installExtension($extKey);
2354 }
2355
2356 // Display:
2357 if (!t3lib_extMgm::isLoaded($extKey)) {
2358 $outputRow[] = '
2359 <tr class="bgColor4">
2360 <td><input type="checkbox" name="'.htmlspecialchars('installOrImportExtension['.$extKey.']').'" value="1" checked="checked" id="check_'.$extKey.'" /></td>
2361 <td><label for="check_'.$extKey.'">'.htmlspecialchars($extKey).'</label></td>
2362 </tr>
2363 ';
2364 }
2365 }
2366
2367 if (count($outputRow)>1 || !$returnUrl) {
2368 $content = '
2369 <!-- ending page form ... -->
2370 <form action="'.htmlspecialchars(t3lib_div::getIndpEnv('REQUEST_URI')).'" method="post">
2371 <table border="0" cellpadding="1" cellspacing="1">'.implode('',$outputRow).'</table>
2372 <input type="submit" name="_" value="Import and Install selected" />
2373 </form>';
2374
2375 if ($returnUrl) {
2376 $content.= '
2377 <br />
2378 <br />
2379 <a href="'.htmlspecialchars($returnUrl).'">Return</a>
2380 ';
2381 }
2382
2383 $this->content.= $this->doc->section('Import/Install Extensions:',$content,0,1);
2384 } else {
2385 header('Location: '.t3lib_div::locationHeaderUrl($returnUrl));
2386 }
2387 }
2388
2389
2390
2391
2392
2393
2394
2395
2396 /***********************************
2397 *
2398 * Application Sub-functions (HTML parts)
2399 *
2400 **********************************/
2401
2402 /**
2403 * Creates a form for an extension which contains all options for configuration, updates of database, clearing of cache etc.
2404 * This form is shown when
2405 *
2406 * @param string Extension key
2407 * @param array Extension information array
2408 * @param boolean If set, the form will ONLY show if fields/tables should be updated (suppressing forms like general configuration and cache clearing).
2409 * @param string Alternative action=""-script
2410 * @param string HTML: Additional form fields
2411 * @return string HTML
2412 */
2413 function updatesForm($extKey,$extInfo,$notSilent=0,$script='',$addFields='') {
2414 $script = $script ? $script : t3lib_div::linkThisScript();
2415 $updates.= $this->checkDBupdates($extKey,$extInfo);
2416 $uCache = $this->checkClearCache($extInfo);
2417 if ($notSilent) $updates.= $uCache;
2418 $updates.= $this->checkUploadFolder($extKey,$extInfo);
2419
2420 $absPath = $this->getExtPath($extKey, $extInfo['type']);
2421 if ($notSilent && @is_file($absPath.'ext_conf_template.txt')) {
2422 $configForm = $this->tsStyleConfigForm($extKey, $extInfo, 1, $script, $updates.$addFields.'<br />');
2423 }
2424
2425 if ($updates || $configForm) {
2426 if ($configForm) {
2427 $updates = '</form>'.$configForm.'<form>';
2428 } else {
2429 $updates = '</form><form action="'.htmlspecialchars($script).'" method="post">'.$updates.$addFields.'
2430 <br /><input type="submit" name="write" value="Make updates" />
2431 ';
2432 }
2433 }
2434
2435 return $updates;
2436 }
2437
2438 /**
2439 * Creates view for dumping static tables and table/fields structures...
2440 *
2441 * @param string Extension key
2442 * @param array Extension information array
2443 * @return void
2444 */
2445 function extDumpTables($extKey,$extInfo) {
2446
2447 // Get dbInfo which holds the structure known from the tables.sql file
2448 $techInfo = $this->makeDetailedExtensionAnalysis($extKey,$extInfo);
2449 $absPath = $this->getExtPath($extKey,$extInfo['type']);
2450
2451 // Static tables:
2452 if (is_array($techInfo['static'])) {
2453 if ($this->CMD['writeSTATICdump']) { // Writing static dump:
2454 $writeFile = $absPath.'ext_tables_static+adt.sql';
2455 if (@is_file($writeFile)) {
2456 $dump_static = $this->dumpStaticTables(implode(',',$techInfo['static']));
2457 t3lib_div::writeFile($writeFile,$dump_static);
2458 $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);
2459 }
2460 } else { // Showing info about what tables to dump - and giving the link to execute it.
2461 $msg = 'Dumping table content for static tables:<br />';
2462 $msg.= '<br />'.implode('<br />',$techInfo['static']).'<br />';
2463
2464 // ... then feed that to this function which will make new CREATE statements of the same fields but based on the current database content.
2465 $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);
2466 $this->content.=$this->doc->spacer(20);
2467 }
2468 }
2469
2470 // Table and field definitions:
2471 if (is_array($techInfo['dump_tf'])) {
2472 $dump_tf_array = $this->getTableAndFieldStructure($techInfo['dump_tf']);
2473 $dump_tf = $this->dumpTableAndFieldStructure($dump_tf_array);
2474 if ($this->CMD['writeTFdump']) {
2475 $writeFile = $absPath.'ext_tables.sql';
2476 if (@is_file($writeFile)) {
2477 t3lib_div::writeFile($writeFile,$dump_tf);
2478 $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);
2479 }
2480 } else {
2481 $msg = 'Dumping current database structure for:<br />';
2482 if (is_array($techInfo['tables'])) {
2483 $msg.= '<br /><strong>Tables:</strong><br />'.implode('<br />',$techInfo['tables']).'<br />';
2484 }
2485 if (is_array($techInfo['fields'])) {
2486 $msg.= '<br /><strong>Solo-fields:</strong><br />'.implode('<br />',$techInfo['fields']).'<br />';
2487 }
2488
2489 // ... then feed that to this function which will make new CREATE statements of the same fields but based on the current database content.
2490 $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 />
2491 <pre>'.htmlspecialchars($dump_tf).'</pre>',0,1);
2492
2493
2494 $details = ' This dump is based on two factors:<br />
2495 <ul>
2496 <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>
2497 <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>
2498 </ul>
2499 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 />';
2500 $this->content.=$this->doc->section('',$details);
2501 }
2502 }
2503 }
2504
2505 /**
2506 * Returns file-listing of an extension
2507 *
2508 * @param string Extension key
2509 * @param array Extension information array
2510 * @return string HTML table.
2511 */
2512 function getFileListOfExtension($extKey,$conf) {
2513 $content = '';
2514 $extPath = $this->getExtPath($extKey,$conf['type']);
2515
2516 if ($extPath) {
2517 // Read files:
2518 $fileArr = array();
2519 $fileArr = t3lib_div::getAllFilesAndFoldersInPath($fileArr,$extPath,'',0,99,$this->excludeForPackaging);
2520
2521 // Start table:
2522 $lines = array();
2523 $totalSize = 0;
2524
2525 // Header:
2526 $lines[] = '
2527 <tr class="bgColor5">
2528 <td>File:</td>
2529 <td>Size:</td>
2530 <td>Edit:</td>
2531 </tr>';
2532
2533 foreach($fileArr as $file) {
2534 $fI = t3lib_div::split_fileref($file);
2535 $lines[] = '
2536 <tr class="bgColor4">
2537 <td><a href="'.htmlspecialchars('index.php?CMD[showExt]='.$extKey.'&CMD[downloadFile]='.rawurlencode($file)).'" title="Download...">'.substr($file,strlen($extPath)).'</a></td>
2538 <td>'.t3lib_div::formatSize(filesize($file)).'</td>
2539 <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>
2540 </tr>';
2541 $totalSize+=filesize($file);
2542 }
2543
2544 $lines[] = '
2545 <tr class="bgColor6">
2546 <td><strong>Total:</strong></td>
2547 <td><strong>'.t3lib_div::formatSize($totalSize).'</strong></td>
2548 <td>&nbsp;</td>
2549 </tr>';
2550
2551 $content = '
2552 Path: '.$extPath.'<br /><br />
2553 <table border="0" cellpadding="1" cellspacing="2">'.implode('',$lines).'</table>';
2554 }
2555
2556 return $content;
2557 }
2558
2559 /**
2560 * Delete extension from the file system
2561 *
2562 * @param string Extension key
2563 * @param array Extension info array
2564 * @return string Returns message string about the status of the operation
2565 */
2566 function extDelete($extKey,$extInfo) {
2567 $absPath = $this->getExtPath($extKey,$extInfo['type']);
2568 if (t3lib_extMgm::isLoaded($extKey)) {
2569 return 'This extension is currently installed (loaded and active) and so cannot be deleted!';
2570 } elseif (!$this->deleteAsType($extInfo['type'])) {
2571 return 'You cannot delete (and install/update) extensions in the '.$this->typeLabels[$extInfo['type']].' scope.';
2572 } elseif (t3lib_div::inList('G,L',$extInfo['type'])) {
2573 if ($this->CMD['doDelete'] && !strcmp($absPath,$this->CMD['absPath'])) {
2574 $res = $this->removeExtDirectory($absPath);
2575 if ($res) {
2576 return 'ERROR: Could not remove extension directory "'.$absPath.'". Had the following errors:<br /><br />'.
2577 nl2br($res);
2578 } else {
2579 return 'Removed extension in path "'.$absPath.'"!';
2580 }
2581 } else {
2582 $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)."';}";
2583 $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>';
2584 $content.= '<br /><br />(Maybe you should make a backup first, see above.)';
2585 return $content;
2586 }
2587 } else return 'Extension is not a global or local extension and cannot be removed.';
2588 }
2589
2590 /**
2591 * Update extension EM_CONF...
2592 *
2593 * @param string Extension key
2594 * @param array Extension information array
2595 * @return string HTML content.
2596 */
2597 function extUpdateEMCONF($extKey,$extInfo) {
2598 $absPath = $this->getExtPath($extKey,$extInfo['type']);
2599 if ($this->CMD['doUpdateEMCONF']) {
2600 return $this->updateLocalEM_CONF($extKey,$extInfo);
2601 } else {
2602 $onClick = "if (confirm('Are you sure you want to update EM_CONF?')) {window.location.href='index.php?CMD[showExt]=".$extKey."&CMD[doUpdateEMCONF]=1';}";
2603 $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>';
2604 $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 />
2605 Updating this file will first of all reset this registration.';
2606 return $content;
2607 }
2608 }
2609
2610 /**
2611 * Download extension as file / make backup
2612 *
2613 * @param string Extension key
2614 * @param array Extension information array
2615 * @return string HTML content
2616 */
2617 function extBackup($extKey,$extInfo) {
2618 $uArr = $this->makeUploadArray($extKey,$extInfo);
2619 if (is_array($uArr)) {
2620 $backUpData = $this->terConnection->makeUploadDataFromArray($uArr);
2621 $filename = 'T3X_'.$extKey.'-'.str_replace('.','_',$extInfo['EM_CONF']['version']).'-z-'.date('YmdHi').'.t3x';
2622 if (intval($this->CMD['doBackup'])==1) {
2623 header('Content-Type: application/octet-stream');
2624 header('Content-Disposition: attachment; filename='.$filename);
2625 echo $backUpData;
2626 exit;
2627 } elseif ($this->CMD['dumpTables']) {
2628 $filename='T3X_'.$extKey;
2629 $cTables = count(explode(',',$this->CMD['dumpTables']));
2630 if ($cTables>1) {
2631 $filename.='-'.$cTables.'tables';
2632 } else {
2633 $filename.='-'.$this->CMD['dumpTables'];
2634 }
2635 $filename.='+adt.sql';
2636
2637 header('Content-Type: application/octet-stream');
2638 header('Content-Disposition: attachment; filename='.$filename);
2639 echo $this->dumpStaticTables($this->CMD['dumpTables']);
2640 exit;
2641 } else {
2642 $techInfo = $this->makeDetailedExtensionAnalysis($extKey,$extInfo);
2643 $lines=array();
2644 $lines[]='<tr class="bgColor5"><td colspan="2"><strong>Make selection:</strong></td></tr>';
2645 $lines[]='<tr class="bgColor4"><td><strong>Extension files:</strong></td><td>'.
2646 '<a href="'.htmlspecialchars('index.php?CMD[doBackup]=1&CMD[showExt]='.$extKey).'">Download extension &