2 namespace wcf\system\package\plugin
;
3 use wcf\data\language\LanguageEditor
;
4 use wcf\system\database\util\PreparedStatementConditionBuilder
;
5 use wcf\system\exception\SystemException
;
6 use wcf\system\language\LanguageFactory
;
11 * Installs, updates and deletes languages, their categories and items.
13 * @author Alexander Ebert
14 * @copyright 2001-2014 WoltLab GmbH
15 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
16 * @package com.woltlab.wcf
17 * @subpackage system.package.plugin
18 * @category Community Framework
20 class LanguagePackageInstallationPlugin
extends AbstractXMLPackageInstallationPlugin
{
22 * @see \wcf\system\package\plugin\AbstractPackageInstallationPlugin::$tableName
24 public $tableName = 'language_item';
27 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::install()
29 public function install() {
30 AbstractPackageInstallationPlugin
::install();
33 $languageFiles = array();
34 $multipleFiles = false;
35 $filename = $this->instruction
['value'];
36 if (strpos($filename, '*') !== false) {
37 // wildcard syntax; import multiple files
38 $multipleFiles = true;
39 $files = $this->installation
->getArchive()->getTar()->getContentList();
40 $pattern = str_replace("\*", ".*", preg_quote($filename));
42 foreach ($files as $file) {
43 if (preg_match('!'.$pattern.'!i', $file['filename'])) {
44 if (preg_match('~([a-z-]+)\.xml$~i', $file['filename'], $match)) {
45 $languageFiles[$match[1]] = $file['filename'];
48 throw new SystemException("Can not determine language code of language file '".$file['filename']."'");
54 if (!empty($this->instruction
['attributes']['languagecode'])) {
55 $languageCode = $this->instruction
['attributes']['languagecode'];
57 else if (!empty($this->instruction
['attributes']['language'])) {
58 $languageCode = $this->instruction
['attributes']['language'];
60 else if (preg_match('~([a-z-]+)\.xml$~i', $filename, $match)) {
61 $languageCode = $match[1];
64 throw new SystemException("Can not determine language code of language file '".$filename."'");
67 $languageFiles[$languageCode] = $filename;
70 // get installed languages
71 $installedLanguages = array();
73 FROM wcf".WCF_N
."_language
74 ORDER BY isDefault DESC";
75 $statement = WCF
::getDB()->prepareStatement($sql);
76 $statement->execute();
77 while ($row = $statement->fetchArray()) {
78 $installedLanguages[] = $row;
82 foreach ($installedLanguages as $installedLanguage) {
84 $updateExistingItems = true;
85 if (isset($languageFiles[$installedLanguage['languageCode']])) {
86 $languageFile = $languageFiles[$installedLanguage['languageCode']];
88 else if ($multipleFiles) {
89 // do not update existing items, only add new ones
90 $updateExistingItems = false;
92 // use default language
93 if (isset($languageFiles[$installedLanguages[0]['languageCode']])) {
94 $languageFile = $languageFiles[$installedLanguages[0]['languageCode']];
97 // use english (if installed)
98 else if (isset($languageFiles['en'])) {
99 foreach ($installedLanguages as $installedLanguage2) {
100 if ($installedLanguage2['languageCode'] == 'en') {
101 $languageFile = $languageFiles['en'];
107 // use any installed language
108 if ($languageFile === null) {
109 foreach ($installedLanguages as $installedLanguage2) {
110 if (isset($languageFiles[$installedLanguage2['languageCode']])) {
111 $languageFile = $languageFiles[$installedLanguage2['languageCode']];
117 // use first delivered language
118 if ($languageFile === null) {
119 foreach ($languageFiles as $languageFile) break;
124 if ($languageFile !== null) {
125 if ($xml = $this->readLanguage($languageFile)) {
126 // get language object
127 $language = LanguageFactory
::getInstance()->getLanguageByCode($installedLanguage['languageCode']);
128 $languageEditor = new LanguageEditor($language);
131 // don't update language files if package is an application
132 $languageEditor->updateFromXML($xml, $this->installation
->getPackageID(), !$this->installation
->getPackage()->isApplication
, $updateExistingItems);
139 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::uninstall()
141 public function uninstall() {
144 // delete language items
145 // Get all items and their categories
146 // which where installed from this package.
147 $sql = "SELECT languageItemID, languageCategoryID, languageID
148 FROM wcf".WCF_N
."_language_item
149 WHERE packageID = ?";
150 $statement = WCF
::getDB()->prepareStatement($sql);
151 $statement->execute(array($this->installation
->getPackageID()));
153 $categoryIDs = array();
154 while ($row = $statement->fetchArray()) {
155 $itemIDs[] = $row['languageItemID'];
158 $categoryIDs[$row['languageCategoryID']] = true;
161 if (!empty($itemIDs)) {
162 $sql = "DELETE FROM wcf".WCF_N
."_".$this->tableName
."
163 WHERE languageItemID = ?
165 $statement = WCF
::getDB()->prepareStatement($sql);
167 foreach ($itemIDs as $itemID) {
168 $statement->execute(array(
170 $this->installation
->getPackageID()
174 $this->deleteEmptyCategories(array_keys($categoryIDs), $this->installation
->getPackageID());
179 * Extracts the language file and parses it. If the specified language file
180 * was not found, an exception message is thrown.
182 * @param string $filename
183 * @return \wcf\util\XML
185 protected function readLanguage($filename) {
186 // search language files in package archive
187 // throw error message if not found
188 if (($fileIndex = $this->installation
->getArchive()->getTar()->getIndexByFilename($filename)) === false) {
189 throw new SystemException("language file '".$filename."' not found.");
192 // extract language file and parse with DOMDocument
194 $xml->loadXML($filename, $this->installation
->getArchive()->getTar()->extractToString($fileIndex));
199 * Deletes categories which where changed by an update or deinstallation
200 * in case they are now empty.
202 * @param array $categoryIDs
203 * @param integer $packageID
205 protected function deleteEmptyCategories(array $categoryIDs, $packageID) {
206 // Get empty categories which where changed by this package.
207 $conditions = new PreparedStatementConditionBuilder();
208 $conditions->add("language_category.languageCategoryID IN (?)", array($categoryIDs));
210 $sql = "SELECT COUNT(item.languageItemID) AS count,
211 language_category.languageCategoryID,
212 language_category.languageCategory
213 FROM wcf".WCF_N
."_language_category language_category
214 LEFT JOIN wcf".WCF_N
."_language_item item
215 ON (item.languageCategoryID = language_category.languageCategoryID)
217 GROUP BY language_category.languageCategoryID ASC,
218 language_category.languageCategory ASC";
219 $statement = WCF
::getDB()->prepareStatement($sql);
220 $statement->execute($conditions->getParameters());
221 $categoriesToDelete = array();
222 while ($row = $statement->fetchArray()) {
223 if ($row['count'] == 0) {
224 $categoriesToDelete[$row['languageCategoryID']] = $row['languageCategory'];
228 // Delete categories from DB.
229 if (!empty($categoriesToDelete)) {
230 $sql = "DELETE FROM wcf".WCF_N
."_language_category
231 WHERE languageCategory = ?";
232 $statement = WCF
::getDB()->prepareStatement($sql);
234 foreach ($categoriesToDelete as $category) {
235 $statement->execute(array($category));
241 * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::handleDelete()
243 protected function handleDelete(array $items) { }
246 * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::prepareImport()
248 protected function prepareImport(array $data) { }
251 * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::findExistingItem()
253 protected function findExistingItem(array $data) { }