Support deleting language items via language PIP
authorMatthias Schmidt <gravatronics@live.com>
Thu, 3 Jun 2021 10:52:21 +0000 (12:52 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Fri, 4 Jun 2021 14:04:41 +0000 (16:04 +0200)
The old `<language><category/></language>` structure is deprecated and replaced with `<language><import><category/></import></language>` like in most other XML-based PIPs so that language items can be deleted via `<language><delete><item name="…"/></delete></language>`.

Close #4178

wcfsetup/install/files/lib/data/language/LanguageEditor.class.php

index bb6bc076411b3b32a4a0b2b2ba7f094ae645a52c..b781500eeeb367715392fc1454e114b37c6ab977 100644 (file)
@@ -257,6 +257,51 @@ class LanguageEditor extends DatabaseObjectEditor implements IEditableCachedObje
         echo "</language>";
     }
 
+    /**
+     * Deletes the language items from the given XML document.
+     *
+     * @throws  \InvalidArgumentException   if given XML document is invalid with regard to deleting language items
+     * @since   5.5
+     */
+    protected function deleteFromXML(XML $xml, int $packageID): void
+    {
+        $xpath = $xml->xpath();
+
+        $items = $xpath->query('/ns:language/ns:delete/ns:item');
+        if (empty($items)) {
+            return;
+        }
+
+        $languageItems = [];
+
+        /** @var \DOMElement $item */
+        foreach ($items as $item) {
+            $itemName = $item->getAttribute('name');
+
+            if (empty($itemName)) {
+                throw new \InvalidArgumentException("The 'name' attribute is missing or empty.");
+            }
+
+            if (StringUtil::trim($itemName) !== $itemName) {
+                throw new \InvalidArgumentException("The name '{$itemName}' contains leading or trailing whitespaces.");
+            }
+
+            $languageItems[] = $itemName;
+        }
+
+        if (empty($languageItems)) {
+            return;
+        }
+
+        $conditions = new PreparedStatementConditionBuilder();
+        $conditions->add('packageID = ?', [$packageID]);
+        $conditions->add('languageItem IN (?)', [$languageItems]);
+        $sql = "DELETE FROM wcf1_language_item
+                {$conditions}";
+        $statement = WCF::getDB()->prepare($sql);
+        $statement->execute($conditions->getParameters());
+    }
+
     /**
      * Imports language items from an XML file into this language.
      * Updates the relevant language files automatically.
@@ -269,11 +314,17 @@ class LanguageEditor extends DatabaseObjectEditor implements IEditableCachedObje
      */
     public function updateFromXML(XML $xml, $packageID, $updateFiles = true, $updateExistingItems = true)
     {
+        $this->deleteFromXML($xml, $packageID);
+
         $xpath = $xml->xpath();
         $usedCategories = [];
 
         // fetch categories
-        $categories = $xpath->query('/ns:language/ns:category');
+        $categories = $xpath->query('/ns:language/ns:import/ns:category');
+        if ($categories->length === 0) {
+            // Fallback for the old, deprecated version before WSC 5.5, which only supported imports.
+            $categories = $xpath->query('/ns:language/ns:category');
+        }
 
         /** @var \DOMElement $category */
         foreach ($categories as $category) {