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
;
7 use wcf\system\package\PackageArchive
;
12 * Installs, updates and deletes languages, their categories and items.
14 * @author Alexander Ebert
15 * @copyright 2001-2014 WoltLab GmbH
16 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
17 * @package com.woltlab.wcf
18 * @subpackage system.package.plugin
19 * @category Community Framework
21 class LanguagePackageInstallationPlugin
extends AbstractXMLPackageInstallationPlugin
{
23 * @see \wcf\system\package\plugin\AbstractPackageInstallationPlugin::$tableName
25 public $tableName = 'language_item';
28 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::install()
30 public function install() {
31 AbstractPackageInstallationPlugin
::install();
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));
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'];
49 throw new SystemException("Can not determine language code of language file '".$file['filename']."'");
55 if (!empty($this->instruction
['attributes']['languagecode'])) {
56 $languageCode = $this->instruction
['attributes']['languagecode'];
58 else if (!empty($this->instruction
['attributes']['language'])) {
59 $languageCode = $this->instruction
['attributes']['language'];
61 else if (preg_match('~([a-z-]+)\.xml$~i', $filename, $match)) {
62 $languageCode = $match[1];
65 throw new SystemException("Can not determine language code of language file '".$filename."'");
68 $languageFiles[$languageCode] = $filename;
71 // get installed languages
72 $installedLanguages = array();
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;
83 foreach ($installedLanguages as $installedLanguage) {
85 $updateExistingItems = true;
86 if (isset($languageFiles[$installedLanguage['languageCode']])) {
87 $languageFile = $languageFiles[$installedLanguage['languageCode']];
89 else if ($multipleFiles) {
90 // do not update existing items, only add new ones
91 $updateExistingItems = false;
93 // use default language
94 if (isset($languageFiles[$installedLanguages[0]['languageCode']])) {
95 $languageFile = $languageFiles[$installedLanguages[0]['languageCode']];
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'];
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']];
118 // use first delivered language
119 if ($languageFile === null) {
120 foreach ($languageFiles as $languageFile) break;
125 if ($languageFile !== null) {
126 if ($xml = $this->readLanguage($languageFile)) {
127 // get language object
128 $language = LanguageFactory
::getInstance()->getLanguageByCode($installedLanguage['languageCode']);
129 $languageEditor = new LanguageEditor($language);
132 // don't update language files if package is an application
133 $languageEditor->updateFromXML($xml, $this->installation
->getPackageID(), !$this->installation
->getPackage()->isApplication
, $updateExistingItems);
140 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::uninstall()
142 public function uninstall() {
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()));
154 $categoryIDs = array();
155 while ($row = $statement->fetchArray()) {
156 $itemIDs[] = $row['languageItemID'];
159 $categoryIDs[$row['languageCategoryID']] = true;
162 if (!empty($itemIDs)) {
163 $sql = "DELETE FROM wcf".WCF_N
."_".$this->tableName
."
164 WHERE languageItemID = ?
166 $statement = WCF
::getDB()->prepareStatement($sql);
168 foreach ($itemIDs as $itemID) {
169 $statement->execute(array(
171 $this->installation
->getPackageID()
175 $this->deleteEmptyCategories(array_keys($categoryIDs), $this->installation
->getPackageID());
180 * Extracts the language file and parses it. If the specified language file
181 * was not found, an exception message is thrown.
183 * @param string $filename
184 * @return \wcf\util\XML
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) {
190 throw new SystemException("language file '".$filename."' not found.");
193 // extract language file and parse with DOMDocument
195 $xml->loadXML($filename, $this->installation
->getArchive()->getTar()->extractToString($fileIndex));
200 * Deletes categories which where changed by an update or deinstallation
201 * in case they are now empty.
203 * @param array $categoryIDs
204 * @param integer $packageID
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));
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)
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'];
229 // Delete categories from DB.
230 if (!empty($categoriesToDelete)) {
231 $sql = "DELETE FROM wcf".WCF_N
."_language_category
232 WHERE languageCategory = ?";
233 $statement = WCF
::getDB()->prepareStatement($sql);
235 foreach ($categoriesToDelete as $category) {
236 $statement->execute(array($category));
242 * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::handleDelete()
244 protected function handleDelete(array $items) { }
247 * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::prepareImport()
249 protected function prepareImport(array $data) { }
252 * @see \wcf\system\package\plugin\AbstractXMLPackageInstallationPlugin::findExistingItem()
254 protected function findExistingItem(array $data) { }
257 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::isValid()
259 public static function isValid(PackageArchive
$archive, $instruction) {