[TASK] Cleanup and update language module 00/34200/21
authorKai Vogel <kai.vogel@speedprogs.de>
Sat, 15 Nov 2014 12:26:59 +0000 (13:26 +0100)
committerBenjamin Mack <benni@typo3.org>
Sat, 10 Jan 2015 19:37:35 +0000 (20:37 +0100)
This patch modernizes the old language module to fit
the new backend design.

Resolves: #62983
Releases: master
Change-Id: I0a607e3ed25d793bf73f6aa7721a1bb26a6078ba
Reviewed-on: http://review.typo3.org/34200
Reviewed-by: Markus Klein <klein.t3@reelworx.at>
Tested-by: Markus Klein <klein.t3@reelworx.at>
Tested-by: Benjamin Mack <benni@typo3.org>
Reviewed-by: Benjamin Mack <benni@typo3.org>
50 files changed:
typo3/sysext/lang/Classes/Command/LanguageCommandController.php
typo3/sysext/lang/Classes/Controller/LanguageController.php
typo3/sysext/lang/Classes/Domain/Model/Extension.php
typo3/sysext/lang/Classes/Domain/Model/Language.php
typo3/sysext/lang/Classes/Domain/Model/LanguageSelectionForm.php [deleted file]
typo3/sysext/lang/Classes/Domain/Model/UpdateTranslationForm.php [deleted file]
typo3/sysext/lang/Classes/Domain/Repository/ExtensionRepository.php
typo3/sysext/lang/Classes/Domain/Repository/LanguageRepository.php
typo3/sysext/lang/Classes/Exception/Lang.php [deleted file]
typo3/sysext/lang/Classes/Exception/Language.php [new file with mode: 0644]
typo3/sysext/lang/Classes/Exception/Ter.php
typo3/sysext/lang/Classes/Exception/XmlParser.php
typo3/sysext/lang/Classes/Service/RegistryService.php [new file with mode: 0755]
typo3/sysext/lang/Classes/Service/TerService.php [new file with mode: 0644]
typo3/sysext/lang/Classes/Service/TranslationService.php [new file with mode: 0644]
typo3/sysext/lang/Classes/Service/UpdateTranslationService.php [deleted file]
typo3/sysext/lang/Classes/Utility/Connection/Ter.php [deleted file]
typo3/sysext/lang/Classes/View/AbstractJsonView.php [new file with mode: 0644]
typo3/sysext/lang/Classes/View/Language/ActivateLanguageJson.php [new file with mode: 0644]
typo3/sysext/lang/Classes/View/Language/CheckTranslationJson.php [deleted file]
typo3/sysext/lang/Classes/View/Language/DeactivateLanguageJson.php [new file with mode: 0644]
typo3/sysext/lang/Classes/View/Language/GetTranslationsJson.php [new file with mode: 0644]
typo3/sysext/lang/Classes/View/Language/UpdateLanguageJson.php [new file with mode: 0644]
typo3/sysext/lang/Classes/View/Language/UpdateTranslationJson.php
typo3/sysext/lang/Classes/ViewHelpers/Be/SpriteIconViewHelper.php [deleted file]
typo3/sysext/lang/Resources/Private/Language/locallang.xlf
typo3/sysext/lang/Resources/Private/Language/locallang_mod.xlf [new file with mode: 0644]
typo3/sysext/lang/Resources/Private/Language/locallang_mod_language.xlf [deleted file]
typo3/sysext/lang/Resources/Private/Layouts/Default.html
typo3/sysext/lang/Resources/Private/Partials/Icons.html [new file with mode: 0644]
typo3/sysext/lang/Resources/Private/Partials/LanguageSelectionList.html [deleted file]
typo3/sysext/lang/Resources/Private/Partials/TranslationUpdateList.html [deleted file]
typo3/sysext/lang/Resources/Private/Templates/Language/Index.html [deleted file]
typo3/sysext/lang/Resources/Private/Templates/Language/ListLanguages.html [new file with mode: 0644]
typo3/sysext/lang/Resources/Private/Templates/Language/ListTranslations.html [new file with mode: 0644]
typo3/sysext/lang/Resources/Public/Icons/module-lang.png [deleted file]
typo3/sysext/lang/Resources/Public/Images/module-lang.png [new file with mode: 0644]
typo3/sysext/lang/Resources/Public/JavaScript/LangModule.js [deleted file]
typo3/sysext/lang/Resources/Public/JavaScript/LanguageModule.js [new file with mode: 0644]
typo3/sysext/lang/Resources/Public/StyleSheet/LangModule.css [deleted file]
typo3/sysext/lang/Tests/Unit/Domain/Model/ExtensionTest.php
typo3/sysext/lang/Tests/Unit/Domain/Model/LanguageSelectionFormTest.php [deleted file]
typo3/sysext/lang/Tests/Unit/Domain/Model/LanguageTest.php
typo3/sysext/lang/Tests/Unit/Domain/Model/UpdateTranslationFormTest.php [deleted file]
typo3/sysext/lang/composer.json
typo3/sysext/lang/ext_icon.gif
typo3/sysext/lang/ext_tables.php
typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/_module_lang.less [new file with mode: 0644]
typo3/sysext/t3skin/Resources/Private/Styles/t3skin.less
typo3/sysext/t3skin/Resources/Public/Css/visual/t3skin.css

index 014646e..d2c9e98 100644 (file)
@@ -29,17 +29,22 @@ class LanguageCommandController extends \TYPO3\CMS\Extbase\Mvc\Controller\Comman
        protected $signalSlotDispatcher;
 
        /**
+        * @var \TYPO3\CMS\Lang\Service\RegistryService
+        * @inject
+        */
+       protected $registryService;
+
+       /**
         * Update language file for each extension
         *
         * @param string $localesToUpdate Comma separated list of locales that needs to be updated
         * @return void
         */
        public function updateCommand($localesToUpdate = '') {
-               /** @var $updateTranslationService \TYPO3\CMS\Lang\Service\UpdateTranslationService */
-               $updateTranslationService = $this->objectManager->get(\TYPO3\CMS\Lang\Service\UpdateTranslationService::class);
+               /** @var $translationService \TYPO3\CMS\Lang\Service\TranslationService */
+               $translationService = $this->objectManager->get(\TYPO3\CMS\Lang\Service\TranslationService::class);
                /** @var $languageRepository \TYPO3\CMS\Lang\Domain\Repository\LanguageRepository */
                $languageRepository = $this->objectManager->get(\TYPO3\CMS\Lang\Domain\Repository\LanguageRepository::class);
-
                $locales = array();
                if (!empty($localesToUpdate)) {
                        $locales = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $localesToUpdate, TRUE);
@@ -53,9 +58,16 @@ class LanguageCommandController extends \TYPO3\CMS\Extbase\Mvc\Controller\Comman
                /** @var PackageManager $packageManager */
                $packageManager = $this->objectManager->get(\TYPO3\CMS\Core\Package\PackageManager::class);
                $this->emitPackagesMayHaveChangedSignal();
-               /** @var PackageInterface $package */
-               foreach ($packageManager->getAvailablePackages() as $package) {
-                       $updateTranslationService->updateTranslation($package->getPackageKey(), $locales);
+               $packages = $packageManager->getAvailablePackages();
+               foreach ($locales as $locale) {
+                       /** @var PackageInterface $package */
+                       foreach ($packages as $package) {
+                               $extensionKey = $package->getPackageKey();
+                               $result = $translationService->updateTranslation($extensionKey, $locale);
+                               if (empty($result[$extensionKey][$locale]['error'])) {
+                                       $this->registryService->set($locale, $GLOBALS['EXEC_TIME']);
+                               }
+                       }
                }
        }
 
index 81c3878..71e25d0 100644 (file)
@@ -15,10 +15,9 @@ namespace TYPO3\CMS\Lang\Controller;
  */
 
 /**
- * Language controller handling the selection of available languages and update of extension translations
+ * Language controller
  *
- * @author Sebastian Fischer <typo3@evoweb.de>
- * @author Kai Vogel <kai.vogel@speedprogs.de>
+ * @author Kai Vogel <k.vogel@reply.de>
  */
 class LanguageController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
 
@@ -35,85 +34,126 @@ class LanguageController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionControl
        protected $extensionRepository;
 
        /**
-        * @var \TYPO3\CMS\Lang\Service\UpdateTranslationService
+        * @var \TYPO3\CMS\Lang\Service\TranslationService
         * @inject
         */
-       protected $updateTranslationService;
+       protected $translationService;
 
        /**
-        * JSON actions
-        * @var array
+        * @var \TYPO3\CMS\Lang\Service\RegistryService
+        * @inject
         */
-       protected $jsonActions = array('updateTranslation');
+       protected $registryService;
 
        /**
-        * Force JSON output for defined actions
+        * List languages
         *
-        * @param \TYPO3\CMS\Extbase\Mvc\View\ViewInterface $view The view to be initialized
         * @return void
         */
-       protected function initializeView(\TYPO3\CMS\Extbase\Mvc\View\ViewInterface $view) {
-               $actionName = $this->request->getControllerActionName();
-               if (in_array($actionName, $this->jsonActions)) {
-                       $viewObjectName = 'TYPO3\\CMS\\Lang\\View\\Language\\' . ucfirst($actionName) . 'Json';
-                       $this->view = $this->objectManager->get($viewObjectName);
-                       $this->view->setControllerContext($this->controllerContext);
-                       $this->view->initializeView();
-               }
+       public function listLanguagesAction() {
+               $languages = $this->languageRepository->findAll();
+               $this->view->assign('languages', $languages);
        }
 
        /**
-        * Index action
+        * List translations
         *
-        * @param \TYPO3\CMS\Lang\Domain\Model\LanguageSelectionForm $languageSelectionForm
-        * @param mixed $extensions Extensions to show in form
         * @return void
-        * @dontvalidate $languageSelectionForm
-        * @dontvalidate $extensions
         */
-       public function indexAction(\TYPO3\CMS\Lang\Domain\Model\LanguageSelectionForm $languageSelectionForm = NULL, $extensions = NULL) {
-               if ($languageSelectionForm === NULL) {
-                       $languageSelectionForm = $this->objectManager->get(\TYPO3\CMS\Lang\Domain\Model\LanguageSelectionForm::class);
-                       $languageSelectionForm->setLanguages($this->languageRepository->findAll());
-                       $languageSelectionForm->setSelectedLanguages($this->languageRepository->findSelected());
-               }
+       public function listTranslationsAction() {
+               $languages = $this->languageRepository->findSelected();
+               $this->view->assign('languages', $languages);
+       }
 
-               $hasSelectedLanguages = !empty($languageSelectionForm->getSelectedLanguages());
-               if ($hasSelectedLanguages && empty($extensions)) {
-                       $extensions = $this->extensionRepository->findAll();
+       /**
+        * Returns the translations
+        *
+        * @return void
+        */
+       public function getTranslationsAction() {
+               $this->view->assign('extensions', $this->extensionRepository->findAll());
+               $this->view->assign('languages', $this->languageRepository->findSelected());
+       }
+
+       /**
+        * Fetch all translations for given locale
+        *
+        * @param array $data The request data
+        * @return void
+        */
+       public function updateLanguageAction(array $data) {
+               $response = array(
+                       'success'  => FALSE,
+                       'progress' => 0,
+               );
+               if (!empty($data['locale'])) {
+                       $extension = $this->extensionRepository->findOneByOffset((int)$data['count']);
+                       if (!empty($extension)) {
+                               $allCount = (int)$this->extensionRepository->countAll();
+                               $offset = (int)$data['count'];
+                               $extensionKey = $extension->getKey();
+                               $result = $this->translationService->updateTranslation($extensionKey, $data['locale']);
+                               $progress = round((($offset + 1) * 100) / $allCount, 2);
+                               if (empty($result[$extensionKey][$data['locale']]['error'])) {
+                                       $this->registryService->set($data['locale'], $GLOBALS['EXEC_TIME']);
+                                       $response = array(
+                                               'success'   => TRUE,
+                                               'result'    => $result,
+                                               'timestamp' => $GLOBALS['EXEC_TIME'],
+                                               'progress'  => $progress > 100 ? 100 : $progress,
+                                       );
+                               }
+                       }
                }
+               $this->view->assign('response', $response);
+       }
 
-               $this->view->assign('hasSelectedLanguages', $hasSelectedLanguages);
-               $this->view->assign('languageSelectionForm', $languageSelectionForm);
-               $this->view->assign('extensions', $extensions);
+       /**
+        * Fetch the translation for given extension and locale
+        *
+        * @param array $data The request data
+        * @return void
+        */
+       public function updateTranslationAction(array $data) {
+               $response = array('success' => FALSE);
+               if (!empty($data['extension']) && !empty($data['locale'])) {
+                       $result = $this->translationService->updateTranslation($data['extension'], $data['locale']);
+                       if (empty($result[$data['extension']][$data['locale']]['error'])) {
+                               $response = array(
+                                       'success' => TRUE,
+                                       'result'  => $result,
+                               );
+                       }
+               }
+               $this->view->assign('response', $response);
        }
 
        /**
-        * Update the language selection form
+        * Activate a language
         *
-        * @param \TYPO3\CMS\Lang\Domain\Model\LanguageSelectionForm $languageSelectionForm
+        * @param array $data The request data
         * @return void
-        * @dontvalidate $languageSelectionForm
         */
-       public function updateLanguageSelectionAction(\TYPO3\CMS\Lang\Domain\Model\LanguageSelectionForm $languageSelectionForm) {
-               if ($languageSelectionForm !== NULL) {
-                       $this->languageRepository->updateSelectedLanguages($languageSelectionForm->getSelectedLanguages());
+       public function activateLanguageAction(array $data) {
+               $response = array('success' => FALSE);
+               if (!empty($data['locale'])) {
+                       $response = $this->languageRepository->activateByLocale($data['locale']);
                }
-               $this->redirect('index');
+               $this->view->assign('response', $response);
        }
 
        /**
-        * Update translation for one extension.
-        * The view of this action returns JSON!
+        * Deactivate a language
         *
-        * @param string $extension The extension key
-        * @param string $locales Comma separated list of locales to update
+        * @param array $data The request data
         * @return void
         */
-       public function updateTranslationAction($extension, $locales) {
-               $locales = $this->updateTranslationService->updateTranslation($extension, $locales);
-               $this->view->assign('extension', $extension);
-               $this->view->assign('locales', $locales);
+       public function deactivateLanguageAction(array $data) {
+               $response = array('success' => FALSE);
+               if (!empty($data['locale'])) {
+                       $response = $this->languageRepository->deactivateByLocale($data['locale']);
+               }
+               $this->view->assign('response', $response);
        }
 
 }
index d7e994d..ac55202 100644 (file)
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Lang\Domain\Model;
  * Extension model
  *
  * @author Sebastian Fischer <typo3@evoweb.de>
+ * @author Kai Vogel <k.vogel@reply.de>
  */
 class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
 
@@ -42,21 +43,6 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
        protected $version = '';
 
        /**
-        * @var string
-        */
-       protected $stateCls = '';
-
-       /**
-        * @var string
-        */
-       protected $versionislower = '';
-
-       /**
-        * @var string
-        */
-       protected $maxversion = '';
-
-       /**
         * @var array
         */
        protected $updateResult = array();
@@ -180,4 +166,18 @@ class Extension extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
                return $this->updateResult;
        }
 
+       /**
+        * Returns an array represetation of current model
+        *
+        * @return array The properties
+        */
+       public function toArray() {
+               return array(
+                       'key'     => $this->getKey(),
+                       'title'   => $this->getTitle(),
+                       'icon'    => $this->getIcon(),
+                       'version' => $this->getVersion(),
+               );
+       }
+
 }
index 839a47e..cf1d607 100644 (file)
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Lang\Domain\Model;
  * Language model
  *
  * @author Sebastian Fischer <typo3@evoweb.de>
+ * @author Kai Vogel <k.vogel@reply.de>
  */
 class Language extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
 
@@ -29,7 +30,7 @@ class Language extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
        /**
         * @var string
         */
-       protected $language = '';
+       protected $label = '';
 
        /**
         * @var bool
@@ -37,16 +38,37 @@ class Language extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
        protected $selected = FALSE;
 
        /**
+        * @var int
+        */
+       protected $lastUpdate;
+
+       /**
         * Constructor of the language model
         *
         * @param string $locale
-        * @param string $language
+        * @param string $label
         * @param bool $selected
+        * @param int $lastUpdate
         */
-       public function __construct($locale = '', $language = '', $selected = FALSE) {
-               $this->setLocale($locale);
-               $this->setLanguage($language);
-               $this->setSelected($selected);
+       public function __construct($locale = '', $label = '', $selected = FALSE, $lastUpdate = NULL) {
+               $this->locale = $locale;
+               $this->label = $label;
+               $this->selected = $selected;
+               $this->lastUpdate = $lastUpdate;
+       }
+
+       /**
+        * @return int
+        */
+       public function getLastUpdate() {
+               return $this->lastUpdate;
+       }
+
+       /**
+        * @param int $lastUpdate
+        */
+       public function setLastUpdate($lastUpdate) {
+               $this->lastUpdate = $lastUpdate;
        }
 
        /**
@@ -55,8 +77,8 @@ class Language extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
         * @param string $language the label of the language
         * @return void
         */
-       public function setLanguage($language) {
-               $this->language = $language;
+       public function setLabel($language) {
+               $this->label = $language;
        }
 
        /**
@@ -64,8 +86,8 @@ class Language extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
         *
         * @return string
         */
-       public function getLanguage() {
-               return $this->language;
+       public function getLabel() {
+               return $this->label;
        }
 
        /**
@@ -106,4 +128,18 @@ class Language extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
                return $this->selected;
        }
 
+       /**
+        * Returns an array represetation of current model
+        *
+        * @return array The properties
+        */
+       public function toArray() {
+               return array(
+                       'locale'   => $this->getLocale(),
+                       'label' => $this->getLabel(),
+                       'selected' => $this->getSelected(),
+                       'lastUpdate' => $this->getLastUpdate(),
+               );
+       }
+
 }
diff --git a/typo3/sysext/lang/Classes/Domain/Model/LanguageSelectionForm.php b/typo3/sysext/lang/Classes/Domain/Model/LanguageSelectionForm.php
deleted file mode 100644 (file)
index 953ea26..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-namespace TYPO3\CMS\Lang\Domain\Model;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Model to contain all information from the form changing the selection of available languages
- *
- * @author Sebastian Fischer <typo3@evoweb.de>
- */
-class LanguageSelectionForm extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
-
-       /**
-        * @var array
-        */
-       protected $languages = array();
-
-       /**
-        * @var array
-        */
-       protected $selectedLanguages = array();
-
-       /**
-        * Setter for the languages
-        *
-        * @param array $locale Selected languages
-        * @return void
-        */
-       public function setLanguages(array $languages) {
-               $this->languages = $languages;
-       }
-
-       /**
-        * Getter for the languages
-        *
-        * @return array
-        */
-       public function getLanguages() {
-               return $this->languages;
-       }
-
-       /**
-        * Setter for the selected languages
-        *
-        * @param array $selectedLanguages Selected languages
-        * @return void
-        */
-       public function setSelectedLanguages(array $selectedLanguages) {
-               $this->selectedLanguages = array_values(array_filter($selectedLanguages));
-       }
-
-       /**
-        * Getter for the selected languages
-        *
-        * @return array
-        */
-       public function getSelectedLanguages() {
-               return $this->selectedLanguages;
-       }
-
-       /**
-        * Returns a comma separated list of selected languages
-        *
-        * @return string
-        */
-       public function getSelectedLanguagesLocaleList() {
-               if (!empty($this->selectedLanguages) && is_array($this->selectedLanguages)) {
-                       $locales = array();
-                       foreach ($this->selectedLanguages as $language) {
-                               $locales[] = $language->getLocale();
-                       }
-
-                       return implode(',', $locales);
-               }
-               return '';
-       }
-
-}
diff --git a/typo3/sysext/lang/Classes/Domain/Model/UpdateTranslationForm.php b/typo3/sysext/lang/Classes/Domain/Model/UpdateTranslationForm.php
deleted file mode 100644 (file)
index 5e0ad15..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-namespace TYPO3\CMS\Lang\Domain\Model;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Model to contain all information from the form requesting to update translations
- *
- * @author Sebastian Fischer <typo3@evoweb.de>
- */
-class UpdateTranslationForm extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
-
-       /**
-        * @var array
-        */
-       protected $selectedLanguages = array();
-
-       /**
-        * @var array
-        */
-       protected $extensions = array();
-
-       /**
-        * Setter for the extensions
-        *
-        * @param array $extensions of extensions that were requested
-        * @return void
-        */
-       public function setExtensions(array $extensions) {
-               $this->extensions = $extensions;
-       }
-
-       /**
-        * Getter for the extensions
-        *
-        * @return array
-        */
-       public function getExtensions() {
-               return $this->extensions;
-       }
-
-       /**
-        * Setter for the selected languages
-        *
-        * @param array $selectedLanguages selected languages that were requested
-        * @return void
-        */
-       public function setSelectedLanguages(array $selectedLanguages) {
-               $this->selectedLanguages = $selectedLanguages;
-       }
-
-       /**
-        * Getter for the selected languages
-        *
-        * @return array
-        */
-       public function getSelectedLanguages() {
-               return $this->selectedLanguages;
-       }
-
-}
index 8ac67b9..a0ac0fb 100644 (file)
@@ -14,19 +14,24 @@ namespace TYPO3\CMS\Lang\Domain\Repository;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+
 /**
  * Extension repository
  *
- * @author Sebastian Fischer <typo3@evoweb.de>
+ * @author Kai Vogel <k.vogel@reply.de>
  */
 class ExtensionRepository {
+
        /**
         * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
+        * @inject
         */
        protected $objectManager;
 
        /**
         * @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
+        * @inject
         */
        protected $listUtility;
 
@@ -36,35 +41,14 @@ class ExtensionRepository {
        protected $extensions = array();
 
        /**
-        * Injects the object manager
-        *
-        * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
-        * @return void
-        */
-       public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) {
-               $this->objectManager = $objectManager;
-       }
-
-       /**
-        * Inject the list utility
+        * Returns all objects of this repository
         *
-        * @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
-        * @return void
-        */
-       public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility) {
-               $this->listUtility = $listUtility;
-       }
-
-       /**
-        * Returns all objects of this repository.
-        *
-        * @return array
+        * @return array The extensions
         */
        public function findAll() {
-               if (!count($this->extensions)) {
-                       $availableAndInstalledExtensions = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
-
-                       foreach ($availableAndInstalledExtensions as $entry) {
+               if (empty($this->extensions)) {
+                       $extensions = $this->listUtility->getAvailableAndInstalledExtensionsWithAdditionalInformation();
+                       foreach ($extensions as $entry) {
                                /** @var $extension \TYPO3\CMS\Lang\Domain\Model\Extension */
                                $extension = $this->objectManager->get(
                                        \TYPO3\CMS\Lang\Domain\Model\Extension::class,
@@ -75,30 +59,53 @@ class ExtensionRepository {
                                $extension->setVersionFromString($entry['version']);
                                $this->extensions[$entry['key']] = $extension;
                        }
-
-                               // Sort the list by extension key
                        ksort($this->extensions);
                }
-
                return $this->extensions;
        }
 
        /**
+        * Counts all objects of this repository
+        *
+        * @return int The extension count
+        */
+       public function countAll() {
+               $extensions = $this->findAll();
+               return count($extensions);
+       }
+
+       /**
+        * Find one extension by offset
+        *
+        * @param int $offset The offset
+        * @return TYPO3\CMS\Lang\Domain\Model\Extension The extension
+        */
+       public function findOneByOffset($offset) {
+               $extensions = $this->findAll();
+               $extensions = array_values($extensions);
+               $offset = (int)$offset;
+               if (!empty($extensions[$offset])) {
+                       return $extensions[$offset];
+               }
+               return NULL;
+       }
+
+       /**
+        * Returns the extension icon
+        *
         * @param array $extensionEntry
         * @return string
         */
        protected function getExtensionIconWithPath($extensionEntry) {
                $extensionIcon = $GLOBALS['TYPO3_LOADED_EXT'][$extensionEntry['key']]['ext_icon'];
                if (empty($extensionIcon)) {
-                       $extensionIcon = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getExtensionIcon(PATH_site . $extensionEntry['siteRelPath'] . '/');
+                       $extensionIcon = ExtensionManagementUtility::getExtensionIcon(PATH_site . $extensionEntry['siteRelPath'] . '/');
                }
-
                if (empty($extensionIcon)) {
                        $extensionIcon = '/typo3/clear.gif';
                } else {
                        $extensionIcon = '../' . $extensionEntry['siteRelPath'] . '/' . $extensionIcon;
                }
-
                return $extensionIcon;
        }
 
index 7a91ddb..8aa9098 100644 (file)
@@ -14,30 +14,35 @@ namespace TYPO3\CMS\Lang\Domain\Repository;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
 /**
  * Language repository
  *
  * @author Sebastian Fischer <typo3@evoweb.de>
+ * @author Kai Vogel <k.vogel@reply.de>
  */
 class LanguageRepository {
 
        /**
         * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
+        * @inject
         */
        protected $objectManager;
 
        /**
-        * @var array
+        * @var \TYPO3\CMS\Core\Localization\Locales
+        * @inject
         */
-       protected $selectedLanguages = array();
+       protected $locales;
 
        /**
-        * @var \TYPO3\CMS\Core\Localization\Locales
+        * @var \TYPO3\CMS\Lang\Domain\Model\Language[]
         */
-       protected $locales;
+       protected $selectedLocales = array();
 
        /**
-        * @var array
+        * @var \TYPO3\CMS\Lang\Domain\Model\Language[]
         */
        protected $languages = array();
 
@@ -47,14 +52,21 @@ class LanguageRepository {
        protected $configurationPath = 'EXTCONF/lang';
 
        /**
+        * @var \TYPO3\CMS\Lang\Service\RegistryService
+        * @inject
+        */
+       protected $registryService;
+
+       /**
         * Constructor of the language repository
         */
        public function __construct() {
+               $configurationManager = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class);
                try {
-                       $globalSettings = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class)->getLocalConfigurationValueByPath($this->configurationPath);
-                       $this->selectedLanguages = (array)$globalSettings['availableLanguages'];
+                       $globalSettings = $configurationManager->getLocalConfigurationValueByPath($this->configurationPath);
+                       $this->selectedLocales = (array)$globalSettings['availableLanguages'];
                } catch (\Exception $e) {
-                       \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class)->setLocalConfigurationValueByPath(
+                       $configurationManager->setLocalConfigurationValueByPath(
                                $this->configurationPath,
                                array('availableLanguages' => array())
                        );
@@ -62,86 +74,60 @@ class LanguageRepository {
        }
 
        /**
-        * Injects the object manager
+        * Returns all objects of this repository
         *
-        * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager
-        * @return void
-        */
-       public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) {
-               $this->objectManager = $objectManager;
-       }
-
-       /**
-        * Injects the locales
-        *
-        * @param \TYPO3\CMS\Core\Localization\Locales $locales
-        * @return void
-        */
-       public function injectLocales(\TYPO3\CMS\Core\Localization\Locales $locales) {
-               $this->locales = $locales;
-       }
-
-       /**
-        * Returns all objects of this repository.
-        *
-        * @return array
+        * @return \TYPO3\CMS\Lang\Domain\Model\Language[] The language objects
         */
        public function findAll() {
                if (!count($this->languages)) {
                        $languages = $this->locales->getLanguages();
                        array_shift($languages);
-
                        foreach ($languages as $locale => $language) {
                                $label = htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:setup/mod/locallang.xlf:lang_' . $locale));
                                if ($label === '') {
                                        $label = htmlspecialchars($language);
                                }
-
                                $this->languages[$locale] = $this->objectManager->get(
                                        \TYPO3\CMS\Lang\Domain\Model\Language::class,
                                        $locale,
                                        $label,
-                                       in_array($locale, $this->selectedLanguages)
+                                       in_array($locale, $this->selectedLocales),
+                                       $this->registryService->get($locale)
                                );
                        }
-
                        usort($this->languages, function($a, $b) {
                                /** @var $a \TYPO3\CMS\Lang\Domain\Model\Language */
                                /** @var $b \TYPO3\CMS\Lang\Domain\Model\Language */
-                               if ($a->getLanguage() == $b->getLanguage()) {
+                               if ($a->getLabel() == $b->getLabel()) {
                                        return 0;
                                }
-                               return $a->getLanguage() < $b->getLanguage() ? -1 : 1;
+                               return $a->getLabel() < $b->getLabel() ? -1 : 1;
                        });
                }
-
                return $this->languages;
        }
 
        /**
         * Find selected languages
         *
-        * @return array
+        * @return \TYPO3\CMS\Lang\Domain\Model\Language[] The language objects
         */
        public function findSelected() {
                $languages = $this->findAll();
-
                $result = array();
-               /** @var $language \TYPO3\CMS\Lang\Domain\Model\Language */
                foreach ($languages as $language) {
                        if ($language->getSelected()) {
                                $result[] = $language;
                        }
                }
-
                return $result;
        }
 
        /**
         * Update selected languages
         *
-        * @param array $languages
-        * @return array
+        * @param array $languages The languages
+        * @return array Update information
         */
        public function updateSelectedLanguages($languages) {
                        // Add possible dependencies for selected languages
@@ -152,15 +138,12 @@ class LanguageRepository {
                if (count($dependencies)) {
                        $languages = array_unique(array_merge($languages, $dependencies));
                }
-
-               $dir = count($languages) - count($this->selectedLanguages);
-               $diff = $dir < 0 ? array_diff($this->selectedLanguages, $languages) : array_diff($languages, $this->selectedLanguages);
-
-               \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class)->setLocalConfigurationValueByPath(
+               $dir = count($languages) - count($this->selectedLocales);
+               $diff = $dir < 0 ? array_diff($this->selectedLocales, $languages) : array_diff($languages, $this->selectedLocales);
+               GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ConfigurationManager::class)->setLocalConfigurationValueByPath(
                        $this->configurationPath,
                        array('availableLanguages' => $languages)
                );
-
                return array(
                        'success' => count($diff) > 0,
                        'dir' => $dir,
@@ -169,4 +152,38 @@ class LanguageRepository {
                );
        }
 
+       /**
+        * Add a language to list of selected languages
+        *
+        * @param string $locale The locale
+        * @return array Update information
+        */
+       public function activateByLocale($locale) {
+               $languages = $this->findAll();
+               $locales = array();
+               foreach ($languages as $language) {
+                       if ($language->getSelected() || $language->getLocale() === $locale) {
+                               $locales[] = $language->getLocale();
+                       }
+               }
+               return $this->updateSelectedLanguages($locales);
+       }
+
+       /**
+        * Remove a language from list of selected languages
+        *
+        * @param string $locale The locale
+        * @return array Update information
+        */
+       public function deactivateByLocale($locale) {
+               $languages = $this->findAll();
+               $locales = array();
+               foreach ($languages as $language) {
+                       if ($language->getSelected() && $language->getLocale() !== $locale) {
+                               $locales[] = $language->getLocale();
+                       }
+               }
+               return $this->updateSelectedLanguages($locales);
+       }
+
 }
diff --git a/typo3/sysext/lang/Classes/Exception/Lang.php b/typo3/sysext/lang/Classes/Exception/Lang.php
deleted file mode 100644 (file)
index 00e4146..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-namespace TYPO3\CMS\Lang\Exception;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * An exception when something is wrong within the lang
- *
- * @author Sebastian Fischer <typo3@evoweb.de>
- */
-class Lang extends \TYPO3\CMS\Core\Exception {
-
-}
diff --git a/typo3/sysext/lang/Classes/Exception/Language.php b/typo3/sysext/lang/Classes/Exception/Language.php
new file mode 100644 (file)
index 0000000..9853650
--- /dev/null
@@ -0,0 +1,24 @@
+<?php
+namespace TYPO3\CMS\Lang\Exception;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * An exception when something is wrong within the language
+ *
+ * @author Sebastian Fischer <typo3@evoweb.de>
+ */
+class Language extends \TYPO3\CMS\Core\Exception {
+
+}
index b4e1b05..31fa580 100644 (file)
@@ -15,7 +15,7 @@ namespace TYPO3\CMS\Lang\Exception;
  */
 
 /**
- * An exception when something is wrong within the lang
+ * An exception when something is wrong within the ter
  *
  * @author Sebastian Fischer <typo3@evoweb.de>
  */
index a9b0fbd..64ea04d 100644 (file)
@@ -20,4 +20,5 @@ namespace TYPO3\CMS\Lang\Exception;
  * @author Sebastian Fischer <typo3@evoweb.de>
  */
 class XmlParser extends \TYPO3\CMS\Core\Exception {
+
 }
diff --git a/typo3/sysext/lang/Classes/Service/RegistryService.php b/typo3/sysext/lang/Classes/Service/RegistryService.php
new file mode 100755 (executable)
index 0000000..a104ba2
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+namespace TYPO3\CMS\Lang\Service;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Registry service
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+class RegistryService {
+
+       /**
+        * @var \TYPO3\CMS\Core\Registry
+        * @inject
+        */
+       protected $registry;
+
+       /**
+        * @var string
+        */
+       protected $namespaceIdentifier = 'TYPO3\\CMS\\Lang';
+
+       /**
+        * Set namespace
+        *
+        * @param string $namespace The namespace
+        * @return void
+        */
+       public function setNamespace($namespace) {
+               $this->namespaceIdentifier = $namespace;
+       }
+
+       /**
+        * Get namespace
+        *
+        * @return string The namespace
+        */
+       public function getNamespace() {
+               return $this->namespaceIdentifier;
+       }
+
+       /**
+        * Check for existing registry entry
+        *
+        * @param string $name Registry entry name
+        * @param string $namespace Optional namespace
+        * @return bool TRUE if exists
+        */
+       public function has($name, $namespace = NULL) {
+               $namespace = (is_string($namespace) ? $namespace : $this->namespaceIdentifier);
+               $value = $this->registry->get($namespace, $name, '__NOTFOUND__');
+               return ($value !== '__NOTFOUND__');
+       }
+
+       /**
+        * Get registry entry
+        *
+        * @param string $name Registry entry name
+        * @param string $namespace Optional namespace
+        * @return mixed Registry content
+        */
+       public function get($name, $namespace = NULL) {
+               $namespace = (is_string($namespace) ? $namespace : $this->namespaceIdentifier);
+               return $this->registry->get($namespace, $name);
+       }
+
+       /**
+        * Add / override registry entry
+        *
+        * @param string $name Registry entry name
+        * @param mixed $value The value
+        * @param string $namespace Optional namespace
+        * @return void
+        */
+       public function set($name, $value, $namespace = NULL) {
+               $namespace = (is_string($namespace) ? $namespace : $this->namespaceIdentifier);
+               $this->registry->set($namespace, $name, $value);
+       }
+
+       /**
+        * Remove registry entry
+        *
+        * @param string $name Registry entry name
+        * @param string $namespace Optional namespace
+        * @return void
+        */
+       public function remove($name, $namespace = NULL) {
+               $namespace = (is_string($namespace) ? $namespace : $this->namespaceIdentifier);
+               $this->registry->remove($namespace, $name);
+       }
+
+}
diff --git a/typo3/sysext/lang/Classes/Service/TerService.php b/typo3/sysext/lang/Classes/Service/TerService.php
new file mode 100644 (file)
index 0000000..8ec920a
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+namespace TYPO3\CMS\Lang\Service;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Extends of extensionmanager ter connection to enrich with translation
+ * related methods
+ *
+ * @author Sebastian Fischer <typo3@evoweb.de>
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+class TerService extends \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * Fetches extensions translation status
+        *
+        * @param string $extensionKey Extension Key
+        * @param string $mirrorUrl URL of mirror to use
+        * @return mixed
+        */
+       public function fetchTranslationStatus($extensionKey, $mirrorUrl) {
+               $result = FALSE;
+               $extPath = GeneralUtility::strtolower($extensionKey);
+               $mirrorUrl .= $extPath[0] . '/' . $extPath[1] . '/' . $extPath . '-l10n/' . $extPath . '-l10n.xml';
+               $remote = GeneralUtility::getURL($mirrorUrl, 0, array(TYPO3_user_agent));
+               if ($remote !== FALSE) {
+                       $parsed = $this->parseL10nXML($remote);
+                       $result = $parsed['languagePackIndex'];
+               }
+               return $result;
+       }
+
+       /**
+        * Parses content of *-l10n.xml into a suitable array
+        *
+        * @param string $string: XML data to parse
+        * @throws \TYPO3\CMS\Lang\Exception\XmlParser
+        * @return array Array representation of XML data
+        */
+       protected function parseL10nXML($string) {
+                       // Create parser:
+               $parser = xml_parser_create();
+               $values = array();
+               $index = array();
+               xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
+               xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
+                       // Parse content
+               xml_parse_into_struct($parser, $string, $values, $index);
+                       // If error, return error message
+               if (xml_get_error_code($parser)) {
+                       $line = xml_get_current_line_number($parser);
+                       $error = xml_error_string(xml_get_error_code($parser));
+                       xml_parser_free($parser);
+                       throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error in XML parser while decoding l10n XML file. Line ' . $line . ': ' . $error, 1345736517);
+               } else {
+                               // Init vars
+                       $stack = array(array());
+                       $stacktop = 0;
+                       $current = array();
+                       $tagName = '';
+                       $documentTag = '';
+                               // Traverse the parsed XML structure:
+                       foreach ($values as $val) {
+                                       // First, process the tag-name (which is used in both cases, whether "complete" or "close")
+                               $tagName = ($val['tag'] == 'languagepack' && $val['type'] == 'open') ? $val['attributes']['language'] : $val['tag'];
+                               if (!$documentTag) {
+                                       $documentTag = $tagName;
+                               }
+                                       // Setting tag-values, manage stack:
+                               switch ($val['type']) {
+                                               // If open tag it means there is an array stored in sub-elements.
+                                               // Therefore increase the stackpointer and reset the accumulation array
+                                       case 'open':
+                                                       // Setting blank place holder
+                                               $current[$tagName] = array();
+                                               $stack[$stacktop++] = $current;
+                                               $current = array();
+                                               break;
+                                               // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
+                                       case 'close':
+                                               $oldCurrent = $current;
+                                               $current = $stack[--$stacktop];
+                                                       // Going to the end of array to get placeholder key, key($current), and fill in array next
+                                               end($current);
+                                               $current[key($current)] = $oldCurrent;
+                                               unset($oldCurrent);
+                                               break;
+                                               // If "complete", then it's a value. Omits the tag if the value is empty.
+                                       case 'complete':
+                                               $trimmedValue = trim((string)$val['value']);
+                                               if ($trimmedValue !== '') {
+                                                       $current[$tagName] = $trimmedValue;
+                                               }
+                                               break;
+                               }
+                       }
+                       $result = $current[$tagName];
+               }
+               return $result;
+       }
+
+       /**
+        * Install translations for all selected languages for an extension
+        *
+        * @param string $extensionKey The extension key to install the translations for
+        * @param string $language Language code of translation to fetch
+        * @param string $mirrorUrl Mirror URL to fetch data from
+        * @return bool TRUE on success, error string on failure
+        */
+       public function updateTranslation($extensionKey, $language, $mirrorUrl) {
+               $result = FALSE;
+               try {
+                       $l10n = $this->fetchTranslation($extensionKey, $language, $mirrorUrl);
+                       if (is_array($l10n)) {
+                               $absolutePathToZipFile = GeneralUtility::getFileAbsFileName('typo3temp/' . $extensionKey . '-l10n-' . $language . '.zip');
+                               $relativeLanguagePath = 'l10n' . '/' . $language . '/';
+                               $absoluteLanguagePath = GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath);
+                               $absoluteExtensionLanguagePath = GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath . $extensionKey . '/');
+                               if (empty($absolutePathToZipFile) || empty($absoluteLanguagePath) || empty($absoluteExtensionLanguagePath)) {
+                                       throw new \TYPO3\CMS\Lang\Exception\Language('Given path is invalid.', 1352565336);
+                               }
+                               if (!is_dir($absoluteLanguagePath)) {
+                                       GeneralUtility::mkdir_deep(PATH_typo3conf, $relativeLanguagePath);
+                               }
+                               GeneralUtility::writeFile($absolutePathToZipFile, $l10n[0]);
+                               if (is_dir($absoluteExtensionLanguagePath)) {
+                                       GeneralUtility::rmdir($absoluteExtensionLanguagePath, TRUE);
+                               }
+                               if ($this->unzipTranslationFile($absolutePathToZipFile, $absoluteLanguagePath)) {
+                                       $result = TRUE;
+                               }
+                       }
+               } catch (\TYPO3\CMS\Core\Exception $exception) {
+                       // @todo logging
+               }
+               return $result;
+       }
+
+       /**
+        * Fetches an extensions l10n file from the given mirror
+        *
+        * @param string $extensionKey Extension Key
+        * @param string $language The language code of the translation to fetch
+        * @param string $mirrorUrl URL of mirror to use
+        * @throws \TYPO3\CMS\Lang\Exception\XmlParser
+        * @return array Array containing l10n data
+        */
+       protected function fetchTranslation($extensionKey, $language, $mirrorUrl) {
+               $extensionPath = GeneralUtility::strtolower($extensionKey);
+               $mirrorUrl .= $extensionPath[0] . '/' . $extensionPath[1] . '/' . $extensionPath .
+                       '-l10n/' . $extensionPath . '-l10n-' . $language . '.zip';
+               $l10nResponse = GeneralUtility::getURL($mirrorUrl, 0, array(TYPO3_user_agent));
+               if ($l10nResponse === FALSE) {
+                       throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error: Translation could not be fetched.', 1345736785);
+               } else {
+                       return array($l10nResponse);
+               }
+       }
+
+       /**
+        * Unzip an language zip file
+        *
+        * @param string $file path to zip file
+        * @param string $path path to extract to
+        * @throws \TYPO3\CMS\Lang\Exception\Language
+        * @return bool
+        */
+       protected function unzipTranslationFile($file, $path) {
+               $zip = zip_open($file);
+               if (is_resource($zip)) {
+                       $result = TRUE;
+                       if (!is_dir($path)) {
+                               GeneralUtility::mkdir_deep($path);
+                       }
+                       while (($zipEntry = zip_read($zip)) !== FALSE) {
+                               $zipEntryName = zip_entry_name($zipEntry);
+                               if (strpos($zipEntryName, '/') !== FALSE) {
+                                       $zipEntryPathSegments =  explode('/', $zipEntryName);
+                                       $fileName = array_pop($zipEntryPathSegments);
+                                       // It is a folder, because the last segment is empty, let's create it
+                                       if (empty($fileName)) {
+                                               GeneralUtility::mkdir_deep($path, implode('/', $zipEntryPathSegments));
+                                       } else {
+                                               $absoluteTargetPath = GeneralUtility::getFileAbsFileName($path . implode('/', $zipEntryPathSegments) . '/' . $fileName);
+                                               if (strlen(trim($absoluteTargetPath)) > 0) {
+                                                       $return = GeneralUtility::writeFile(
+                                                               $absoluteTargetPath, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry))
+                                                       );
+                                                       if ($return === FALSE) {
+                                                               throw new \TYPO3\CMS\Lang\Exception\Language('Could not write file ' . $zipEntryName, 1345304560);
+                                                       }
+                                               } else {
+                                                       throw new \TYPO3\CMS\Lang\Exception\Language('Could not write file ' . $zipEntryName, 1352566904);
+                                               }
+                                       }
+                               } else {
+                                       throw new \TYPO3\CMS\Lang\Exception\Language('Extension directory missing in zip file!', 1352566905);
+                               }
+                       }
+               } else {
+                       throw new \TYPO3\CMS\Lang\Exception\Language('Unable to open zip file ' . $file, 1345304561);
+               }
+               return $result;
+       }
+
+}
diff --git a/typo3/sysext/lang/Classes/Service/TranslationService.php b/typo3/sysext/lang/Classes/Service/TranslationService.php
new file mode 100644 (file)
index 0000000..51c6913
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+namespace TYPO3\CMS\Lang\Service;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Translation service
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+class TranslationService implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * Status codes for AJAX response
+        */
+       const TRANSLATION_NOT_AVAILABLE = 0;
+       const TRANSLATION_AVAILABLE = 1;
+       const TRANSLATION_FAILED = 2;
+       const TRANSLATION_OK = 3;
+       const TRANSLATION_INVALID = 4;
+       const TRANSLATION_UPDATED = 5;
+
+       /**
+        * @var \TYPO3\CMS\Lang\Service\TerService
+        * @inject
+        */
+       protected $terService;
+
+       /**
+        * @var string
+        */
+       protected $mirrorUrl = '';
+
+       /**
+        * @param \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper $helper The helper
+        * @return void
+        */
+       public function injectRepositoryHelper(\TYPO3\CMS\Extensionmanager\Utility\Repository\Helper $helper) {
+               $this->mirrorUrl = $helper->getMirrors()->getMirrorUrl();
+       }
+
+       /**
+        * Update translation for given extension
+        *
+        * @param string $extensionKey The extension key
+        * @param mixed $locales Comma separated list or array of locales
+        * @return array Update information
+        */
+       public function updateTranslation($extensionKey, $locales) {
+               if (is_string($locales)) {
+                       $locales = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $locales);
+               }
+               $locales = array_flip((array) $locales);
+               foreach ($locales as $locale => $key) {
+                       $state = static::TRANSLATION_INVALID;
+                       try {
+                               $state = $this->updateTranslationForExtension($extensionKey, $locale);
+                       } catch (\Exception $exception) {
+                               $error = $exception->getMessage();
+                       }
+                       $locales[$locale] = array(
+                               'state'  => $state,
+                               'error'  => $error,
+                       );
+               }
+               return $locales;
+       }
+
+       /**
+        * Update the translation for an extension
+        *
+        * @param string $extensionKey The extension key
+        * @param string $locale Locale to update
+        * @return int Translation state
+        */
+       protected function updateTranslationForExtension($extensionKey, $locale) {
+               if (empty($extensionKey) || empty($locale)) {
+                       return static::TRANSLATION_INVALID;
+               }
+               $state = static::TRANSLATION_FAILED;
+               $updateResult = $this->terService->updateTranslation($extensionKey, $locale, $this->mirrorUrl);
+               if ($updateResult === TRUE) {
+                       $state = static::TRANSLATION_UPDATED;
+               }
+               return $state;
+       }
+
+}
diff --git a/typo3/sysext/lang/Classes/Service/UpdateTranslationService.php b/typo3/sysext/lang/Classes/Service/UpdateTranslationService.php
deleted file mode 100644 (file)
index 3131230..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-<?php
-namespace TYPO3\CMS\Lang\Service;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Update languages service
- */
-class UpdateTranslationService {
-
-       /**
-        * Status codes for AJAX response
-        */
-       const TRANSLATION_NOT_AVAILABLE = 0;
-       const TRANSLATION_AVAILABLE = 1;
-       const TRANSLATION_FAILED = 2;
-       const TRANSLATION_OK = 3;
-       const TRANSLATION_INVALID = 4;
-       const TRANSLATION_UPDATED = 5;
-
-       /**
-        * @var \TYPO3\CMS\Lang\Domain\Repository\LanguageRepository
-        * @inject
-        */
-       protected $languageRepository;
-
-       /**
-        * @var \TYPO3\CMS\Extensionmanager\Utility\Repository\Helper
-        * @inject
-        */
-       protected $repositoryHelper;
-
-       /**
-        * @var \TYPO3\CMS\Lang\Utility\Connection\Ter
-        * @inject
-        */
-       protected $terConnection;
-
-       /**
-        * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
-        * @inject
-        */
-       protected $signalSlotDispatcher;
-
-       /**
-        * @var array
-        */
-       protected $translationStates = array();
-
-       /**
-        * Update translation for given extension
-        *
-        * @param string $extension The extension key
-        * @param string $locales Comma separated list of locales to update
-        * @return array
-        */
-       public function updateTranslation($extension, $locales) {
-               if (is_string($locales)) {
-                       $locales = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $locales);
-               }
-               $locales = array_flip((array)$locales);
-
-               foreach ($locales as $locale => $key) {
-                       $state = static::TRANSLATION_INVALID;
-                       try {
-                               $state = $this->getTranslationStateForExtension($extension, $locale);
-                               if ($state === static::TRANSLATION_AVAILABLE) {
-                                       $state = $this->updateTranslationForExtension($extension, $locale);
-                               }
-                       } catch (\Exception $exception) {
-                               $error = $exception->getMessage();
-                       }
-                       $locales[$locale] = array(
-                               'state'  => $state,
-                               'error'  => $error,
-                       );
-               }
-               return $locales;
-       }
-
-       /**
-        * Returns the translation state for an extension
-        *
-        * @param string $extensionKey The extension key
-        * @param string $locale Locale to return
-        * @return int Translation state
-        */
-       protected function getTranslationStateForExtension($extensionKey, $locale) {
-               if (empty($extensionKey) || empty($locale)) {
-                       return static::TRANSLATION_INVALID;
-               }
-
-               $identifier = $extensionKey . '-' . $locale;
-               if (isset($this->translationStates[$identifier])) {
-                       return $this->translationStates[$identifier];
-               }
-
-               $selectedLanguages = $this->languageRepository->findSelected();
-               if (empty($selectedLanguages) || !is_array($selectedLanguages)) {
-                       return static::TRANSLATION_INVALID;
-               }
-
-               $mirrorUrl = $this->getMirrorUrl($extensionKey);
-               $status = $this->terConnection->fetchTranslationStatus($extensionKey, $mirrorUrl);
-
-               foreach ($selectedLanguages as $language) {
-                       $stateLocale = $language->getLocale();
-                       $stateIdentifier = $extensionKey . '-' . $stateLocale;
-                       $this->translationStates[$stateIdentifier] = static::TRANSLATION_INVALID;
-
-                       if (empty($status[$stateLocale]) || !is_array($status[$stateLocale])) {
-                               $this->translationStates[$stateIdentifier] = static::TRANSLATION_NOT_AVAILABLE;
-                               continue;
-                       }
-
-                       $md5 = $this->getTranslationFileMd5($extensionKey, $stateLocale);
-                       if ($md5 !== $status[$stateLocale]['md5']) {
-                               $this->translationStates[$stateIdentifier] = static::TRANSLATION_AVAILABLE;
-                               continue;
-                       }
-
-                       $this->translationStates[$stateIdentifier] = static::TRANSLATION_OK;
-               }
-
-               return $this->translationStates[$identifier];
-       }
-
-       /**
-        * Returns the md5 of a translation file
-        *
-        * @param string $extensionKey The extension key
-        * @param string $locale The locale
-        * @return string The md5 value
-        */
-       protected function getTranslationFileMd5($extensionKey, $locale) {
-               if (empty($extensionKey) || empty($locale)) {
-                       return '';
-               }
-               $fileName = PATH_site . 'typo3temp' . DIRECTORY_SEPARATOR . $extensionKey . '-l10n-' . $locale . '.zip';
-               if (is_file($fileName)) {
-                       return md5_file($fileName);
-               }
-               return '';
-       }
-
-       /**
-        * Update the translation for an extension
-        *
-        * @param string $extensionKey The extension key
-        * @param string $locale Locale to update
-        * @return int Translation state
-        */
-       protected function updateTranslationForExtension($extensionKey, $locale) {
-               if (empty($extensionKey) || empty($locale)) {
-                       return static::TRANSLATION_INVALID;
-               }
-
-               $state = static::TRANSLATION_FAILED;
-               $mirrorUrl = $this->getMirrorUrl($extensionKey);
-               $updateResult = $this->terConnection->updateTranslation($extensionKey, $locale, $mirrorUrl);
-               if ($updateResult === TRUE) {
-                       $state = static::TRANSLATION_UPDATED;
-               }
-
-               return $state;
-       }
-
-       /**
-        * Returns the mirror URL for a given extension.
-        *
-        * @param string $extensionKey
-        * @return string
-        */
-       protected function getMirrorUrl($extensionKey) {
-               $mirrorUrl = $this->repositoryHelper->getMirrors()->getMirrorUrl();
-
-               $mirrorUrl = $this->emitPostProcessMirrorUrlSignal($extensionKey, $mirrorUrl);
-
-               return $mirrorUrl;
-       }
-
-       /**
-        * Emits a signal after the mirror URL of an extension was fetched
-        *
-        * @param string $extensionKey
-        * @param string $mirrorUrl
-        * @return string Modified mirror url
-        */
-       protected function emitPostProcessMirrorUrlSignal($extensionKey, $mirrorUrl) {
-               $this->signalSlotDispatcher->dispatch(
-                       __CLASS__,
-                       'postProcessMirrorUrl',
-                       array(
-                               $extensionKey,
-                               &$mirrorUrl,
-                       )
-               );
-               return $mirrorUrl;
-       }
-
-}
diff --git a/typo3/sysext/lang/Classes/Utility/Connection/Ter.php b/typo3/sysext/lang/Classes/Utility/Connection/Ter.php
deleted file mode 100644 (file)
index 68b5262..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-<?php
-namespace TYPO3\CMS\Lang\Utility\Connection;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Extends of extensionmanager ter connection to enrich with translation related methods
- *
- * @author Sebastian Fischer <typo3@evoweb.de>
- */
-class Ter extends \TYPO3\CMS\Extensionmanager\Utility\Connection\TerUtility {
-
-       /**
-        * Fetches extensions translation status
-        *
-        * @param string $extensionKey Extension Key
-        * @param string $mirrorUrl URL of mirror to use
-        * @return mixed
-        */
-       public function fetchTranslationStatus($extensionKey, $mirrorUrl) {
-               $result = FALSE;
-               $extPath = \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($extensionKey);
-               $mirrorUrl .= $extPath[0] . '/' . $extPath[1] . '/' . $extPath . '-l10n/' . $extPath . '-l10n.xml';
-               $remote = \TYPO3\CMS\Core\Utility\GeneralUtility::getURL($mirrorUrl, 0, array(TYPO3_user_agent));
-
-               if ($remote !== FALSE) {
-                       $parsed = $this->parseL10nXML($remote);
-                       $result = $parsed['languagePackIndex'];
-               }
-
-               return $result;
-       }
-
-       /**
-        * Parses content of *-l10n.xml into a suitable array
-        *
-        * @param string $string: XML data to parse
-        * @throws \TYPO3\CMS\Lang\Exception\XmlParser
-        * @return array Array representation of XML data
-        */
-       protected function parseL10nXML($string) {
-                       // Create parser:
-               $parser = xml_parser_create();
-               $values = array();
-               $index = array();
-
-               xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
-               xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
-
-                       // Parse content
-               xml_parse_into_struct($parser, $string, $values, $index);
-
-                       // If error, return error message
-               if (xml_get_error_code($parser)) {
-                       $line = xml_get_current_line_number($parser);
-                       $error = xml_error_string(xml_get_error_code($parser));
-                       xml_parser_free($parser);
-                       throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error in XML parser while decoding l10n XML file. Line ' . $line . ': ' . $error, 1345736517);
-               } else {
-                               // Init vars
-                       $stack = array(array());
-                       $stacktop = 0;
-                       $current = array();
-                       $tagName = '';
-                       $documentTag = '';
-
-                               // Traverse the parsed XML structure:
-                       foreach ($values as $val) {
-                                       // First, process the tag-name (which is used in both cases, whether "complete" or "close")
-                               $tagName = ($val['tag'] == 'languagepack' && $val['type'] == 'open') ? $val['attributes']['language'] : $val['tag'];
-                               if (!$documentTag) {
-                                       $documentTag = $tagName;
-                               }
-
-                                       // Setting tag-values, manage stack:
-                               switch ($val['type']) {
-                                               // If open tag it means there is an array stored in sub-elements.
-                                               // Therefore increase the stackpointer and reset the accumulation array
-                                       case 'open':
-                                                       // Setting blank place holder
-                                               $current[$tagName] = array();
-                                               $stack[$stacktop++] = $current;
-                                               $current = array();
-                                               break;
-                                               // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
-                                       case 'close':
-                                               $oldCurrent = $current;
-                                               $current = $stack[--$stacktop];
-                                                       // Going to the end of array to get placeholder key, key($current), and fill in array next
-                                               end($current);
-                                               $current[key($current)] = $oldCurrent;
-                                               unset($oldCurrent);
-                                               break;
-                                               // If "complete", then it's a value. Omits the tag if the value is empty.
-                                       case 'complete':
-                                               $trimmedValue = trim((string)$val['value']);
-                                               if ($trimmedValue !== '') {
-                                                       $current[$tagName] = $trimmedValue;
-                                               }
-                                               break;
-                               }
-                       }
-                       $result = $current[$tagName];
-               }
-
-               return $result;
-       }
-
-       /**
-        * Install translations for all selected languages for an extension
-        *
-        * @param string $extensionKey The extension key to install the translations for
-        * @param string $language Language code of translation to fetch
-        * @param string $mirrorUrl Mirror URL to fetch data from
-        * @return bool TRUE on success, error string on failure
-        */
-       public function updateTranslation($extensionKey, $language, $mirrorUrl) {
-               $result = FALSE;
-               try {
-                       $l10n = $this->fetchTranslation($extensionKey, $language, $mirrorUrl);
-                       if (is_array($l10n)) {
-                               $absolutePathToZipFile = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName('typo3temp/' . $extensionKey . '-l10n-' . $language . '.zip');
-                               $relativeLanguagePath = 'l10n' . '/' . $language . '/';
-                               $absoluteLanguagePath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath);
-                               $absoluteExtensionLanguagePath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName(PATH_typo3conf . $relativeLanguagePath . $extensionKey . '/');
-                               if (empty($absolutePathToZipFile) || empty($absoluteLanguagePath) || empty($absoluteExtensionLanguagePath)) {
-                                       throw new \TYPO3\CMS\Lang\Exception\Lang('Given path is invalid.', 1352565336);
-                               }
-                               if (!is_dir($absoluteLanguagePath)) {
-                                       \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep(PATH_typo3conf, $relativeLanguagePath);
-                               }
-                               \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile($absolutePathToZipFile, $l10n[0]);
-                               if (is_dir($absoluteExtensionLanguagePath)) {
-                                       \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir($absoluteExtensionLanguagePath, TRUE);
-                               }
-
-                               if ($this->unzipTranslationFile($absolutePathToZipFile, $absoluteLanguagePath)) {
-                                       $result = TRUE;
-                               }
-                       }
-               } catch (\TYPO3\CMS\Core\Exception $exception) {
-                       // @todo logging
-               }
-               return $result;
-       }
-
-       /**
-        * Fetches an extensions l10n file from the given mirror
-        *
-        * @param string $extensionKey Extension Key
-        * @param string $language The language code of the translation to fetch
-        * @param string $mirrorUrl URL of mirror to use
-        * @throws \TYPO3\CMS\Lang\Exception\XmlParser
-        * @return array Array containing l10n data
-        */
-       protected function fetchTranslation($extensionKey, $language, $mirrorUrl) {
-               $extensionPath = \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($extensionKey);
-               $mirrorUrl .= $extensionPath[0] . '/' . $extensionPath[1] . '/' . $extensionPath .
-                       '-l10n/' . $extensionPath . '-l10n-' . $language . '.zip';
-               $l10nResponse = \TYPO3\CMS\Core\Utility\GeneralUtility::getURL($mirrorUrl, 0, array(TYPO3_user_agent));
-
-               if ($l10nResponse === FALSE) {
-                       throw new \TYPO3\CMS\Lang\Exception\XmlParser('Error: Translation could not be fetched.', 1345736785);
-               } else {
-                       return array($l10nResponse);
-               }
-       }
-
-       /**
-        * Unzip an language.zip.
-        *
-        * @param string $file path to zip file
-        * @param string $path path to extract to
-        * @throws \TYPO3\CMS\Lang\Exception\Lang
-        * @return bool
-        */
-       protected function unzipTranslationFile($file, $path) {
-               $zip = zip_open($file);
-               if (is_resource($zip)) {
-                       $result = TRUE;
-
-                       if (!is_dir($path)) {
-                               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path);
-                       }
-
-                       while (($zipEntry = zip_read($zip)) !== FALSE) {
-                               $zipEntryName = zip_entry_name($zipEntry);
-                               if (strpos($zipEntryName, '/') !== FALSE) {
-                                       $zipEntryPathSegments =  explode('/', $zipEntryName);
-                                       $fileName = array_pop($zipEntryPathSegments);
-                                       // It is a folder, because the last segment is empty, let's create it
-                                       if (empty($fileName)) {
-                                               \TYPO3\CMS\Core\Utility\GeneralUtility::mkdir_deep($path, implode('/', $zipEntryPathSegments));
-                                       } else {
-                                               $absoluteTargetPath = \TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($path . implode('/', $zipEntryPathSegments) . '/' . $fileName);
-                                               if (strlen(trim($absoluteTargetPath)) > 0) {
-                                                       $return = \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile(
-                                                               $absoluteTargetPath, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry))
-                                                       );
-                                                       if ($return === FALSE) {
-                                                               throw new \TYPO3\CMS\Lang\Exception\Lang('Could not write file ' . $zipEntryName, 1345304560);
-                                                       }
-                                               } else {
-                                                       throw new \TYPO3\CMS\Lang\Exception\Lang('Could not write file ' . $zipEntryName, 1352566904);
-                                               }
-                                       }
-                               } else {
-                                       throw new \TYPO3\CMS\Lang\Exception\Lang('Extension directory missing in zip file!', 1352566905);
-                               }
-                       }
-               } else {
-                       throw new \TYPO3\CMS\Lang\Exception\Lang('Unable to open zip file ' . $file, 1345304561);
-               }
-
-               return $result;
-       }
-
-}
diff --git a/typo3/sysext/lang/Classes/View/AbstractJsonView.php b/typo3/sysext/lang/Classes/View/AbstractJsonView.php
new file mode 100644 (file)
index 0000000..1028e76
--- /dev/null
@@ -0,0 +1,62 @@
+<?php
+namespace TYPO3\CMS\Lang\View;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Base class for JSON views
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+abstract class AbstractJsonView extends \TYPO3\CMS\Extbase\Mvc\View\AbstractView {
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Object\ObjectManager
+        * @inject
+        */
+       protected $objectManager;
+
+       /**
+        * Render template content
+        *
+        * @return void
+        */
+       public function render() {
+               $result = $this->getReponseData();
+               $this->sendResponse($result);
+       }
+
+       /**
+        * Returns the response data
+        *
+        * @return array The response data
+        */
+       abstract protected function getReponseData();
+
+       /**
+        * Send response to browser
+        *
+        * @param array $data The response data
+        * @return void
+        */
+       protected function sendResponse(array $data) {
+               $response = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Response::class);
+               $response->setHeader('Content-Type', 'application/json; charset=utf-8');
+               $response->setContent(json_encode($data));
+               $response->sendHeaders();
+               $response->send();
+               exit;
+       }
+
+}
diff --git a/typo3/sysext/lang/Classes/View/Language/ActivateLanguageJson.php b/typo3/sysext/lang/Classes/View/Language/ActivateLanguageJson.php
new file mode 100644 (file)
index 0000000..6d65592
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+namespace TYPO3\CMS\Lang\View\Language;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * JSON view for "activateLanguage" action in "Language" controller
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+class ActivateLanguageJson extends \TYPO3\CMS\Lang\View\AbstractJsonView {
+
+       /**
+        * Returns the response data
+        *
+        * @return array The response data
+        */
+       protected function getReponseData() {
+               return $this->variables['response'];
+       }
+
+}
diff --git a/typo3/sysext/lang/Classes/View/Language/CheckTranslationJson.php b/typo3/sysext/lang/Classes/View/Language/CheckTranslationJson.php
deleted file mode 100644 (file)
index 7a7a951..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-namespace TYPO3\CMS\Lang\View\Language;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * JSON view for "checkTranslation" action in "Language" controller
- *
- * @author Kai Vogel <kai.vogel@speedprogs.de>
- */
-class CheckTranslationJson extends \TYPO3\CMS\Extbase\Mvc\View\AbstractView {
-
-       /**
-        * Render method, returns json encoded template variables
-        *
-        * @return string JSON content
-        */
-       public function render() {
-               return json_encode($this->variables);
-       }
-
-}
diff --git a/typo3/sysext/lang/Classes/View/Language/DeactivateLanguageJson.php b/typo3/sysext/lang/Classes/View/Language/DeactivateLanguageJson.php
new file mode 100644 (file)
index 0000000..847c200
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+namespace TYPO3\CMS\Lang\View\Language;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * JSON view for "deactivateLanguage" action in "Language" controller
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+class DeactivateLanguageJson extends \TYPO3\CMS\Lang\View\AbstractJsonView {
+
+       /**
+        * Returns the response data
+        *
+        * @return array The response data
+        */
+       protected function getReponseData() {
+               return $this->variables['response'];
+       }
+
+}
diff --git a/typo3/sysext/lang/Classes/View/Language/GetTranslationsJson.php b/typo3/sysext/lang/Classes/View/Language/GetTranslationsJson.php
new file mode 100644 (file)
index 0000000..9ce3a70
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+namespace TYPO3\CMS\Lang\View\Language;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * JSON view for "getTranslations" action in "Language" controller
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+class GetTranslationsJson extends \TYPO3\CMS\Lang\View\AbstractJsonView {
+
+       /**
+        * Returns the response data
+        *
+        * @return array The response data
+        */
+       protected function getReponseData() {
+               $data = array();
+               $languages = $this->variables['languages'];
+               foreach ($this->variables['extensions'] as $extension) {
+                       $extensionArray = $extension->toArray();
+                       $row = array(
+                               $extensionArray,
+                               $extensionArray,
+                       );
+                       foreach ($languages as $language) {
+                               $row[] = $language->toArray();
+                       }
+                       $data[] = $row;
+               }
+               return array(
+                       'data' => $data,
+               );
+       }
+
+}
diff --git a/typo3/sysext/lang/Classes/View/Language/UpdateLanguageJson.php b/typo3/sysext/lang/Classes/View/Language/UpdateLanguageJson.php
new file mode 100644 (file)
index 0000000..8180772
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+namespace TYPO3\CMS\Lang\View\Language;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * JSON view for "updateLanguage" action in "Language" controller
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+class UpdateLanguageJson extends \TYPO3\CMS\Lang\View\AbstractJsonView {
+
+       /**
+        * Returns the response data
+        *
+        * @return array The response data
+        */
+       protected function getReponseData() {
+               return $this->variables['response'];
+       }
+
+}
index fbbf986..61f7cca 100644 (file)
@@ -17,17 +17,17 @@ namespace TYPO3\CMS\Lang\View\Language;
 /**
  * JSON view for "updateTranslation" action in "Language" controller
  *
- * @author Kai Vogel <kai.vogel@speedprogs.de>
+ * @author Kai Vogel <k.vogel@reply.de>
  */
-class UpdateTranslationJson extends CheckTranslationJson {
+class UpdateTranslationJson extends \TYPO3\CMS\Lang\View\AbstractJsonView {
 
        /**
-        * Render method, returns json encoded template variables
+        * Returns the response data
         *
-        * @return string JSON content
+        * @return array The response data
         */
-       public function render() {
-               return json_encode($this->variables);
+       protected function getReponseData() {
+               return $this->variables['response'];
        }
 
 }
diff --git a/typo3/sysext/lang/Classes/ViewHelpers/Be/SpriteIconViewHelper.php b/typo3/sysext/lang/Classes/ViewHelpers/Be/SpriteIconViewHelper.php
deleted file mode 100644 (file)
index 95702b1..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-namespace TYPO3\CMS\Lang\ViewHelpers\Be;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Sprite icon view helper
- *
- * Usage:
- *
- * {namespace myext=ENET\MyExt\ViewHelpers}
- * <myext:be.spriteIcon icon="actions-document-close" title="Close" class="myClass" />
- *
- * @author Kai Vogel <kai.vogel@speedprogs.de>
- * @internal
- */
-class SpriteIconViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper {
-
-       /**
-        * Renders an icon as known from the TYPO3 backend
-        *
-        * @param string $icon Icon to be used
-        * @param string $title Title attribute
-        * @param string $class Class attribute
-        * @return string the rendered icon
-        */
-       public function render($icon, $title = '', $class = '') {
-               $options = array(
-                       'title' => $title,
-                       'class' => $class,
-               );
-               return \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon($icon, $options);
-       }
-
-}
index 97beca7..a4fa68e 100644 (file)
@@ -6,44 +6,67 @@
                        <trans-unit id="state.error" xml:space="preserve">
                                <source>Error</source>
                        </trans-unit>
-                       <trans-unit id="state.notAvailable" xml:space="preserve">
+                       <trans-unit id="state.notAvailable" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Not available</source>
                        </trans-unit>
-                       <trans-unit id="state.failed" xml:space="preserve">
+                       <trans-unit id="state.failed" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Failed</source>
                        </trans-unit>
-                       <trans-unit id="state.upToDate" xml:space="preserve">
+                       <trans-unit id="state.upToDate" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Up to date</source>
                        </trans-unit>
-                       <trans-unit id="state.invalid" xml:space="preserve">
+                       <trans-unit id="state.invalid" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Invalid</source>
                        </trans-unit>
-                       <trans-unit id="state.updated" xml:space="preserve">
+                       <trans-unit id="state.updated" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Updated</source>
                        </trans-unit>
-                       <trans-unit id="state.checking" xml:space="preserve">
+                       <trans-unit id="state.checking" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Checking...</source>
                        </trans-unit>
-                       <trans-unit id="state.notChecked" xml:space="preserve">
+                       <trans-unit id="state.notChecked" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Not checked</source>
                        </trans-unit>
-
-                       <trans-unit id="button.update" xml:space="preserve">
+                       <trans-unit id="button.update" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Update from repository</source>
                        </trans-unit>
+                       <trans-unit id="button.downloadAll" xml:space="preserve">
+                               <source>Download all</source>
+                       </trans-unit>
+                       <trans-unit id="button.download" xml:space="preserve">
+                               <source>Download</source>
+                       </trans-unit>
                        <trans-unit id="button.cancel" xml:space="preserve">
                                <source>Cancel</source>
                        </trans-unit>
+                       <trans-unit id="button.activate" xml:space="preserve">
+                               <source>Activate</source>
+                       </trans-unit>
+                       <trans-unit id="button.deactivate" xml:space="preserve">
+                               <source>Deactivate</source>
+                       </trans-unit>
+                       <trans-unit id="button.complete" xml:space="preserve">
+                               <source>Complete</source>
+                       </trans-unit>
+                       <trans-unit id="button.loading" xml:space="preserve">
+                               <source>Loading</source>
+                       </trans-unit>
 
-                       <trans-unit id="header.module" xml:space="preserve">
+                       <trans-unit id="header.module" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Language Administration</source>
                        </trans-unit>
-                       <trans-unit id="header.languageSelectionList" xml:space="preserve">
+                       <trans-unit id="header.languageSelectionList" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Languages</source>
                        </trans-unit>
-                       <trans-unit id="header.translationUpdateList" xml:space="preserve">
+                       <trans-unit id="header.translationUpdateList" xml:space="preserve" deprecated="Unused since CMS 7">
                                <source>Translations</source>
                        </trans-unit>
+                       <trans-unit id="header.languages" xml:space="preserve">
+                               <source>Installed Languages</source>
+                       </trans-unit>
+                       <trans-unit id="header.translations" xml:space="preserve">
+                               <source>Translation Overview</source>
+                       </trans-unit>
 
                        <trans-unit id="flashmessage.error" xml:space="preserve">
                                <source>Error</source>
                        <trans-unit id="flashmessage.canceled" xml:space="preserve">
                                <source>The process has been canceled.</source>
                        </trans-unit>
+                       <trans-unit id="flashmessage.languageActivated" xml:space="preserve">
+                               <source>Language was successfully activated.</source>
+                       </trans-unit>
+                       <trans-unit id="flashmessage.languageDeactivated" xml:space="preserve">
+                               <source>Language was successfully deactivated.</source>
+                       </trans-unit>
+                       <trans-unit id="flashmessage.errorOccurred" xml:space="preserve">
+                               <source>The request has been failed.</source>
+                       </trans-unit>
 
+                       <trans-unit id="table.ad" xml:space="preserve">
+                               <source>A/D</source>
+                       </trans-unit>
+                       <trans-unit id="table.ad.title" xml:space="preserve">
+                               <source>Activate / Deactivate</source>
+                       </trans-unit>
                        <trans-unit id="table.language" xml:space="preserve">
                                <source>Language</source>
                        </trans-unit>
                        <trans-unit id="table.extension" xml:space="preserve">
                                <source>Extension</source>
                        </trans-unit>
+                       <trans-unit id="table.actions" xml:space="preserve">
+                               <source>Actions</source>
+                       </trans-unit>
+                       <trans-unit id="table.date" xml:space="preserve">
+                               <source>Last update</source>
+                       </trans-unit>
+                       <trans-unit id="table.processing" xml:space="preserve">
+                               <source>Processing...</source>
+                       </trans-unit>
+                       <trans-unit id="table.search" xml:space="preserve">
+                               <source>Search:</source>
+                       </trans-unit>
+                       <trans-unit id="table.loadingRecords" xml:space="preserve">
+                               <source>Loading...</source>
+                       </trans-unit>
+                       <trans-unit id="table.zeroRecords" xml:space="preserve">
+                               <source>No matching records found</source>
+                       </trans-unit>
+                       <trans-unit id="table.emptyTable" xml:space="preserve">
+                               <source>No data available in table</source>
+                       </trans-unit>
+                       <trans-unit id="table.dateFormat" xml:space="preserve">
+                               <source>Y-m-d H:i:s</source>
+                       </trans-unit>
                </body>
        </file>
 </xliff>
diff --git a/typo3/sysext/lang/Resources/Private/Language/locallang_mod.xlf b/typo3/sysext/lang/Resources/Private/Language/locallang_mod.xlf
new file mode 100644 (file)
index 0000000..cafa37b
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
+       <file t3:id="1415814892" source-language="en" datatype="plaintext" original="messages" date="2012-08-31T17:54:12Z" product-name="language">
+               <header/>
+               <body>
+                       <trans-unit id="mlang_labels_tablabel" xml:space="preserve">
+                               <source>Languages</source>
+                       </trans-unit>
+                       <trans-unit id="mlang_labels_tabdescr" xml:space="preserve">
+                               <source>Select languages to be available for backend and frontend. Provide update of chosen languages.</source>
+                       </trans-unit>
+                       <trans-unit id="mlang_tabs_tab" xml:space="preserve">
+                               <source>Languages</source>
+                       </trans-unit>
+               </body>
+       </file>
+</xliff>
diff --git a/typo3/sysext/lang/Resources/Private/Language/locallang_mod_language.xlf b/typo3/sysext/lang/Resources/Private/Language/locallang_mod_language.xlf
deleted file mode 100644 (file)
index 763ad8d..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
-       <file t3:id="1415814892" source-language="en" datatype="plaintext" original="messages" date="2012-08-31T17:54:12Z" product-name="lang">
-               <header/>
-               <body>
-                       <trans-unit id="mlang_labels_tablabel" xml:space="preserve">
-                               <source>Language</source>
-                       </trans-unit>
-                       <trans-unit id="mlang_labels_tabdescr" xml:space="preserve">
-                               <source>Select language to be available for backend and frontend. Provide update of chosen languages.</source>
-                       </trans-unit>
-                       <trans-unit id="mlang_tabs_tab" xml:space="preserve">
-                               <source>Language</source>
-                       </trans-unit>
-               </body>
-       </file>
-</xliff>
index e6a9fb9..ad2fae0 100644 (file)
@@ -4,12 +4,8 @@
        loadScriptaculous="0"
        loadExtJs="0"
        loadJQuery="true"
-       includeCssFiles="{
-               0: '{f:uri.resource(path:\'StyleSheet/LangModule.css\')}'
-       }"
-       includeJsFiles="{
-               0: 'contrib/jquery/jquery.dataTables.min.js',
-               1: '{f:uri.resource(path:\'JavaScript/LangModule.js\')}'
+       includeRequireJsModules="{
+               0: 'TYPO3/CMS/Lang/LanguageModule'
        }"
        addJsInlineLabels="{
                0: 'flashmessage.error',
                2: 'flashmessage.success',
                3: 'flashmessage.multipleErrors',
                4: 'flashmessage.updateComplete',
-               5: 'flashmessage.canceled'
+               5: 'flashmessage.canceled',
+               6: 'flashmessage.languageActivated',
+               7: 'flashmessage.languageDeactivated',
+               8: 'flashmessage.errorOccurred',
+               9: 'table.processing',
+               10: 'table.search',
+               11: 'table.loadingRecords',
+               12: 'table.zeroRecords',
+               13: 'table.emptyTable',
+               14: 'table.dateFormat'
        }"
+>
+       <div
+               class="typo3-fullDoc typo3-module-lang"
+               data-list-translations-uri="{f:uri.action(action:'getTranslations',format:'json')}"
+               data-update-language-uri="{f:uri.action(action:'updateLanguage',format:'json')}"
+               data-update-translation-uri="{f:uri.action(action:'updateTranslation',format:'json')}"
+               data-activate-language-uri="{f:uri.action(action:'activateLanguage',format:'json')}"
+               data-deactivate-language-uri="{f:uri.action(action:'deactivateLanguage',format:'json')}"
+               data-prefix="tx_lang_tools_langlanguage"
        >
-       <div class="typo3-fullDoc">
-
                <div id="typo3-docheader">
                        <div class="typo3-docheader-functions">
-                               <div class="left">
-                                       <f:be.buttons.csh />
-                               </div>
-                               <div class="right">
-                               </div>
+                               <f:be.menus.actionMenu>
+                                       <f:be.menus.actionMenuItem label="{f:translate(key: 'header.languages')}" controller="Language" action="listLanguages" />
+                                       <f:be.menus.actionMenuItem label="{f:translate(key: 'header.translations')}" controller="Language" action="listTranslations" />
+                               </f:be.menus.actionMenu>
                        </div>
                        <div class="typo3-docheader-buttons">
-                               <div class="left">
-                                       <f:render section="iconButtons" />
-                               </div>
-                               <div class="right">
-                                       <f:be.buttons.shortcut />
-                               </div>
+                               <f:render section="iconButtons" />
                        </div>
                </div>
-
                <div id="typo3-docbody">
                        <div id="typo3-inner-docbody">
                                <f:render section="content" />
+                               <f:render partial="Icons" />
                        </div>
                </div>
-
        </div>
 </f:be.container>
diff --git a/typo3/sysext/lang/Resources/Private/Partials/Icons.html b/typo3/sysext/lang/Resources/Private/Partials/Icons.html
new file mode 100644 (file)
index 0000000..9d0321f
--- /dev/null
@@ -0,0 +1,13 @@
+<div style="display:none">
+       <span class="activateIcon"><f:be.buttons.icon icon="actions-system-extension-install" title="{f:translate(key:'button.activate')}" /></span>
+       <span class="deactivateIcon"><f:be.buttons.icon icon="actions-system-extension-uninstall" title="{f:translate(key:'button.deactivate')}" /></span>
+       <span class="downloadIcon"><f:be.buttons.icon icon="actions-system-extension-download" title="{f:translate(key:'button.download')}" /></span>
+       <span class="completeIcon"><span class="t3-icon fa fa-check-circle-o" title="{f:translate(key:'button.complete')}">&nbsp;</span></span>
+       <span class="loadingIcon"><span class="t3-icon fa fa-spin fa-circle-o-notch" title="{f:translate(key:'button.loading')}">&nbsp;</span></span>
+       <div class="progressBar">
+               <div class="progress">
+                       <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>
+               </div>
+       </div>
+</div>
+
diff --git a/typo3/sysext/lang/Resources/Private/Partials/LanguageSelectionList.html b/typo3/sysext/lang/Resources/Private/Partials/LanguageSelectionList.html
deleted file mode 100644 (file)
index 520661d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{namespace lang=TYPO3\CMS\Lang\ViewHelpers}
-
-<f:form action="updateLanguageSelection" name="languageSelectionForm" object="{languageSelectionForm}">
-       <f:form.hidden name="languageSelectionForm[selectedLanguages][]" />
-       <table border="0" cellpadding="0" cellspacing="0" class="gridTable selectionList">
-               <thead>
-                       <tr>
-                               <td class="check notSortable"></td>
-                               <td class="language"><f:translate key="table.language">Language</f:translate></td>
-                               <td class="locale"><f:translate key="table.locale">Locale</f:translate></td>
-                       </tr>
-               </thead>
-               <tbody>
-                       <f:for each="{languageSelectionForm.languages}" as="language" iteration="iteration">
-                               <tr class="{f:if(condition: language.selected, then: 'selected', else: '')}">
-                                       <td class="check"><f:form.checkbox name="languageSelectionForm[selectedLanguages][{iteration.index}]" value="{language.locale}" checked="{f:if(condition: language.selected, then: 'checked', else: '')}" id="locale-{language.locale}" /></td>
-                                       <td class="language"><label for="locale-{language.locale}">{language.language}</label></td>
-                                       <td class="locale"><label for="locale-{language.locale}">{language.locale}</label></td>
-                               </tr>
-                       </f:for>
-               </tbody>
-       </table>
-</f:form>
\ No newline at end of file
diff --git a/typo3/sysext/lang/Resources/Private/Partials/TranslationUpdateList.html b/typo3/sysext/lang/Resources/Private/Partials/TranslationUpdateList.html
deleted file mode 100644 (file)
index f74baf8..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-{namespace lang=TYPO3\CMS\Lang\ViewHelpers}
-
-<table border="0" cellpadding="0" cellspacing="0" class="gridTable translationList">
-       <thead>
-               <tr>
-                       <td class="icon notSortable"></td>
-                       <td class="extension"><f:translate key="table.extension">Extension</f:translate></td>
-                       <f:for each="{languageSelectionForm.selectedLanguages}" as="selectedLanguage">
-                               <td class="language notSortable">{selectedLanguage.language}</td>
-                       </f:for>
-               </tr>
-       </thead>
-       <tbody>
-               <f:for each="{extensions}" as="extension">
-                       <tr
-                               class="translationListRow"
-                               data-updateurl="<f:uri.action action="updateTranslation" arguments="{extension:'{extension.key}', locales:'{languageSelectionForm.selectedLanguagesLocaleList}'}" />"
-                       >
-                               <td class="icon">
-                                       <f:if condition="{extension.icon}"><img class="ext-icon" src="{extension.icon}" alt="{extension.key}" /></f:if>
-                               </td>
-                               <td class="extension">{extension.key}</td>
-                               <f:for each="{languageSelectionForm.selectedLanguages}" as="selectedLanguage">
-                                       <td
-                                               class="language languageStateNone translationListCell"
-                                               data-extension="{extension.key}"
-                                               data-locale="{selectedLanguage.locale}"
-                                               data-updateurl="<f:uri.action action="updateTranslation" arguments="{extension:'{extension.key}', locales:'{selectedLanguage.locale}'}" />"
-                                       >
-                                               <span class="stateIcon stateIconNone"><f:translate key="state.notChecked"/></span>
-                                       </td>
-                               </f:for>
-                       </tr>
-               </f:for>
-       </tbody>
-</table>
\ No newline at end of file
diff --git a/typo3/sysext/lang/Resources/Private/Templates/Language/Index.html b/typo3/sysext/lang/Resources/Private/Templates/Language/Index.html
deleted file mode 100644 (file)
index f332826..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-{namespace lang=TYPO3\CMS\Lang\ViewHelpers}
-
-<f:layout name="Default"/>
-
-<f:section name="iconButtons">
-       <f:if condition="{hasSelectedLanguages}">
-               <span class="menuItem updateItem"><lang:be.spriteIcon icon="actions-system-extension-download" title="{f:translate(key:'button.update')}" /><f:translate key="button.update"/></span>
-               &nbsp;
-               <span class="menuItem cancelItem"><lang:be.spriteIcon icon="status-status-permission-denied" title="{f:translate(key:'button.cancel')}" /><f:translate key="button.cancel"/></span>
-       </f:if>
-</f:section>
-
-
-<f:section name="content">
-       <h1><f:translate key="header.module" /></h1>
-
-       <f:flashMessages renderMode="div" />
-       <table border="0" cellpadding="0" cellspacing="0">
-               <tr>
-                       <td class="languageSelectionListContainer">
-                               <h3><f:translate key="header.languageSelectionList" /></h3>
-                               <f:render partial="LanguageSelectionList" arguments="{_all}" />
-                       </td>
-                       <td class="translationUpdateListContainer">
-                               <f:if condition="{hasSelectedLanguages}">
-                                       <h3><f:translate key="header.translationUpdateList" /></h3>
-                                       <f:render partial="TranslationUpdateList" arguments="{_all}" />
-                               </f:if>
-                       </td>
-               </tr>
-       </table>
-
-       <div style="display: none;">
-               <span class="stateIcon" id="stateIconError">
-                       <lang:be.spriteIcon icon="status-status-permission-denied" class="stateIcon" /><f:translate key="state.error"/>
-               </span>
-               <span class="stateIcon" id="stateIcon0">
-                       <lang:be.spriteIcon icon="actions-document-info" class="stateIcon" /><f:translate key="state.notAvailable"/>
-               </span>
-               <span class="stateIcon" id="stateIcon1">
-                       <lang:be.spriteIcon icon="actions-system-extension-update" class="stateIcon" /><f:translate key="state.updateAvailable"/>
-               </span>
-               <span class="stateIcon" id="stateIcon2">
-                       <lang:be.spriteIcon icon="status-status-permission-denied" class="stateIcon" /><f:translate key="state.failed"/>
-               </span>
-               <span class="stateIcon" id="stateIcon3">
-                       <lang:be.spriteIcon icon="status-status-checked" class="stateIcon" /><f:translate key="state.upToDate"/>
-               </span>
-               <span class="stateIcon" id="stateIcon4">
-                       <lang:be.spriteIcon icon="status-status-permission-denied" class="stateIcon" /><f:translate key="state.invalid"/>
-               </span>
-               <span class="stateIcon" id="stateIcon5">
-                       <lang:be.spriteIcon icon="status-status-checked" class="stateIcon" /><f:translate key="state.updated"/>
-               </span>
-               <span class="stateIcon" id="stateIconChecking">
-                       <span class="stateIcon stateIconChecking"><f:translate key="state.checking"/></span>
-               </span>
-               <span class="stateIcon" id="stateIconNone">
-                       <span class="stateIcon stateIconNone"><f:translate key="state.notChecked"/></span>
-               </span>
-       </div>
-</f:section>
diff --git a/typo3/sysext/lang/Resources/Private/Templates/Language/ListLanguages.html b/typo3/sysext/lang/Resources/Private/Templates/Language/ListLanguages.html
new file mode 100644 (file)
index 0000000..d53e1c6
--- /dev/null
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
+      xmlns:f="http://xsd.helmut-hummel.de/ns/TYPO3/CMS/Fluid/ViewHelpers">
+<f:layout name="Default"/>
+
+<f:section name="iconButtons">
+       <div class="menuItems">
+               <span class="menuItem updateItem" data-action="updateActiveLanguages">
+                       <f:be.buttons.icon icon="actions-system-extension-download" title="{f:translate(key:'button.downloadAll')}" />
+               </span>
+               <span class="menuItem loadingItem">
+                       <span title="" class="t3-icon fa fa-spin fa-circle-o-notch">&nbsp;</span>
+               </span>
+               <span class="menuItem cancelItem" data-action="cancelLanguageUpdate">
+                       &nbsp;<f:be.buttons.icon icon="actions-document-close" title="{f:translate(key:'button.cancel')}" />
+               </span>
+       </div>
+</f:section>
+
+<f:section name="content">
+       <h1><f:translate key="header.languages" /></h1>
+       <f:flashMessages renderMode="div" />
+       <table id="typo3-language-list" class="t3-table t3-datatable">
+               <thead>
+                       <tr>
+                               <th title="{f:translate(key:'table.ad.title')}"><f:translate key="table.ad" /></th>
+                               <th><f:translate key="table.language" /></th>
+                               <th><f:translate key="table.locale" /></th>
+                               <th><f:translate key="table.date" /></th>
+                               <th><f:translate key="table.actions" /></th>
+                       </tr>
+               </thead>
+               <tbody>
+                       <f:for each="{languages}" as="language" iteration="iterator">
+                               <tr id="language-{language.locale}" data-locale="{language.locale}" class="{f:if(condition: language.selected, then: 'enabled', else: 'disabled')} {f:if(condition: iterator.isEven, then: 'even', else: 'odd')}" role="row">
+                                       <td>
+                                               <a class="deactivateLanguageLink" data-action="deactivateLanguage" data-locale="{language.locale}" data-language="{language.label}" data-selected="{f:if(condition: language.selected, then: 'true', else: 'false')}"><span title="Deactivate" class="t3-icon fa fa-minus-square"> </span></a>
+                                               <a class="activateLanguageLink" data-action="activateLanguage" data-locale="{language.locale}" data-language="{language.label}" data-selected="{f:if(condition: language.selected, then: 'true', else: 'false')}"><span title="Activate" class="t3-icon fa fa-plus-circle"> </span></a>
+                                       </td>
+                                       <td class="sorting_1">{language.label}</td>
+                                       <td>{language.locale}</td>
+                                       <td class="lastUpdate">
+                                               <f:format.date date="{language.lastUpdate}" format="d.M.Y" />
+                                       </td>
+                                       <td class="actions">
+                                               <a class="updateLanguageLink" data-action="updateLanguage" data-locale="{language.locale}" data-language="{language.label}" data-selected="true"><span title="Download" class="t3-icon fa fa-download"> </span></a>
+                                               <div class="progressBar">
+                                                       <div class="progress">
+                                                               <div class="progress-text"></div>
+                                                               <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"></div>
+                                                       </div>
+                                               </div>
+                                       </td>
+                               </tr>
+                       </f:for>
+               </tbody>
+       </table>
+</f:section>
+
+</html>
\ No newline at end of file
diff --git a/typo3/sysext/lang/Resources/Private/Templates/Language/ListTranslations.html b/typo3/sysext/lang/Resources/Private/Templates/Language/ListTranslations.html
new file mode 100644 (file)
index 0000000..675665a
--- /dev/null
@@ -0,0 +1,24 @@
+<f:layout name="Default"/>
+
+<f:section name="iconButtons">
+
+</f:section>
+
+<f:section name="content">
+       <h1><f:translate key="header.translations" /></h1>
+       <f:flashMessages renderMode="div" />
+       <table id="typo3-translation-list" class="t3-table t3-datatable" data-language-count="{f:count(subject: languages)}">
+               <thead>
+                       <tr>
+                               <th><!-- Icon --></th>
+                               <th><f:translate key="table.extension" /></th>
+                               <f:for each="{languages}" as="language">
+                                       <th title="{language.label}">{language.locale}</th>
+                               </f:for>
+                       </tr>
+               </thead>
+               <tbody>
+                       <!-- Will be filled by JS -->
+               </tbody>
+       </table>
+</f:section>
\ No newline at end of file
diff --git a/typo3/sysext/lang/Resources/Public/Icons/module-lang.png b/typo3/sysext/lang/Resources/Public/Icons/module-lang.png
deleted file mode 100644 (file)
index dfac65e..0000000
Binary files a/typo3/sysext/lang/Resources/Public/Icons/module-lang.png and /dev/null differ
diff --git a/typo3/sysext/lang/Resources/Public/Images/module-lang.png b/typo3/sysext/lang/Resources/Public/Images/module-lang.png
new file mode 100644 (file)
index 0000000..dfac65e
Binary files /dev/null and b/typo3/sysext/lang/Resources/Public/Images/module-lang.png differ
diff --git a/typo3/sysext/lang/Resources/Public/JavaScript/LangModule.js b/typo3/sysext/lang/Resources/Public/JavaScript/LangModule.js
deleted file mode 100644 (file)
index 21d94cf..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-var languageModule = {
-
-       /**
-        * @var array
-        */
-       elementStack: [],
-
-       /**
-        * @var integer
-        */
-       errorCount: 0,
-
-       /**
-        * @var object
-        */
-       states: {
-               notAvailable: 0,
-               available: 1,
-               failed: 2,
-               ok: 3,
-               invalid: 4,
-               updated: 5
-       },
-
-       /**
-        * @var object
-        */
-       tableSetup: {
-               'pageLength': 1000,
-               'paging': false,
-               'info': false,
-               'searching': false,
-               'scrollY': '96px',
-               'columnDefs': [{
-                       'searchable': false,
-                       'orderable': false,
-                       'targets': ['notSortable']
-               }]
-       },
-
-       /**
-        * @var jqXHR
-        */
-       currentRequest: null,
-
-       /**
-        * @var array
-        */
-       tables: new Array,
-
-       /**
-        * Initialize the language module
-        *
-        * @return void
-        */
-       initialize: function() {
-                       // Initialize dataTable for selection and translation list
-               languageModule.tableSetup.sScrollY = languageModule.calculateTableHeight();
-               jQuery('.selectionList, .translationList').DataTable(languageModule.tableSetup);
-
-                       // Prevent "jumping" style of the tables while generating
-               jQuery('.languageSelectionListContainer').css('visibility', 'visible');
-               jQuery('.translationUpdateListContainer').css('visibility', 'visible');
-
-                       // Enable event handlers
-               languageModule.toggleEventHandlers('on');
-
-                       // Resize tables on window resize
-               jQuery(window).resize(function() {
-                       jQuery('div.dataTables_scrollBody').css('height', languageModule.calculateTableHeight());
-               });
-       },
-
-       /**
-        * Execute AJAX call for given cell of the translation matrix
-        *
-        * @param mixed cell The cell to process
-        * @return void
-        */
-       processCell: function(cell) {
-                       // Initialize
-               var $cell = jQuery(cell);
-               languageModule.toggleEventHandlers('off');
-               languageModule.errorCount = 0;
-               $cell.html(jQuery('#stateIconChecking').html());
-
-                       // Process AJAX call
-               languageModule.executeAjaxCall($cell.data('updateurl'), function(response, error) {
-                       var state = 'Error';
-                       if (error === undefined || error === null) {
-                               var locale = $cell.data('locale');
-                               if (!response.locales[locale].error) {
-                                       state = parseInt(response.locales[locale].state);
-                               }
-                       } else if (error === 'abort') {
-                               languageModule.displayInformation('flashmessage.canceled');
-                               languageModule.toggleEventHandlers('on');
-                               return;
-                       }
-                       languageModule.updateCellState($cell, state);
-                       languageModule.displaySuccess('flashmessage.updateComplete');
-                       languageModule.toggleEventHandlers('on');
-               });
-       },
-
-       /**
-        * Execute AJAX calls for given rows of the translation matrix
-        *
-        * @param mixed rows Rows to process
-        * @return void
-        */
-       processRows: function(rows) {
-                       // Initialize processing within first run
-               if (rows) {
-                       languageModule.addElementsToStack(rows);
-                       languageModule.toggleEventHandlers('off');
-                       languageModule.errorCount = 0;
-               }
-
-                       // Stop processing if stack is empty
-               if (languageModule.isStackEmpty()) {
-                       languageModule.displaySuccess('flashmessage.updateComplete');
-                       languageModule.toggleEventHandlers('on');
-                       return;
-               }
-
-                       // Find row to process
-               var $row = languageModule.getElementFromStack();
-               if ($row === null) {
-                       languageModule.toggleEventHandlers('on');
-                       return;
-               }
-
-                       // Find all cells in row
-               var $cells = $row.find('.translationListCell');
-               $cells.html(jQuery('#stateIconChecking').html());
-
-                       // Process AJAX call
-               languageModule.executeAjaxCall($row.data('updateurl'), function(response, error) {
-                       if (error !== undefined && error !== null) {
-                               $cells.html(jQuery('#stateIconError').html());
-                               if (error === 'abort') {
-                                       languageModule.displayInformation('flashmessage.canceled');
-                                       languageModule.toggleEventHandlers('on');
-                                       return;
-                               }
-                       } else {
-                               $cells.each(function(index, element) {
-                                       var $cell = jQuery(this);
-                                       var state = 'Error';
-                                       if (error === undefined || error === null) {
-                                               var locale = $cell.data('locale');
-                                               if (!response.locales[locale].error) {
-                                                       state = parseInt(response.locales[locale].state);
-                                               }
-                                       }
-                                       languageModule.updateCellState($cell, state);
-                               });
-                       }
-                       languageModule.processRows();
-               });
-       },
-
-       /**
-        * Update the state of a cell of the translation matrix
-        *
-        * @param mixed cell The cell to process
-        * @param string state Switch to this state
-        * @return void
-        */
-       updateCellState: function(cell, state) {
-               var $icon = jQuery('#stateIcon' + state);
-               if ($icon === undefined) {
-                       $icon = jQuery('#stateIconError');
-               }
-               jQuery(cell)
-                       .removeClass('languageStateNone')
-                       .addClass('languageState' + state)
-                       .html($icon.html());
-       },
-
-       /**
-        * Execute AJAX call
-        *
-        * @param string url The url to call
-        * @param string callback Callback function
-        * @param boolean ignoreErrors Ignore errors
-        * @return void
-        */
-       executeAjaxCall: function(url, callback, ignoreErrors) {
-               if (url === undefined || callback === undefined) {
-                       return;
-               }
-               languageModule.currentRequest = jQuery.ajax({
-                       url: url,
-                       dataType: 'json',
-                       cache: false,
-                       success: function(response) {
-                               callback(response);
-                       },
-                       error: function(response, status, error) {
-                               if (ignoreErrors !== false && error !== 'abort') {
-                                       languageModule.errorCount++;
-                                       if (languageModule.errorCount >= 3) {
-                                               languageModule.displayError('flashmessage.multipleErrors');
-                                               languageModule.clearElementStack();
-                                               return;
-                                       } else {
-                                               languageModule.displayError(error);
-                                       }
-                               }
-                               callback(response, error);
-                       }
-               });
-       },
-
-       /**
-        * Bind / unbind event handlers
-        *
-        * @param string action The value "on" or "off"
-        * @return void
-        */
-       toggleEventHandlers: function(action) {
-               var className = 'waiting';
-               var fadeSpeed = 150;
-               if (action === 'on') {
-                       jQuery('.updateItem').on('click', languageModule.updateTranslations).removeClass(className);
-                       jQuery('.cancelItem').off().fadeOut(fadeSpeed);
-                       jQuery('.selectionList input, .selectionList label').off().parent().removeClass(className);
-                       jQuery('.selectionList input:checkbox').on('change', languageModule.submitSelectionForm);
-                       jQuery('.selectionList tr, .selectionList td').removeClass(className);
-                       jQuery('.translationList tr, .translationList td').removeClass(className);
-                       jQuery('.languageStateNone').on('click', function() {
-                               languageModule.updateSingleTranslation(this);
-                       });
-               } else {
-                       jQuery('.updateItem').off().addClass(className);
-                       jQuery('.cancelItem').on('click', languageModule.cancelProcess).fadeIn(fadeSpeed);
-                       jQuery('.selectionList input:checkbox').off();
-                       jQuery('.selectionList input, .selectionList label').on('click', function(event) {
-                               event.preventDefault();
-                       }).parent().addClass(className);
-                       jQuery('.selectionList tr, .selectionList td').addClass(className);
-                       jQuery('.translationList tr, .translationList td').addClass(className);
-                       jQuery('.languageStateNone').off();
-               }
-       },
-
-       /**
-        * Display error flash message
-        *
-        * @param string label The label to show
-        * @return void
-        */
-       displayError: function(label) {
-               if (typeof label !== 'string' || label === '') {
-                       return;
-               }
-               top.TYPO3.Flashmessage.display(
-                       top.TYPO3.Severity.error,
-                       TYPO3.l10n.localize('flashmessage.error'),
-                       TYPO3.l10n.localize(label),
-                       5
-               );
-       },
-
-       /**
-        * Display information flash message
-        *
-        * @param string label The label to show
-        * @return void
-        */
-       displayInformation: function(label) {
-               if (typeof label !== 'string' || label === '') {
-                       return;
-               }
-               top.TYPO3.Flashmessage.display(
-                       top.TYPO3.Severity.info,
-                       TYPO3.l10n.localize('flashmessage.information'),
-                       TYPO3.l10n.localize(label),
-                       3
-               );
-       },
-
-       /**
-        * Display success flash message
-        *
-        * @param string label The label to show
-        * @return void
-        */
-       displaySuccess: function(label) {
-               if (typeof label !== 'string' || label === '') {
-                       return;
-               }
-               top.TYPO3.Flashmessage.display(
-                       top.TYPO3.Severity.ok,
-                       TYPO3.l10n.localize('flashmessage.success'),
-                       TYPO3.l10n.localize(label),
-                       3
-               );
-       },
-
-       /**
-        * Calculate the height of data tables
-        *
-        * @return void
-        */
-       calculateTableHeight: function() {
-               var documentHeight = parseInt(jQuery(document).height());
-               var tableTop = parseInt(jQuery('.selectionList').offset().top);
-               var result = documentHeight - tableTop - 50;
-               return (result > 96 ? result : 96);
-       },
-
-       /**
-        * Submit language selection form
-        *
-        * @return void
-        */
-       submitSelectionForm: function() {
-               jQuery('form[name="languageSelectionForm"]').submit();
-       },
-
-       /**
-        * Update translations
-        *
-        * @return void
-        */
-       updateTranslations: function() {
-               languageModule.processRows('.translationListRow');
-       },
-
-       /**
-        * Update translation for a single element
-        *
-        * @param mixed element The element to process
-        * @return void
-        */
-       updateSingleTranslation: function(element) {
-               languageModule.processCell(element);
-       },
-
-       /**
-        * Cancel current process
-        *
-        * @return void
-        */
-       cancelProcess: function() {
-               languageModule.clearElementStack();
-               if (languageModule.currentRequest) {
-                       languageModule.currentRequest.abort();
-               }
-       },
-
-       /**
-        * Fill call stack
-        *
-        * @param string elements Element identificator
-        * @return void
-        */
-       addElementsToStack: function(elements) {
-               jQuery(elements).each(function(i, element) {
-                       languageModule.elementStack.push(element);
-               });
-       },
-
-       /**
-        * Get and remove first element from stack
-        *
-        * @return object The element
-        */
-       getElementFromStack: function() {
-               var element = languageModule.elementStack.shift();
-               if (element !== undefined) {
-                       return jQuery(element);
-               }
-               return null;
-       },
-
-       /**
-        * Clear element stack
-        *
-        * @return void
-        */
-       clearElementStack: function() {
-               languageModule.elementStack = [];
-       },
-
-       /**
-        * Check if stack contains elements
-        *
-        * @return boolean False if empty
-        */
-       isStackEmpty: function() {
-               return languageModule.elementStack.length ? false : true;
-       }
-
-}
-
-
-/**
- * Initialize when DOM is ready
- */
-jQuery(document).ready(function($) {
-       languageModule.initialize();
-});
diff --git a/typo3/sysext/lang/Resources/Public/JavaScript/LanguageModule.js b/typo3/sysext/lang/Resources/Public/JavaScript/LanguageModule.js
new file mode 100644 (file)
index 0000000..3fe8238
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Language module class
+ *
+ * @author Kai Vogel <k.vogel@reply.de>
+ */
+define('TYPO3/CMS/Lang/LanguageModule', ['jquery', 'datatables', 'jquery/jquery.clearable'], function($) {
+       var LanguageModule = {
+               me: this,
+               context: null,
+               table: null,
+               topMenu: null,
+               currentRequest: null,
+               settings: {},
+               icons: {},
+               labels: {},
+               identifiers: {
+                       searchField: 'div.dataTables_filter input',
+                       topMenu: 'div.menuItems',
+                       activateIcon: 'span.activateIcon',
+                       deactivateIcon: 'span.deactivateIcon',
+                       downloadIcon: 'span.downloadIcon',
+                       loadingIcon: 'span.loadingIcon',
+                       completeIcon: 'span.completeIcon',
+                       progressBar: 'div.progressBar',
+                       progressBarText: 'div.progress-text',
+                       progressBarInner: 'div.progress-bar',
+                       lastUpdate: 'td.lastUpdate',
+                       languagePrefix: 'language-',
+                       extensionPrefix: 'extension-'
+               },
+               classes: {
+                       enabled: 'enabled',
+                       disabled: 'disabled',
+                       processing: 'processing',
+                       complete: 'complete',
+                       extension: 'extensionName',
+                       actions: 'actions',
+                       progressBar: 'progressBar',
+                       loading: 'loading',
+                       lastUpdate: 'lastUpdate'
+               }
+       };
+
+       /**
+        * Initialize language table
+        */
+       LanguageModule.initializeLanguageTable = function(contextElement, tableElement) {
+               LanguageModule.context = $(contextElement);
+               LanguageModule.topMenu = $(LanguageModule.identifiers.topMenu);
+               LanguageModule.settings = LanguageModule.context.data();
+               LanguageModule.icons = LanguageModule.buildIcons();
+               LanguageModule.labels = LanguageModule.buildLabels();
+               LanguageModule.table = LanguageModule.buildLanguageTable(tableElement);
+               LanguageModule.initializeSearchField();
+               LanguageModule.initializeEventHandler();
+       }
+
+       /**
+        * Initialize translation table
+        */
+       LanguageModule.initializeTranslationTable = function(contextElement, tableElement) {
+               LanguageModule.context = $(contextElement);
+               LanguageModule.topMenu = $(LanguageModule.identifiers.topMenu);
+               LanguageModule.settings = LanguageModule.context.data();
+               LanguageModule.icons = LanguageModule.buildIcons();
+               LanguageModule.labels = LanguageModule.buildLabels();
+               LanguageModule.table = LanguageModule.buildTranslationTable(tableElement);
+               LanguageModule.initializeSearchField();
+               LanguageModule.initializeEventHandler();
+       };
+
+       /**
+        * Activate a language
+        */
+       LanguageModule.activateLanguageAction = function(triggerElement, parameters) {
+               var $row = $(triggerElement).closest('tr'),
+                       locale = $row.data('locale');
+
+               if ($row.hasClass(LanguageModule.classes.processing)) {
+                       LanguageModule.abortAjaxRequest();
+               }
+               LanguageModule.executeAjaxRequest(LanguageModule.settings.activateLanguageUri, {locale: locale}, function(response, status) {
+                       if (status === 'success' && response.success) {
+                               $row.removeClass(LanguageModule.classes.disabled).addClass(LanguageModule.classes.enabled);
+                               LanguageModule.displaySuccess(LanguageModule.labels.languageActivated);
+                       } else {
+                               LanguageModule.displayError(LanguageModule.labels.errorOccurred);
+                       }
+               });
+       };
+
+       /**
+        * Deactivate a language
+        */
+       LanguageModule.deactivateLanguageAction = function(triggerElement, parameters) {
+               var $row = $(triggerElement).closest('tr'),
+                       locale = $row.data('locale');
+
+               if ($row.hasClass(LanguageModule.classes.processing)) {
+                       LanguageModule.abortAjaxRequest();
+               }
+               LanguageModule.executeAjaxRequest(LanguageModule.settings.deactivateLanguageUri, {locale: locale}, function(response, status) {
+                       if (status === 'success' && response.success) {
+                               $row.removeClass(LanguageModule.classes.enabled).removeClass(LanguageModule.classes.complete).addClass(LanguageModule.classes.disabled);
+                               LanguageModule.displaySuccess(LanguageModule.labels.languageDeactivated);
+                       } else {
+                               LanguageModule.displayError(LanguageModule.labels.errorOccurred);
+                       }
+               });
+       };
+
+       /**
+        * Update a language
+        */
+       LanguageModule.updateLanguageAction = function(triggerElement, parameters) {
+               var $row = $(triggerElement).closest('tr'),
+                       locale = $row.data('locale'),
+                       $progressBar = $(LanguageModule.identifiers.progressBar, $row),
+                       $lastUpdate = $(LanguageModule.identifiers.lastUpdate, $row);
+
+               $row.addClass(LanguageModule.classes.processing);
+               LanguageModule.loadTranslationsByLocale(locale, function(status, data, response) {
+                       if (status === 'success') {
+                               LanguageModule.setProgress($progressBar, 100);
+                               LanguageModule.displaySuccess(LanguageModule.labels.updateComplete);
+                               $row.removeClass(LanguageModule.classes.processing).addClass(LanguageModule.classes.complete);
+                               $lastUpdate.html(LanguageModule.formatDate(response.timestamp));
+                       } else if (status === 'progress') {
+                               LanguageModule.setProgress($progressBar, parseFloat(response.progress));
+                       } else if (status === 'error') {
+                               LanguageModule.displayError(LanguageModule.labels.errorOccurred);
+                       }
+               });
+       };
+
+       /**
+        * Update all active languages
+        */
+       LanguageModule.updateActiveLanguagesAction = function(triggerElement, parameters) {
+               var $activeRows = $('tr.' + LanguageModule.classes.enabled, LanguageModule.table.table().container());
+
+               LanguageModule.topMenu.addClass(LanguageModule.classes.processing);
+               $activeRows.addClass(LanguageModule.classes.processing);
+               LanguageModule.loadTranslationsByRows($activeRows, function(row, status, data, response) {
+                       var $progressBar = $(LanguageModule.identifiers.progressBar, row),
+                               $lastUpdate = $(LanguageModule.identifiers.lastUpdate, row);
+
+                       if (status === 'success') {
+                               LanguageModule.setProgress($progressBar, 100);
+                               row.removeClass(LanguageModule.classes.processing).addClass(LanguageModule.classes.complete);
+                               $lastUpdate.html(LanguageModule.formatDate(response.timestamp));
+                       } else if (status === 'progress') {
+                               LanguageModule.setProgress($progressBar, parseFloat(response.progress));
+                       } else if (status === 'error') {
+                               LanguageModule.displayError(LanguageModule.labels.errorOccurred);
+                       } else if (status === 'finished') {
+                               LanguageModule.displaySuccess(LanguageModule.labels.updateComplete);
+                               LanguageModule.topMenu.removeClass(LanguageModule.classes.processing);
+                       }
+               });
+       };
+
+       /**
+        * Cancel language update
+        */
+       LanguageModule.cancelLanguageUpdateAction = function(triggerElement, parameters) {
+               var $activeRows = $('tr.' + LanguageModule.classes.enabled, LanguageModule.table.table().container());
+
+               LanguageModule.topMenu.removeClass(LanguageModule.classes.processing);
+               $activeRows.removeClass(LanguageModule.classes.processing);
+               LanguageModule.abortAjaxRequest();
+       };
+
+       /**
+        * Update an extension translation
+        */
+       LanguageModule.updateTranslationAction = function(triggerElement, parameters) {
+               var $row = $(triggerElement).closest('tr'),
+                       $cell = $(triggerElement).closest('td'),
+                       extension = $row.data('extension'),
+                       locale = LanguageModule.table.cell($cell).data().locale;
+
+               $cell.addClass(LanguageModule.classes.processing);
+               LanguageModule.loadTranslationByExtensionAndLocale(extension, locale, function(status, data, response) {
+                       if (status === 'success') {
+                               LanguageModule.displaySuccess(LanguageModule.labels.updateComplete);
+                               $cell.removeClass(LanguageModule.classes.processing).addClass(LanguageModule.classes.complete);
+                       } else if (status === 'error') {
+                               LanguageModule.displayError(LanguageModule.labels.errorOccurred);
+                       }
+               });
+       };
+
+       /**
+        * Build icons
+        */
+       LanguageModule.buildIcons = function() {
+               return {
+                       activate: $(LanguageModule.identifiers.activateIcon, LanguageModule.context).html(),
+                       deactivate: $(LanguageModule.identifiers.deactivateIcon, LanguageModule.context).html(),
+                       download: $(LanguageModule.identifiers.downloadIcon, LanguageModule.context).html(),
+                       loading: $(LanguageModule.identifiers.loadingIcon, LanguageModule.context).html(),
+                       complete: $(LanguageModule.identifiers.completeIcon, LanguageModule.context).html(),
+                       progressBar: $(LanguageModule.identifiers.progressBar, LanguageModule.context).html()
+               }
+       };
+
+       /**
+        * Build labels
+        */
+       LanguageModule.buildLabels = function() {
+               return {
+                       processing: TYPO3.lang['table.processing'],
+                       search: TYPO3.lang['table.search'],
+                       loadingRecords: TYPO3.lang['table.loadingRecords'],
+                       zeroRecords: TYPO3.lang['table.zeroRecords'],
+                       emptyTable: TYPO3.lang['table.emptyTable'],
+                       dateFormat: TYPO3.lang['table.dateFormat'],
+                       errorHeader: TYPO3.lang['flashmessage.error'],
+                       infoHeader: TYPO3.lang['flashmessage.information'],
+                       successHeader: TYPO3.lang['flashmessage.success'],
+                       languageActivated: TYPO3.lang['flashmessage.languageActivated'],
+                       errorOccurred: TYPO3.lang['flashmessage.errorOccurred'],
+                       languageDeactivated: TYPO3.lang['flashmessage.languageDeactivated'],
+                       updateComplete: TYPO3.lang['flashmessage.updateComplete']
+               }
+       };
+
+       /**
+        * Build language table
+        */
+       LanguageModule.buildLanguageTable = function(tableElement) {
+               return $(tableElement).DataTable({
+                       serverSide: false,
+                       stateSave: true,
+                       paging: false,
+                       info: false,
+                       ordering: true,
+                       language: LanguageModule.labels,
+                       order: [[1, 'asc']]
+               });
+       };
+
+       /**
+        * Initialize translation table
+        */
+       LanguageModule.buildTranslationTable = function(tableElement) {
+               var languageCount = $(tableElement).data('languageCount'),
+                       columns = [
+                               {
+                                       render: function(data, type, row) {
+                                               return LanguageModule.buildImage(data.icon, data.title);
+                                       },
+                                       width: '20px',
+                                       orderable: false,
+                                       targets: 0
+                               }, {
+                                       render: function(data, type, row) {
+                                               return data.title;
+                                       },
+                                       className: LanguageModule.classes.extension,
+                                       targets: 1
+                               }
+                       ];
+
+               for (var i = 0; i < languageCount; i++) {
+                       columns.push({
+                               render: function(data, type, row) {
+                                       var links = [
+                                               LanguageModule.buildActionLink('updateTranslation', data, LanguageModule.icons.download),
+                                               LanguageModule.buildLoadingIndicator(),
+                                               LanguageModule.buildCompleteIndicator()
+                                       ];
+                                       return links.join('');
+                               },
+                               className: 'dt-center',
+                               targets: (i + 2)
+                       });
+               }
+
+               return $(tableElement).DataTable({
+                       serverSide: false,
+                       stateSave: true,
+                       paging: false,
+                       info: false,
+                       ordering: true,
+                       language: LanguageModule.labels,
+                       ajax: LanguageModule.settings.listTranslationsUri,
+                       order: [[1, 'asc']],
+                       columnDefs: columns,
+                       createdRow: function (row, data, index) {
+                               var $row = $(row);
+                               $row.attr('id', LanguageModule.identifiers.extensionPrefix + data[1].key);
+                               $row.attr('data-extension', data[1].key);
+                       }
+               });
+       };
+
+       /**
+        * Initialize search field
+        */
+       LanguageModule.initializeSearchField = function() {
+               $(LanguageModule.identifiers.searchField, LanguageModule.context).clearable({
+                       onClear: function() {
+                               if (LanguageModule.table !== null) {
+                                       LanguageModule.table.search('').draw();
+                               }
+                       }
+               });
+       };
+
+       /**
+        * Initialize event handler, redirect clicks to controller actions
+        */
+       LanguageModule.initializeEventHandler = function() {
+               $(document).on('click', function(event) {
+                       var $element = $(event.target);
+
+                       if ($element.data('action') !== undefined) {
+                               LanguageModule.handleActionEvent($element, event);
+                       } else if ($element.parent().data('action') !== undefined) {
+                               LanguageModule.handleActionEvent($element.parent(), event);
+                       } else if ($element.parent().parent().data('action') !== undefined) {
+                               LanguageModule.handleActionEvent($element.parent().parent(), event);
+                       }
+               });
+       };
+
+       /**
+        * Handler for "action" events
+        */
+       LanguageModule.handleActionEvent = function(element, event) {
+               event.preventDefault();
+               var data = element.data();
+               var actionName = data.action + 'Action';
+               if (actionName in LanguageModule) {
+                       LanguageModule[actionName](element, data);
+               }
+       };
+
+       /**
+        * Load translations for all extensions by given locale
+        */
+       LanguageModule.loadTranslationsByLocale = function(locale, callback, counter) {
+               counter = counter || 0;
+               var data = {locale: locale, count: counter};
+               LanguageModule.executeAjaxRequest(LanguageModule.settings.updateLanguageUri, data, function(response, status) {
+                       if (status === 'success' && response.success) {
+                               if (parseFloat(response.progress) < 100) {
+                                       callback('progress', data, response);
+                                       counter++;
+                                       LanguageModule.loadTranslationsByLocale(locale, callback, counter);
+                               } else {
+                                       callback('success', data, response);
+                               }
+                       } else {
+                               callback('error', data, response);
+                       }
+               });
+       };
+
+       /**
+        * Load translations for all extensions by given rows
+        */
+       LanguageModule.loadTranslationsByRows = function(rows, callback) {
+               if (rows) {
+                       rows = $(rows).toArray();
+                       var $row = $(rows.shift()),
+                               locale = row.data('locale');
+
+                       LanguageModule.loadTranslationsByLocale(locale, function(status, data, response) {
+                               callback($row, status, data, response);
+                               if (status === 'success') {
+                                       if (rows.length) {
+                                               loadTranslationsByRows(rows, callback);
+                                       } else {
+                                               callback($row, 'finished', data, response);
+                                       }
+                               }
+                       });
+               }
+       };
+
+       /**
+        * Load translation for one extension by given locale
+        */
+       LanguageModule.loadTranslationByExtensionAndLocale = function(extension, locale, callback) {
+               var data = {extension: extension, locale: locale};
+               LanguageModule.executeAjaxRequest(LanguageModule.settings.updateTranslationUri, data, function(response, status) {
+                       if (status === 'success' && response.success) {
+                               callback('success', data, response);
+                       } else {
+                               callback('error', data, response);
+                       }
+               });
+       };
+
+       /**
+        * Execute AJAX request
+        */
+       LanguageModule.executeAjaxRequest = function(uri, data, callback) {
+               var newData = {};
+               newData[LanguageModule.settings.prefix] = {
+                       data: data
+               };
+               LanguageModule.currentRequest = $.ajax({
+                       type: 'POST',
+                       cache: false,
+                       url: uri,
+                       data: newData,
+                       dataType: 'json',
+                       success: function(response, status) {
+                               if (typeof callback === 'function') {
+                                       callback(response, status, '');
+                               }
+                       },
+                       error: function(response, status, error) {
+                               if (typeof callback === 'function') {
+                                       callback(response, status, error);
+                               }
+                       }
+               });
+       };
+
+       /**
+        * Abort current AJAX request
+        */
+       LanguageModule.abortAjaxRequest = function() {
+               if (LanguageModule.currentRequest) {
+                       LanguageModule.currentRequest.abort();
+               }
+       };
+
+       /**
+        * Display error flash message
+        */
+       LanguageModule.displayError = function(label) {
+               if (typeof label === 'string' && label !== '') {
+                       top.TYPO3.Flashmessage.display(top.TYPO3.Severity.error, LanguageModule.labels.errorHeader, label, 5);
+               }
+       };
+
+       /**
+        * Display information flash message
+        */
+       LanguageModule.displayInformation = function(label) {
+               if (typeof label === 'string' && label !== '') {
+                       top.TYPO3.Flashmessage.display(top.TYPO3.Severity.info, LanguageModule.labels.infoHeader, label, 3);
+               }
+       };
+
+       /**
+        * Display success flash message
+        */
+       LanguageModule.displaySuccess = function(label) {
+               if (typeof label === 'string' && label !== '') {
+                       top.TYPO3.Flashmessage.display(top.TYPO3.Severity.ok, LanguageModule.labels.successHeader, label, 3);
+               }
+       };
+
+       /**
+        * Build action link
+        */
+       LanguageModule.buildActionLink = function(action, parameters, content) {
+               var $link = $('<a>');
+
+               $link.addClass(action + 'Link');
+               $link.attr('data-action', action);
+               for (var name in parameters) {
+                       $link.attr('data-' + name, parameters[name]);
+               }
+               $link.html(content);
+               return $link.wrap('<span>').parent().html();
+       };
+
+       /**
+        * Build progress bar
+        */
+       LanguageModule.buildProgressBar = function() {
+               var $span = $('<span>');
+               $span.addClass(LanguageModule.classes.progressBar);
+               $span.html(LanguageModule.icons.progressBar);
+               return $span.wrap('<span>').parent().html();
+       };
+
+       /**
+        * Build loading indicator
+        */
+       LanguageModule.buildLoadingIndicator = function() {
+               var $span = $('<span>');
+               $span.addClass(LanguageModule.classes.loading);
+               $span.html(LanguageModule.icons.loading);
+               return $span.wrap('<span>').parent().html();
+       };
+
+       /**
+        * Build complete state indicator
+        */
+       LanguageModule.buildCompleteIndicator = function() {
+               var $span = $('<span>');
+               $span.addClass(LanguageModule.classes.complete);
+               $span.html(LanguageModule.icons.complete);
+               return $span.wrap('<span>').parent().html();
+       };
+
+       /**
+        * Build image
+        */
+       LanguageModule.buildImage = function(uri, alt, tilte) {
+               var $image = $('<img>');
+               $image.attr('src', uri);
+               $image.attr('alt', alt ? alt : '');
+               $image.attr('tilte', tilte ? tilte : '');
+               return $image.wrap('<span>').parent().html();
+       };
+
+       /**
+        * Format date
+        */
+       LanguageModule.formatDate = function(timestamp) {
+               var date = new Date(timestamp * 1000);
+               return date.format(LanguageModule.labels.dateFormat);
+       };
+
+       /**
+        * Set progress bar progress
+        */
+       LanguageModule.setProgress = function(progressBar, progress) {
+               var $inner = $(LanguageModule.identifiers.progressBarInner, progressBar),
+                       $text = $(LanguageModule.identifiers.progressBarText, progressBar);
+               $inner.css({width: progress + '%'});
+               $inner.attr('aria-valuenow', progress);
+               $text.text(Math.round(progress) + '%');
+       };
+
+       return function() {
+               $(document).ready(function() {
+                       if ($('div.typo3-module-lang #typo3-language-list').length) {
+                               LanguageModule.initializeLanguageTable('div.typo3-module-lang', '#typo3-language-list');
+                       } else if ($('div.typo3-module-lang #typo3-translation-list').length) {
+                               LanguageModule.initializeTranslationTable('div.typo3-module-lang', '#typo3-translation-list');
+                       }
+               });
+
+               TYPO3.LanguageModule = LanguageModule;
+               return LanguageModule;
+       }();
+});
\ No newline at end of file
diff --git a/typo3/sysext/lang/Resources/Public/StyleSheet/LangModule.css b/typo3/sysext/lang/Resources/Public/StyleSheet/LangModule.css
deleted file mode 100644 (file)
index 6f7183b..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/**
- * Style definitions for the whole module
- */
-.waiting {
-       color: #888;
-}
-
-span.menuItem {
-       cursor: pointer;
-}
-
-span.cancelItem {
-       display: none;
-}
-
-span.waiting {
-       cursor: wait !important;
-}
-
-span.stateIcon {
-       display: inline-block;
-}
-
-span.stateIcon span {
-       margin-right: 6px;
-}
-
-/* IE7 hack to prevent fixed icons in list */
-*:first-child+html span.t3-icon {
-       position: static !important;
-}
-
-span.stateIconChecking {
-       background: url(../../../../t3skin/images/spinner/ffffff.gif) top left no-repeat;
-       height: 16px;
-       width: 16px;
-       padding-left: 20px;
-}
-
-span.stateIconNone {
-       color: #888;
-}
-
-
-/**
- * Style definitions for dataTables
- */
-div.dataTables_wrapper {
-       position: relative;
-       clear: both;
-}
-
-div.dataTables_wrapper div.dataTables_scroll {
-       margin-bottom: 10px;
-       background-color: #ffffff;
-       border: 1px solid #aaa;
-       overflow: hidden;
-       clear: both;
-}
-
-div.dataTables_wrapper div.dataTables_scrollHead {
-       background-color: #666;
-       color: white;
-       font-weight: bold;
-}
-
-div.dataTables_wrapper div.dataTables_scrollBody {
-       -webkit-overflow-scrolling: touch;
-}
-
-div.dataTables_wrapper table.translationList {
-       border: 0;
-       margin: 0;
-       background: transparent:
-}
-
-div.dataTables_wrapper tr.even {
-       background: #f7f7f7;
-}
-
-div.dataTables_wrapper tr.selected {
-       background: #d8ecd0;
-}
-
-div.dataTables_wrapper thead td {
-       cursor: pointer;
-}
-
-div.dataTables_wrapper thead td.sorting_asc {
-       background: url(../../../../t3skin/images/icons/status/status-sorting-asc.png) top right no-repeat;
-}
-
-div.dataTables_wrapper thead td.sorting_desc {
-       background: url(../../../../t3skin/images/icons/status/status-sorting-desc.png) bottom right no-repeat;
-}
-
-div.dataTables_wrapper thead td.notSortable {
-       background: none;
-       cursor: default;
-}
-
-div.dataTables_wrapper tbody tr:hover {
-       background-color: #dedede;
-}
-
-div.dataTables_wrapper td {
-       vertical-align: middle;
-       white-space: nowrap;
-       padding: 0 12px 0 0;
-       cursor: default;
-}
-
-div.dataTables_wrapper td.center {
-       text-align: center;
-}
-
-div.dataTables_wrapper td img {
-       height: 16px;
-       width: 16px;
-       margin: 0 0 0 6px;
-}
-
-div.dataTables_wrapper td input {
-       margin: 0 0 0 6px;
-}
-
-div.dataTables_wrapper td input,
-div.dataTables_wrapper td label {
-       cursor: pointer;
-}
-
-div.dataTables_wrapper td.waiting,
-div.dataTables_wrapper td.waiting input,
-div.dataTables_wrapper td.waiting label {
-       cursor: wait !important;
-}
-
-div.dataTables_wrapper td label {
-       display: block;
-       width: 100%;
-}
-
-div.dataTables_wrapper tbody td {
-       height: 24px;
-}
-
-
-/**
- * Style definitions for the language selection field
- */
-td.languageSelectionListContainer {
-       width: 250px;
-       padding-right: 12px;
-       visibility: hidden;
-}
-
-td.languageSelectionListContainer td.language {
-       padding: 0;
-}
-
-td.languageSelectionListContainer td.locale {
-       width: 50px;
-}
-
-
-/**
- * Style definitions for the translation overview field
- */
-td.translationUpdateListContainer {
-       visibility: hidden;
-}
-
-td.translationUpdateListContainer td.icon {
-       width: 16px;
-}
-
-td.translationUpdateListContainer td.extension {
-       min-width: 180px;
-}
-
-td.translationUpdateListContainer td.language {
-       text-align: left;
-       width: 150px !important;
-}
-
-td.translationUpdateListContainer td.languageStateNone,
-td.translationUpdateListContainer td.languageState1 {
-       cursor: pointer;
-}
\ No newline at end of file
diff --git a/typo3/sysext/lang/Tests/Unit/Domain/Model/LanguageSelectionFormTest.php b/typo3/sysext/lang/Tests/Unit/Domain/Model/LanguageSelectionFormTest.php
deleted file mode 100644 (file)
index 487d038..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-namespace TYPO3\CMS\Lang\Tests\Unit\Domain\Model;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Test case
- */
-class LanguageSelectionFormTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
-
-       /**
-        * @var \TYPO3\CMS\Lang\Domain\Model\LanguageSelectionForm
-        */
-       protected $fixture = NULL;
-
-       /**
-        * Set up
-        */
-       public function setUp() {
-               $this->fixture = new \TYPO3\CMS\Lang\Domain\Model\LanguageSelectionForm();
-       }
-
-       /**
-        * @test
-        */
-       public function getLanguagesInitiallyReturnsEmptyArray() {
-               $this->assertSame(
-                       array(),
-                       $this->fixture->getLanguages()
-               );
-       }
-
-       /**
-        * @test
-        */
-       public function setLanguagesSetsLanguage() {
-               $language = array(
-                       'nl' => '1',
-                       'de' => '0',
-               );
-               $this->fixture->setLanguages($language);
-
-               $this->assertSame(
-                       $language,
-                       $this->fixture->getLanguages()
-               );
-       }
-
-       /**
-        * @test
-        */
-       public function getSelectedLanguagesInitiallyReturnsEmptyArray() {
-               $this->assertSame(
-                       array(),
-                       $this->fixture->getSelectedLanguages()
-               );
-       }
-
-       /**
-        * @test
-        */
-       public function setSelectedLanguagesSetsSelectedLanguages() {
-               $selectedLanguages = array('', '', '', '', 'de', '', '', 'nl');
-               $cleanedSelectedLanguages = array('de', 'nl');
-               $this->fixture->setSelectedLanguages($selectedLanguages);
-
-               $this->assertSame(
-                       $cleanedSelectedLanguages,
-                       $this->fixture->getSelectedLanguages()
-               );
-       }
-
-}
index afc9c94..7e1f908 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 namespace TYPO3\CMS\Lang\Tests\Unit\Domain\Model;
 
 /*
@@ -16,7 +15,9 @@ namespace TYPO3\CMS\Lang\Tests\Unit\Domain\Model;
  */
 
 /**
- * Test case for Language
+ * Testcase for Language
+ *
+ * @author Wouter Wolters <typo3@wouterwolters.nl>
  */
 class LanguageTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
@@ -74,7 +75,7 @@ class LanguageTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        public function getLanguageInitiallyReturnsEmptyString() {
                $this->assertSame(
                        '',
-                       $this->fixture->getLanguage()
+                       $this->fixture->getLabel()
                );
        }
 
@@ -87,7 +88,7 @@ class LanguageTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
 
                $this->assertSame(
                        $language,
-                       $this->fixture->getLanguage()
+                       $this->fixture->getLabel()
                );
        }
 
@@ -96,11 +97,11 @@ class LanguageTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         */
        public function setLanguageSetsLanguage() {
                $language = 'nl';
-               $this->fixture->setLanguage($language);
+               $this->fixture->setLabel($language);
 
                $this->assertSame(
                        $language,
-                       $this->fixture->getLanguage()
+                       $this->fixture->getLabel()
                );
        }
 
diff --git a/typo3/sysext/lang/Tests/Unit/Domain/Model/UpdateTranslationFormTest.php b/typo3/sysext/lang/Tests/Unit/Domain/Model/UpdateTranslationFormTest.php
deleted file mode 100644 (file)
index 2595438..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-namespace TYPO3\CMS\Lang\Tests\Unit\Domain\Model;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Test case
- */
-class UpdateTranslationFormTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
-
-       /**
-        * @var \TYPO3\CMS\Lang\Domain\Model\UpdateTranslationForm
-        */
-       protected $fixture = NULL;
-
-       public function setUp() {
-               $this->fixture = new \TYPO3\CMS\Lang\Domain\Model\UpdateTranslationForm();
-       }
-
-       /**
-        * @test
-        */
-       public function getSelectedLanguagesInitiallyReturnsEmptyArray() {
-               $this->assertSame(
-                       array(),
-                       $this->fixture->getSelectedLanguages()
-               );
-       }
-
-       /**
-        * @test
-        */
-       public function setSelectedLanguagesSetsSelectedLanguages() {
-               $languages = array(
-                       'nl',
-                       'de',
-               );
-               $this->fixture->setSelectedLanguages($languages);
-
-               $this->assertSame(
-                       $languages,
-                       $this->fixture->getSelectedLanguages()
-               );
-       }
-
-       /**
-        * @test
-        */
-       public function getExtensionsInitiallyReturnsEmptyArray() {
-               $this->assertSame(
-                       array(),
-                       $this->fixture->getExtensions()
-               );
-       }
-
-       /**
-        * @test
-        */
-       public function setExtensionsSetsExtensions() {
-               $extensions = array(
-                       1 => 'about',
-                       2 => 'aboutmodules',
-                       3 => 'adodb',
-               );
-               $this->fixture->setExtensions($extensions);
-
-               $this->assertSame(
-                       $extensions,
-                       $this->fixture->getExtensions()
-               );
-       }
-
-}
index 9277134..be24dfd 100644 (file)
@@ -10,7 +10,7 @@
                "typo3/cms-core": "*"
        },
        "replace": {
-               "lang": "*"
+               "language": "*"
        },
        "extra": {
                "typo3/cms": {
index a955b18..9ac1559 100644 (file)
Binary files a/typo3/sysext/lang/ext_icon.gif and b/typo3/sysext/lang/ext_icon.gif differ
index 9adaca9..f71e4c7 100644 (file)
@@ -2,20 +2,21 @@
 defined('TYPO3_MODE') or die();
 
 if (TYPO3_MODE === 'BE' && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_INSTALL)) {
-               // Registers a Backend Module
+
+       // Register the backend module
        \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule(
                'TYPO3.CMS.' . $_EXTKEY,
-               'tools', // Make module a submodule of 'tools'
-               'language', // Submodule key
-               'after:extensionmanager', // Position
+               'tools',
+               'language',
+               'after:extensionmanager',
                array(
-                               // An array holding the controller-action-combinations that are accessible
-                       'Language' => 'index, updateLanguageSelection, updateTranslation'
+                       'Language' => 'listLanguages, listTranslations, getTranslations, updateLanguage, updateTranslation, activateLanguage, deactivateLanguage',
                ),
                array(
                        'access' => 'admin',
-                       'icon' => 'EXT:' . $_EXTKEY . '/Resources/Public/Icons/module-lang.png',
-                       'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod_language.xlf',
+                       'icon' => 'EXT:' . $_EXTKEY . '/Resources/Public/Images/module-lang.png',
+                       'labels' => 'LLL:EXT:' . $_EXTKEY . '/Resources/Private/Language/locallang_mod.xlf',
                )
        );
+
 }
diff --git a/typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/_module_lang.less b/typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/_module_lang.less
new file mode 100644 (file)
index 0000000..03b7258
--- /dev/null
@@ -0,0 +1,154 @@
+//
+// Language Module
+//
+div.typo3-module-lang {
+       table.t3-datatable {
+               width: 100% !important;
+
+               th {
+                       &.sorting,
+                       &.sorting_asc,
+                       &.sorting_desc {
+                               cursor: pointer;
+                       }
+               }
+
+               tr {
+                       > .extensionName {
+                               padding-left: 0 !important;
+                       }
+
+                       > .dt-center {
+                               text-align: center;
+                       }
+
+                       &.disabled {
+                               color: #aaaaaa;
+
+                               a {
+                                       &.deactivateLanguageLink,
+                                       &.updateLanguageLink,
+                                       &.updateTranslationLink {
+                                               display: none;
+                                       }
+                               }
+                       }
+
+                       &.enabled {
+                               a.activateLanguageLink {
+                                       display: none;
+                               }
+                       }
+
+                       div.progressBar {
+                               display: none;
+
+                               .progress {
+                                       position: relative;
+
+                                       .progress-text {
+                                               position: absolute;
+                                               color: #FFFFFF;
+                                               text-align: center;
+                                               width: 100%;
+                                               text-shadow: 0 0 6px #000000;
+                                       }
+                               }
+                       }
+
+                       &.processing,
+                       &.complete {
+                               div.progressBar {
+                                       display: inline;
+                                       cursor: default;
+                               }
+                       }
+               }
+
+               tr.processing,
+               td.processing,
+               tr.complete,
+               td.complete {
+                       a {
+                               &.updateLanguageLink,
+                               &.updateTranslationLink {
+                                       display: none;
+                               }
+                       }
+               }
+
+               td {
+                       span {
+                               &.loading,
+                               &.complete {
+                                       display: none;
+                               }
+                       }
+
+                       &.actions {
+                               span {
+                                       cursor: pointer;
+                               }
+                       }
+
+                       &.processing {
+                               span.loading {
+                                       display: inline;
+                                       cursor: default;
+
+                                       .t3-icon {
+                                               cursor: default;
+                                               width: 14px;
+                                               height: 14px;
+                                               margin: 0;
+                                       }
+                               }
+                       }
+
+                       &.complete {
+                               span.complete {
+                                       display: inline;
+                               }
+                       }
+               }
+
+               div.progress {
+                       margin-bottom: 0;
+               }
+       }
+
+       div.menuItems {
+               &.processing {
+                       span {
+                               &.updateItem {
+                                       display: none;
+                               }
+
+                               &.loadingItem,
+                               &.cancelItem {
+                                       display: inline;
+                               }
+                       }
+               }
+
+               span {
+                       &.loadingItem,
+                       &.cancelItem {
+                               display: none;
+                       }
+
+                       &.updateItem,
+                       &.cancelItem {
+                               cursor: pointer;
+                       }
+               }
+       }
+
+       .div.dataTables_filter input {
+               margin-left: 3px;
+       }
+
+       div.progress-bar {
+               background-color: #222222;
+       }
+}
\ No newline at end of file
index 765722f..c9fd06e 100644 (file)
 @import "TYPO3/_module_web_list_sysnote.less";
 @import "TYPO3/_module_web_new_element.less";
 @import "TYPO3/_module_extensionmanager.less";
+@import "TYPO3/_module_lang.less";
 @import "TYPO3/_module_beuser.less";
 
 @import "TYPO3/structure/_element_move_element.less";
index 35a5cb0..bf11d5f 100644 (file)
@@ -11482,6 +11482,103 @@ span.ter-ext-state {
   right: 3px;
   top: 4px;
 }
+div.typo3-module-lang table.t3-datatable {
+  width: 100% !important;
+}
+div.typo3-module-lang table.t3-datatable th.sorting,
+div.typo3-module-lang table.t3-datatable th.sorting_asc,
+div.typo3-module-lang table.t3-datatable th.sorting_desc {
+  cursor: pointer;
+}
+div.typo3-module-lang table.t3-datatable tr > .extensionName {
+  padding-left: 0 !important;
+}
+div.typo3-module-lang table.t3-datatable tr > .dt-center {
+  text-align: center;
+}
+div.typo3-module-lang table.t3-datatable tr.disabled {
+  color: #aaaaaa;
+}
+div.typo3-module-lang table.t3-datatable tr.disabled a.deactivateLanguageLink,
+div.typo3-module-lang table.t3-datatable tr.disabled a.updateLanguageLink,
+div.typo3-module-lang table.t3-datatable tr.disabled a.updateTranslationLink {
+  display: none;
+}
+div.typo3-module-lang table.t3-datatable tr.enabled a.activateLanguageLink {
+  display: none;
+}
+div.typo3-module-lang table.t3-datatable tr div.progressBar {
+  display: none;
+}
+div.typo3-module-lang table.t3-datatable tr div.progressBar .progress {
+  position: relative;
+}
+div.typo3-module-lang table.t3-datatable tr div.progressBar .progress .progress-text {
+  position: absolute;
+  color: #FFFFFF;
+  text-align: center;
+  width: 100%;
+  text-shadow: 0 0 6px #000000;
+}
+div.typo3-module-lang table.t3-datatable tr.processing div.progressBar,
+div.typo3-module-lang table.t3-datatable tr.complete div.progressBar {
+  display: inline;
+  cursor: default;
+}
+div.typo3-module-lang table.t3-datatable tr.processing a.updateLanguageLink,
+div.typo3-module-lang table.t3-datatable td.processing a.updateLanguageLink,
+div.typo3-module-lang table.t3-datatable tr.complete a.updateLanguageLink,
+div.typo3-module-lang table.t3-datatable td.complete a.updateLanguageLink,
+div.typo3-module-lang table.t3-datatable tr.processing a.updateTranslationLink,
+div.typo3-module-lang table.t3-datatable td.processing a.updateTranslationLink,
+div.typo3-module-lang table.t3-datatable tr.complete a.updateTranslationLink,
+div.typo3-module-lang table.t3-datatable td.complete a.updateTranslationLink {
+  display: none;
+}
+div.typo3-module-lang table.t3-datatable td span.loading,
+div.typo3-module-lang table.t3-datatable td span.complete {
+  display: none;
+}
+div.typo3-module-lang table.t3-datatable td.actions span {
+  cursor: pointer;
+}
+div.typo3-module-lang table.t3-datatable td.processing span.loading {
+  display: inline;
+  cursor: default;
+}
+div.typo3-module-lang table.t3-datatable td.processing span.loading .t3-icon {
+  cursor: default;
+  width: 14px;
+  height: 14px;
+  margin: 0;
+}
+div.typo3-module-lang table.t3-datatable td.complete span.complete {
+  display: inline;
+}
+div.typo3-module-lang table.t3-datatable div.progress {
+  margin-bottom: 0;
+}
+div.typo3-module-lang div.menuItems.processing span.updateItem {
+  display: none;
+}
+div.typo3-module-lang div.menuItems.processing span.loadingItem,
+div.typo3-module-lang div.menuItems.processing span.cancelItem {
+  display: inline;
+}
+div.typo3-module-lang div.menuItems span.loadingItem,
+div.typo3-module-lang div.menuItems span.cancelItem {
+  display: none;
+}
+div.typo3-module-lang div.menuItems span.updateItem,
+div.typo3-module-lang div.menuItems span.cancelItem {
+  cursor: pointer;
+}
+div.typo3-module-lang .div.dataTables_filter input {
+  margin-left: 3px;
+}
+div.typo3-module-lang div.progress-bar {
+  background-color: #222222;
+}
 .beuser-legend {
   margin-bottom: 18px;
 }