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