[BUGFIX] Re-read extListArray in em at runtime
[Packages/TYPO3.CMS.git] / typo3 / sysext / em / classes / tools / class.tx_em_tools.php
1 <?php
2 /* **************************************************************
3 * Copyright notice
4 *
5 * (c) webservices.nl
6 * (c) 2006-2010 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 * class.tx_em_tools.php
30 */
31
32 /**
33 * Static tools for extension manager
34 * Some of them should be moved later to t3lib static libraries
35 *
36 */
37 final class tx_em_Tools {
38
39 /**
40 * Keeps default categories.
41 *
42 * @var array
43 */
44 protected static $defaultCategories = array(
45 'be' => 0,
46 'module' => 1,
47 'fe' => 2,
48 'plugin' => 3,
49 'misc' => 4,
50 'services' => 5,
51 'templates' => 6,
52 'doc' => 8,
53 'example' => 9,
54 );
55 /**
56 * Keeps default states.
57 *
58 * @var array
59 */
60 protected static $defaultStates = array(
61 'alpha' => 0,
62 'beta' => 1,
63 'stable' => 2,
64 'experimental' => 3,
65 'test' => 4,
66 'obsolete' => 5,
67 'excludeFromUpdates' => 6,
68 'n/a' => 999,
69 );
70
71 /**
72 * Colors for states
73 *
74 * @var array
75 */
76 protected static $stateColors = array(
77 'alpha' => '#d12438',
78 'beta' => '#97b17e',
79 'stable' => '#3bb65c',
80 'experimental' => '#007eba',
81 'test' => '#979797',
82 'obsolete' => '#000000',
83 'excludeFromUpdates' => '#cf7307'
84 );
85
86 /**
87 * Gets the stateColor array
88 *
89 * @static
90 * @return array
91 */
92 public static function getStateColors() {
93 return self::$stateColors;
94 }
95
96 /**
97 * Unzips a zip file in the given path.
98 *
99 * Uses unzip binary if available, otherwise a pure PHP unzip is used.
100 *
101 * @param string $file Full path to zip file
102 * @param string $path Path to change to before extracting
103 * @return boolean TRUE on success, FALSE in failure
104 */
105 public static function unzip($file, $path) {
106 $unzipPath = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['unzip_path']);
107 if (strlen($unzipPath)) {
108 if (substr($unzipPath, -1) !== '/' && is_dir($unzipPath)) {
109 // Make sure the path ends with a slash
110 $unzipPath.= '/';
111 }
112
113 chdir($path);
114 // for compatiblity reasons, we have to accept the full path of the unzip command
115 // or the directory containing the unzip binary
116 if (substr($unzipPath, -1) === '/') {
117 $cmd = $unzipPath . 'unzip -o ' . escapeshellarg($file);
118 } else {
119 $cmd = $unzipPath . ' -o ' . escapeshellarg($file);
120 }
121 t3lib_utility_Command::exec($cmd, $list, $ret);
122 return ($ret === 0);
123 } else {
124 // we use a pure PHP unzip
125 $unzip = t3lib_div::makeInstance('tx_em_Tools_Unzip', $file);
126 $ret = $unzip->extract(array('add_path' => $path));
127 return (is_array($ret));
128 }
129 }
130
131
132 /**
133 * Refreshes the global extension list
134 *
135 * @return void
136 */
137 public static function refreshGlobalExtList() {
138 // Set new extlist / extlistArray for extension load changes at runtime
139 $localConfiguration = t3lib_Configuration::getLocalConfiguration();
140 $GLOBALS['TYPO3_CONF_VARS']['EXT']['extList'] = $localConfiguration['EXT']['extList'];
141 $GLOBALS['TYPO3_CONF_VARS']['EXT']['extListArray'] = $localConfiguration['EXT']['extListArray'];
142
143 Typo3_Bootstrap::getInstance()
144 ->populateTypo3LoadedExtGlobal(FALSE)
145 ->loadAdditionalConfigurationFromExtensions(FALSE);
146 }
147
148 /**
149 * Set category array entries for extension
150 *
151 * @param array Category index array
152 * @param array Part of list array for extension.
153 * @param string Extension key
154 * @return array Modified category index array
155 */
156 public static function setCat(&$cat, $listArrayPart, $extKey) {
157
158 // Getting extension title:
159 $extTitle = $listArrayPart['EM_CONF']['title'];
160
161 // Category index:
162 $index = $listArrayPart['EM_CONF']['category'];
163 $cat['cat'][$index][$extKey] = $extTitle;
164
165 // Author index:
166 $index = $listArrayPart['EM_CONF']['author'] . ($listArrayPart['EM_CONF']['author_company'] ? ', ' . $listArrayPart['EM_CONF']['author_company'] : '');
167 $cat['author_company'][$index][$extKey] = $extTitle;
168
169 // State index:
170 $index = $listArrayPart['EM_CONF']['state'];
171 $cat['state'][$index][$extKey] = $extTitle;
172
173 // Type index:
174 $index = $listArrayPart['type'];
175 $cat['type'][$index][$extKey] = $extTitle;
176
177 // Return categories:
178 return $cat;
179 }
180
181 /**
182 * Returns upload folder for extension
183 *
184 * @param string Extension key
185 * @return string Upload folder for extension
186 */
187 public static function uploadFolder($extKey) {
188 return 'uploads/tx_' . str_replace('_', '', $extKey) . '/';
189 }
190
191
192 /**
193 * Returns image tag for "uninstall"
194 *
195 * @return string <img> tag
196 */
197 public static function removeButton() {
198 return t3lib_iconWorks::getSpriteIcon('actions-system-extension-uninstall', array('title' => $GLOBALS['LANG']->getLL('ext_details_remove_ext')));
199 }
200
201 /**
202 * Returns image for "install"
203 *
204 * @return string <img> tag
205 */
206 public static function installButton() {
207 return t3lib_iconWorks::getSpriteIcon('actions-system-extension-install', array('title' => $GLOBALS['LANG']->getLL('helperFunction_install_extension')));
208 }
209
210 /**
211 * Warning (<img> + text string) message about the impossibility to import extensions (both local and global locations are disabled...)
212 *
213 * @return string <img> + text string.
214 */
215 public static function noImportMsg() {
216 return t3lib_iconWorks::getSpriteIcon('status-dialog-warning') .
217 '<strong>' . $GLOBALS['LANG']->getLL('helperFunction_import_not_possible') . '</strong>';
218 }
219
220
221 /**
222 * Fixes an old style ext_emconf.php array by adding constraints if needed and removing deprecated keys
223 *
224 * @param array $emConf
225 * @return array
226 */
227 public static function fixEMCONF($emConf) {
228 if (!isset($emConf['constraints']) || !isset($emConf['constraints']['depends']) || !isset($emConf['constraints']['conflicts']) || !isset($emConf['constraints']['suggests'])) {
229 if (!isset($emConf['constraints']) || !isset($emConf['constraints']['depends'])) {
230 $emConf['constraints']['depends'] = self::stringToDep($emConf['dependencies']);
231 if (strlen($emConf['PHP_version'])) {
232 $versionRange = self::splitVersionRange($emConf['PHP_version']);
233 if (version_compare($versionRange[0], '3.0.0', '<')) {
234 $versionRange[0] = '3.0.0';
235 }
236 if (version_compare($versionRange[1], '3.0.0', '<')) {
237 $versionRange[1] = '0.0.0';
238 }
239 $emConf['constraints']['depends']['php'] = implode('-', $versionRange);
240 }
241 if (strlen($emConf['TYPO3_version'])) {
242 $versionRange = self::splitVersionRange($emConf['TYPO3_version']);
243 if (version_compare($versionRange[0], '3.5.0', '<')) {
244 $versionRange[0] = '3.5.0';
245 }
246 if (version_compare($versionRange[1], '3.5.0', '<')) {
247 $versionRange[1] = '0.0.0';
248 }
249 $emConf['constraints']['depends']['typo3'] = implode('-', $versionRange);
250 }
251 }
252 if (!isset($emConf['constraints']) || !isset($emConf['constraints']['conflicts'])) {
253 $emConf['constraints']['conflicts'] = self::stringToDep($emConf['conflicts']);
254 }
255 if (!isset($emConf['constraints']) || !isset($emConf['constraints']['suggests'])) {
256 $emConf['constraints']['suggests'] = array();
257 }
258 } elseif (isset($emConf['constraints']) && isset($emConf['dependencies'])) {
259 $emConf['suggests'] = isset($emConf['suggests']) ? $emConf['suggests'] : array();
260 $emConf['dependencies'] = self::depToString($emConf['constraints']);
261 $emConf['conflicts'] = self::depToString($emConf['constraints'], 'conflicts');
262 }
263
264 // sanity check for version numbers, intentionally only checks php and typo3
265 if (isset($emConf['constraints']['depends']) && isset($emConf['constraints']['depends']['php'])) {
266 $versionRange = self::splitVersionRange($emConf['constraints']['depends']['php']);
267 if (version_compare($versionRange[0], '3.0.0', '<')) {
268 $versionRange[0] = '3.0.0';
269 }
270 if (version_compare($versionRange[1], '3.0.0', '<')) {
271 $versionRange[1] = '0.0.0';
272 }
273 $emConf['constraints']['depends']['php'] = implode('-', $versionRange);
274 }
275 if (isset($emConf['constraints']['depends']) && isset($emConf['constraints']['depends']['typo3'])) {
276 $versionRange = self::splitVersionRange($emConf['constraints']['depends']['typo3']);
277 if (version_compare($versionRange[0], '3.5.0', '<')) {
278 $versionRange[0] = '3.5.0';
279 }
280 if (version_compare($versionRange[1], '3.5.0', '<')) {
281 $versionRange[1] = '0.0.0';
282 }
283 $emConf['constraints']['depends']['typo3'] = implode('-', $versionRange);
284 }
285
286 unset($emConf['private']);
287 unset($emConf['download_password']);
288 unset($emConf['TYPO3_version']);
289 unset($emConf['PHP_version']);
290
291 return $emConf;
292 }
293
294
295 /**
296 * Returns the $EM_CONF array from an extensions ext_emconf.php file
297 *
298 * @param string Absolute path to EMCONF file.
299 * @param string Extension key.
300 * @return array EMconf array values.
301 */
302 public static function includeEMCONF($path, $_EXTKEY) {
303 $EM_CONF = NULL;
304 include($path);
305 if (is_array($EM_CONF[$_EXTKEY])) {
306 return self::fixEMCONF($EM_CONF[$_EXTKEY]);
307 }
308 return FALSE;
309 }
310
311
312 /**
313 * Extracts the directories in the $files array
314 *
315 * @param array Array of files / directories
316 * @return array Array of directories from the input array.
317 */
318 public static function extractDirsFromFileList($files) {
319 $dirs = array();
320
321 if (is_array($files)) {
322 // Traverse files / directories array:
323 foreach ($files as $file) {
324 if (substr($file, -1) == '/') {
325 $dirs[$file] = $file;
326 } else {
327 $pI = pathinfo($file);
328 if (strcmp($pI['dirname'], '') && strcmp($pI['dirname'], '.')) {
329 $dirs[$pI['dirname'] . '/'] = $pI['dirname'] . '/';
330 }
331 }
332 }
333 }
334 return $dirs;
335 }
336
337 /**
338 * Splits a version range into an array.
339 *
340 * If a single version number is given, it is considered a minimum value.
341 * If a dash is found, the numbers left and right are considered as minimum and maximum. Empty values are allowed.
342 *
343 * @param string $ver A string with a version range.
344 * @return array
345 */
346 public static function splitVersionRange($ver) {
347 $versionRange = array();
348 if (strstr($ver, '-')) {
349 $versionRange = explode('-', $ver, 2);
350 } else {
351 $versionRange[0] = $ver;
352 $versionRange[1] = '';
353 }
354
355 if (!$versionRange[0]) {
356 $versionRange[0] = '0.0.0';
357 }
358 if (!$versionRange[1]) {
359 $versionRange[1] = '0.0.0';
360 }
361
362 return $versionRange;
363 }
364
365 /**
366 * Checks whether the passed dependency is TER2-style (array) and returns a single string for displaying the dependencies.
367 *
368 * It leaves out all version numbers and the "php" and "typo3" dependencies, as they are implicit and of no interest without the version number.
369 *
370 * @param mixed $dep Either a string or an array listing dependencies.
371 * @param string $type The dependency type to list if $dep is an array
372 * @return string A simple dependency list for display
373 */
374 public static function depToString($dep, $type = 'depends') {
375 if (is_array($dep)) {
376 unset($dep[$type]['php']);
377 unset($dep[$type]['typo3']);
378 $s = (count($dep[$type])) ? implode(',', array_keys($dep[$type])) : '';
379 return $s;
380 }
381 return '';
382 }
383
384 /**
385 * Checks whether the passed dependency is TER-style (string) or TER2-style (array) and returns a single string for displaying the dependencies.
386 *
387 * It leaves out all version numbers and the "php" and "typo3" dependencies, as they are implicit and of no interest without the version number.
388 *
389 * @param mixed $dep Either a string or an array listing dependencies.
390 * @param string $type The dependency type to list if $dep is an array
391 * @return string A simple dependency list for display
392 */
393 public static function stringToDep($dep) {
394 $constraint = array();
395 if (is_string($dep) && strlen($dep)) {
396 $dep = explode(',', $dep);
397 foreach ($dep as $v) {
398 $constraint[$v] = '';
399 }
400 }
401 return $constraint;
402 }
403
404
405 /**
406 * Returns version information
407 *
408 * @param string Version code, x.x.x
409 * @param string part: "", "int", "main", "sub", "dev"
410 * @return string
411 * @see renderVersion()
412 */
413 public static function makeVersion($v, $mode) {
414 $vDat = self::renderVersion($v);
415 return $vDat['version_' . $mode];
416 }
417
418 /**
419 * Parses the version number x.x.x and returns an array with the various parts.
420 *
421 * @param string Version code, x.x.x
422 * @param string Increase version part: "main", "sub", "dev"
423 * @return string
424 */
425 public static function renderVersion($v, $raise = '') {
426 $parts = t3lib_div::intExplode('.', $v . '..');
427 $parts[0] = t3lib_utility_Math::forceIntegerInRange($parts[0], 0, 999);
428 $parts[1] = t3lib_utility_Math::forceIntegerInRange($parts[1], 0, 999);
429 $parts[2] = t3lib_utility_Math::forceIntegerInRange($parts[2], 0, 999);
430
431 switch ((string) $raise) {
432 case 'main':
433 $parts[0]++;
434 $parts[1] = 0;
435 $parts[2] = 0;
436 break;
437 case 'sub':
438 $parts[1]++;
439 $parts[2] = 0;
440 break;
441 case 'dev':
442 $parts[2]++;
443 break;
444 }
445
446 $res = array();
447 $res['version'] = $parts[0] . '.' . $parts[1] . '.' . $parts[2];
448 $res['version_int'] = intval($parts[0] * 1000000 + $parts[1] * 1000 + $parts[2]);
449 $res['version_main'] = $parts[0];
450 $res['version_sub'] = $parts[1];
451 $res['version_dev'] = $parts[2];
452
453 return $res;
454 }
455
456 /**
457 * Render version from intVersion
458 *
459 * @static
460 * @param int $intVersion
461 * @return string version
462 */
463 public static function versionFromInt($intVersion) {
464 $versionString = str_pad($intVersion, 9, '0', STR_PAD_LEFT);
465 $parts = array(
466 substr($versionString, 0, 3),
467 substr($versionString, 3, 3),
468 substr($versionString, 6, 3)
469 );
470 return intval($parts[0]) . '.' . intval($parts[1]) . '.' . intval($parts[2]);
471 }
472
473 /**
474 * Evaluates differences in version numbers with three parts, x.x.x. Returns TRUE if $v1 is greater than $v2
475 *
476 * @param string Version number 1
477 * @param string Version number 2
478 * @param integer Tolerance factor. For instance, set to 1000 to ignore difference in dev-version (third part)
479 * @return boolean TRUE if version 1 is greater than version 2
480 */
481 public static function versionDifference($v1, $v2, $div = 1) {
482 return floor(self::makeVersion($v1, 'int') / $div) > floor(self::makeVersion($v2, 'int') / $div);
483 }
484
485
486 /**
487 * Returns TRUE if the $str is found as the first part of a string in $array
488 *
489 * @param string String to test with.
490 * @param array Input array
491 * @param boolean If set, the test is case insensitive
492 * @return boolean TRUE if found.
493 */
494 public static function first_in_array($str, $array, $caseInsensitive = FALSE) {
495 if ($caseInsensitive) {
496 $str = strtolower($str);
497 }
498 if (is_array($array)) {
499 foreach ($array as $cl) {
500 if ($caseInsensitive) {
501 $cl = strtolower($cl);
502 }
503 if (t3lib_div::isFirstPartOfStr($cl, $str)) {
504 return TRUE;
505 }
506 }
507 }
508 return FALSE;
509 }
510
511 /**
512 * Compares two arrays with MD5-hash values for analysis of which files has changed.
513 *
514 * @param array Current values
515 * @param array Past values
516 * @return array Affected files
517 */
518 public static function findMD5ArrayDiff($current, $past) {
519 if (!is_array($current)) {
520 $current = array();
521 }
522 if (!is_array($past)) {
523 $past = array();
524 }
525 $filesInCommon = array_intersect($current, $past);
526 $diff1 = array_keys(array_diff($past, $filesInCommon));
527 $diff2 = array_keys(array_diff($current, $filesInCommon));
528 $affectedFiles = array_unique(array_merge($diff1, $diff2));
529 return $affectedFiles;
530 }
531
532 /**
533 * Returns title and style attribute for mouseover help text.
534 *
535 * @param string Help text.
536 * @return string title="" attribute prepended with a single space
537 */
538 public static function labelInfo($str) {
539 return ' title="' . htmlspecialchars($str) . '" style="cursor:help;"';
540 }
541
542
543 /**
544 * Returns the absolute path where the extension $extKey is installed (based on 'type' (SGL))
545 *
546 * @param string Extension key
547 * @param string Install scope type: L, G, S
548 * @return string Returns the absolute path to the install scope given by input $type variable. It is checked if the path is a directory. Slash is appended.
549 */
550 public static function getExtPath($extKey, $type, $returnWithoutExtKey = FALSE) {
551 $typePath = self::typePath($type);
552
553 if ($typePath) {
554 $path = $typePath . ($returnWithoutExtKey ? '' : $extKey . '/');
555 return $path;
556 } else {
557 return '';
558 }
559 }
560
561 /**
562 * Get type of extension (G,S,L) from extension path
563 *
564 * @param string $path
565 */
566 public static function getExtTypeFromPath($path) {
567 if (strpos($path, TYPO3_mainDir . 'sysext/') !== FALSE) {
568 return 'S';
569 } elseif (strpos($path, TYPO3_mainDir . 'ext/') !== FALSE) {
570 return 'G';
571 } elseif (strpos($path, 'typo3conf/ext/') !== FALSE) {
572 return 'L';
573 }
574 }
575
576 /**
577 * Get path from type
578 *
579 * @param string $type S/G/L
580 */
581 public static function typePath($type) {
582 if ($type === 'S') {
583 return PATH_typo3 . 'sysext/';
584 } elseif ($type === 'G') {
585 return PATH_typo3 . 'ext/';
586 } elseif ($type === 'L') {
587 return PATH_typo3conf . 'ext/';
588 }
589 }
590
591 /**
592 * Get relative path from type
593 *
594 * @param string $type S/G/L
595 */
596 public static function typeRelPath($type) {
597 if ($type === 'S') {
598 return 'sysext/';
599 } elseif ($type === 'G') {
600 return 'ext/';
601 } elseif ($type === 'L') {
602 return '../typo3conf/ext/';
603 }
604 }
605
606 /**
607 * Get backpath from type
608 *
609 * @param string $type S/G/L
610 */
611 public static function typeBackPath($type) {
612 if ($type === 'L') {
613 return '../../../../' . TYPO3_mainDir;
614 } else {
615 return '../../../';
616 }
617 }
618
619 /**
620 * Include a locallang file and return the $LOCAL_LANG array serialized.
621 *
622 * @param string Absolute path to locallang file to include.
623 * @param string Old content of a locallang file (keeping the header content)
624 * @return array Array with header/content as key 0/1
625 * @see makeUploadarray()
626 */
627 public static function getSerializedLocalLang($file, $content) {
628 $LOCAL_LANG = NULL;
629 $returnParts = explode('$LOCAL_LANG', $content, 2);
630
631 include($file);
632 if (is_array($LOCAL_LANG)) {
633 $returnParts[1] = serialize($LOCAL_LANG);
634 return $returnParts;
635 } else {
636 return array();
637 }
638 }
639
640
641 /**
642 * Enter description here...
643 *
644 * @param unknown_type $array
645 * @param unknown_type $lines
646 * @param unknown_type $level
647 * @return unknown
648 */
649 public static function arrayToCode($array, $level = 0) {
650 $lines = 'array(' . LF;
651 $level++;
652 foreach ($array as $k => $v) {
653 if (strlen($k) && is_array($v)) {
654 $lines .= str_repeat(TAB, $level) . "'" . $k . "' => " . self::arrayToCode($v, $level);
655 } elseif (strlen($k)) {
656 $lines .= str_repeat(TAB, $level) . "'" . $k . "' => " . (t3lib_utility_Math::canBeInterpretedAsInteger($v) ? intval($v) : "'" . t3lib_div::slashJS(trim($v), 1) . "'") . ',' . LF;
657 }
658 }
659
660 $lines .= str_repeat(TAB, $level - 1) . ')' . ($level - 1 == 0 ? '' : ',' . LF);
661 return $lines;
662 }
663
664
665 /**
666 * Traverse the array of installed extensions keys and arranges extensions in the priority order they should be in
667 *
668 * @param array Array of extension keys as values
669 * @param array Extension information array
670 * @return array Modified array of extention keys as values
671 * @see addExtToList()
672 */
673 public static function managesPriorities($listArr, $instExtInfo) {
674
675 // Initialize:
676 $levels = array(
677 'top' => array(),
678 'middle' => array(),
679 'bottom' => array(),
680 );
681
682 // Traverse list of extensions:
683 foreach ($listArr as $ext) {
684 $prio = trim($instExtInfo[$ext]['EM_CONF']['priority']);
685 switch ((string) $prio) {
686 case 'top':
687 case 'bottom':
688 $levels[$prio][] = $ext;
689 break;
690 default:
691 $levels['middle'][] = $ext;
692 break;
693 }
694 }
695 return array_merge(
696 $levels['top'],
697 $levels['middle'],
698 $levels['bottom']
699 );
700 }
701
702
703 /**
704 * Returns either array with all default categories or index/title
705 * of a category entry.
706 *
707 * @access public
708 * @param mixed $cat category title or category index
709 * @return mixed
710 */
711 public static function getDefaultCategory($cat = NULL) {
712 if (is_null($cat)) {
713 return self::$defaultCategories;
714 } else {
715 if (is_string($cat)) {
716 // default category
717 $catIndex = 4;
718 if (array_key_exists(strtolower($cat), self::$defaultCategories)) {
719 $catIndex = self::$defaultCategories[strtolower($cat)];
720 }
721 return $catIndex;
722 } else {
723 if (is_int($cat) && $cat >= 0) {
724 $catTitle = array_search($cat, self::$defaultCategories);
725 // default category
726 if (!$catTitle) {
727 $catTitle = 'misc';
728 }
729 return $catTitle;
730 }
731 }
732 }
733 }
734
735 /**
736 * Returns either array with all default states or index/title
737 * of a state entry.
738 *
739 * @access public
740 * @param mixed $state state title or state index
741 * @return mixed
742 */
743 public static function getDefaultState($state = NULL) {
744 if (is_null($state)) {
745 return self::$defaultStates;
746 } else {
747 if (is_string($state)) {
748 // default state
749 $stateIndex = 999;
750 if (array_key_exists(strtolower($state), self::$defaultStates)) {
751 $stateIndex = self::$defaultStates[strtolower($state)];
752 }
753 return $stateIndex;
754 } else {
755 if (is_int($state) && $state >= 0) {
756 $stateTitle = array_search($state, self::$defaultStates);
757 // default state
758 if (!$stateTitle) {
759 $stateTitle = 'n/a';
760 }
761 return $stateTitle;
762 }
763 }
764 }
765 }
766
767 /**
768 * Extension States
769 * Content must be redundant with the same internal variable as in class.tx_extrep.php!
770 *
771 * @static
772 * @return array
773 */
774 public static function getStates() {
775 return array(
776 'alpha' => $GLOBALS['LANG']->sL('LLL:EXT:em/language/locallang.xml:state_alpha'),
777 'beta' => $GLOBALS['LANG']->sL('LLL:EXT:em/language/locallang.xml:state_beta'),
778 'stable' => $GLOBALS['LANG']->sL('LLL:EXT:em/language/locallang.xml:state_stable'),
779 'experimental' => $GLOBALS['LANG']->sL('LLL:EXT:em/language/locallang.xml:state_experimental'),
780 'test' => $GLOBALS['LANG']->sL('LLL:EXT:em/language/locallang.xml:state_test'),
781 'obsolete' => $GLOBALS['LANG']->sL('LLL:EXT:em/language/locallang.xml:state_obsolete'),
782 'excludeFromUpdates' => $GLOBALS['LANG']->sL('LLL:EXT:em/language/locallang.xml:state_exclude_from_updates')
783 );
784 }
785
786 /**
787 * Reports back if installation in a certain scope is possible.
788 *
789 * @param string Scope: G, L, S
790 * @param string Extension lock-type (eg. "L" or "G")
791 * @return boolean TRUE if installation is allowed.
792 */
793 public static function importAsType($type, $lockType = '') {
794 switch ($type) {
795 case 'G':
796 return $GLOBALS['TYPO3_CONF_VARS']['EXT']['allowGlobalInstall'] && (!$lockType || !strcmp($lockType, $type));
797 break;
798 case 'L':
799 return $GLOBALS['TYPO3_CONF_VARS']['EXT']['allowLocalInstall'] && (!$lockType || !strcmp($lockType, $type));
800 break;
801 case 'S':
802 return isset($GLOBALS['TYPO3_CONF_VARS']['EXT']['allowSystemInstall']) && $GLOBALS['TYPO3_CONF_VARS']['EXT']['allowSystemInstall'];
803 break;
804 default:
805 return FALSE;
806 }
807 }
808
809 /**
810 * Returns TRUE if extensions in scope, $type, can be deleted (or installed for that sake)
811 *
812 * @param string Scope: "G" or "L"
813 * @return boolean TRUE if possible.
814 */
815 public static function deleteAsType($type) {
816 switch ($type) {
817 case 'G':
818 return $GLOBALS['TYPO3_CONF_VARS']['EXT']['allowGlobalInstall'];
819 break;
820 case 'L':
821 return $GLOBALS['TYPO3_CONF_VARS']['EXT']['allowLocalInstall'];
822 break;
823 default:
824 return FALSE;
825 }
826 }
827
828
829 /**
830 * Creates directories in $extDirPath
831 *
832 * @param array Array of directories to create relative to extDirPath, eg. "blabla", "blabla/blabla" etc...
833 * @param string Absolute path to directory.
834 * @return mixed Returns FALSE on success or an error string
835 */
836 public static function createDirsInPath($dirs, $extDirPath) {
837 if (is_array($dirs)) {
838 foreach ($dirs as $dir) {
839 $error = t3lib_div::mkdir_deep($extDirPath, $dir);
840 if ($error) {
841 return $error;
842 }
843 }
844 }
845
846 return FALSE;
847 }
848
849 /**
850 * Analyses the php-scripts of an available extension on server
851 *
852 * @param string Absolute path to extension
853 * @param string Prefix for tables/classes.
854 * @param string Extension key
855 * @return array Information array.
856 * @see makeDetailedExtensionAnalysis()
857 */
858 public static function getClassIndexLocallangFiles($absPath, $table_class_prefix, $extKey) {
859 $excludeForPackaging = $GLOBALS['TYPO3_CONF_VARS']['EXT']['excludeForPackaging'];
860 $filesInside = t3lib_div::removePrefixPathFromList(t3lib_div::getAllFilesAndFoldersInPath(array(), $absPath, 'php,inc', 0, 99, $excludeForPackaging), $absPath);
861 $out = array();
862 $reg = array();
863
864 foreach ($filesInside as $fileName) {
865 if (substr($fileName, 0, 4) != 'ext_' && substr($fileName, 0, 6) != 'tests/') { // ignore supposed-to-be unit tests as well
866 $baseName = basename($fileName);
867 if (substr($baseName, 0, 9) == 'locallang' && substr($baseName, -4) == '.php') {
868 $out['locallang'][] = $fileName;
869 } elseif ($baseName != 'conf.php') {
870 if (filesize($absPath . $fileName) < 500 * 1024) {
871 $fContent = t3lib_div::getUrl($absPath . $fileName);
872 unset($reg);
873 if (preg_match('/\n[[:space:]]*class[[:space:]]*([[:alnum:]_]+)([[:alnum:][:space:]_]*)/', $fContent, $reg)) {
874
875 // Find classes:
876 $lines = explode(LF, $fContent);
877 foreach ($lines as $l) {
878 $line = trim($l);
879 unset($reg);
880 if (preg_match('/^class[[:space:]]*([[:alnum:]_]+)([[:alnum:][:space:]_]*)/', $line, $reg)) {
881 $out['classes'][] = $reg[1];
882 $out['files'][$fileName]['classes'][] = $reg[1];
883 if ($reg[1] !== 'ext_update' && substr($reg[1], 0, 3) != 'ux_' && !t3lib_div::isFirstPartOfStr($reg[1], $table_class_prefix) && strcmp(substr($table_class_prefix, 0, -1), $reg[1])) {
884 $out['NSerrors']['classname'][] = $reg[1];
885 } else {
886 $out['NSok']['classname'][] = $reg[1];
887 }
888 }
889 }
890 // If class file prefixed 'class.'....
891 if (substr($baseName, 0, 6) == 'class.') {
892 $fI = pathinfo($baseName);
893 $testName = substr($baseName, 6, -(1 + strlen($fI['extension'])));
894 if ($testName !== 'ext_update' && substr($testName, 0, 3) != 'ux_' && !t3lib_div::isFirstPartOfStr($testName, $table_class_prefix) && strcmp(substr($table_class_prefix, 0, -1), $testName)) {
895 $out['NSerrors']['classfilename'][] = $baseName;
896 } else {
897 $out['NSok']['classfilename'][] = $baseName;
898 if (is_array($out['files'][$fileName]['classes']) && self::first_in_array($testName, $out['files'][$fileName]['classes'], 1)) {
899 $out['msg'][] = sprintf($GLOBALS['LANG']->getLL('detailedExtAnalysis_class_ok'),
900 $fileName, $testName
901 );
902 } else {
903 $out['errors'][] = sprintf($GLOBALS['LANG']->getLL('detailedExtAnalysis_class_not_ok'),
904 $fileName, $testName
905 );
906 }
907 }
908 }
909 // Check for proper XCLASS definition
910 // Match $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS'] with single or doublequotes
911 $XclassSearch = '\$TYPO3_CONF_VARS\[TYPO3_MODE\]\[[\'"]XCLASS[\'"]\]';
912 $XclassParts = preg_split('/if \(defined\([\'"]TYPO3_MODE[\'"]\)(.*)' . $XclassSearch . '/', $fContent, 2);
913 if (count($XclassParts) !== 2) {
914 // Match $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS'] with single or doublequotes
915 $XclassSearch = '\$GLOBALS\[[\'"]TYPO3_CONF_VARS[\'"]\]\[TYPO3_MODE\]\[[\'"]XCLASS[\'"]\]';
916 $XclassParts = preg_split('/if \(defined\([\'"]TYPO3_MODE[\'"]\)(.*)' . $XclassSearch . '/', $fContent, 2);
917 }
918
919 if (count($XclassParts) == 2) {
920 unset($reg);
921 preg_match('/^\[[\'"]([[:alnum:]_\/\.]*)[\'"]\]/', $XclassParts[1], $reg);
922 if ($reg[1]) {
923 $cmpF = 'ext/' . $extKey . '/' . $fileName;
924 if (!strcmp($reg[1], $cmpF)) {
925 if (preg_match('/_once[[:space:]]*\(' . $XclassSearch . '\[[\'"]' . preg_quote($cmpF, '/') . '[\'"]\]\);/', $XclassParts[1])) {
926 $out['msg'][] = sprintf($GLOBALS['LANG']->getLL('detailedExtAnalysis_xclass_ok'), $fileName);
927 } else {
928 $out['errors'][] = $GLOBALS['LANG']->getLL('detailedExtAnalysis_xclass_no_include');
929 }
930 } else {
931 $out['errors'][] = sprintf($GLOBALS['LANG']->getLL('detailedExtAnalysis_xclass_incorrect'),
932 $reg[1], $cmpF
933 );
934 }
935 } else {
936 $out['errors'][] = sprintf($GLOBALS['LANG']->getLL('detailedExtAnalysis_no_xclass_filename'), $fileName);
937 }
938 } elseif (!self::first_in_array('ux_', $out['files'][$fileName]['classes'])) {
939 // No Xclass definition required if classname starts with 'ux_'
940 $out['errors'][] = sprintf($GLOBALS['LANG']->getLL('detailedExtAnalysis_no_xclass_found'), $fileName);
941 }
942 }
943 }
944 }
945 }
946 }
947 return $out;
948 }
949
950 /**
951 * Write new TYPO3_MOD_PATH to "conf.php" file.
952 *
953 * @param string Absolute path to a "conf.php" file of the backend module which we want to write back to.
954 * @param string Install scope type: L, G, S
955 * @param string Relative path for the module folder in extension
956 * @return string Returns message about the status.
957 * @see modConfFileAnalysis()
958 */
959 public static function writeTYPO3_MOD_PATH($confFilePath, $type, $mP) {
960 $lines = explode(LF, t3lib_div::getUrl($confFilePath));
961 $confFileInfo = array();
962 $confFileInfo['lines'] = $lines;
963 $reg = array();
964
965 $flag_M = 0;
966 $flag_B = 0;
967 $flag_Dispatch = 0;
968
969 foreach ($lines as $k => $l) {
970 $line = trim($l);
971
972 unset($reg);
973 if (preg_match('/^define[[:space:]]*\([[:space:]]*["\']TYPO3_MOD_PATH["\'][[:space:]]*,[[:space:]]*["\']([[:alnum:]_\/\.]+)["\'][[:space:]]*\)[[:space:]]*;/', $line, $reg)) {
974 $lines[$k] = str_replace($reg[0], 'define(\'TYPO3_MOD_PATH\', \'' . self::typeRelPath($type) . $mP . '\');', $lines[$k]);
975 $flag_M = $k + 1;
976 }
977
978 unset($reg);
979 if (preg_match('/^\$BACK_PATH[[:space:]]*=[[:space:]]*["\']([[:alnum:]_\/\.]+)["\'][[:space:]]*;/', $line, $reg)) {
980 $lines[$k] = str_replace($reg[0], '$BACK_PATH=\'' . self::typeBackPath($type) . '\';', $lines[$k]);
981 $flag_B = $k + 1;
982 }
983
984 // Check if this module uses new API (see http://bugs.typo3.org/view.php?id=5278)
985 // where TYPO3_MOD_PATH and BACK_PATH are not required
986 unset($reg);
987 if (preg_match('/^\$MCONF\[["\']script["\']\][[:space:]]*=[[:space:]]*["\']_DISPATCH["\'][[:space:]]*;/', $line, $reg)) {
988 $flag_Dispatch = $k + 1;
989 }
990
991 }
992
993 if ($flag_B && $flag_M) {
994 t3lib_div::writeFile($confFilePath, implode(LF, $lines));
995 return sprintf($GLOBALS['LANG']->getLL('writeModPath_ok'),
996 substr($confFilePath, strlen(PATH_site)));
997 } elseif ($flag_Dispatch) {
998 return sprintf(
999 $GLOBALS['LANG']->getLL('writeModPath_notRequired'),
1000 substr($confFilePath, strlen(PATH_site))
1001 );
1002 } else {
1003 return self::rfw(
1004 sprintf($GLOBALS['LANG']->getLL('writeModPath_error'),
1005 $confFilePath)
1006 );
1007 }
1008 }
1009
1010 /**
1011 * Sends content of file for download
1012 *
1013 * @static
1014 * @param $path
1015 * @return void
1016 */
1017 public static function sendFile($path) {
1018 $path = t3lib_div::resolveBackPath(PATH_site . $path);
1019
1020 if (is_file($path) && is_readable($path) && t3lib_div::isAllowedAbsPath($path)) {
1021 header('Content-Type: application/octet-stream');
1022 header('Content-Disposition: attachment; filename=' . basename($path));
1023 readfile($path);
1024 exit;
1025 }
1026 }
1027
1028 /**
1029 * Rename a file / folder
1030 * @static
1031 * @param $file
1032 * @param $newName
1033 * @return bool
1034 */
1035 public static function renameFile($file, $newName) {
1036 if($file[0] == '/') {
1037 $file = substr($file, 1);
1038 }
1039 if($newName[0] == '/') {
1040 $newName = substr($newName, 1);
1041 }
1042
1043 $file = t3lib_div::resolveBackPath(PATH_site . $file);
1044 $newName = t3lib_div::resolveBackPath(PATH_site . $newName);
1045 if (is_writable($file) && t3lib_div::isAllowedAbsPath($file) && t3lib_div::isAllowedAbsPath($newName)) {
1046 return rename($file, $newName);
1047 }
1048
1049 return FALSE;
1050 }
1051
1052
1053 /**
1054 * Creates a new file
1055 *
1056 * Returns an array with
1057 * 0: boolean success
1058 * 1: string absolute path of written file/folder
1059 * 2: error code
1060 *
1061 * The error code returns
1062 * 0: no error
1063 * -1: not writable
1064 * -2: not allowed path
1065 * -3: already exists
1066 * -4: not able to create
1067 *
1068 * @static
1069 * @param $folder
1070 * @param $file
1071 * @param $isFolder
1072 * @return array
1073 */
1074 public static function createNewFile($folder, $file, $isFolder) {
1075 $success = FALSE;
1076 $error = 0;
1077
1078 if (substr($folder, -1) !== '/') {
1079 $folder .= '/';
1080 }
1081
1082 $newFile = t3lib_div::resolveBackPath(PATH_site . $folder . $file);
1083
1084 if (!is_writable(dirname($newFile))) {
1085 $error = -1;
1086 } elseif (!t3lib_div::isAllowedAbsPath($newFile)) {
1087 $error = -2;
1088 } elseif (file_exists($newFile)) {
1089 $error = -3;
1090 } else {
1091 if ($isFolder) {
1092 $success = t3lib_div::mkdir($newFile);
1093 } else {
1094 $success = t3lib_div::writeFile($newFile, '');
1095 }
1096
1097 if (!$success) {
1098 $error = -4;
1099 }
1100 }
1101
1102 return array(
1103 $success,
1104 $newFile,
1105 $error
1106 );
1107 }
1108
1109
1110 /**
1111 * Wrapping input string in a link tag with link to email address
1112 *
1113 * @param string Input string, being wrapped in <a> tags
1114 * @param string Email address for use in link.
1115 * @return string Output
1116 */
1117 public static function wrapEmail($str, $email) {
1118 if ($email) {
1119 $str = '<a href="mailto:' . htmlspecialchars($email) . '">' . htmlspecialchars($str) . '</a>';
1120 }
1121 return $str;
1122 }
1123
1124 /**
1125 * red-fontwrap. Returns the string wrapped in a <span>-tag defining the color to be red
1126 *
1127 * @param string Input string
1128 * @return string Output string
1129 */
1130 public static function rfw($string) {
1131 return '<span class="typo3-red">' . $string . '</span>';
1132 }
1133
1134 /**
1135 * dimmed-fontwrap. Returns the string wrapped in a <span>-tag defining the color to be gray/dimmed
1136 *
1137 * @param string Input string
1138 * @return string Output string
1139 */
1140 public static function dfw($string) {
1141 return '<span class="typo3-dimmed">' . $string . '</span>';
1142 }
1143 }
1144
1145 ?>