From: Matthias Schmidt Date: Tue, 8 Jun 2021 11:45:08 +0000 (+0200) Subject: Move parts of `FileDeletePackageInstallationPlugin` into abstract parent class X-Git-Tag: 5.5.0_Alpha_1~682^2~4 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=31094ad89351abfbc3e1565e23141b78ddd67d82;p=GitHub%2FWoltLab%2FWCF.git Move parts of `FileDeletePackageInstallationPlugin` into abstract parent class --- 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 index 0000000000..f089361936 --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/plugin/AbstractFileDeletePackageInstallationPlugin.class.php @@ -0,0 +1,306 @@ + + * @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; + } +} diff --git a/wcfsetup/install/files/lib/system/package/plugin/FileDeletePackageInstallationPlugin.class.php b/wcfsetup/install/files/lib/system/package/plugin/FileDeletePackageInstallationPlugin.class.php index 89138fb1df..66b192aae1 100644 --- a/wcfsetup/install/files/lib/system/package/plugin/FileDeletePackageInstallationPlugin.class.php +++ b/wcfsetup/install/files/lib/system/package/plugin/FileDeletePackageInstallationPlugin.class.php @@ -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; + return 'filename'; } }