Commit | Line | Data |
---|---|---|
11ade432 AE |
1 | <?php |
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; | |
25c89d0a | 7 | use wcf\system\package\PackageArchive; |
11ade432 AE |
8 | use wcf\system\WCF; |
9 | use wcf\util\XML; | |
10 | ||
11 | /** | |
a17de04e | 12 | * Installs, updates and deletes languages, their categories and items. |
9f959ced | 13 | * |
849f69b7 | 14 | * @author Alexander Ebert |
ca4ba303 | 15 | * @copyright 2001-2014 WoltLab GmbH |
11ade432 AE |
16 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
17 | * @package com.woltlab.wcf | |
18 | * @subpackage system.package.plugin | |
9f959ced | 19 | * @category Community Framework |
11ade432 | 20 | */ |
2f7488d7 | 21 | class LanguagePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin { |
b5be6fb0 | 22 | /** |
0ad90fc3 | 23 | * @see \wcf\system\package\plugin\AbstractPackageInstallationPlugin::$tableName |
9f959ced | 24 | */ |
11ade432 AE |
25 | public $tableName = 'language_item'; |
26 | ||
27 | /** | |
0ad90fc3 | 28 | * @see \wcf\system\package\plugin\IPackageInstallationPlugin::install() |
11ade432 AE |
29 | */ |
30 | public function install() { | |
31 | AbstractPackageInstallationPlugin::install(); | |
32 | ||
33 | // get language files | |
34 | $languageFiles = array(); | |
35 | $multipleFiles = false; | |
36 | $filename = $this->instruction['value']; | |
37 | if (strpos($filename, '*') !== false) { | |
38 | // wildcard syntax; import multiple files | |
39 | $multipleFiles = true; | |
40 | $files = $this->installation->getArchive()->getTar()->getContentList(); | |
41 | $pattern = str_replace("\*", ".*", preg_quote($filename)); | |
42 | ||
43 | foreach ($files as $file) { | |
44 | if (preg_match('!'.$pattern.'!i', $file['filename'])) { | |
45 | if (preg_match('~([a-z-]+)\.xml$~i', $file['filename'], $match)) { | |
46 | $languageFiles[$match[1]] = $file['filename']; | |
47 | } | |
48 | else { | |
a2587738 | 49 | throw new SystemException("Can not determine language code of language file '".$file['filename']."'"); |
11ade432 AE |
50 | } |
51 | } | |
52 | } | |
53 | } | |
54 | else { | |
55 | if (!empty($this->instruction['attributes']['languagecode'])) { | |
56 | $languageCode = $this->instruction['attributes']['languagecode']; | |
57 | } | |
58 | else if (!empty($this->instruction['attributes']['language'])) { | |
59 | $languageCode = $this->instruction['attributes']['language']; | |
60 | } | |
61 | else if (preg_match('~([a-z-]+)\.xml$~i', $filename, $match)) { | |
62 | $languageCode = $match[1]; | |
63 | } | |
64 | else { | |
65 | throw new SystemException("Can not determine language code of language file '".$filename."'"); | |
66 | } | |
67 | ||
68 | $languageFiles[$languageCode] = $filename; | |
69 | } | |
70 | ||
71 | // get installed languages | |
72 | $installedLanguages = array(); | |
73 | $sql = "SELECT * | |
74 | FROM wcf".WCF_N."_language | |
75 | ORDER BY isDefault DESC"; | |
76 | $statement = WCF::getDB()->prepareStatement($sql); | |
77 | $statement->execute(); | |
78 | while ($row = $statement->fetchArray()) { | |
79 | $installedLanguages[] = $row; | |
80 | } | |
81 | ||
82 | // install language | |
11ade432 AE |
83 | foreach ($installedLanguages as $installedLanguage) { |
84 | $languageFile = null; | |
6b4766db | 85 | $updateExistingItems = true; |
11ade432 AE |
86 | if (isset($languageFiles[$installedLanguage['languageCode']])) { |
87 | $languageFile = $languageFiles[$installedLanguage['languageCode']]; | |
88 | } | |
89 | else if ($multipleFiles) { | |
6b4766db AE |
90 | // do not update existing items, only add new ones |
91 | $updateExistingItems = false; | |
92 | ||
11ade432 AE |
93 | // use default language |
94 | if (isset($languageFiles[$installedLanguages[0]['languageCode']])) { | |
95 | $languageFile = $languageFiles[$installedLanguages[0]['languageCode']]; | |
96 | } | |
9f959ced | 97 | |
11ade432 AE |
98 | // use english (if installed) |
99 | else if (isset($languageFiles['en'])) { | |
100 | foreach ($installedLanguages as $installedLanguage2) { | |
101 | if ($installedLanguage2['languageCode'] == 'en') { | |
102 | $languageFile = $languageFiles['en']; | |
103 | break; | |
104 | } | |
105 | } | |
106 | } | |
9f959ced | 107 | |
11ade432 AE |
108 | // use any installed language |
109 | if ($languageFile === null) { | |
110 | foreach ($installedLanguages as $installedLanguage2) { | |
111 | if (isset($languageFiles[$installedLanguage2['languageCode']])) { | |
112 | $languageFile = $languageFiles[$installedLanguage2['languageCode']]; | |
113 | break; | |
114 | } | |
115 | } | |
116 | } | |
9f959ced | 117 | |
11ade432 AE |
118 | // use first delivered language |
119 | if ($languageFile === null) { | |
120 | foreach ($languageFiles as $languageFile) break; | |
121 | } | |
122 | } | |
123 | ||
124 | // save language | |
125 | if ($languageFile !== null) { | |
126 | if ($xml = $this->readLanguage($languageFile)) { | |
127 | // get language object | |
83f9480c | 128 | $language = LanguageFactory::getInstance()->getLanguageByCode($installedLanguage['languageCode']); |
11ade432 AE |
129 | $languageEditor = new LanguageEditor($language); |
130 | ||
131 | // import xml | |
aac1247e | 132 | // don't update language files if package is an application |
6b4766db | 133 | $languageEditor->updateFromXML($xml, $this->installation->getPackageID(), !$this->installation->getPackage()->isApplication, $updateExistingItems); |
11ade432 AE |
134 | } |
135 | } | |
136 | } | |
11ade432 AE |
137 | } |
138 | ||
139 | /** | |
0ad90fc3 | 140 | * @see \wcf\system\package\plugin\IPackageInstallationPlugin::uninstall() |
11ade432 AE |
141 | */ |
142 | public function uninstall() { | |
143 | parent::uninstall(); | |
144 | ||
11ade432 AE |
145 | // delete language items |
146 | // Get all items and their categories | |
147 | // which where installed from this package. | |
148 | $sql = "SELECT languageItemID, languageCategoryID, languageID | |
149 | FROM wcf".WCF_N."_language_item | |
150 | WHERE packageID = ?"; | |
151 | $statement = WCF::getDB()->prepareStatement($sql); | |
152 | $statement->execute(array($this->installation->getPackageID())); | |
153 | $itemIDs = array(); | |
154 | $categoryIDs = array(); | |
155 | while ($row = $statement->fetchArray()) { | |
156 | $itemIDs[] = $row['languageItemID']; | |
157 | ||
158 | // Store categories | |
159 | $categoryIDs[$row['languageCategoryID']] = true; | |
160 | } | |
161 | ||
15fa2802 | 162 | if (!empty($itemIDs)) { |
11ade432 AE |
163 | $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName." |
164 | WHERE languageItemID = ? | |
165 | AND packageID = ?"; | |
166 | $statement = WCF::getDB()->prepareStatement($sql); | |
167 | ||
168 | foreach ($itemIDs as $itemID) { | |
169 | $statement->execute(array( | |
170 | $itemID, | |
171 | $this->installation->getPackageID() | |
172 | )); | |
173 | } | |
174 | ||
175 | $this->deleteEmptyCategories(array_keys($categoryIDs), $this->installation->getPackageID()); | |
176 | } | |
177 | } | |
178 | ||
179 | /** | |
9f959ced MS |
180 | * Extracts the language file and parses it. If the specified language file |
181 | * was not found, an exception message is thrown. | |
182 | * | |
11ade432 | 183 | * @param string $filename |
0ad90fc3 | 184 | * @return \wcf\util\XML |
11ade432 AE |
185 | */ |
186 | protected function readLanguage($filename) { | |
187 | // search language files in package archive | |
188 | // throw error message if not found | |
189 | if (($fileIndex = $this->installation->getArchive()->getTar()->getIndexByFilename($filename)) === false) { | |
4fe0b42b | 190 | throw new SystemException("language file '".$filename."' not found."); |
11ade432 AE |
191 | } |
192 | ||
193 | // extract language file and parse with DOMDocument | |
194 | $xml = new XML(); | |
195 | $xml->loadXML($filename, $this->installation->getArchive()->getTar()->extractToString($fileIndex)); | |
196 | return $xml; | |
197 | } | |
198 | ||
199 | /** | |
a17de04e MS |
200 | * Deletes categories which where changed by an update or deinstallation |
201 | * in case they are now empty. | |
9f959ced | 202 | * |
11ade432 | 203 | * @param array $categoryIDs |
9f959ced | 204 | * @param integer $packageID |
11ade432 AE |
205 | */ |
206 | protected function deleteEmptyCategories(array $categoryIDs, $packageID) { | |
207 | // Get empty categories which where changed by this package. | |
208 | $conditions = new PreparedStatementConditionBuilder(); | |
209 | $conditions->add("language_category.languageCategoryID IN (?)", array($categoryIDs)); | |
210 | ||
211 | $sql = "SELECT COUNT(item.languageItemID) AS count, | |
212 | language_category.languageCategoryID, | |
213 | language_category.languageCategory | |
214 | FROM wcf".WCF_N."_language_category language_category | |
215 | LEFT JOIN wcf".WCF_N."_language_item item | |
216 | ON (item.languageCategoryID = language_category.languageCategoryID) | |
217 | ".$conditions." | |
218 | GROUP BY language_category.languageCategoryID ASC, | |
219 | language_category.languageCategory ASC"; | |
220 | $statement = WCF::getDB()->prepareStatement($sql); | |
221 | $statement->execute($conditions->getParameters()); | |
222 | $categoriesToDelete = array(); | |
223 | while ($row = $statement->fetchArray()) { | |
224 | if ($row['count'] == 0) { | |
225 | $categoriesToDelete[$row['languageCategoryID']] = $row['languageCategory']; | |
226 | } | |
227 | } | |
228 | ||
229 | // Delete categories from DB. | |
15fa2802 | 230 | if (!empty($categoriesToDelete)) { |
11ade432 AE |
231 | $sql = "DELETE FROM wcf".WCF_N."_language_category |
232 | WHERE languageCategory = ?"; | |
233 | $statement = WCF::getDB()->prepareStatement($sql); | |
234 | ||
235 | foreach ($categoriesToDelete as $category) { | |
236 | $statement->execute(array($category)); | |
237 | } | |
238 | } | |
239 | } | |
240 | ||
241 | /** | |
0ad90fc3 | 242 | * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::handleDelete() |
11ade432 AE |
243 | */ |
244 | protected function handleDelete(array $items) { } | |
245 | ||
246 | /** | |
0ad90fc3 | 247 | * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::prepareImport() |
11ade432 AE |
248 | */ |
249 | protected function prepareImport(array $data) { } | |
250 | ||
251 | /** | |
0ad90fc3 | 252 | * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::findExistingItem() |
11ade432 AE |
253 | */ |
254 | protected function findExistingItem(array $data) { } | |
25c89d0a AE |
255 | |
256 | /** | |
257 | * @see \wcf\system\package\plugin\IPackageInstallationPlugin::isValid() | |
258 | */ | |
259 | public static function isValid(PackageArchive $archive, $instruction) { | |
260 | return true; | |
261 | } | |
11ade432 | 262 | } |