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