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