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