f2d969047ef47ca6b9b0e856a87de97d01894194
[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\container\FormContainer;
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 /** @var FormContainer $dataContainer */
93 $dataContainer = $form->getNodeById('data');
94
95 $dataContainer->appendChildren([
96 TextFormField::create('name')
97 ->label('wcf.acp.pip.objectTypeDefinition.definitionName')
98 ->description('wcf.acp.pip.objectTypeDefinition.definitionName.description', ['project' => $this->installation->getProject()])
99 ->required()
100 ->addValidator(new FormFieldValidator('format', function(TextFormField $formField) {
101 if ($formField->getValue()) {
102 $segments = explode('.', $formField->getValue());
103 if (count($segments) < 4) {
104 $formField->addValidationError(
105 new FormFieldValidationError(
106 'tooFewSegments',
107 'wcf.acp.pip.objectTypeDefinition.definitionName.error.tooFewSegments',
108 ['segmentCount' => count($segments)]
109 )
110 );
111 }
112 else {
113 $invalidSegments = [];
114 foreach ($segments as $key => $segment) {
115 if (!preg_match('~^[A-z0-9\-\_]+$~', $segment)) {
116 $invalidSegments[$key] = $segment;
117 }
118 }
119
120 if (!empty($invalidSegments)) {
121 $formField->addValidationError(
122 new FormFieldValidationError(
123 'invalidSegments',
124 'wcf.acp.pip.objectTypeDefinition.definitionName.error.invalidSegments',
125 ['invalidSegments' => $invalidSegments]
126 )
127 );
128 }
129 }
130 }
131 }))
132 ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) {
133 if ($formField->getValue()) {
134 $objectTypeDefinition = ObjectTypeCache::getInstance()->getDefinitionByName($formField->getValue());
135
136 // the definition name is not unique if such an object type definition
137 // already exists and (a) a new definition is added or (b) an existing
138 // definition is edited but the new definition name is not the old definition
139 // name so that the existing definition is not the definition currently edited
140 if ($objectTypeDefinition !== null && (
141 $formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE ||
142 $this->editedEntry->getElementsByTagName('name')->item(0)->nodeValue !== $formField->getValue()
143 )) {
144 $formField->addValidationError(
145 new FormFieldValidationError(
146 'notUnique',
147 'wcf.acp.pip.objectTypeDefinition.definitionName.error.notUnique'
148 )
149 );
150 }
151 }
152 })),
153
154 TextFormField::create('interfaceName')
155 ->objectProperty('interfacename')
156 ->label('wcf.acp.pip.objectTypeDefinition.interfaceName')
157 ->description('wcf.acp.pip.objectTypeDefinition.interfaceName.description')
158 ->addValidator(new FormFieldValidator('interfaceExists', function(TextFormField $formField) {
159 if ($formField->getValue() && !interface_exists($formField->getValue())) {
160 $formField->addValidationError(
161 new FormFieldValidationError(
162 'nonExistent',
163 'wcf.acp.pip.objectTypeDefinition.interfaceName.error.nonExistent'
164 )
165 );
166 }
167 }))
168 ]);
169 }
170
171 /**
172 * @inheritDoc
173 * @since 3.2
174 */
175 protected function getElementData(\DOMElement $element, bool $saveData = false) {
176 $data = [
177 'definitionName' => $element->getElementsByTagName('name')->item(0)->nodeValue,
178 'packageID' => $this->installation->getPackage()->packageID
179 ];
180
181 $interfaceName = $element->getElementsByTagName('interfacename')->item(0);
182 if ($interfaceName) {
183 $data['interfaceName'] = $interfaceName->nodeValue;
184 }
185
186 return $data;
187 }
188
189 /**
190 * @inheritDoc
191 * @since 3.2
192 */
193 public function getElementIdentifier(\DOMElement $element) {
194 return $element->getElementsByTagName('name')->item(0)->nodeValue;
195 }
196
197 /**
198 * @inheritDoc
199 * @since 3.2
200 */
201 protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) {
202 $entryList->setKeys([
203 'definitionName' => 'wcf.acp.pip.objectTypeDefinition.definitionName',
204 'interfaceName' => 'wcf.acp.pip.objectTypeDefinition.interfaceName'
205 ]);
206 }
207
208 /**
209 * @inheritDoc
210 * @since 3.2
211 */
212 protected function sortDocument(\DOMDocument $document) {
213 $this->sortImportDelete($document);
214
215 $this->sortChildNodes($document->getElementsByTagName('import'), function(\DOMElement $element1, \DOMElement $element2) {
216 return strcmp(
217 $element1->getElementsByTagName('name')->item(0)->nodeValue,
218 $element2->getElementsByTagName('name')->item(0)->nodeValue
219 );
220 });
221
222 $this->sortChildNodes($document->getElementsByTagName('delete'), function(\DOMElement $element1, \DOMElement $element2) {
223 return strcmp(
224 $element1->getAttribute('name'),
225 $element2->getAttribute('name')
226 );
227 });
228 }
229
230 /**
231 * @inheritDoc
232 * @since 3.2
233 */
234 protected function writeEntry(\DOMDocument $document, IFormDocument $form) {
235 $data = $form->getData()['data'];
236
237 $definition = $document->createElement('definition');
238 $definition->appendChild($document->createElement('name', $data['name']));
239
240 if (!empty($data['interfacename'])) {
241 $definition->appendChild($document->createElement('interfacename', $data['interfacename']));
242 }
243
244 $import = $document->getElementsByTagName('import')->item(0);
245 $import->appendChild($definition);
246
247 return $definition;
248 }
249 }