Move parts of `FileDeletePackageInstallationPlugin` into abstract parent class
authorMatthias Schmidt <gravatronics@live.com>
Tue, 8 Jun 2021 11:45:08 +0000 (13:45 +0200)
committerMatthias Schmidt <gravatronics@live.com>
Tue, 8 Jun 2021 15:12:35 +0000 (17:12 +0200)
wcfsetup/install/files/lib/system/package/plugin/AbstractFileDeletePackageInstallationPlugin.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/package/plugin/FileDeletePackageInstallationPlugin.class.php

diff --git a/wcfsetup/install/files/lib/system/package/plugin/AbstractFileDeletePackageInstallationPlugin.class.php b/wcfsetup/install/files/lib/system/package/plugin/AbstractFileDeletePackageInstallationPlugin.class.php
new file mode 100644 (file)
index 0000000..f089361
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+
+namespace wcf\system\package\plugin;
+
+use wcf\data\application\Application;
+use wcf\data\package\Package;
+use wcf\data\package\PackageCache;
+use wcf\system\application\ApplicationHandler;
+use wcf\system\devtools\pip\IDevtoolsPipEntryList;
+use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
+use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\field\SingleSelectionFormField;
+use wcf\system\form\builder\field\TextFormField;
+use wcf\system\form\builder\IFormDocument;
+use wcf\system\WCF;
+use wcf\util\DOMUtil;
+use wcf\util\XML;
+
+/**
+ * Abstract implementation of a package installation plugin deleting a certain type of files.
+ *
+ * @author  Matthias Schmidt
+ * @copyright   2001-2021 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Package\Plugin
+ * @since   5.5
+ */
+abstract class AbstractFileDeletePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements
+    IGuiPackageInstallationPlugin
+{
+    use TXmlGuiPackageInstallationPlugin;
+
+    /**
+     * Returns the name of the database table that logs the installed files.
+     */
+    abstract protected function getLogTableName(): string;
+
+    /**
+     * Returns the name of the column in the log table returned by `getLogTableName()` that contains
+     * the names of the relevant files.
+     */
+    abstract protected function getFilenameTableColumn(): string;
+
+    protected function getPipName(): string
+    {
+        return $this->getXsdFilename();
+    }
+
+    /**
+     * Returns the actual absolute path of the given file.
+     */
+    protected function getFilePath(string $filename, string $application): string
+    {
+        return Application::getDirectory($application) . $filename;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function handleDelete(array $items)
+    {
+        $sql = "SELECT  packageID
+                FROM    {$this->getLogTableName()}
+                WHERE   {$this->getFilenameTableColumn()} = ?
+                    AND application = ?
+                    AND packageID = ?";
+        $searchStatement = WCF::getDB()->prepare($sql);
+
+        $sql = "DELETE FROM {$this->getLogTableName()}
+                WHERE       packageID = ?
+                        AND {$this->getFilenameTableColumn()} = ?";
+        $deleteStatement = WCF::getDB()->prepare($sql);
+
+        foreach ($items as $item) {
+            $file = $item['value'];
+            $application = 'wcf';
+            if (!empty($item['attributes']['application'])) {
+                $application = $item['attributes']['application'];
+            } elseif ($this->installation->getPackage()->isApplication) {
+                $application = Package::getAbbreviation($this->installation->getPackage()->package);
+            }
+
+            $searchStatement->execute([
+                $file,
+                $application,
+                $this->installation->getPackageID(),
+            ]);
+
+            $filePackageID = $searchStatement->fetchSingleColumn();
+            if ($filePackageID !== false && $filePackageID != $this->installation->getPackageID()) {
+                throw new \UnexpectedValueException(
+                    "'{$file}' does not belong to package '{$this->installation->getPackage()->package}'
+                    but to package '" . PackageCache::getInstance()->getPackage($filePackageID)->package . "'."
+                );
+            }
+
+            $filePath = $this->getFilePath($file, $application);
+            if (\file_exists($filePath)) {
+                \unlink($filePath);
+            }
+
+            $deleteStatement->execute([
+                $this->installation->getPackageID(),
+                $file,
+            ]);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    final protected function import(array $row, array $data)
+    {
+        // Does nothing, imports are not supported.
+    }
+
+    /**
+     * @inheritDoc
+     */
+    final protected function prepareImport(array $data)
+    {
+        return $data;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    final protected function findExistingItem(array $data)
+    {
+        return null;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public static function getSyncDependencies()
+    {
+        return [];
+    }
+
+    /**
+     * Returns the language item with the description of the file field or `null` if no description
+     * should be shown.
+     */
+    protected function getFileFieldDescription(): ?string
+    {
+        $languageItem = "wcf.acp.pip.{$this->getPipName()}.{$this->tagName}.description";
+
+        return WCF::getLanguage()->get($languageItem, true) ?: null;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function addFormFields(IFormDocument $form)
+    {
+        /** @var FormContainer $dataContainer */
+        $dataContainer = $form->getNodeById('data');
+
+        $dataContainer->appendChildren([
+            TextFormField::create($this->tagName)
+                ->label("wcf.acp.pip.{$this->getPipName()}.{$this->tagName}")
+                ->description($this->getFileFieldDescription())
+                ->required(),
+            SingleSelectionFormField::create('application')
+                ->label("wcf.acp.pip.{$this->getPipName()}.application")
+                ->options(static function (): array {
+                    $options = [
+                        '' => 'wcf.global.noSelection',
+                    ];
+
+                    $apps = ApplicationHandler::getInstance()->getApplications();
+                    \usort($apps, static function (Application $a, Application $b) {
+                        return $a->getPackage()->getTitle() <=> $b->getPackage()->getTitle();
+                    });
+
+                    foreach ($apps as $application) {
+                        $options[$application->getAbbreviation()] = $application->getPackage()->getTitle();
+                    }
+
+                    return $options;
+                })
+                ->nullable(),
+        ]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function fetchElementData(\DOMElement $element, $saveData)
+    {
+        return [
+            'application' => $element->getAttribute('application') ?? 'wcf',
+            $this->tagName => $element->nodeValue,
+            'packageID' => $this->installation->getPackage()->packageID,
+        ];
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getElementIdentifier(\DOMElement $element)
+    {
+        $app = $element->getAttribute('application') ?? 'wcf';
+
+        return \sha1($app . '_' . $element->nodeValue);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function setEntryListKeys(IDevtoolsPipEntryList $entryList)
+    {
+        $entryList->setKeys([
+            $this->tagName => "wcf.acp.pip.{$this->getPipName()}.{$this->tagName}",
+            'application' => "wcf.acp.pip.{$this->getPipName()}.application",
+        ]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function insertNewXmlElement(XML $xml, \DOMElement $newElement)
+    {
+        $delete = $xml->xpath()->query('/ns:data/ns:delete')->item(0);
+        if ($delete === null) {
+            $data = $xml->xpath()->query('/ns:data')->item(0);
+            $delete = $xml->getDocument()->createElement('delete');
+            DOMUtil::prepend($delete, $data);
+        }
+
+        $delete->appendChild($newElement);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function prepareXmlElement(\DOMDocument $document, IFormDocument $form)
+    {
+        $file = $document->createElement($this->tagName);
+
+        $data = $form->getData()['data'];
+        if (!empty($data['application'])) {
+            $file->setAttribute('application', $data['application']);
+        }
+        $file->nodeValue = $data[$this->tagName];
+
+        return $file;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    final protected function prepareDeleteXmlElement(\DOMElement $element)
+    {
+        return null;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function saveObject(\DOMElement $newElement, ?\DOMElement $oldElement = null)
+    {
+        $newElementData = $this->getElementData($newElement, true);
+
+        $this->handleDelete([[
+            'attributes' => [
+                'application' => $newElementData['application'],
+            ],
+            'value' => $newElementData[$this->tagName],
+        ]]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    final protected function deleteObject(\DOMElement $element)
+    {
+        // Reverting file deletions is not supported. Use the `file` PIP instead.
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function getImportElements(\DOMXPath $xpath)
+    {
+        return $xpath->query('/ns:data/ns:delete/ns:' . $this->tagName);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    protected function getEmptyXml()
+    {
+        $xsdFilename = $this->getXsdFilename();
+        $apiVersion = WSC_API_VERSION;
+
+        return <<<XML
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/{$apiVersion}/{$xsdFilename}.xsd">
+       <delete></delete>
+</data>
+XML;
+    }
+}
index 89138fb1df13c90e854b8743abbfc2429eb7f123..66b192aae1cd359cf88b8628ce80b31eb0f497e5 100644 (file)
@@ -2,23 +2,8 @@
 
 namespace wcf\system\package\plugin;
 
-use wcf\data\application\Application;
-use wcf\data\package\Package;
-use wcf\data\package\PackageCache;
-use wcf\system\application\ApplicationHandler;
-use wcf\system\devtools\pip\IDevtoolsPipEntryList;
-use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
-use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
-use wcf\system\form\builder\container\FormContainer;
-use wcf\system\form\builder\field\SingleSelectionFormField;
-use wcf\system\form\builder\field\TextFormField;
-use wcf\system\form\builder\IFormDocument;
-use wcf\system\WCF;
-use wcf\util\DOMUtil;
-use wcf\util\XML;
-
 /**
- * Files files installed with the `file` package installation plugin.
+ * Deletes files installed with the `file` package installation plugin.
  *
  * @author  Matthias Schmidt
  * @copyright   2001-2021 WoltLab GmbH
@@ -26,10 +11,8 @@ use wcf\util\XML;
  * @package WoltLabSuite\Core\System\Package\Plugin
  * @since   5.5
  */
-class FileDeletePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin
+final class FileDeletePackageInstallationPlugin extends AbstractFileDeletePackageInstallationPlugin
 {
-    use TXmlGuiPackageInstallationPlugin;
-
     /**
      * @inheritDoc
      */
@@ -38,237 +21,16 @@ class FileDeletePackageInstallationPlugin extends AbstractXMLPackageInstallation
     /**
      * @inheritDoc
      */
-    protected function handleDelete(array $items)
-    {
-        $sql = "SELECT  packageID
-                FROM    wcf1_package_installation_file_log
-                WHERE   filename = ?
-                    AND application = ?
-                    AND packageID = ?";
-        $searchStatement = WCF::getDB()->prepare($sql);
-
-        $sql = "DELETE FROM wcf1_package_installation_file_log
-                WHERE       packageID = ?
-                        AND filename = ?";
-        $deleteStatement = WCF::getDB()->prepare($sql);
-
-        foreach ($items as $item) {
-            $file = $item['value'];
-            $application = 'wcf';
-            if (!empty($item['attributes']['application'])) {
-                $application = $item['attributes']['application'];
-            } elseif ($this->installation->getPackage()->isApplication) {
-                $application = Package::getAbbreviation($this->installation->getPackage()->package);
-            }
-
-            $searchStatement->execute([
-                $file,
-                $application,
-                $this->installation->getPackageID(),
-            ]);
-
-            $filePackageID = $searchStatement->fetchSingleColumn();
-            if ($filePackageID !== false && $filePackageID != $this->installation->getPackageID()) {
-                throw new \UnexpectedValueException(
-                    "File '{$file}' does not belong to package '{$this->installation->getPackage()->package}'
-                    but to package '" . PackageCache::getInstance()->getPackage($filePackageID)->package . "'."
-                );
-            }
-
-            $filePath = Application::getDirectory($application) . $file;
-            if (\file_exists($filePath)) {
-                \unlink($filePath);
-            }
-
-            $deleteStatement->execute([
-                $this->installation->getPackageID(),
-                $file,
-            ]);
-        }
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function import(array $row, array $data)
-    {
-        throw new \LogicException("The `fileDelete` package installation plugin does not support imports.");
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function prepareImport(array $data)
-    {
-        return $data;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function findExistingItem(array $data)
-    {
-        return null;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public static function getSyncDependencies()
-    {
-        return [];
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function addFormFields(IFormDocument $form)
-    {
-        /** @var FormContainer $dataContainer */
-        $dataContainer = $form->getNodeById('data');
-
-        $dataContainer->appendChildren([
-            TextFormField::create('file')
-                ->label('wcf.acp.pip.fileDelete.file')
-                ->required(),
-            SingleSelectionFormField::create('application')
-                ->label('wcf.acp.pip.fileDelete.application')
-                ->options(static function (): array {
-                    $options = [
-                        '' => 'wcf.global.noSelection',
-                    ];
-
-                    $apps = ApplicationHandler::getInstance()->getApplications();
-                    \usort($apps, static function (Application $a, Application $b) {
-                        return $a->getPackage()->getTitle() <=> $b->getPackage()->getTitle();
-                    });
-
-                    foreach ($apps as $application) {
-                        $options[$application->getAbbreviation()] = $application->getPackage()->getTitle();
-                    }
-
-                    return $options;
-                })
-                ->nullable(),
-        ]);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function fetchElementData(\DOMElement $element, $saveData)
-    {
-        return [
-            'application' => $element->getAttribute('application') ?? 'wcf',
-            'file' => $element->nodeValue,
-            'packageID' => $this->installation->getPackage()->packageID,
-        ];
-    }
-
-    /**
-     * @inheritDoc
-     */
-    public function getElementIdentifier(\DOMElement $element)
-    {
-        $app = $element->getAttribute('application') ?? 'wcf';
-
-        return \sha1($app . '_' . $element->nodeValue);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function setEntryListKeys(IDevtoolsPipEntryList $entryList)
-    {
-        $entryList->setKeys([
-            'file' => 'wcf.acp.pip.fileDelete.file',
-            'application' => 'wcf.acp.pip.fileDelete.application',
-        ]);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function insertNewXmlElement(XML $xml, \DOMElement $newElement)
+    protected function getLogTableName(): string
     {
-        $delete = $xml->xpath()->query('/ns:data/ns:delete')->item(0);
-        if ($delete === null) {
-            $data = $xml->xpath()->query('/ns:data')->item(0);
-            $delete = $xml->getDocument()->createElement('delete');
-            DOMUtil::prepend($delete, $data);
-        }
-
-        $delete->appendChild($newElement);
+        return 'wcf1_package_installation_file_log';
     }
 
     /**
      * @inheritDoc
      */
-    protected function prepareXmlElement(\DOMDocument $document, IFormDocument $form)
+    protected function getFilenameTableColumn(): string
     {
-        $file = $document->createElement($this->tagName);
-
-        $data = $form->getData()['data'];
-        if (!empty($data['application'])) {
-            $file->setAttribute('application', $data['application']);
-        }
-        $file->nodeValue = $data['file'];
-
-        return $file;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function prepareDeleteXmlElement(\DOMElement $element)
-    {
-        return null;
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function saveObject(\DOMElement $newElement, ?\DOMElement $oldElement = null)
-    {
-        $newElementData = $this->getElementData($newElement, true);
-
-        $this->handleDelete([[
-            'attributes' => [
-                'application' => $newElementData['application'],
-            ],
-            'value' => $newElementData['file'],
-        ]]);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function deleteObject(\DOMElement $element)
-    {
-        // Reverting file deletions is not supported. Use the `file` PIP instead.
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function getImportElements(\DOMXPath $xpath)
-    {
-        return $xpath->query('/ns:data/ns:delete/ns:' . $this->tagName);
-    }
-
-    /**
-     * @inheritDoc
-     */
-    protected function getEmptyXml()
-    {
-        $xsdFilename = $this->getXsdFilename();
-        $apiVersion = WSC_API_VERSION;
-
-        return <<<XML
-<?xml version="1.0" encoding="UTF-8"?>
-<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/{$apiVersion}/{$xsdFilename}.xsd">
-       <delete></delete>
-</data>
-XML;
+        return 'filename';
     }
 }