d8404f7d4c386fa8bb48ce47868f08ffb7196775
[GitHub/WoltLab/WCF.git] /
1 <?php
2 declare(strict_types=1);
3 namespace wcf\system\package\plugin;
4 use wcf\data\object\type\definition\ObjectTypeDefinitionEditor;
5 use wcf\data\object\type\ObjectTypeCache;
6 use wcf\system\devtools\pip\DevtoolsPipEntryList;
7 use wcf\system\devtools\pip\IDevtoolsPipEntryList;
8 use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
9 use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
10 use wcf\system\form\builder\field\validation\FormFieldValidationError;
11 use wcf\system\form\builder\field\validation\FormFieldValidator;
12 use wcf\system\form\builder\field\TextFormField;
13 use wcf\system\form\builder\IFormDocument;
14 use wcf\system\WCF;
15
16 /**
17 * Installs, updates and deletes object type definitions.
18 *
19 * @author Alexander Ebert
20 * @copyright 2001-2018 WoltLab GmbH
21 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
22 * @package WoltLabSuite\Core\Acp\Package\Plugin
23 */
24 class ObjectTypeDefinitionPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin {
25 use TXmlGuiPackageInstallationPlugin;
26
27 /**
28 * @inheritDoc
29 */
30 public $className = ObjectTypeDefinitionEditor::class;
31
32 /**
33 * @inheritDoc
34 */
35 public $tagName = 'definition';
36
37 /**
38 * @inheritDoc
39 */
40 protected function handleDelete(array $items) {
41 $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName."
42 WHERE definitionName = ?
43 AND packageID = ?";
44 $statement = WCF::getDB()->prepareStatement($sql);
45 foreach ($items as $item) {
46 $statement->execute([
47 $item['attributes']['name'],
48 $this->installation->getPackageID()
49 ]);
50 }
51 }
52
53 /**
54 * @inheritDoc
55 */
56 protected function prepareImport(array $data) {
57 return [
58 'interfaceName' => isset($data['elements']['interfacename']) ? $data['elements']['interfacename'] : '',
59 'definitionName' => $data['elements']['name'],
60 'categoryName' => isset($data['elements']['categoryname']) ? $data['elements']['categoryname'] : ''
61 ];
62 }
63
64 /**
65 * @inheritDoc
66 */
67 protected function findExistingItem(array $data) {
68 $sql = "SELECT *
69 FROM wcf".WCF_N."_".$this->tableName."
70 WHERE definitionName = ?";
71 $parameters = [$data['definitionName']];
72
73 return [
74 'sql' => $sql,
75 'parameters' => $parameters
76 ];
77 }
78
79 /**
80 * @inheritDoc
81 * @since 3.1
82 */
83 public static function getSyncDependencies() {
84 return [];
85 }
86
87 /**
88 * @inheritDoc
89 * @since 3.2
90 */
91 public function addFormFields(IFormDocument $form) {
92 $form->getNodeById('data')->appendChildren([
93 TextFormField::create('name')
94 ->label('wcf.acp.pip.objectTypeDefinition.definitionName')
95 ->description('wcf.acp.pip.objectTypeDefinition.definitionName.description', ['project' => $this->installation->getProject()])
96 ->required()
97 ->addValidator(new FormFieldValidator('format', function(TextFormField $formField) {
98 if ($formField->getValue()) {
99 $segments = explode('.', $formField->getValue());
100 if (count($segments) < 4) {
101 $formField->addValidationError(
102 new FormFieldValidationError(
103 'tooFewSegments',
104 'wcf.acp.pip.objectTypeDefinition.definitionName.error.tooFewSegments',
105 ['segmentCount' => count($segments)]
106 )
107 );
108 }
109 else {
110 $invalidSegments = [];
111 foreach ($segments as $key => $segment) {
112 if (!preg_match('~^[A-z0-9\-\_]+$~', $segment)) {
113 $invalidSegments[$key] = $segment;
114 }
115 }
116
117 if (!empty($invalidSegments)) {
118 $formField->addValidationError(
119 new FormFieldValidationError(
120 'invalidSegments',
121 'wcf.acp.pip.objectTypeDefinition.definitionName.error.invalidSegments',
122 ['invalidSegments' => $invalidSegments]
123 )
124 );
125 }
126 }
127 }
128 }))
129 ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) {
130 if ($formField->getValue()) {
131 $objectTypeDefinition = ObjectTypeCache::getInstance()->getDefinitionByName($formField->getValue());
132
133 // the definition name is not unique if such an object type definition
134 // already exists and (a) a new definition is added or (b) an existing
135 // definition is edited but the new definition name is not the old definition
136 // name so that the existing definition is not the definition currently edited
137 if ($objectTypeDefinition !== null && (
138 $formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE ||
139 $this->editedEntry->getElementsByTagName('name')->item(0)->nodeValue !== $formField->getValue()
140 )) {
141 $formField->addValidationError(
142 new FormFieldValidationError(
143 'notUnique',
144 'wcf.acp.pip.objectTypeDefinition.definitionName.error.notUnique'
145 )
146 );
147 }
148 }
149 })),
150
151 TextFormField::create('interfaceName')
152 ->objectProperty('interfacename')
153 ->label('wcf.acp.pip.objectTypeDefinition.interfaceName')
154 ->description('wcf.acp.pip.objectTypeDefinition.interfaceName.description')
155 ->addValidator(new FormFieldValidator('interfaceExists', function(TextFormField $formField) {
156 if ($formField->getValue() && !interface_exists($formField->getValue())) {
157 $formField->addValidationError(
158 new FormFieldValidationError(
159 'nonExistent',
160 'wcf.acp.pip.objectTypeDefinition.interfaceName.error.nonExistent'
161 )
162 );
163 }
164 }))
165 ]);
166 }
167
168 /**
169 * @inheritDoc
170 * @since 3.2
171 */
172 protected function getElementData(\DOMElement $element, bool $saveData = false): array {
173 $data = [
174 'definitionName' => $element->getElementsByTagName('name')->item(0)->nodeValue,
175 'packageID' => $this->installation->getPackage()->packageID
176 ];
177
178 $interfaceName = $element->getElementsByTagName('interfacename')->item(0);
179 if ($interfaceName) {
180 $data['interfaceName'] = $interfaceName->nodeValue;
181 }
182
183 return $data;
184 }
185
186 /**
187 * @inheritDoc
188 * @since 3.2
189 */
190 public function getElementIdentifier(\DOMElement $element): string {
191 return $element->getElementsByTagName('name')->item(0)->nodeValue;
192 }
193
194 /**
195 * @inheritDoc
196 * @since 3.2
197 */
198 protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) {
199 $entryList->setKeys([
200 'definitionName' => 'wcf.acp.pip.objectTypeDefinition.definitionName',
201 'interfaceName' => 'wcf.acp.pip.objectTypeDefinition.interfaceName'
202 ]);
203 }
204
205 /**
206 * @inheritDoc
207 * @since 3.2
208 */
209 protected function sortDocument(\DOMDocument $document) {
210 $this->sortImportDelete($document);
211
212 $this->sortChildNodes($document->getElementsByTagName('import'), function(\DOMElement $element1, \DOMElement $element2) {
213 return strcmp(
214 $element1->getElementsByTagName('name')->item(0)->nodeValue,
215 $element2->getElementsByTagName('name')->item(0)->nodeValue
216 );
217 });
218
219 $this->sortChildNodes($document->getElementsByTagName('delete'), function(\DOMElement $element1, \DOMElement $element2) {
220 return strcmp(
221 $element1->getAttribute('name'),
222 $element2->getAttribute('name')
223 );
224 });
225 }
226
227 /**
228 * @inheritDoc
229 * @since 3.2
230 */
231 protected function writeEntry(\DOMDocument $document, IFormDocument $form): \DOMElement {
232 $definition = $document->createElement('definition');
233 $definition->appendChild($document->createElement('name', $form->getNodeById('name')->getSaveValue()));
234
235 /** @var TextFormField $interfaceName */
236 $interfaceName = $form->getNodeById('interfaceName');
237 if ($interfaceName->getSaveValue()) {
238 $definition->appendChild($document->createElement('interfacename', $interfaceName->getSaveValue()));
239 }
240
241 $import = $document->getElementsByTagName('import')->item(0);
242 $import->appendChild($definition);
243
244 return $definition;
245 }
246 }