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(&apo