├── ext_icon.gif ├── Documentation └── Images │ └── ExtensionApi │ ├── uploadfolder.png │ ├── ExtensionApiFetch.png │ ├── uploadfolder.graphml │ └── ExtensionApiFetch.graphml ├── Tests └── Unit │ ├── Resources │ └── vfsStream │ │ └── importCommand │ │ └── path │ │ └── to │ │ └── importfolder │ │ └── realurl_1.12.8.t3x │ └── Service │ ├── CacheApiServiceTest.php │ ├── DatabaseApiServiceTest.php │ └── SiteApiServiceTest.php ├── .gitignore ├── composer.json ├── ext_localconf.php ├── ext_emconf.php ├── .scrutinizer.yml ├── ext_autoload.php ├── .travis.yml ├── Classes ├── Service │ ├── DatabaseCompareDry.php │ ├── DatabaseApiService.php │ ├── DatabaseComparator.php │ ├── DatabaseCompareReal.php │ ├── SiteApiService.php │ ├── CacheApiService.php │ └── ExtensionApiService.php └── Command │ ├── SiteApiCommandController.php │ ├── BackendApiCommandController.php │ ├── DatabaseApiCommandController.php │ ├── CacheApiCommandController.php │ └── ExtensionApiCommandController.php └── README.md /ext_icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TYPO3-coreapi/ext-coreapi/HEAD/ext_icon.gif -------------------------------------------------------------------------------- /Documentation/Images/ExtensionApi/uploadfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TYPO3-coreapi/ext-coreapi/HEAD/Documentation/Images/ExtensionApi/uploadfolder.png -------------------------------------------------------------------------------- /Documentation/Images/ExtensionApi/ExtensionApiFetch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TYPO3-coreapi/ext-coreapi/HEAD/Documentation/Images/ExtensionApi/ExtensionApiFetch.png -------------------------------------------------------------------------------- /Tests/Unit/Resources/vfsStream/importCommand/path/to/importfolder/realurl_1.12.8.t3x: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TYPO3-coreapi/ext-coreapi/HEAD/Tests/Unit/Resources/vfsStream/importCommand/path/to/importfolder/realurl_1.12.8.t3x -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ######################### 2 | # global ignore file 3 | ######################## 4 | # ignoring temporary files (left by e.g. vim) 5 | # ignoring by common IDE's used directories/files 6 | # dont ignore .rej and .orig as we want to see/clean files after conflict resolution 7 | # 8 | # for local exclude patterns please edit .git/info/exclude 9 | # 10 | *~ 11 | *.bak 12 | *.idea 13 | *.project 14 | *.swp 15 | .buildpath 16 | .cache 17 | .project 18 | .session 19 | .settings 20 | .TemporaryItems 21 | .webprj 22 | nbproject 23 | 24 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "etobi/coreapi", 3 | "description": "Provides a simple to use API for common core features. Goal is to be able to do the most common tasks by CLI instead of doing it in the backend/browser.", 4 | "type": "typo3-cms-extension", 5 | "keywords": ["typo3", "extension", "deployment", "commandline", "cli"], 6 | "authors": [ 7 | { 8 | "name": "Tobias Liebig", 9 | "homepage": "http://etobi.de" 10 | }, 11 | { 12 | "name": "Stefano Kowalke" 13 | } 14 | ], 15 | "minimum-stability": "dev", 16 | "autoload": { 17 | "psr-4": { 18 | "Etobi\\CoreAPI\\": "Classes/" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ext_localconf.php: -------------------------------------------------------------------------------- 1 | 'Coreapi', 5 | 'description' => 'coreapi', 6 | 'category' => 'plugin', 7 | 'author' => 'Tobias Liebig,Georg Ringer,Stefano Kowalke', 8 | 'author_email' => 'tobias.liebig@typo3.org,georg.ringer@cyberhouse.at,blueduck@gmx.net', 9 | 'author_company' => '', 10 | 'shy' => '', 11 | 'priority' => '', 12 | 'module' => '', 13 | 'state' => 'stable', 14 | 'internal' => '', 15 | 'uploadfolder' => '0', 16 | 'createDirs' => '', 17 | 'modify_tables' => '', 18 | 'clearCacheOnLoad' => 0, 19 | 'lockType' => '', 20 | 'version' => '1.3.0', 21 | 'constraints' => array( 22 | 'depends' => array( 23 | 'typo3' => '6.2.0-7.99.99', 24 | 'extbase' => '6.2.0-7.99.99', 25 | 'fluid' => '6.2.0-7.99.99', 26 | ), 27 | 'conflicts' => array( 28 | ), 29 | 'suggests' => array( 30 | ), 31 | ), 32 | ); 33 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: 3 | - 'Resources/Public/vendor/*' 4 | - 'Tests/*' 5 | 6 | tools: 7 | 8 | external_code_coverage: 9 | timeout: 700 10 | 11 | php_sim: true 12 | 13 | php_code_sniffer: 14 | enabled: true 15 | command: phpcs -n 16 | config: 17 | standard: TYPO3CMS 18 | 19 | php_cs_fixer: false 20 | 21 | php_mess_detector: 22 | enabled: true 23 | config: 24 | code_size_rules: 25 | cyclomatic_complexity: true 26 | npath_complexity: true 27 | excessive_class_complexity: true 28 | controversial_rules: 29 | superglobals: false 30 | 31 | php_pdepend: true 32 | 33 | php_analyzer: 34 | enabled: true 35 | config: 36 | basic_semantic_checks: 37 | enabled: true 38 | property_on_interface: true 39 | missing_abstract_methods: true 40 | deprecation_checks: 41 | enabled: true 42 | simplify_boolean_return: 43 | enabled: true 44 | metrics_lack_of_cohesion_methods: 45 | enabled: true 46 | dead_assignments: 47 | enabled: true 48 | 49 | sensiolabs_security_checker: true -------------------------------------------------------------------------------- /ext_autoload.php: -------------------------------------------------------------------------------- 1 | $extensionClassesPath . 'Command/BackendApiCommandController.php', 8 | 'Etobi\CoreAPI\Command\DatabaseApiCommandController' => $extensionClassesPath . 'Command/DatabaseApiCommandController.php', 9 | 'Etobi\CoreAPI\Command\SiteApiCommandController' => $extensionClassesPath . 'Command/SiteApiCommandController.php', 10 | 'Etobi\CoreAPI\Command\CacheApiCommandController' => $extensionClassesPath . 'Command/CacheApiCommandController.php', 11 | 'Etobi\CoreAPI\Command\ExtensionApiCommandController' => $extensionClassesPath . 'Command/ExtensionApiCommandController.php', 12 | 'Etobi\CoreAPI\Service\CacheApiService' => $extensionClassesPath . 'Service/CacheApiService.php', 13 | 'Etobi\CoreAPI\Service\SiteApiService' => $extensionClassesPath . 'Service/SiteApiService.php', 14 | 'Etobi\CoreAPI\Service\DatabaseApiService' => $extensionClassesPath . 'Service/DatabaseApiService.php', 15 | 'Etobi\CoreAPI\Service\DatabaseComparator' => $extensionClassesPath . 'Service/DatabaseComparator.php', 16 | 'Etobi\CoreAPI\Service\DatabaseCompareDry' => $extensionClassesPath . 'Service/DatabaseCompareDry.php', 17 | 'Etobi\CoreAPI\Service\DatabaseCompareReal' => $extensionClassesPath . 'Service/DatabaseCompareReal.php', 18 | 'Etobi\CoreAPI\Service\ExtensionApiService' => $extensionClassesPath . 'Service/ExtensionApiService.php' 19 | ); 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: php 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | - hhvm 9 | 10 | env: 11 | - DB=mysql TYPO3=coreapi/develop INTEGRATION=master COVERAGE=0 12 | 13 | matrix: 14 | allow_failures: 15 | - php: hhvm 16 | 17 | services: 18 | - memcached 19 | - redis-server 20 | 21 | addons: 22 | apt: 23 | packages: 24 | # Get latest git version cause of travis issues (https://github.com/travis-ci/travis-ci/issues/1710) 25 | - git 26 | - parallel 27 | - tree 28 | 29 | notifications: 30 | email: 31 | - blueduck@gmx.net 32 | slack: 33 | rooms: 34 | - typo3:DHkQdCNWc6x2znPAYA5T2LXO#gsoc-coreapi 35 | 36 | before_script: 37 | - > 38 | if [[ "$TRAVIS_PHP_VERSION" = "5.3" || "$TRAVIS_PHP_VERSION" = "5.4" ]]; then 39 | echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; 40 | echo "apc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; 41 | echo "apc.slam_defense=0" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; 42 | pecl install igbinary > /dev/null; 43 | fi 44 | - > 45 | if [[ "$TRAVIS_PHP_VERSION" = "5.5" || "$TRAVIS_PHP_VERSION" = "5.6" ]]; then 46 | echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; 47 | echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; 48 | fi 49 | - > 50 | if [[ "$TRAVIS_PHP_VERSION" = "hhv*" ]]; then 51 | echo "extension = memcache.so" >> /etc/hhvm/php.ini; 52 | echo "extension = redis.so" >> /etc/hhvm/php.ini; 53 | fi 54 | - cd .. 55 | - git clone --single-branch --branch $INTEGRATION --depth 1 git://github.com/TYPO3-coreapi/TYPO3-Travis-Integration.git build-environment 56 | - source build-environment/install-helper.sh 57 | - git clone --single-branch --branch $TYPO3 --depth 1 https://github.com/TYPO3-coreapi/TYPO3CMS.git core 58 | - mv core/* . 59 | - composer self-update 60 | - composer --dev install 61 | - mkdir -p fileadmin uploads typo3temp typo3conf/ext/ 62 | - mv ext-coreapi typo3conf/ext/coreapi 63 | 64 | script: 65 | - phpLint typo3conf/ext/coreapi 66 | - > 67 | echo; 68 | echo "Running unit tests"; 69 | ./bin/phpunit --colors --coverage-clover=coverage.clover -c typo3/sysext/core/Build/UnitTests.xml typo3conf/ext/coreapi/Tests/Unit/ 70 | - wget https://scrutinizer-ci.com/ocular.phar 71 | - cp -R typo3conf/ext/coreapi/.git . 72 | - php ocular.phar code-coverage:upload --format=php-clover coverage.clover -------------------------------------------------------------------------------- /Classes/Service/DatabaseCompareDry.php: -------------------------------------------------------------------------------- 1 | 24 | * @author Stefano Kowalke 25 | */ 26 | class DatabaseCompareDry extends DatabaseComparator { 27 | 28 | /** 29 | * Database compare. 30 | * 31 | * @param string $actions comma separated list of IDs 32 | * @throws InvalidArgumentException 33 | * @return array 34 | */ 35 | public function compare($actions) { 36 | $errors = array(); 37 | $results = array(); 38 | 39 | $allowedActions = $this->getAllowedActions($actions); 40 | 41 | $expectedSchema = $this->sqlExpectedSchemaService->getExpectedDatabaseSchema(); 42 | $currentSchema = $this->schemaMigrationService->getFieldDefinitions_database(); 43 | 44 | $addCreateChange = $this->schemaMigrationService->getDatabaseExtra($expectedSchema, $currentSchema); 45 | $addCreateChange = $this->schemaMigrationService->getUpdateSuggestions($addCreateChange); 46 | 47 | $dropRemove = $this->schemaMigrationService->getDatabaseExtra($currentSchema, $expectedSchema); 48 | $dropRemove = $this->schemaMigrationService->getUpdateSuggestions($dropRemove, 'remove'); 49 | 50 | if ($allowedActions[self::ACTION_UPDATE_CLEAR_TABLE] == 1) { 51 | $results['update_clear_table'] = $addCreateChange['clear_table']; 52 | } 53 | 54 | if ($allowedActions[self::ACTION_UPDATE_ADD] == 1) { 55 | $results['update_add'] = $addCreateChange['add']; 56 | } 57 | 58 | if ($allowedActions[self::ACTION_UPDATE_CHANGE] == 1) { 59 | $results['update_change'] = $addCreateChange['change']; 60 | } 61 | 62 | if ($allowedActions[self::ACTION_UPDATE_CREATE_TABLE] == 1) { 63 | $results['update_create_table'] = $addCreateChange['create_table']; 64 | } 65 | 66 | if ($allowedActions[self::ACTION_REMOVE_CHANGE] == 1) { 67 | $results['remove_change'] = $dropRemove['change']; 68 | } 69 | 70 | if ($allowedActions[self::ACTION_REMOVE_DROP] == 1) { 71 | $results['remove_drop'] = $dropRemove['drop']; 72 | } 73 | 74 | if ($allowedActions[self::ACTION_REMOVE_CHANGE_TABLE] == 1) { 75 | $results['remove_change_table'] = $dropRemove['change_table']; 76 | } 77 | 78 | if ($allowedActions[self::ACTION_REMOVE_DROP_TABLE] == 1) { 79 | $results['remove_drop_table'] = $dropRemove['drop_table']; 80 | } 81 | 82 | foreach ($results as $key => $resultSet) { 83 | if (!empty($resultSet)) { 84 | $errors[$key] = $resultSet; 85 | } 86 | } 87 | 88 | return $errors; 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /Classes/Service/DatabaseApiService.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | use InvalidArgumentException; 28 | use TYPO3\CMS\Core\Utility\GeneralUtility; 29 | 30 | /** 31 | * DB API service 32 | * 33 | * @author Georg Ringer 34 | * @author Stefano Kowalke 35 | * @package Etobi\CoreAPI\Service\SiteApiService 36 | */ 37 | class DatabaseApiService { 38 | 39 | /** 40 | * @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager 41 | */ 42 | protected $objectManager; 43 | 44 | /** 45 | * @var \Etobi\CoreAPI\Service\DatabaseComparator $comparator 46 | */ 47 | protected $comparator = NULL; 48 | 49 | /** 50 | * Inject the ObjectManager 51 | * 52 | * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager 53 | */ 54 | public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) { 55 | $this->objectManager = $objectManager; 56 | } 57 | 58 | /** 59 | * Database compare. 60 | * 61 | * @param string $actions comma separated list of IDs 62 | * @param boolean $dry 63 | * 64 | * @throws \InvalidArgumentException 65 | * @return array 66 | */ 67 | public function databaseCompare($actions, $dry) { 68 | if ($dry) { 69 | $this->comparator = $this->objectManager->get('Etobi\\CoreAPI\\Service\\DatabaseCompareDry'); 70 | } else { 71 | $this->comparator = $this->objectManager->get('Etobi\\CoreAPI\\Service\\DatabaseCompareReal'); 72 | } 73 | 74 | try { 75 | $result = $this->comparator->databaseCompare($actions); 76 | } catch (\Exception $e) { 77 | throw new \InvalidArgumentException($e->getMessage()); 78 | } 79 | 80 | return $result; 81 | } 82 | 83 | /** 84 | * Get all available actions. 85 | * 86 | * @return array 87 | */ 88 | public function databaseCompareAvailableActions() { 89 | $availableActions = array_flip($this->objectManager->get('TYPO3\\CMS\\Extbase\\Reflection\\ClassReflection', 'Etobi\\CoreAPI\\Service\\DatabaseComparator')->getConstants()); 90 | 91 | foreach ($availableActions as $number => $action) { 92 | if (!$this->isFirstPartOfString($action, 'ACTION_')) { 93 | unset($availableActions[$number]); 94 | } 95 | } 96 | return $availableActions; 97 | } 98 | 99 | /** 100 | * Wrapper around GeneralUtility::isFirstPartOfStr() 101 | * 102 | * @param string $str 103 | * @param string $partStr 104 | * 105 | * @return bool 106 | */ 107 | protected function isFirstPartOfString($str, $partStr) { 108 | return GeneralUtility::isFirstPartOfStr($str, $partStr); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Classes/Command/SiteApiCommandController.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | 28 | use TYPO3\CMS\Extbase\Mvc\Controller\CommandController; 29 | 30 | /** 31 | * Site API Command Controller 32 | * 33 | * @author Georg Ringer 34 | * @author Stefano Kowalke 35 | * @package Etobi\CoreAPI\Service\SiteApiService 36 | */ 37 | class SiteApiCommandController extends CommandController { 38 | 39 | const MAXIMUM_LINE_LENGTH = 79; 40 | 41 | /** 42 | * @var \TYPO3\CMS\Core\Log\LogManager $logManager 43 | */ 44 | protected $logManager; 45 | 46 | /** 47 | * @var \TYPO3\CMS\Core\Log\Logger $logger 48 | */ 49 | protected $logger; 50 | 51 | /** 52 | * @param \TYPO3\CMS\Core\Log\LogManager $logManager 53 | * 54 | * @return void 55 | */ 56 | public function injectLogManager(\TYPO3\CMS\Core\Log\LogManager $logManager) { 57 | $this->logManager = $logManager; 58 | } 59 | 60 | /** 61 | * Initialize the object 62 | */ 63 | public function initializeObject() { 64 | $this->logger = $this->objectManager->get('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__); 65 | } 66 | 67 | /** 68 | * @var \Etobi\CoreAPI\Service\SiteApiService 69 | */ 70 | protected $siteApiService; 71 | 72 | /** 73 | * Inject the SiteApiService 74 | * 75 | * @param \Etobi\CoreAPI\Service\SiteApiService $siteApiService 76 | * 77 | * @return void 78 | */ 79 | public function injectSiteApiService(\Etobi\CoreAPI\Service\SiteApiService $siteApiService) { 80 | $this->siteApiService = $siteApiService; 81 | } 82 | 83 | /** 84 | * Basic information about the system. 85 | * 86 | * @return void 87 | */ 88 | public function infoCommand() { 89 | $data = $this->siteApiService->getSiteInfo(); 90 | 91 | foreach ($data as $key => $value) { 92 | $line = wordwrap($value, $this->output->getMaximumLineLength() - 43, PHP_EOL . str_repeat(' ', 43), TRUE); 93 | $this->outputLine('%-2s%-40s %s', array(' ', $key, $line)); 94 | } 95 | 96 | $this->logger->info('siteApi:info executes successfully.'); 97 | } 98 | 99 | /** 100 | * Sys news record is displayed at the login page. 101 | * 102 | * @param string $header Header text 103 | * @param string $text Basic text 104 | * 105 | * @return void 106 | */ 107 | public function createSysNewsCommand($header, $text = '') { 108 | $result = FALSE; 109 | 110 | try { 111 | $result = $this->siteApiService->createSysNews($header, $text); 112 | } catch (\Exception $e) { 113 | $this->outputLine($e->getMessage()); 114 | $this->quit(1); 115 | } 116 | 117 | if ($result) { 118 | $this->outputLine('News entry successfully created.'); 119 | } else { 120 | $this->outputLine('News entry NOT created.'); 121 | $this->quit(1); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Classes/Command/BackendApiCommandController.php: -------------------------------------------------------------------------------- 1 | 8 | * All rights reserved 9 | * 10 | * This script is part of the TYPO3 project. The TYPO3 project is 11 | * free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * The GNU General Public License can be found at 17 | * http://www.gnu.org/copyleft/gpl.html. 18 | * A copy is found in the text file GPL.txt and important notices to the license 19 | * from the author is found in LICENSE.txt distributed with these scripts. 20 | * 21 | * 22 | * This script is distributed in the hope that it will be useful, 23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | * GNU General Public License for more details. 26 | * 27 | * This copyright notice MUST APPEAR in all copies of the script! 28 | ***************************************************************/ 29 | use TYPO3\CMS\Extbase\Mvc\Controller\CommandController; 30 | 31 | /** 32 | * API Command Controller 33 | */ 34 | class BackendApiCommandController extends CommandController { 35 | 36 | /** 37 | * @var \TYPO3\CMS\Core\Log\LogManager $logManager 38 | */ 39 | protected $logManager; 40 | 41 | /** 42 | * @var \TYPO3\CMS\Core\Log\Logger $logger 43 | */ 44 | protected $logger; 45 | 46 | /** 47 | * @param \TYPO3\CMS\Core\Log\LogManager $logManager 48 | * 49 | * @return void 50 | */ 51 | public function injectLogManager(\TYPO3\CMS\Core\Log\LogManager $logManager) { 52 | $this->logManager = $logManager; 53 | } 54 | 55 | /** 56 | * Initialize the object 57 | */ 58 | public function initializeObject() { 59 | $this->logger = $this->objectManager->get('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__); 60 | } 61 | 62 | /** 63 | * Locks backend access for all users by writing a lock file that is checked when the backend is accessed. 64 | * 65 | * @param string $redirectUrl URL to redirect to when the backend is accessed 66 | */ 67 | public function lockCommand($redirectUrl = NULL) { 68 | if (@is_file((PATH_typo3conf . 'LOCK_BACKEND'))) { 69 | $message = 'A lockfile already exists. Overwriting it...'; 70 | $this->outputLine($message); 71 | $this->logger->info($message); 72 | } 73 | 74 | \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile(PATH_typo3conf . 'LOCK_BACKEND', (string)$redirectUrl); 75 | 76 | if ($redirectUrl === NULL) { 77 | $message = 'Wrote lock file to \'typo3conf/LOCK_BACKEND\''; 78 | $this->outputLine($message); 79 | $this->logger->info($message); 80 | } else { 81 | $message = 'Wrote lock file to \'typo3conf/LOCK_BACKEND\' with instruction to redirect to: \'' . $redirectUrl . '\''; 82 | $this->outputLine($message); 83 | $this->logger->info($message); 84 | } 85 | } 86 | 87 | /** 88 | * Unlocks the backend access by deleting the lock file 89 | */ 90 | public function unlockCommand() { 91 | if (@is_file((PATH_typo3conf . 'LOCK_BACKEND'))) { 92 | unlink(PATH_typo3conf . 'LOCK_BACKEND'); 93 | if (@is_file((PATH_typo3conf . 'LOCK_BACKEND'))) { 94 | $message = 'ERROR: Could not remove lock file \'typo3conf/LOCK_BACKEND\'!'; 95 | $this->outputLine($message); 96 | $this->logger->error($message); 97 | $this->quit(1); 98 | } else { 99 | $message = 'Removed lock file \'typo3conf/LOCK_BACKEND\''; 100 | $this->outputLine($message); 101 | $this->logger->info($message); 102 | } 103 | } else { 104 | $message = 'No lock file \'typo3conf/LOCK_BACKEND\' was found, hence no lock could be removed.'; 105 | $this->outputLine($message); 106 | $this->logger->info($message); 107 | $this->quit(1); 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /Classes/Service/DatabaseComparator.php: -------------------------------------------------------------------------------- 1 | 27 | */ 28 | abstract class DatabaseComparator { 29 | const ACTION_UPDATE_CLEAR_TABLE = 1; 30 | const ACTION_UPDATE_ADD = 2; 31 | const ACTION_UPDATE_CHANGE = 3; 32 | const ACTION_UPDATE_CREATE_TABLE = 4; 33 | const ACTION_REMOVE_CHANGE = 5; 34 | const ACTION_REMOVE_DROP = 6; 35 | const ACTION_REMOVE_CHANGE_TABLE = 7; 36 | const ACTION_REMOVE_DROP_TABLE = 8; 37 | 38 | /** 39 | * @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager 40 | */ 41 | protected $objectManager; 42 | 43 | /** 44 | * @var \TYPO3\CMS\Install\Service\SqlSchemaMigrationService $schemaMigrationService Instance of SQL handler 45 | */ 46 | protected $schemaMigrationService; 47 | 48 | /** 49 | * @var \TYPO3\CMS\Install\Service\SqlExpectedSchemaService 50 | */ 51 | protected $sqlExpectedSchemaService; 52 | 53 | /** 54 | * Inject the ObjectManager 55 | * 56 | * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager 57 | */ 58 | public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) { 59 | $this->objectManager = $objectManager; 60 | } 61 | 62 | /** 63 | * @param \TYPO3\CMS\Install\Service\SqlExpectedSchemaService $sqlExpectedSchemaService 64 | */ 65 | public function injectSqlExpectedSchemaService(SqlExpectedSchemaService $sqlExpectedSchemaService) { 66 | $this->sqlExpectedSchemaService = $sqlExpectedSchemaService; 67 | } 68 | 69 | /** 70 | * Inject the SchemaMigrationService 71 | * 72 | * @param \TYPO3\CMS\Install\Service\SqlSchemaMigrationService $schemaMigrationService 73 | */ 74 | public function injectSchemaMigrationService(\TYPO3\CMS\Install\Service\SqlSchemaMigrationService $schemaMigrationService) { 75 | $this->schemaMigrationService = $schemaMigrationService; 76 | } 77 | 78 | /** 79 | * @param $actions 80 | * 81 | * @return mixed 82 | */ 83 | public final function databaseCompare($actions) { 84 | return $this->compare($actions); 85 | } 86 | 87 | protected abstract function compare($action); 88 | 89 | /** 90 | * Wrapper around GeneralUtility::trimExplode 91 | * 92 | * @param string $values The values to explode 93 | * 94 | * @return array 95 | */ 96 | protected function trimExplode($values) { 97 | return GeneralUtility::trimExplode(',', $values); 98 | } 99 | 100 | /** 101 | * Wrapper around GeneralUtility::getUrl() 102 | * @param $url 103 | * 104 | * @return mixed 105 | */ 106 | protected function getUrl($url) { 107 | return GeneralUtility::getUrl($url); 108 | } 109 | 110 | /** 111 | * Reflect, checks and return the allowed actions 112 | * 113 | * @param string $actions comma separated list of IDs 114 | * @return array 115 | */ 116 | protected function getAllowedActions($actions) { 117 | if (empty($actions)) { 118 | throw new InvalidArgumentException('No compare modes defined'); 119 | } 120 | 121 | $allowedActions = array(); 122 | $availableActions = array_flip( 123 | $this->objectManager->get( 124 | 'TYPO3\\CMS\\Extbase\\Reflection\\ClassReflection', 125 | 'Etobi\\CoreAPI\\Service\\DatabaseComparator' 126 | )->getConstants() 127 | ); 128 | 129 | $actions = $this->trimExplode($actions); 130 | foreach ($actions as $action) { 131 | if (!isset($availableActions[$action])) { 132 | throw new InvalidArgumentException(sprintf('Action "%s" is not available!', $action)); 133 | } 134 | $allowedActions[$action] = 1; 135 | } 136 | 137 | return $allowedActions; 138 | } 139 | } 140 | 141 | -------------------------------------------------------------------------------- /Classes/Command/DatabaseApiCommandController.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | use TYPO3\CMS\Extbase\Mvc\Controller\CommandController; 28 | 29 | /** 30 | * API Command Controller 31 | * 32 | * @author Georg Ringer 33 | * @author Stefano Kowalke 34 | * @package Etobi\CoreAPI\Service\SiteApiService 35 | */ 36 | class DatabaseApiCommandController extends CommandController { 37 | 38 | /** 39 | * @var \TYPO3\CMS\Core\Log\LogManager $logManager 40 | */ 41 | protected $logManager; 42 | 43 | /** 44 | * @var \TYPO3\CMS\Core\Log\Logger $logger 45 | */ 46 | protected $logger; 47 | 48 | /** 49 | * @param \TYPO3\CMS\Core\Log\LogManager $logManager 50 | * 51 | * @return void 52 | */ 53 | public function injectLogManager(\TYPO3\CMS\Core\Log\LogManager $logManager) { 54 | $this->logManager = $logManager; 55 | } 56 | 57 | /** 58 | * Initialize the object 59 | */ 60 | public function initializeObject() { 61 | $this->logger = $this->objectManager->get('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__); 62 | } 63 | 64 | /** 65 | * @var \Etobi\CoreAPI\Service\DatabaseApiService $databaseApiService 66 | */ 67 | protected $databaseApiService; 68 | 69 | /** 70 | * Injects the DatabaseApiService object 71 | * 72 | * @param \Etobi\CoreAPI\Service\DatabaseApiService $databaseApiService 73 | * 74 | * @return void 75 | */ 76 | public function injectDatabaseApiService(\Etobi\CoreAPI\Service\DatabaseApiService $databaseApiService) { 77 | $this->databaseApiService = $databaseApiService; 78 | } 79 | 80 | /** 81 | * Database compare. 82 | * Leave the argument 'actions' empty or use "help" to see the available ones 83 | * 84 | * @param string $actions List of actions which will be executed 85 | * @param bool $dry 86 | */ 87 | public function databaseCompareCommand($actions = '', $dry = FALSE) { 88 | if ($actions === 'help' || strlen($actions) === 0) { 89 | $actions = $this->databaseApiService->databaseCompareAvailableActions(); 90 | foreach ($actions as $number => $action) { 91 | $this->outputLine(' - ' . $action . ' => ' . $number); 92 | } 93 | $this->quit(); 94 | } 95 | 96 | $result = $this->databaseApiService->databaseCompare($actions, $dry); 97 | 98 | if ($dry) { 99 | $this->outputLine('DB compare would execute the following queries:'); 100 | foreach($result as $key => $set) { 101 | $this->outputLine(sprintf('### Action: %s ###', $key)); 102 | $this->outputLine('==================================='); 103 | $this->logger->info(sprintf('### Action: %s ###', $key)); 104 | $this->logger->info('==================================='); 105 | foreach($set as $line) { 106 | $this->outputLine($line); 107 | $this->logger->info($line); 108 | } 109 | $this->outputLine(LF); 110 | } 111 | $this->logger->info('DB compare executed in dry mode'); 112 | } else { 113 | if (empty($result)) { 114 | $message = 'DB has been compared'; 115 | $this->outputLine($message); 116 | $this->logger->info($message); 117 | } else { 118 | $message = vsprintf('DB could not be compared, Error(s): %s', array(LF . implode(LF, $result))); 119 | $this->outputLine($message); 120 | $this->logger->error($message); 121 | $this->quit(1); 122 | } 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Stories in Ready](https://badge.waffle.io/typo3-coreapi/ext-coreapi.png?label=ready&title=Ready)](https://waffle.io/typo3-coreapi/ext-coreapi) [![Build Status](https://travis-ci.org/TYPO3-coreapi/ext-coreapi.svg?branch=feature%2FMakeExtensionApiCompatibleTo62)](https://travis-ci.org/TYPO3-coreapi/ext-coreapi) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/TYPO3-coreapi/ext-coreapi/badges/quality-score.png?b=feature%2FMakeExtensionApiCompatibleTo62)](https://scrutinizer-ci.com/g/TYPO3-coreapi/ext-coreapi/?branch=feature%2FMakeExtensionApiCompatibleTo62) [![Code Coverage](https://scrutinizer-ci.com/g/TYPO3-coreapi/ext-coreapi/badges/coverage.png?b=feature%2FMakeExtensionApiCompatibleTo62)](https://scrutinizer-ci.com/g/TYPO3-coreapi/ext-coreapi/?branch=feature%2FMakeExtensionApiCompatibleTo62) 2 | 3 | ## TYPO3 Extension 'coreapi' 4 | 5 | The EXT:coreapi should provide a simple to use API for common core features. Goal is to be able to do the most common tasks by CLI instead of doing it in the backend/browser. 6 | 7 | Beside of CLI commands, EXT:coreapi provides service classes, which can be used in your own implementation/extension. 8 | 9 | Checkout the project website at forge.typo3.org: 10 | http://forge.typo3.org/projects/show/extension-coreapi 11 | 12 | ### Tasks 13 | * DatabaseApi 14 | * databaseCompare 15 | * CacheApi 16 | * clearAllCaches 17 | * clearPageCache 18 | * clearConfigurationCache 19 | * ExtensionApi 20 | * info 21 | * listInstalled 22 | * updateList from TER 23 | * fetch an extension from TER 24 | * import an extension 25 | * install / uninstall extension 26 | * ~~create upload folders~~ 27 | * configure extension 28 | * SiteApi 29 | * info 30 | * createSysNews 31 | 32 | #### planned/comming soon 33 | 34 | * Backend 35 | * manage users (list, create, update, delete) 36 | * lock/unlock the TYPO3 backend 37 | * PageTree 38 | * print/get 39 | * DataApi 40 | * generic list/create/update/delete records (and not doing the plain SQL, but using the DataHandler (aka tcemain)!) 41 | * getRecordsByPid 42 | * create a database dump (exclude "temporary" tables like caches, sys_log, ...) 43 | * ReportsApi 44 | * run/check the reports from the reports module 45 | * ConfigurationApi 46 | * list, get and set TYPO3 configurations 47 | 48 | 49 | ### CLI call: ### 50 | 51 | Make sure you have a backend user called `_cli_lowlevel` 52 | 53 | If you want to use the cache clearing commands, you need to add the following snippet to the TSconfig field of this backend user: 54 | 55 | options.clearCache.all=1 56 | options.clearCache.pages=1 57 | 58 | #### TYPO3 6.2 and below #### 59 | Support for TYPO3 CMS below 6.2 was removed with version 0.2.0 of this extension. In case you need to use ext:coreapi in combination with lower version of TYPO3 CMS use version [0.1.0-beta](https://github.com/TYPO3-coreapi/ext-coreapi/releases/tag/0.1.0-beta "0.1.0-beta"). 60 | 61 | #### TYPO3 6.2+ #### 62 | If you are using TYPO3 6.2+, you can use the awesome CommandController of Extbase. 63 | 64 | This will show you all available calls 65 | ./typo3/cli_dispatch.phpsh extbase help 66 | 67 | ### Usage in Composer ### 68 | 69 | { 70 | "name": "typo3cms/test-website", 71 | "description": "TYPO3 CMS: test.com", 72 | "keywords": ["typo3", "cms"], 73 | "require": { 74 | "php": ">=5.3.3", 75 | "typo3core/cms": "*", 76 | "etobi/coreapi": "dev-master", 77 | }, 78 | "extra": { 79 | "installer-paths": { 80 | "typo3conf/ext/{$name}": [ 81 | "type:typo3-cms-extension" 82 | ] 83 | } 84 | }, 85 | "minimum-stability": "dev", 86 | "require-dev": {}, 87 | "scripts": {} 88 | } 89 | 90 | ### Running the unit tests 91 | 92 | The Unit Tests rely on [vfsStream](https://github.com/mikey179/vfsStream "vfsStream"). For some reasons ext:coreapi don't add this dependencies by itself but uses the one which is alread defined for Core Unit Tests. 93 | To install vfsStream copy the composer.json from the TYPO3 CMS package into you webroot folder and execute the command `composer install`. This will install all dependencies into Packages/Libraries/. 94 | 95 | Then run the Unit Tests. 96 | 97 | cp typo3_src/composer.json . 98 | composer install 99 | ./bin/phpunit --colors -c typo3/sysext/core/Build/UnitTests.xml typo3conf/ext/coreapi/Tests/Unit/ 100 | -------------------------------------------------------------------------------- /Classes/Service/DatabaseCompareReal.php: -------------------------------------------------------------------------------- 1 | 24 | * @author Stefano Kowalke 25 | */ 26 | class DatabaseCompareReal extends DatabaseComparator { 27 | 28 | /** 29 | * Database compare. 30 | * 31 | * @param string $actions comma separated list of IDs 32 | * 33 | * @throws InvalidArgumentException 34 | * @return array 35 | */ 36 | public function compare($actions) { 37 | $errors = array(); 38 | $results = array(); 39 | 40 | $allowedActions = $this->getAllowedActions($actions); 41 | 42 | $expectedSchema = $this->sqlExpectedSchemaService->getExpectedDatabaseSchema(); 43 | $currentSchema = $this->schemaMigrationService->getFieldDefinitions_database(); 44 | 45 | $addCreateChange = $this->schemaMigrationService->getDatabaseExtra($expectedSchema, $currentSchema); 46 | $addCreateChange = $this->schemaMigrationService->getUpdateSuggestions($addCreateChange); 47 | 48 | $dropRemove = $this->schemaMigrationService->getDatabaseExtra($currentSchema, $expectedSchema); 49 | $dropRemove = $this->schemaMigrationService->getUpdateSuggestions($dropRemove, 'remove'); 50 | 51 | $allowedRequestKeys = $this->getRequestKeys($addCreateChange, $dropRemove); 52 | 53 | if ($allowedActions[self::ACTION_UPDATE_CLEAR_TABLE] == 1) { 54 | $results[] = $this->schemaMigrationService->performUpdateQueries($addCreateChange['clear_table'], $allowedRequestKeys); 55 | } 56 | 57 | if ($allowedActions[self::ACTION_UPDATE_ADD] == 1) { 58 | $results[] = $this->schemaMigrationService->performUpdateQueries($addCreateChange['add'], $allowedRequestKeys); 59 | } 60 | 61 | if ($allowedActions[self::ACTION_UPDATE_CHANGE] == 1) { 62 | $results[] = $this->schemaMigrationService->performUpdateQueries($addCreateChange['change'], $allowedRequestKeys); 63 | } 64 | 65 | if ($allowedActions[self::ACTION_UPDATE_CREATE_TABLE] == 1) { 66 | $results[] = $this->schemaMigrationService->performUpdateQueries($addCreateChange['create_table'], $allowedRequestKeys); 67 | } 68 | 69 | if ($allowedActions[self::ACTION_REMOVE_CHANGE] == 1) { 70 | $results[] = $this->schemaMigrationService->performUpdateQueries($dropRemove['change'], $allowedRequestKeys); 71 | } 72 | 73 | if ($allowedActions[self::ACTION_REMOVE_DROP] == 1) { 74 | $results[] = $this->schemaMigrationService->performUpdateQueries($dropRemove['drop'], $allowedRequestKeys); 75 | } 76 | 77 | if ($allowedActions[self::ACTION_REMOVE_CHANGE_TABLE] == 1) { 78 | $results[] = $this->schemaMigrationService->performUpdateQueries($dropRemove['change_table'], $allowedRequestKeys); 79 | } 80 | 81 | if ($allowedActions[self::ACTION_REMOVE_DROP_TABLE] == 1) { 82 | $results[] = $this->schemaMigrationService->performUpdateQueries($dropRemove['drop_table'], $allowedRequestKeys); 83 | } 84 | 85 | foreach ($results as $resultSet) { 86 | if (is_array($resultSet)) { 87 | foreach ($resultSet as $key => $errorMessage) { 88 | $errors[$key] = $errorMessage; 89 | } 90 | } 91 | } 92 | 93 | return $errors; 94 | } 95 | 96 | /** 97 | * Get all request keys, even for those requests which are not used. 98 | * 99 | * @param array $update 100 | * @param array $remove 101 | * 102 | * @return array 103 | */ 104 | protected function getRequestKeys(array $update, array $remove) { 105 | $tmpKeys = array(); 106 | 107 | $updateActions = array('clear_table', 'add', 'change', 'create_table'); 108 | $removeActions = array('change', 'drop', 'change_table', 'drop_table'); 109 | 110 | foreach ($updateActions as $updateAction) { 111 | if (isset($update[$updateAction]) && is_array($update[$updateAction])) { 112 | $tmpKeys[] = array_keys($update[$updateAction]); 113 | } 114 | } 115 | 116 | foreach ($removeActions as $removeAction) { 117 | if (isset($remove[$removeAction]) && is_array($remove[$removeAction])) { 118 | $tmpKeys[] = array_keys($remove[$removeAction]); 119 | } 120 | } 121 | 122 | $finalKeys = array(); 123 | foreach ($tmpKeys as $keys) { 124 | foreach ($keys as $key) { 125 | $finalKeys[$key] = TRUE; 126 | } 127 | } 128 | return $finalKeys; 129 | } 130 | } 131 | 132 | -------------------------------------------------------------------------------- /Classes/Service/SiteApiService.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * (c) 2013 Claus Due , Wildside A/S 10 | * All rights reserved 11 | * 12 | * This script is part of the TYPO3 project. The TYPO3 project is 13 | * free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * The GNU General Public License can be found at 19 | * http://www.gnu.org/copyleft/gpl.html. 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 | use InvalidArgumentException; 29 | use TYPO3\CMS\Core\Utility\GeneralUtility; 30 | 31 | /** 32 | * Site API service 33 | * 34 | * @author Georg Ringer 35 | * @author Stefano Kowalke 36 | * @author Claus Due , Wildside A/S 37 | * @package Etobi\CoreAPI\Service\SiteApiService 38 | */ 39 | class SiteApiService { 40 | 41 | /** 42 | * @var \TYPO3\CMS\Extbase\Object\ObjectManager 43 | */ 44 | protected $objectManager; 45 | 46 | /** 47 | * Inject the ObjectManager 48 | * 49 | * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager 50 | * 51 | * @return void 52 | */ 53 | public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) { 54 | $this->objectManager = $objectManager; 55 | } 56 | 57 | /** 58 | * Get some basic site information. 59 | * 60 | * @return array 61 | */ 62 | public function getSiteInfo() { 63 | $data = array( 64 | 'TYPO3 version' => TYPO3_version, 65 | 'Site name' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 66 | ); 67 | 68 | $data = $this->getDiskUsage($data); 69 | $data = $this->getDatabaseSize($data); 70 | $data = $this->getCountOfExtensions($data); 71 | 72 | return $data; 73 | } 74 | 75 | /** 76 | * Create a sys news record. 77 | * 78 | * @param string $header header 79 | * @param string $text text 80 | * 81 | * @throws InvalidArgumentException 82 | * @return boolean 83 | */ 84 | public function createSysNews($header, $text) { 85 | if (strlen($header) === 0) { 86 | throw new InvalidArgumentException('No header given'); 87 | } 88 | 89 | return $this->getDatabaseHandler()->exec_INSERTquery('sys_news', array( 90 | 'title' => $header, 91 | 'content' => $text, 92 | 'tstamp' => $GLOBALS['EXEC_TIME'], 93 | 'crdate' => $GLOBALS['EXEC_TIME'], 94 | 'cruser_id' => $GLOBALS['BE_USER']->user['uid'] 95 | )); 96 | } 97 | 98 | /** 99 | * Get disk usage. 100 | * 101 | * @param array $data 102 | * 103 | * @return array 104 | */ 105 | public function getDiskUsage($data) { 106 | if (TYPO3_OS !== 'WIN') { 107 | $data['Combined disk usage'] = trim(array_shift(explode("\t", shell_exec('du -sh ' . PATH_site)))); 108 | } 109 | 110 | return $data; 111 | } 112 | 113 | /** 114 | * Get database size. 115 | * 116 | * @param array $data 117 | * 118 | * @return array 119 | */ 120 | public function getDatabaseSize($data) { 121 | $databaseHandler = $this->getDatabaseHandler(); 122 | $databaseSizeResult = $databaseHandler->sql_query("SELECT SUM( data_length + index_length ) / 1024 / 1024 AS size FROM information_schema.TABLES WHERE table_schema = '" . TYPO3_db . "'"); 123 | $databaseSizeRow = $databaseHandler->sql_fetch_assoc($databaseSizeResult); 124 | $databaseSize = array_pop($databaseSizeRow); 125 | $value = number_format($databaseSize, ($databaseSize > 10 ? 0 : 1)) . 'M'; 126 | $data['Database size'] = $value; 127 | 128 | return $data; 129 | } 130 | 131 | /** 132 | * Get count of local installed extensions. 133 | * 134 | * @param array $data 135 | * 136 | * @return array 137 | */ 138 | public function getCountOfExtensions($data) { 139 | /** @var \Etobi\CoreAPI\Service\ExtensionApiService $extensionService */ 140 | $extensionService = $this->objectManager->get('Etobi\\CoreAPI\\Service\\ExtensionApiService'); 141 | $extensions = $extensionService->listExtensions('Local'); 142 | $data['Count local installed extensions'] = count($extensions); 143 | 144 | return $data; 145 | } 146 | 147 | /** 148 | * Returns the DatabaseConnection 149 | * 150 | * @return \TYPO3\CMS\Core\Database\DatabaseConnection 151 | */ 152 | protected function getDatabaseHandler() { 153 | return $GLOBALS['TYPO3_DB']; 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Tests/Unit/Service/CacheApiServiceTest.php: -------------------------------------------------------------------------------- 1 | 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * A copy is found in the textfile GPL.txt and important notices to the license 20 | * from the author is found in LICENSE.txt distributed with these scripts. 21 | * 22 | * 23 | * This script is distributed in the hope that it will be useful, 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | * GNU General Public License for more details. 27 | * 28 | * This copyright notice MUST APPEAR in all copies of the script! 29 | ***************************************************************/ 30 | 31 | use TYPO3\CMS\Core\Tests\UnitTestCase; 32 | 33 | /** 34 | * Class CacheApiServiceTest 35 | * 36 | * @package Etobi\CoreApi\Tests\Unit\Service 37 | * @author Stefano Kowalke 38 | * @coversDefaultClass \Etobi\CoreAPI\Service\CacheApiService 39 | */ 40 | class CacheApiServiceTest extends UnitTestCase { 41 | /** 42 | * @var \Etobi\CoreApi\Service\CacheApiService|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject 43 | */ 44 | protected $subject; 45 | 46 | /** 47 | * @var \TYPO3\CMS\Core\DataHandling\DataHandler|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject 48 | */ 49 | protected $dataHandlerMock; 50 | 51 | /** 52 | * @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject 53 | */ 54 | protected $backendUserAuthenticationMock; 55 | 56 | /** 57 | * @var \TYPO3\CMS\Extbase\Object\ObjectManager|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject 58 | */ 59 | protected $objectManagerMock; 60 | 61 | /** 62 | * Setup the test 63 | */ 64 | public function setup() { 65 | $this->subject = $this->getMock('Etobi\\CoreApi\\Service\\CacheApiService', array('clear_cacheCmd')); 66 | $this->dataHandlerMock = $this->getMock('TYPO3\\CMS\\Core\\DataHandling\\DataHandler', array('clear_cacheCmd')); 67 | $this->objectManagerMock = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManager'); 68 | $this->backendUserAuthenticationMock = $this->getMock('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication', array('dummy')); 69 | $this->objectManagerMock->expects($this->once())->method('get')->will($this->returnValue($this->backendUserAuthenticationMock)); 70 | 71 | $this->subject->injectDataHandler($this->dataHandlerMock); 72 | $this->subject->injectObjectManager($this->objectManagerMock); 73 | $this->subject->initializeObject(); 74 | } 75 | 76 | /** 77 | * @test 78 | * @covers ::clearAllCaches 79 | */ 80 | public function clearAllCachesClearAllCaches() { 81 | $this->dataHandlerMock->expects($this->once())->method('clear_cacheCmd')->with('all'); 82 | $this->subject->clearAllCaches(); 83 | } 84 | 85 | /** 86 | * @test 87 | * @covers ::clearPageCache 88 | */ 89 | public function clearPageCacheClearPageCache() { 90 | $this->dataHandlerMock->expects($this->once())->method('clear_cacheCmd')->with('pages'); 91 | $this->subject->clearPageCache(); 92 | } 93 | 94 | /** 95 | * @test 96 | * @covers ::clearConfigurationCache 97 | */ 98 | public function clearConfigurationCacheClearsConfigurationCache() { 99 | $this->dataHandlerMock->expects($this->once())->method('clear_cacheCmd')->with('temp_cached'); 100 | $this->subject->clearConfigurationCache(); 101 | } 102 | 103 | /** 104 | * @test 105 | * @covers ::clearAllExceptPageCache 106 | */ 107 | public function clearAllExceptPageCacheClearsAllExceptPageCache() { 108 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] = array( 109 | 0 => 'cache_core', 110 | 1 => 'cache_classes', 111 | 2 => 'cache_hash', 112 | 3 => 'cache_pages', 113 | 4 => 'cache_pagesection', 114 | 5 => 'cache_phpcode', 115 | 6 => 'cache_runtime', 116 | 7 => 'cache_rootline', 117 | 8 => 'l10n', 118 | 9 => 'extbase_object', 119 | 10 => 'extbase_reflection', 120 | ); 121 | 122 | $cacheManager = $this->getMock('TYPO3\\CMS\\Core\\Cache\\CacheManager'); 123 | $cacheManager->expects($this->exactly(11))->method('hasCache'); 124 | $GLOBALS['typo3CacheManager'] = $cacheManager; 125 | $this->subject->clearAllExceptPageCache(); 126 | } 127 | 128 | /** 129 | * @test 130 | * @covers ::clearSystemCache 131 | */ 132 | public function clearSystemCacheClearsSystemCache() { 133 | $this->dataHandlerMock->expects($this->once())->method('clear_cacheCmd')->with('system'); 134 | $this->subject->clearSystemCache(); 135 | } 136 | } -------------------------------------------------------------------------------- /Classes/Command/CacheApiCommandController.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | use TYPO3\CMS\Extbase\Mvc\Controller\CommandController; 28 | 29 | /** 30 | * API Command Controller 31 | * 32 | * @author Georg Ringer 33 | * @author Stefano Kowalke 34 | * @package Etobi\CoreAPI\Service\SiteApiService 35 | */ 36 | class CacheApiCommandController extends CommandController { 37 | /** 38 | * @var \TYPO3\CMS\Core\Log\LogManager $logManager 39 | */ 40 | protected $logManager; 41 | 42 | /** 43 | * @var \TYPO3\CMS\Core\Log\Logger $logger 44 | */ 45 | protected $logger; 46 | 47 | /** 48 | * @param \TYPO3\CMS\Core\Log\LogManager $logManager 49 | * 50 | * @return void 51 | */ 52 | public function injectLogManager(\TYPO3\CMS\Core\Log\LogManager $logManager) { 53 | $this->logManager = $logManager; 54 | } 55 | 56 | /** 57 | * Initialize the object 58 | */ 59 | public function initializeObject() { 60 | $this->logger = $this->objectManager->get('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__); 61 | } 62 | 63 | /** 64 | * @var \Etobi\CoreAPI\Service\CacheApiService 65 | */ 66 | protected $cacheApiService; 67 | 68 | /** 69 | * Inject the CacheApiService 70 | * 71 | * @param \Etobi\CoreAPI\Service\CacheApiService $cacheApiService 72 | */ 73 | public function injectCacheApiService(\Etobi\CoreAPI\Service\CacheApiService $cacheApiService) { 74 | $this->cacheApiService = $cacheApiService; 75 | } 76 | 77 | /** 78 | * Clear all caches. 79 | * If hard, cache will be cleared in a more straightforward approach and the according backend hooks are not executed. 80 | * 81 | * @param boolean $hard 82 | * @return void 83 | */ 84 | public function clearAllCachesCommand($hard = false) { 85 | $this->cacheApiService->clearAllCaches($hard); 86 | $message = 'All caches have been cleared%s.'; 87 | $this->logger->info($message); 88 | $this->outputLine($message, $hard ? array(' hard') : array('')); 89 | } 90 | 91 | /** 92 | * Clear system cache. 93 | * 94 | * @return void 95 | */ 96 | public function clearSystemCacheCommand() { 97 | $this->cacheApiService->clearSystemCache(); 98 | $message = 'System cache has been cleared'; 99 | $this->logger->info($message); 100 | $this->outputLine($message); 101 | } 102 | 103 | /** 104 | * Clears the opcode cache. 105 | * 106 | * @param string|NULL $fileAbsPath The file as absolute path to be cleared 107 | * or NULL to clear completely. 108 | * 109 | * @return void 110 | */ 111 | public function clearAllActiveOpcodeCacheCommand($fileAbsPath = NULL) { 112 | $this->cacheApiService->clearAllActiveOpcodeCache($fileAbsPath); 113 | 114 | if ($fileAbsPath !== NULL) { 115 | $message = sprintf('The opcode cache for the file %s has been cleared', $fileAbsPath); 116 | $this->outputLine($message); 117 | $this->logger->info($message); 118 | } else { 119 | $message = 'The complete opcode cache has been cleared'; 120 | $this->outputLine($message); 121 | $this->logger->info($message); 122 | } 123 | } 124 | 125 | /** 126 | * Clear configuration cache (temp_CACHED_..). 127 | * 128 | * @return void 129 | */ 130 | public function clearConfigurationCacheCommand() { 131 | $this->cacheApiService->clearConfigurationCache(); 132 | $message = 'Configuration cache has been cleared.'; 133 | $this->logger->info($message); 134 | $this->outputLine($message); 135 | } 136 | 137 | /** 138 | * Clear page cache. 139 | * 140 | * @return void 141 | */ 142 | public function clearPageCacheCommand() { 143 | $this->cacheApiService->clearPageCache(); 144 | $message = 'Page cache has been cleared.'; 145 | $this->logger->info($message); 146 | $this->outputLine($message); 147 | } 148 | 149 | /** 150 | * Clear all caches except the page cache. 151 | * This is especially useful on big sites when you can't just drop the page cache. 152 | * 153 | * @return void 154 | */ 155 | public function clearAllExceptPageCacheCommand() { 156 | $clearedCaches = $this->cacheApiService->clearAllExceptPageCache(); 157 | $message = 'Cleared caches: ' . implode(', ', $clearedCaches); 158 | $this->logger->info($message); 159 | $this->outputLine($message); 160 | } 161 | } -------------------------------------------------------------------------------- /Classes/Service/CacheApiService.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; 28 | use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; 29 | use TYPO3\CMS\Core\Utility\GeneralUtility; 30 | 31 | /** 32 | * Cache API service 33 | * 34 | * @author Georg Ringer 35 | * @author Stefano Kowalke 36 | * @package Etobi\CoreAPI\Service\SiteApiService 37 | */ 38 | class CacheApiService { 39 | 40 | /** 41 | * @var \TYPO3\CMS\Core\DataHandling\DataHandler 42 | */ 43 | protected $dataHandler; 44 | 45 | /** 46 | * @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager 47 | */ 48 | protected $objectManager; 49 | 50 | /** 51 | * @var \TYPO3\CMS\Install\Service\ClearCacheService 52 | */ 53 | protected $installToolClearCacheService; 54 | 55 | /** 56 | * @param \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler 57 | * 58 | * @return void 59 | */ 60 | public function injectDataHandler(\TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler) { 61 | $this->dataHandler = $dataHandler; 62 | } 63 | 64 | /** 65 | * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager 66 | * 67 | * @return void 68 | */ 69 | public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) { 70 | $this->objectManager = $objectManager; 71 | } 72 | 73 | /** 74 | * @param \TYPO3\CMS\Install\Service\ClearCacheService $installToolClearCacheService 75 | * 76 | * @return void 77 | */ 78 | public function injectInstallToolClearCacheService(\TYPO3\CMS\Install\Service\ClearCacheService $installToolClearCacheService) { 79 | $this->installToolClearCacheService = $installToolClearCacheService; 80 | } 81 | 82 | /** 83 | * Initialize the object. 84 | * 85 | * @return void 86 | */ 87 | public function initializeObject() { 88 | // Create a fake admin user 89 | $adminUser = $this->objectManager->get('TYPO3\\CMS\\Core\\Authentication\\BackendUserAuthentication'); 90 | $adminUser->user['uid'] = $GLOBALS['BE_USER']->user['uid']; 91 | $adminUser->user['username'] = '_CLI_lowlevel'; 92 | $adminUser->user['admin'] = 1; 93 | $adminUser->workspace = 0; 94 | 95 | $this->dataHandler->start(Array(), Array(), $adminUser); 96 | } 97 | 98 | /** 99 | * Clear all caches. 100 | * 101 | * @param bool $hard 102 | * @return void 103 | */ 104 | public function clearAllCaches($hard = FALSE) { 105 | !$hard ? $this->dataHandler->clear_cacheCmd('all') : $this->installToolClearCacheService->clearAll(); 106 | } 107 | 108 | /** 109 | * Clear the page cache. 110 | * 111 | * @return void 112 | */ 113 | public function clearPageCache() { 114 | $this->dataHandler->clear_cacheCmd('pages'); 115 | } 116 | 117 | /** 118 | * Clears the configuration cache. 119 | * 120 | * @return void 121 | */ 122 | public function clearConfigurationCache() { 123 | $this->dataHandler->clear_cacheCmd('temp_cached'); 124 | } 125 | 126 | /** 127 | * Clear the system cache 128 | * 129 | * @return void 130 | */ 131 | public function clearSystemCache() { 132 | $this->dataHandler->clear_cacheCmd('system'); 133 | } 134 | 135 | /** 136 | * Clears the opcode cache. 137 | * 138 | * @param string|NULL $fileAbsPath The file as absolute path to be cleared 139 | * or NULL to clear completely. 140 | * 141 | * @return void 142 | */ 143 | public function clearAllActiveOpcodeCache($fileAbsPath = NULL) { 144 | $this->clearAllActiveOpcodeCacheWrapper($fileAbsPath); 145 | } 146 | 147 | /** 148 | * Clear all caches except the page cache. 149 | * This is especially useful on big sites when you can't 150 | * just drop the page cache. 151 | * 152 | * @return array with list of cleared caches 153 | */ 154 | public function clearAllExceptPageCache() { 155 | $out = array(); 156 | $cacheKeys = array_keys($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']); 157 | $ignoredCaches = array('cache_pages', 'cache_pagesection'); 158 | 159 | $toBeFlushed = array_diff($cacheKeys, $ignoredCaches); 160 | 161 | /** @var \TYPO3\CMS\Core\Cache\CacheManager $cacheManager */ 162 | $cacheManager = $GLOBALS['typo3CacheManager']; 163 | foreach ($cacheKeys as $cacheKey) { 164 | if ($cacheManager->hasCache($cacheKey)) { 165 | $out[] = $cacheKey; 166 | $singleCache = $cacheManager->getCache($cacheKey); 167 | $singleCache->flush(); 168 | } 169 | } 170 | 171 | return $toBeFlushed; 172 | } 173 | 174 | /** 175 | * Clears the opcode cache. This just wraps the static call for testing purposes. 176 | * 177 | * @param string|NULL $fileAbsPath The file as absolute path to be cleared 178 | * or NULL to clear completely. 179 | * 180 | * @return void 181 | */ 182 | protected function clearAllActiveOpcodeCacheWrapper($fileAbsPath) { 183 | if (version_compare(TYPO3_version, '7.4.0', '<')) { 184 | \TYPO3\CMS\Core\Utility\OpcodeCacheUtility::clearAllActive($fileAbsPath); 185 | } else { 186 | \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\CMS\Core\Service\OpcodeCacheService')->clearAllActive($fileAbsPath); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Tests/Unit/Service/DatabaseApiServiceTest.php: -------------------------------------------------------------------------------- 1 | 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * A copy is found in the textfile GPL.txt and important notices to the license 20 | * from the author is found in LICENSE.txt distributed with these scripts. 21 | * 22 | * 23 | * This script is distributed in the hope that it will be useful, 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | * GNU General Public License for more details. 27 | * 28 | * This copyright notice MUST APPEAR in all copies of the script! 29 | ***************************************************************/ 30 | 31 | use TYPO3\CMS\Core\Tests\UnitTestCase; 32 | 33 | /** 34 | * Class DatabaseApiServiceTest 35 | * 36 | * @package Etobi\CoreApi\Tests\Unit\Service 37 | * @author Stefano Kowalke 38 | * @coversDefaultClass \Etobi\CoreAPI\Service\DatabaseApiService 39 | */ 40 | class DatabaseApiServiceTest extends UnitTestCase { 41 | 42 | /** 43 | * @var \Etobi\CoreApi\Service\DatabaseApiService|\PHPUnit_Framework_MockObject_MockObject $subject 44 | */ 45 | protected $subject; 46 | 47 | /** 48 | * @var \TYPO3\CMS\Extbase\Object\ObjectManager|\PHPUnit_Framework_MockObject_MockObject $objectManagerMock 49 | */ 50 | protected $objectManagerMock; 51 | 52 | /** 53 | * Setup the test 54 | */ 55 | public function setup() { 56 | $this->subject = $this->getMock('Etobi\\CoreApi\\Service\\DatabaseApiService', array('dummy')); 57 | $this->objectManagerMock = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManager', array('get')); 58 | } 59 | 60 | /** 61 | * Tears the test down 62 | */ 63 | public function tearDown() { 64 | unset($this->subject); 65 | } 66 | 67 | /** 68 | * @test 69 | * @covers ::databaseCompare 70 | */ 71 | public function databaseCompareCreatesDatabaseCompareRealObjectWhenNoDryRunIsDemanded() { 72 | $comparator = $this->getMock('Etobi\\CoreAPI\\Service\\DatabaseCompareReal'); 73 | $this->objectManagerMock->expects($this->once())->method('get')->with('Etobi\\CoreAPI\\Service\\DatabaseCompareReal')->will($this->returnValue($comparator)); 74 | 75 | $this->subject->injectObjectManager($this->objectManagerMock); 76 | $this->subject->databaseCompare(1, FALSE); 77 | } 78 | 79 | /** 80 | * @test 81 | * @covers ::databaseCompare 82 | */ 83 | public function databaseCompareCreatesDatabaseCompareDryObjectWhenDryRunIsDemanded() { 84 | $comparator = $this->getMock('Etobi\\CoreAPI\\Service\\DatabaseCompareDry'); 85 | $this->objectManagerMock->expects($this->once())->method('get')->with('Etobi\\CoreAPI\\Service\\DatabaseCompareDry')->will($this->returnValue($comparator)); 86 | 87 | $this->subject->injectObjectManager($this->objectManagerMock); 88 | $this->subject->databaseCompare(1, TRUE); 89 | } 90 | 91 | 92 | /** 93 | * @test 94 | * @covers ::databaseCompare 95 | */ 96 | public function databaseCompareOneAction() { 97 | $comparator = $this->getMock('Etobi\\CoreAPI\\Service\\DatabaseCompareReal'); 98 | $this->objectManagerMock->expects($this->once())->method('get')->with('Etobi\\CoreAPI\\Service\\DatabaseCompareReal')->will($this->returnValue($comparator)); 99 | $this->subject->injectObjectManager($this->objectManagerMock); 100 | 101 | $this->subject->databaseCompare('1', FALSE); 102 | } 103 | 104 | /** 105 | * Returns the complete available actions 106 | * 107 | * @return array 108 | */ 109 | protected function getAvailableActions() { 110 | return array( 111 | 'ACTION_UPDATE_CLEAR_TABLE' => 1, 112 | 'ACTION_UPDATE_ADD' => 2, 113 | 'ACTION_UPDATE_CHANGE' => 3, 114 | 'ACTION_UPDATE_CREATE_TABLE' => 4, 115 | 'ACTION_REMOVE_CHANGE' => 5, 116 | 'ACTION_REMOVE_DROP' => 6, 117 | 'ACTION_REMOVE_CHANGE_TABLE' => 7, 118 | 'ACTION_REMOVE_DROP_TABLE' => 8 119 | ); 120 | } 121 | 122 | protected function getLoadedExtensions() { 123 | return array( 124 | 'core' => array( 125 | 'type' => 'S', 126 | 'siteRelPath' => 'typo3/sysext/core/', 127 | 'typo3RelPath' => 'sysext/core/', 128 | 'ext_localconf.php' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/core/ext_localconf.php', 129 | 'ext_tables.php' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/core/ext_tables.php', 130 | 'ext_tables.sql' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/core/ext_tables.sql', 131 | 'ext_icon' => 'ext_icon.png' 132 | ), 133 | 'backend' => array( 134 | 'type' => 'S', 135 | 'siteRelPath' => 'typo3/sysext/backend/', 136 | 'typo3RelPath' => 'sysext/backend/', 137 | 'ext_localconf.php' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/backend/ext_localconf.php', 138 | 'ext_tables.php' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/backend/ext_tables.php', 139 | 'ext_icon' => 'ext_icon.png' 140 | ), 141 | 'extbase' => array( 142 | 'type' => 'S', 143 | 'siteRelPath' => 'typo3/sysext/extbase/', 144 | 'typo3RelPath' => 'sysext/extbase/', 145 | 'ext_localconf.php' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/extbase/ext_localconf.php', 146 | 'ext_tables.php' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/extbase/ext_tables.php', 147 | 'ext_tables.sql' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/extbase/ext_tables.sql', 148 | 'ext_typoscript_setup.txt' => '/Volumes/HDD/Users/sok/Sites/TYPO3/www.coreapi.dev/http/typo3/sysext/extbase/ext_typoscript_setup.txt', 149 | 'ext_icon' => 'ext_icon.png' 150 | ), 151 | ); 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /Tests/Unit/Service/SiteApiServiceTest.php: -------------------------------------------------------------------------------- 1 | 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * A copy is found in the textfile GPL.txt and important notices to the license 20 | * from the author is found in LICENSE.txt distributed with these scripts. 21 | * 22 | * 23 | * This script is distributed in the hope that it will be useful, 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 | * GNU General Public License for more details. 27 | * 28 | * This copyright notice MUST APPEAR in all copies of the script! 29 | ***************************************************************/ 30 | 31 | use TYPO3\CMS\Core\Tests\UnitTestCase; 32 | 33 | /** 34 | * Class SiteApiServiceTest 35 | * 36 | * @package Etobi\CoreApi\Tests\Unit\Service 37 | * @author Stefano Kowalke 38 | * @coversDefaultClass \Etobi\CoreAPI\Service\SiteApiService 39 | */ 40 | class SiteApiServiceTest extends UnitTestCase { 41 | 42 | /** 43 | * @var \Etobi\CoreApi\Service\SiteApiService|\PHPUnit_Framework_MockObject_MockObject $subject 44 | */ 45 | protected $subject; 46 | 47 | /** 48 | * Setup the test 49 | */ 50 | public function setUp() { 51 | $this->subject = $this->getMock('Etobi\\CoreApi\\Service\\SiteApiService', array('dummy')); 52 | } 53 | 54 | /** 55 | * Tears the test down 56 | */ 57 | public function tearDown() { 58 | unset($this->subject); 59 | } 60 | 61 | /** 62 | * @test 63 | * @covers ::getSiteInfo 64 | */ 65 | public function getSiteInfoReturnsSiteInfo() { 66 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] = 'CoreApi Testpage'; 67 | 68 | $expectedData = array( 69 | 'TYPO3 version' => TYPO3_version, 70 | 'Site name' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 71 | 'Combined disk usage' => '42M', 72 | 'Database size' => '23M', 73 | 'Count local installed extensions' => 4 74 | ); 75 | 76 | $data1 = array( 77 | 'TYPO3 version' => TYPO3_version, 78 | 'Site name' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 79 | ); 80 | 81 | $data2 = array( 82 | 'TYPO3 version' => TYPO3_version, 83 | 'Site name' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 84 | 'Combined disk usage' => '42M', 85 | ); 86 | 87 | $data3 = array( 88 | 'TYPO3 version' => TYPO3_version, 89 | 'Site name' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 90 | 'Combined disk usage' => '42M', 91 | 'Database size' => '23M', 92 | 93 | ); 94 | 95 | $data4 = array( 96 | 'TYPO3 version' => TYPO3_version, 97 | 'Site name' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 98 | 'Combined disk usage' => '42M', 99 | 'Database size' => '23M', 100 | 'Count local installed extensions' => 4 101 | ); 102 | 103 | /** @var \Etobi\CoreApi\Service\SiteApiService|\PHPUnit_Framework_MockObject_MockObject $subject */ 104 | $subject = $this->getMock('Etobi\\CoreApi\\Service\\SiteApiService', array('getDiskUsage', 'getDatabaseSize', 'getCountOfExtensions')); 105 | 106 | $subject->expects($this->once())->method('getDiskUsage')->with($data1)->will($this->returnValue($data2)); 107 | $subject->expects($this->once())->method('getDatabaseSize')->with($data2)->will($this->returnValue($data3)); 108 | $subject->expects($this->once())->method('getCountOfExtensions')->with($data3)->will($this->returnValue($data4)); 109 | $calculatedData = $subject->getSiteInfo(); 110 | $this->assertEquals($expectedData, $calculatedData); 111 | } 112 | 113 | /** 114 | * @test 115 | * @covers ::createSysNews 116 | * @expectedException \InvalidArgumentException 117 | * @expectedExceptionMessage No header given 118 | */ 119 | public function createSysNewsNoHeaderGivenThrowsException() { 120 | $this->subject->createSysNews('', 'Foo'); 121 | } 122 | 123 | /** 124 | * @test 125 | * @covers ::createSysNews 126 | */ 127 | public function createSysNewsCreateNewsEntry() { 128 | $databaseConnectionMock = $this->getMock('TYPO3\CMS\Core\Database\DatabaseConnection', array('exec_INSERTquery'), array(), '', FALSE); 129 | /** @var \Etobi\CoreApi\Service\SiteApiService|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject */ 130 | $subject = $this->getAccessibleMock('Etobi\\CoreApi\\Service\\SiteApiService', array('getDatabaseHandler')); 131 | 132 | $databaseConnectionMock->expects($this->once())->method('exec_INSERTquery')->with('sys_news', array('title' => 'Foo', 'content' => 'Bar', 'tstamp' => $GLOBALS['EXEC_TIME'], 'crdate' => $GLOBALS['EXEC_TIME'], 'cruser_id' => $GLOBALS['BE_USER']->user['uid'])); 133 | $subject->expects($this->once())->method('getDatabaseHandler')->will($this->returnValue($databaseConnectionMock)); 134 | 135 | $subject->createSysNews('Foo', 'Bar'); 136 | } 137 | 138 | /** 139 | * @test 140 | * @covers ::getDiskUsage 141 | */ 142 | public function getDiskUsageCalculatesDiskUsageWhenOsIsNotWindows() { 143 | if (TYPO3_OS === 'WIN') { 144 | $this->markTestSkipped('Test is only valid for Unix based platforms'); 145 | } 146 | $data = array(); 147 | $diskUsage = trim(array_shift(explode("\t", shell_exec('du -sh ' . PATH_site)))); 148 | 149 | $this->assertEquals(array('Combined disk usage' => $diskUsage), $this->subject->getDiskUsage($data)); 150 | } 151 | 152 | /** 153 | * @test 154 | * @covers ::getDiskUsage 155 | */ 156 | public function getDiskUsageCalculatesNoDiskUsageWhenOsIsWindows() { 157 | if (TYPO3_OS === 'WIN') { 158 | $data = array(); 159 | $this->assertEquals(array(), $this->subject->getDiskUsage($data)); 160 | } else { 161 | $this->markTestSkipped('Test is only valid for Windows based platforms'); 162 | } 163 | } 164 | 165 | /** 166 | * @test 167 | * @covers ::getDatabaseSize 168 | */ 169 | public function getDatabaseSizeReturnsDatabaseSize() { 170 | $data = array(); 171 | 172 | $databaseConnectionMock = $this->getMock('TYPO3\CMS\Core\Database\DatabaseConnection', array('sql_query', 'sql_fetch_assoc'), array(), '', FALSE); 173 | /** @var \Etobi\CoreApi\Service\SiteApiService|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject */ 174 | $subject = $this->getAccessibleMock('Etobi\\CoreApi\\Service\\SiteApiService', array('getDatabaseHandler')); 175 | 176 | $databaseConnectionMock->expects($this->once())->method('sql_query')->with("SELECT SUM( data_length + index_length ) / 1024 / 1024 AS size FROM information_schema.TABLES WHERE table_schema = '" . TYPO3_db . "'"); 177 | $databaseConnectionMock->expects($this->once())->method('sql_fetch_assoc')->will($this->returnValue(array('size' => 30.06250000))); 178 | $subject->expects($this->once())->method('getDatabaseHandler')->will($this->returnValue($databaseConnectionMock)); 179 | 180 | $data = $subject->getDatabaseSize($data); 181 | $this->assertEquals('30M', $data['Database size']); 182 | } 183 | 184 | /** 185 | * @test 186 | * @covers ::getCountOfExtensions 187 | */ 188 | public function getCountOfExtensionsCountsLocalInstalledExtensions() { 189 | $data = array(); 190 | $extensionApiServiceMock = $this->getMock('Etobi\\CoreAPI\\Service\\ExtensionApiService', array('listExtensions')); 191 | $extensionApiServiceMock->expects($this->once())->method('listExtensions')->with('Local')->will($this->returnValue($this->getExtensionArrayForCreateUploadFolders())); 192 | $objectManagerMock = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManager', array('get')); 193 | $objectManagerMock->expects($this->once())->method('get')->will($this->returnValue($extensionApiServiceMock)); 194 | $this->subject->injectObjectManager($objectManagerMock); 195 | $data = $this->subject->getCountOfExtensions($data); 196 | $this->assertEquals(4, $data['Count local installed extensions']); 197 | } 198 | 199 | /** 200 | * Provides the array which is return value from ExtensionServiceApi::getInstalledExtensions() 201 | * 202 | * @return array 203 | */ 204 | public function getExtensionArrayForCreateUploadFolders() { 205 | return array( 206 | 'about' => array( 207 | 'title' => 'Help>About', 208 | 'description' => 'Shows info about TYPO3 and installed extensions', 209 | 'category' => 'module', 210 | 'shy' => 1, 211 | 'dependencies' => '', 212 | 'conflicts' => '', 213 | 'priority' => '', 214 | 'loadOrder' => '', 215 | 'module' => 'mod', 216 | 'state' => 'stable', 217 | 'internal' => 0, 218 | 'uploadfolder' => 0, 219 | 'createDirs' => '', 220 | 'modify_tables' => '', 221 | 'clearCacheOnLoad' => '', 222 | 'lockType' => '', 223 | 'author' => 'Kasper Skaarhoj', 224 | 'author_email' => 'kasperYYYY@typo3.com', 225 | 'author_company' => 'Curby Soft Multimedia', 226 | 'CGLcompilance' => '', 227 | 'CGLcompilance_note' => '', 228 | 'version' => '6.2.0', 229 | '_md5_values_when_last_written' => '', 230 | 'constraints' => array(), 231 | 'suggests' => array(), 232 | 'key' => 'about' 233 | ), 234 | 'backend' => array( 235 | 'title' => 'TYPO3 Backend', 236 | 'description' => 'Classes for the TYPO3 backend.', 237 | 'category' => 'be', 238 | 'shy' => 1, 239 | 'dependencies' => '', 240 | 'conflicts' => '', 241 | 'priority' => 'top', 242 | 'loadOrder' => '', 243 | 'module' => '', 244 | 'state' => 'stable', 245 | 'internal' => 1, 246 | 'uploadfolder' => 0, 247 | 'createDirs' => '', 248 | 'modify_tables' => '', 249 | 'clearCacheOnLoad' => 0, 250 | 'lockType' => 'S', 251 | 'author' => 'Kasper Skaarhoj', 252 | 'author_email' => 'kasperYYYY@typo3.com', 253 | 'author_company' => 'Curby Soft Multimedia', 254 | 'CGLcompilance' => '', 255 | 'CGLcompilance_note' => '', 256 | 'version' => '6.2.0', 257 | '_md5_values_when_last_written' => '', 258 | 'constraints' => array(), 259 | 'suggests' => array(), 260 | 'key' => 'about' 261 | ), 262 | 'core' => array( 263 | 'title' => 'TYPO3 Core', 264 | 'description' => 'Classes for the TYPO3 backend.', 265 | 'category' => 'be', 266 | 'shy' => 1, 267 | 'dependencies' => '', 268 | 'conflicts' => '', 269 | 'priority' => 'top', 270 | 'loadOrder' => '', 271 | 'module' => '', 272 | 'state' => 'stable', 273 | 'internal' => 1, 274 | 'uploadfolder' => 0, 275 | 'createDirs' => '', 276 | 'modify_tables' => '', 277 | 'clearCacheOnLoad' => 0, 278 | 'lockType' => 'S', 279 | 'author' => 'Kasper Skaarhoj', 280 | 'author_email' => 'kasperYYYY@typo3.com', 281 | 'author_company' => 'Curby Soft Multimedia', 282 | 'CGLcompilance' => '', 283 | 'CGLcompilance_note' => '', 284 | 'version' => '6.2.0', 285 | '_md5_values_when_last_written' => '', 286 | 'constraints' => array(), 287 | 'suggests' => array(), 288 | 'key' => 'about' 289 | ), 290 | 'dummy' => array( 291 | 'title' => 'Dummy Extension for testing', 292 | 'description' => 'This is just a dummy extension', 293 | 'category' => 'experimental', 294 | 'shy' => 1, 295 | 'dependencies' => '', 296 | 'conflicts' => '', 297 | 'priority' => '', 298 | 'loadOrder' => '', 299 | 'module' => 'mod', 300 | 'state' => 'stable', 301 | 'internal' => 0, 302 | 'uploadfolder' => 0, 303 | 'createDirs' => '', 304 | 'modify_tables' => '', 305 | 'clearCacheOnLoad' => '', 306 | 'lockType' => '', 307 | 'author' => 'Stefano Kowalke', 308 | 'author_email' => 'blueduck@gmx.net', 309 | 'author_company' => 'Arroba IT', 310 | 'CGLcompilance' => '', 311 | 'CGLcompilance_note' => '', 312 | 'version' => '6.2.0', 313 | '_md5_values_when_last_written' => '', 314 | 'constraints' => array(), 315 | 'suggests' => array(), 316 | 'key' => 'about' 317 | ) 318 | ); 319 | } 320 | } 321 | 322 | -------------------------------------------------------------------------------- /Classes/Command/ExtensionApiCommandController.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | use Exception; 28 | use InvalidArgumentException; 29 | use TYPO3\CMS\Core\Utility\GeneralUtility; 30 | use TYPO3\CMS\Extbase\Mvc\Controller\CommandController; 31 | 32 | /** 33 | * Extension API Command Controller 34 | * 35 | * @author Georg Ringer 36 | * @author Stefano Kowalke 37 | * @package Etobi\CoreAPI\Service\SiteApiService 38 | */ 39 | class ExtensionApiCommandController extends CommandController { 40 | 41 | const MAXIMUM_LINE_LENGTH = 79; 42 | 43 | /** 44 | * @var \TYPO3\CMS\Core\Log\LogManager $logManager 45 | */ 46 | protected $logManager; 47 | 48 | /** 49 | * @var \TYPO3\CMS\Core\Log\Logger $logger 50 | */ 51 | protected $logger; 52 | 53 | /** 54 | * @param \TYPO3\CMS\Core\Log\LogManager $logManager 55 | * 56 | * @return void 57 | */ 58 | public function injectLogManager(\TYPO3\CMS\Core\Log\LogManager $logManager) { 59 | $this->logManager = $logManager; 60 | } 61 | 62 | /** 63 | * Initialize the object 64 | */ 65 | public function initializeObject() { 66 | $this->logger = $this->objectManager->get('TYPO3\CMS\Core\Log\LogManager')->getLogger(__CLASS__); 67 | } 68 | 69 | /** 70 | * @var \Etobi\CoreAPI\Service\ExtensionApiService 71 | * @inject 72 | */ 73 | protected $extensionApiService; 74 | 75 | /** 76 | * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher 77 | * @inject 78 | */ 79 | protected $signalSlotDispatcher; 80 | 81 | /** 82 | * Information about an extension. 83 | * 84 | * @param string $key The extension key 85 | * 86 | * @return void 87 | */ 88 | public function infoCommand($key) { 89 | $data = array(); 90 | try { 91 | $data = $this->extensionApiService->getExtensionInformation($key); 92 | } catch (Exception $e) { 93 | $message = $e->getMessage(); 94 | $this->outputLine($message); 95 | $this->logger->error($message); 96 | $this->quit(1); 97 | } 98 | 99 | $this->outputLine(''); 100 | $this->outputLine('EXTENSION "%s": %s %s', array(strtoupper($key), $data['em_conf']['version'], $data['em_conf']['state'])); 101 | $this->outputLine(str_repeat('-', $this->output->getMaximumLineLength())); 102 | 103 | $outputInformation = array(); 104 | $outputInformation['is installed'] = ($data['is_installed'] ? 'yes' : 'no'); 105 | if (!empty($data['em_conf'])) { 106 | foreach ($data['em_conf'] as $emConfKey => $emConfValue) { 107 | // Skip empty properties 108 | if (empty($emConfValue)) { 109 | continue; 110 | } 111 | // Skip properties which are already handled 112 | if ($emConfKey === 'title' || $emConfKey === 'version' || $emConfKey === 'state') { 113 | continue; 114 | } 115 | $outputInformation[$emConfKey] = $emConfValue; 116 | } 117 | } 118 | 119 | foreach ($outputInformation as $outputKey => $outputValue) { 120 | $description = ''; 121 | if (is_array($outputValue)) { 122 | foreach ($outputValue as $additionalKey => $additionalValue) { 123 | if (is_array($additionalValue)) { 124 | 125 | if (empty($additionalValue)) { 126 | continue; 127 | } 128 | $description .= LF . str_repeat(' ', 28) . $additionalKey; 129 | $description .= LF; 130 | foreach ($additionalValue as $ak => $av) { 131 | $description .= str_repeat(' ', 30) . $ak . ': ' . $av . LF; 132 | } 133 | } else { 134 | $description .= LF . str_repeat(' ', 28) . $additionalKey . ': ' . $additionalValue; 135 | } 136 | } 137 | } else { 138 | $description = wordwrap($outputValue, $this->output->getMaximumLineLength() - 28, PHP_EOL . str_repeat(' ', 28), TRUE); 139 | } 140 | $this->outputLine('%-2s%-25s %s', array(' ', $outputKey, $description)); 141 | } 142 | 143 | $this->logger->info('extensionApi:info executes successfully.'); 144 | } 145 | 146 | /** 147 | * List all installed extensions. 148 | * 149 | * @param string $type Extension type, can either be "Local", 150 | * "System" or "Global". Leave it empty for all 151 | * 152 | * @return void 153 | */ 154 | public function listInstalledCommand($type = '') { 155 | $type = ucfirst(strtolower($type)); 156 | if (!empty($type) && $type !== 'Local' && $type !== 'Global' && $type !== 'System') { 157 | // TODO: Throw a exception here? 158 | $message = 'Only "Local", "System" and "Global" are supported as type (or nothing)'; 159 | $this->outputLine($message); 160 | $this->logger->error($message); 161 | $this->quit(1); 162 | } 163 | 164 | $extensions = $this->extensionApiService->listExtensions($type); 165 | 166 | foreach ($extensions as $key => $details) { 167 | $title = $key . ' - ' . $details['version'] . '/' . $details['state']; 168 | $description = $details['title']; 169 | $description = wordwrap($description, $this->output->getMaximumLineLength() - 43, PHP_EOL . str_repeat(' ', 43), TRUE); 170 | $this->outputLine('%-2s%-40s %s', array(' ', $title, $description)); 171 | } 172 | 173 | $this->outputLine('%-2s%-40s', array(' ', str_repeat('-', $this->output->getMaximumLineLength() - 3))); 174 | $this->outputLine(' Total: ' . count($extensions) . ' extensions'); 175 | $this->logger->info('extensionApi:listInstalled executed successfully'); 176 | } 177 | 178 | /** 179 | * Update list. 180 | * 181 | * @return void 182 | */ 183 | public function updateListCommand() { 184 | $this->outputLine('This may take a while...'); 185 | $result = $this->extensionApiService->updateMirrors(); 186 | 187 | if ($result) { 188 | $message = 'Extension list has been updated.'; 189 | $this->outputLine($message); 190 | $this->logger->info($message); 191 | } else { 192 | $message = 'Extension list already up-to-date.'; 193 | $this->outputLine($message); 194 | $this->logger->info($message); 195 | } 196 | } 197 | 198 | /** 199 | * Install(activate) an extension. 200 | * 201 | * @param string $key The extension key 202 | * 203 | * @return void 204 | */ 205 | public function installCommand($key) { 206 | try { 207 | $this->emitPackagesMayHaveChangedSignal(); 208 | $this->extensionApiService->installExtension($key); 209 | } catch (Exception $e) { 210 | $message = $e->getMessage(); 211 | $this->outputLine($message); 212 | $this->logger->error($message); 213 | $this->quit(1); 214 | } 215 | 216 | $message = sprintf('Extension "%s" is now installed!', $key); 217 | $this->outputLine($message); 218 | $this->logger->info($message); 219 | } 220 | 221 | /** 222 | * UnInstall(deactivate) an extension. 223 | * 224 | * @param string $key The extension key 225 | * 226 | * @return void 227 | */ 228 | public function uninstallCommand($key) { 229 | try { 230 | $this->extensionApiService->uninstallExtension($key); 231 | } catch (Exception $e) { 232 | $message = $e->getMessage(); 233 | $this->outputLine($message); 234 | $this->logger->error($message); 235 | $this->quit(1); 236 | } 237 | 238 | $message = sprintf('Extension "%s" is now uninstalled!', $key); 239 | $this->outputLine($message); 240 | $this->logger->info($message); 241 | } 242 | 243 | /** 244 | * Configure an extension. 245 | * This command enables you to configure an extension. 246 | * 247 | * 248 | * [1] Using a standard formatted ini-file 249 | * ./cli_dispatch.phpsh extbase extensionapi:configure rtehtmlarea --configfile=C:\rteconf.txt 250 | * 251 | * [2] Adding configuration settings directly on the command line 252 | * ./cli_dispatch.phpsh extbase extensionapi:configure rtehtmlarea --settings="enableImages=1;allowStyleAttribute=0" 253 | * 254 | * [3] A combination of [1] and [2] 255 | * ./cli_dispatch.phpsh extbase extensionapi:configure rtehtmlarea --configfile=C:\rteconf.txt --settings="enableImages=1;allowStyleAttribute=0" 256 | * 257 | * 258 | * @param string $key The extension key 259 | * @param string $configFile Path to file containing configuration settings. Must be formatted as a standard ini-file 260 | * @param string $settings String containing configuration settings separated on the form "k1=v1;k2=v2;" 261 | * 262 | * @return void 263 | */ 264 | public function configureCommand($key, $configFile = '', $settings = '') { 265 | try { 266 | $conf = array(); 267 | if (is_file($configFile)) { 268 | $conf = parse_ini_file($configFile); 269 | } 270 | 271 | if (strlen($settings)) { 272 | $arr = explode(';', $settings); 273 | foreach ($arr as $v) { 274 | if (strpos($v, '=') === FALSE) { 275 | throw new InvalidArgumentException(sprintf('Ill-formed setting "%s"!', $v)); 276 | } 277 | $parts = GeneralUtility::trimExplode('=', $v, FALSE, 2); 278 | if (!empty($parts[0])) { 279 | $conf[$parts[0]] = $parts[1]; 280 | } 281 | } 282 | } 283 | 284 | if (empty($conf)) { 285 | $this->response->setExitCode(1); 286 | $message = sprintf('No configuration settings!', $key); 287 | $this->logger->error($message); 288 | throw new InvalidArgumentException($message); 289 | } 290 | 291 | $this->extensionApiService->configureExtension($key, $conf); 292 | 293 | } catch (Exception $e) { 294 | $message = $e->getMessage(); 295 | $this->outputLine($message); 296 | $this->logger->error($message); 297 | $this->quit(1); 298 | } 299 | 300 | $message = sprintf('Extension "%s" has been configured!', $key); 301 | $this->outputLine($message); 302 | $this->logger->info($message); 303 | } 304 | 305 | /** 306 | * Fetch an extension from TER. 307 | * 308 | * @param string $key The extension key 309 | * @param string $version The exact version of the extension, otherwise the latest will be picked 310 | * @param string $location Where to put the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext 311 | * @param bool $overwrite Overwrite the extension if already exists 312 | * @param int $mirror Mirror to fetch the extension from. Run extensionapi:listmirrors to get the list of all available repositories, otherwise a random mirror will be selected 313 | * 314 | * @return void 315 | */ 316 | public function fetchCommand($key, $version = '', $location = 'Local', $overwrite = FALSE, $mirror = -1) { 317 | try { 318 | $data = $this->extensionApiService->fetchExtension($key, $version, $location, $overwrite, $mirror); 319 | $message = sprintf('Extension "%s" version %s has been fetched from repository! Dependencies were not resolved.', $data['main']['extKey'], $data['main']['version']); 320 | $this->outputLine($message); 321 | $this->logger->info($message); 322 | } catch (Exception $e) { 323 | $message = $e->getMessage(); 324 | $this->outputLine($message); 325 | $this->logger->error($message); 326 | $this->quit(1); 327 | } 328 | } 329 | 330 | /** 331 | * Lists the possible mirrors 332 | * 333 | * @return void 334 | */ 335 | public function listMirrorsCommand() { 336 | try { 337 | $mirrors = $this->extensionApiService->listMirrors(); 338 | $key = 0; 339 | foreach ($mirrors as $mirror) { 340 | $this->outputLine($key . ' = ' . $mirror['title'] . ' ' . $mirror['host']); 341 | ++$key; 342 | } 343 | } catch (Exception $e) { 344 | $message = $e->getMessage(); 345 | $this->outputLine($message); 346 | $this->logger->error($message); 347 | $this->quit(1); 348 | } 349 | $this->logger->info('extensionApi:listMirrors executed successfully.'); 350 | } 351 | 352 | /** 353 | * Import extension from file. 354 | * 355 | * @param string $file Path to t3x file 356 | * @param string $location Where to import the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext 357 | * @param boolean $overwrite Overwrite the extension if already exists 358 | * 359 | * @return void 360 | */ 361 | public function importCommand($file, $location = 'Local', $overwrite = FALSE) { 362 | try { 363 | $data = $this->extensionApiService->importExtension($file, $location, $overwrite); 364 | $message = sprintf('Extension "%s" has been imported!', $data['extKey']); 365 | $this->outputLine($message); 366 | $this->logger->info($message); 367 | } catch (Exception $e) { 368 | $this->outputLine($e->getMessage()); 369 | $this->logger->error($e->getMessage()); 370 | $this->quit(1); 371 | } 372 | } 373 | 374 | /** 375 | * Emits packages may have changed signal 376 | * 377 | * @return \Etobi\CoreAPI\Service\ExtensionApiService object 378 | */ 379 | protected function emitPackagesMayHaveChangedSignal() { 380 | $this->signalSlotDispatcher->dispatch('PackageManagement', 'packagesMayHaveChanged'); 381 | } 382 | } 383 | -------------------------------------------------------------------------------- /Documentation/Images/ExtensionApi/uploadfolder.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | S 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | upload folder already exists? 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Create upload folder 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | upload folder exists now? 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | E 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Error 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | No 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | Yes 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | Yes 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | No 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | -------------------------------------------------------------------------------- /Classes/Service/ExtensionApiService.php: -------------------------------------------------------------------------------- 1 | 8 | * (c) 2014 Stefano Kowalke 9 | * All rights reserved 10 | * 11 | * This script is part of the TYPO3 project. The TYPO3 project is 12 | * free software; you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation; either version 2 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * The GNU General Public License can be found at 18 | * http://www.gnu.org/copyleft/gpl.html. 19 | * 20 | * This script is distributed in the hope that it will be useful, 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | * GNU General Public License for more details. 24 | * 25 | * This copyright notice MUST APPEAR in all copies of the script! 26 | ***************************************************************/ 27 | use InvalidArgumentException; 28 | use RuntimeException; 29 | use TYPO3\CMS\Core\Configuration\ConfigurationManager; 30 | use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; 31 | use TYPO3\CMS\Core\Utility\GeneralUtility; 32 | use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; 33 | use TYPO3\CMS\Extensionmanager\Domain\Model\Extension; 34 | use TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository; 35 | use TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository; 36 | use TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService; 37 | use TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility; 38 | use TYPO3\CMS\Extensionmanager\Utility\InstallUtility; 39 | use TYPO3\CMS\Extensionmanager\Utility\ListUtility; 40 | use TYPO3\CMS\Extensionmanager\Utility\Repository\Helper; 41 | 42 | /** 43 | * Extension API service 44 | * 45 | * @author Georg Ringer 46 | * @author Stefano Kowalke 47 | * @package Etobi\CoreAPI\Service\SiteApiService 48 | */ 49 | class ExtensionApiService { 50 | 51 | /** 52 | * @var \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility $terConnection 53 | */ 54 | public $terConnection; 55 | 56 | /** 57 | * @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager 58 | */ 59 | protected $configurationManager; 60 | 61 | /** 62 | * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility $extensionList 63 | */ 64 | public $listUtility; 65 | 66 | /** 67 | * @var \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility 68 | */ 69 | protected $installUtility; 70 | 71 | /** 72 | * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository $repositoryRepository 73 | */ 74 | protected $repositoryRepository; 75 | 76 | /** 77 | * @var Helper $repositoryHelper 78 | */ 79 | protected $repositoryHelper; 80 | 81 | /** 82 | * @var ExtensionRepository $extensionRepository 83 | */ 84 | protected $extensionRepository; 85 | 86 | /** 87 | * @var ExtensionManagementService $extensionManagementService 88 | */ 89 | protected $extensionManagementService; 90 | 91 | /** 92 | * @var ObjectManagerInterface $objectManager 93 | */ 94 | protected $objectManager; 95 | 96 | /** 97 | * @var \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility 98 | */ 99 | protected $fileHandlingUtility; 100 | 101 | /** 102 | * @var \TYPO3\CMS\Extensionmanager\Utility\EmConfUtility $emConfUtility 103 | */ 104 | protected $emConfUtility; 105 | 106 | /** 107 | * @param \TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility $fileHandlingUtility 108 | * 109 | * @return void 110 | */ 111 | public function injectFileHandlingUtility(FileHandlingUtility $fileHandlingUtility) { 112 | $this->fileHandlingUtility = $fileHandlingUtility; 113 | } 114 | 115 | /** 116 | * @param \TYPO3\CMS\Extensionmanager\Utility\InstallUtility $installUtility 117 | * 118 | * @return void 119 | */ 120 | public function injectInstallUtility(InstallUtility $installUtility) { 121 | $this->installUtility = $installUtility; 122 | } 123 | 124 | /** 125 | * @param RepositoryRepository $repositoryRepository 126 | * 127 | * @return void 128 | */ 129 | public function injectRepositoryRepository(RepositoryRepository $repositoryRepository) { 130 | $this->repositoryRepository = $repositoryRepository; 131 | } 132 | 133 | /** 134 | * @param Helper $repositoryHelper 135 | * 136 | * @return void 137 | */ 138 | public function injectRepositoryHelper(Helper $repositoryHelper) { 139 | $this->repositoryHelper = $repositoryHelper; 140 | } 141 | 142 | /** 143 | * @param ExtensionRepository $extensionRepository 144 | * 145 | * @return void 146 | */ 147 | public function injectExtensionRepository(ExtensionRepository $extensionRepository){ 148 | $this->extensionRepository = $extensionRepository; 149 | } 150 | 151 | /** 152 | * @param ExtensionManagementService $extensionManagementService 153 | * 154 | * @return void 155 | */ 156 | public function injectExtensionManagementService(ExtensionManagementService $extensionManagementService) { 157 | $this->extensionManagementService = $extensionManagementService; 158 | } 159 | 160 | /** 161 | * @param ObjectManagerInterface $objectManager 162 | * 163 | * @return void 164 | */ 165 | public function injectObjectManager(ObjectManagerInterface $objectManager) { 166 | $this->objectManager = $objectManager; 167 | } 168 | 169 | /** 170 | * The constructor 171 | */ 172 | public function __construct() { 173 | $this->configurationManager = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\ConfigurationManager'); 174 | } 175 | 176 | /** 177 | * Get information about an extension. 178 | * 179 | * @param string $extensionKey extension key 180 | * 181 | * @throws InvalidArgumentException 182 | * @return array 183 | */ 184 | public function getExtensionInformation($extensionKey) { 185 | if (strlen($extensionKey) === 0) { 186 | throw new InvalidArgumentException('No extension key given!'); 187 | } 188 | 189 | $this->checkExtensionExists($extensionKey); 190 | 191 | $extensions = $this->listExtensions(); 192 | 193 | $information = array( 194 | 'em_conf' => $extensions[$extensionKey], 195 | 'is_installed' => $this->installUtility->isLoaded($extensionKey) 196 | ); 197 | 198 | return $information; 199 | } 200 | 201 | /** 202 | * Get array of installed extensions. 203 | * 204 | * @param string $type Local, System, Global or empty (for all) 205 | * 206 | * @throws InvalidArgumentException 207 | * @return array 208 | */ 209 | public function listExtensions($type = '') { 210 | $type = ucfirst(strtolower($type)); 211 | if (!empty($type) && $type !== 'Local' && $type !== 'Global' && $type !== 'System') { 212 | throw new InvalidArgumentException('Only "Local", "System", "Global" and "" (all) are supported as type'); 213 | } 214 | 215 | $this->initializeExtensionManagerObjects(); 216 | 217 | // TODO: Make listUtlity local var 218 | $extensions = $this->listUtility->getAvailableExtensions(); 219 | 220 | $list = array(); 221 | 222 | foreach ($extensions as $key => $extension) { 223 | if ((!empty($type) && $type !== $extension['type']) 224 | || (!$this->installUtility->isLoaded($extension['key'])) 225 | ) { 226 | continue; 227 | } 228 | 229 | // TODO: Make emConfUtility local var 230 | $configuration = $this->emConfUtility->includeEmConf($extension); 231 | if (!empty($configuration)) { 232 | $list[$key] = $configuration; 233 | } 234 | } 235 | ksort($list); 236 | 237 | return $list; 238 | } 239 | 240 | /** 241 | * Update the mirrors, using the scheduler task of EXT:em. 242 | * 243 | * @throws RuntimeException 244 | * @return boolean 245 | */ 246 | public function updateMirrors() { 247 | $result = FALSE; 248 | $repositories = $this->repositoryRepository->findAll(); 249 | 250 | // update all repositories 251 | foreach ($repositories as $repository) { 252 | $this->repositoryHelper->setRepository($repository); 253 | $result = $this->repositoryHelper->updateExtList(); 254 | unset($objRepository, $this->repositoryHelper); 255 | } 256 | 257 | return $result; 258 | } 259 | 260 | /** 261 | * Install (load) an extension. 262 | * 263 | * @param string $extensionKey extension key 264 | * 265 | * @throws RuntimeException 266 | * @throws InvalidArgumentException 267 | * @return void 268 | */ 269 | public function installExtension($extensionKey) { 270 | $this->checkExtensionExists($extensionKey); 271 | 272 | $this->installUtility->install($extensionKey); 273 | } 274 | 275 | /** 276 | * Uninstall (unload) an extension. 277 | * 278 | * @param string $extensionKey extension key 279 | * 280 | * @throws RuntimeException 281 | * @throws InvalidArgumentException 282 | * @return void 283 | */ 284 | public function uninstallExtension($extensionKey) { 285 | if ($extensionKey === 'coreapi') { 286 | throw new InvalidArgumentException('Extension "coreapi" cannot be uninstalled!'); 287 | } 288 | 289 | $this->checkExtensionExists($extensionKey); 290 | $this->checkExtensionLoaded($extensionKey); 291 | 292 | $this->installUtility->uninstall($extensionKey); 293 | } 294 | 295 | 296 | /** 297 | * Configure an extension. 298 | * 299 | * @param string $extensionKey The extension key 300 | * @param array $newExtensionConfiguration 301 | * 302 | * @throws RuntimeException 303 | * @throws InvalidArgumentException 304 | * @return void 305 | */ 306 | public function configureExtension($extensionKey, $newExtensionConfiguration = array()) { 307 | $this->checkExtensionExists($extensionKey); 308 | $this->checkExtensionLoaded($extensionKey); 309 | 310 | // checks if conf array is empty 311 | if (empty($newExtensionConfiguration)) { 312 | throw new InvalidArgumentException(sprintf('No configuration provided for extension "%s"!', $extensionKey)); 313 | } 314 | 315 | // check if extension can be configured 316 | $extAbsPath = $this->getExtensionPath($extensionKey); 317 | $extConfTemplateFile = $extAbsPath . 'ext_conf_template.txt'; 318 | if (!file_exists($extConfTemplateFile)) { 319 | throw new InvalidArgumentException(sprintf('Extension "%s" has no configuration options!', $extensionKey)); 320 | } 321 | 322 | /** @var $configurationUtility \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility */ 323 | $configurationUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\ConfigurationUtility'); 324 | // get existing configuration 325 | $currentExtensionConfig = $configurationUtility->getCurrentConfiguration($extensionKey); 326 | 327 | // check for unknown configuration settings 328 | foreach ($newExtensionConfiguration as $key => $_) { 329 | if (!isset($currentExtensionConfig[$key])) { 330 | throw new InvalidArgumentException(sprintf('No configuration setting with name "%s" for extension "%s"!', $key, $extensionKey)); 331 | } 332 | } 333 | 334 | // fill with missing values 335 | $newExtensionConfiguration = $this->mergeNewExtensionConfiguratonWithCurrentConfiguration( 336 | $newExtensionConfiguration, 337 | $currentExtensionConfig 338 | ); 339 | 340 | // write configuration to typo3conf/LocalConfiguration.php 341 | $configurationUtility->writeConfiguration($newExtensionConfiguration, $extensionKey); 342 | } 343 | 344 | /** 345 | * Fetch an extension from TER. 346 | * 347 | * @param string $extensionKey The extension key 348 | * @param string $location Where to import the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext 349 | * @param bool $overwrite Overwrite the extension if it already exists 350 | * @param int $mirror The mirror to fetch the extension from 351 | * 352 | * @throws \RuntimeException 353 | * @throws \InvalidArgumentException 354 | * @return array 355 | */ 356 | public function fetchExtension($extensionKey, $version = '', $location = 'Local', $overwrite = FALSE, $mirror = -1) { 357 | if (!is_numeric($mirror)) { 358 | throw new InvalidArgumentException('Option --mirror must be a number. Run the command extensionapi:listmirrors to get the list of all available repositories'); 359 | } 360 | 361 | if ($version === '') { 362 | $extension = $this->extensionRepository->findHighestAvailableVersion($extensionKey); 363 | if ($extension === NULL) { 364 | throw new InvalidArgumentException(sprintf('Extension "%s" was not found on TER', $extensionKey)); 365 | } 366 | } else { 367 | $extension = $this->extensionRepository->findOneByExtensionKeyAndVersion($extensionKey, $version); 368 | if ($extension === NULL) { 369 | throw new InvalidArgumentException(sprintf('Version %s of extension "%s" does not exist', $version, $extensionKey)); 370 | } 371 | } 372 | 373 | if (!$overwrite) { 374 | $comingExtPath = $this->fileHandlingUtility->getExtensionDir($extensionKey, $location); 375 | if (@is_dir($comingExtPath)) { 376 | throw new InvalidArgumentException(sprintf('Extension "%s" already exists at "%s"!', $extensionKey, $comingExtPath)); 377 | } 378 | } 379 | 380 | $mirrors = $this->repositoryHelper->getMirrors(); 381 | 382 | if ($mirrors === NULL) { 383 | throw new RuntimeException('No mirrors found!'); 384 | } 385 | 386 | if ($mirror === -1) { 387 | $mirrors->setSelect(); 388 | } elseif ($mirror > 0 && $mirror <= count($mirrors->getMirrors())) { 389 | $mirrors->setSelect($mirror); 390 | } else { 391 | throw new InvalidArgumentException(sprintf('Mirror "%s" does not exist', $mirror)); 392 | } 393 | 394 | /** 395 | * @var \TYPO3\CMS\Extensionmanager\Utility\DownloadUtility $downloadUtility 396 | */ 397 | $downloadUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\DownloadUtility'); 398 | $downloadUtility->setDownloadPath($location); 399 | 400 | $this->extensionManagementService->downloadMainExtension($extension); 401 | 402 | $return = array(); 403 | $extensionDir = $this->fileHandlingUtility->getExtensionDir($extensionKey, $location); 404 | if (is_dir($extensionDir)) { 405 | $return['main']['extKey'] = $extension->getExtensionKey(); 406 | $return['main']['version'] = $extension->getVersion(); 407 | } else { 408 | throw new RuntimeException( 409 | sprintf('Extension "%s" version %s could not installed!', $extensionKey, $extension->getVersion()) 410 | ); 411 | } 412 | 413 | return $return; 414 | } 415 | 416 | /** 417 | * Lists the possible mirrors 418 | * 419 | * @return array 420 | */ 421 | public function listMirrors() { 422 | /** @var $repositoryHelper Helper */ 423 | $repositoryHelper = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\Repository\\Helper'); 424 | $mirrors = $repositoryHelper->getMirrors(); 425 | 426 | return $mirrors->getMirrors(); 427 | } 428 | 429 | /** 430 | * Extracts and returns the file content of the given file 431 | * 432 | * @param string $file The file with file path 433 | * 434 | * @return array 435 | */ 436 | protected function getFileContentFromUrl($file) { 437 | return GeneralUtility::getUrl($file); 438 | } 439 | 440 | /** 441 | * Imports extension from file. 442 | * 443 | * @param string $file Path to t3x file 444 | * @param string $location Where to import the extension. System = typo3/sysext, Global = typo3/ext, Local = typo3conf/ext 445 | * @param bool $overwrite Overwrite the extension if it already exists 446 | * 447 | * @throws \RuntimeException 448 | * @throws \InvalidArgumentException 449 | * @return array The extension data 450 | */ 451 | public function importExtension($file, $location = 'Local', $overwrite = FALSE) { 452 | if (!is_file($file)) { 453 | throw new InvalidArgumentException(sprintf('File "%s" does not exist!', $file)); 454 | } 455 | 456 | $this->checkInstallLocation($location); 457 | 458 | $uploadExtensionFileController = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Controller\\UploadExtensionFileController'); 459 | 460 | $filename = pathinfo($file, PATHINFO_BASENAME); 461 | $return = $uploadExtensionFileController->extractExtensionFromFile($file, $filename, $overwrite, FALSE); 462 | 463 | return $return; 464 | } 465 | 466 | /** 467 | * Checks if the function exists in the system 468 | * 469 | * @param string $extensionKey The extension key 470 | * 471 | * @return void 472 | * @throws \InvalidArgumentException 473 | */ 474 | protected function checkExtensionExists($extensionKey) { 475 | if (!$this->installUtility->isAvailable($extensionKey)) { 476 | throw new InvalidArgumentException(sprintf('Extension "%s" does not exist!', $extensionKey)); 477 | } 478 | } 479 | 480 | /** 481 | * Check if an extension is loaded. 482 | * 483 | * @param string $extensionKey The extension key 484 | * 485 | * @throws \InvalidArgumentException 486 | * @return void 487 | */ 488 | protected function checkExtensionLoaded($extensionKey) { 489 | if (!$this->installUtility->isLoaded($extensionKey)) { 490 | throw new InvalidArgumentException(sprintf('Extension "%s" is not installed!', $extensionKey)); 491 | } 492 | } 493 | 494 | /** 495 | * Returns the absolute extension path. 496 | * Wrapper around the static method. This makes the method mockable. 497 | * 498 | * @param string $extensionKey The extension key 499 | * 500 | * @return string 501 | */ 502 | protected function getExtensionPath($extensionKey) { 503 | return ExtensionManagementUtility::extPath($extensionKey); 504 | } 505 | 506 | /** 507 | * Add missing values from current configuration to the new configuration 508 | * 509 | * @param array $newExtensionConfiguration The new configuration which was provided as argument 510 | * @param array $currentExtensionConfig The current configuration of the extension 511 | * 512 | * @return array The merged configuration 513 | */ 514 | protected function mergeNewExtensionConfiguratonWithCurrentConfiguration($newExtensionConfiguration, $currentExtensionConfig) { 515 | foreach (array_keys($currentExtensionConfig) as $key) { 516 | if (!isset($newExtensionConfiguration[$key])) { 517 | if (!empty($currentExtensionConfig[$key]['value'])) { 518 | $newExtensionConfiguration[$key] = $currentExtensionConfig[$key]['value']; 519 | } else { 520 | $newExtensionConfiguration[$key] = $currentExtensionConfig[$key]['default_value']; 521 | } 522 | } 523 | } 524 | 525 | return $newExtensionConfiguration; 526 | } 527 | 528 | /** 529 | * Checks if the extension is able to install at the demanded location 530 | * 531 | * @param string $location The location 532 | * @param array $allowedInstallTypes The allowed locations 533 | * 534 | * @return boolean 535 | * @throws \InvalidArgumentException 536 | */ 537 | protected function checkInstallLocation($location) { 538 | $allowedInstallTypes = Extension::returnAllowedInstallTypes(); 539 | $location = ucfirst(strtolower($location)); 540 | 541 | if (!in_array($location, $allowedInstallTypes)) { 542 | if ($location === 'Global') { 543 | throw new InvalidArgumentException('Global installation is not allowed!'); 544 | } 545 | if ($location === 'Local') { 546 | throw new InvalidArgumentException('Local installation is not allowed!'); 547 | } 548 | if ($location === 'System') { 549 | throw new InvalidArgumentException('System installation is not allowed!'); 550 | } 551 | throw new InvalidArgumentException(sprintf('Unknown location "%s"!', $location)); 552 | } 553 | } 554 | 555 | /** 556 | * Initialize ExtensionManager Objects. 557 | * 558 | * @return void 559 | */ 560 | protected function initializeExtensionManagerObjects() { 561 | $this->listUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\ListUtility'); 562 | $this->emConfUtility = $this->objectManager->get('TYPO3\\CMS\\Extensionmanager\\Utility\\EmConfUtility'); 563 | } 564 | 565 | /** 566 | * Clear the caches. 567 | * 568 | * @return void 569 | */ 570 | protected function clearCaches() { 571 | $cacheApiService = GeneralUtility::makeInstance('Etobi\\CoreAPI\\Service\\CacheApiService'); 572 | $cacheApiService->initializeObject(); 573 | $cacheApiService->clearAllCaches(); 574 | } 575 | } 576 | -------------------------------------------------------------------------------- /Documentation/Images/ExtensionApi/ExtensionApiFetch.graphml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | S 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | extensionapi:fetch 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Extension already installed? 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Extension already downloaded? 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | override? 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Extension available at TER? 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | download 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | E 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | no 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | yes 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | no 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | yes 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | no 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | yes 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | no 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | yes 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | --------------------------------------------------------------------------------