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