Merge branch '5.2' into 5.3
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / package / plugin / EventListenerPackageInstallationPlugin.class.php
CommitLineData
11ade432
AE
1<?php
2namespace wcf\system\package\plugin;
e0497659
MS
3use wcf\data\event\listener\EventListener;
4use wcf\data\event\listener\EventListenerEditor;
efbc545e 5use wcf\data\event\listener\EventListenerList;
b401cd0d 6use wcf\system\cache\builder\EventListenerCacheBuilder;
efbc545e
MS
7use wcf\system\devtools\pip\IDevtoolsPipEntryList;
8use wcf\system\devtools\pip\IGuiPackageInstallationPlugin;
9use wcf\system\devtools\pip\TXmlGuiPackageInstallationPlugin;
10use wcf\system\event\listener\IParameterizedEventListener;
11use wcf\system\form\builder\container\FormContainer;
12use wcf\system\form\builder\field\BooleanFormField;
13use wcf\system\form\builder\field\ClassNameFormField;
14use wcf\system\form\builder\field\IntegerFormField;
8a53406e 15use wcf\system\form\builder\field\ItemListFormField;
35d95be3 16use wcf\system\form\builder\field\option\OptionFormField;
efbc545e
MS
17use wcf\system\form\builder\field\SingleSelectionFormField;
18use wcf\system\form\builder\field\TextFormField;
35d95be3 19use wcf\system\form\builder\field\user\group\option\UserGroupOptionFormField;
efbc545e
MS
20use wcf\system\form\builder\field\validation\FormFieldValidationError;
21use wcf\system\form\builder\field\validation\FormFieldValidator;
22use wcf\system\form\builder\IFormDocument;
11ade432 23use wcf\system\WCF;
4f2d3f58 24use wcf\util\StringUtil;
11ade432
AE
25
26/**
a17de04e 27 * Installs, updates and deletes event listeners.
6e01c38f
TD
28 *
29 * @author Matthias Schmidt, Marcel Werk
30 * @copyright 2001-2021 WoltLab GmbH
31 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
32 * @package WoltLabSuite\Core\System\Package\Plugin
11ade432 33 */
6e01c38f
TD
34class EventListenerPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin {
35 use TXmlGuiPackageInstallationPlugin;
36
37 /**
38 * @inheritDoc
39 */
40 public $className = EventListenerEditor::class;
41
42 /**
43 * @inheritDoc
44 */
45 public $tagName = 'eventlistener';
46
47 /**
48 * @inheritDoc
49 */
50 protected function handleDelete(array $items) {
51 $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName."
52 WHERE packageID = ?
53 AND environment = ?
54 AND eventClassName = ?
55 AND eventName = ?
56 AND inherit = ?
57 AND listenerClassName = ?";
58 $legacyStatement = WCF::getDB()->prepareStatement($sql);
59
60 $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName."
61 WHERE packageID = ?
62 AND listenerName = ?";
63 $statement = WCF::getDB()->prepareStatement($sql);
64
65 foreach ($items as $item) {
66 if (!isset($item['attributes']['name'])) {
67 $legacyStatement->execute([
68 $this->installation->getPackageID(),
69 isset($item['elements']['environment']) ? $item['elements']['environment'] : 'user',
70 $item['elements']['eventclassname'],
71 $item['elements']['eventname'],
72 isset($item['elements']['inherit']) ? $item['elements']['inherit'] : 0,
73 $item['elements']['listenerclassname']
74 ]);
75 }
76 else {
77 $statement->execute([
78 $this->installation->getPackageID(),
79 $item['attributes']['name']
80 ]);
81 }
82 }
83 }
84
85 /**
86 * @inheritDoc
87 */
88 protected function prepareImport(array $data) {
89 $nice = isset($data['elements']['nice']) ? intval($data['elements']['nice']) : 0;
90 if ($nice < -128) $nice = -128;
91 else if ($nice > 127) $nice = 127;
92
93 return [
94 'environment' => isset($data['elements']['environment']) ? $data['elements']['environment'] : 'user',
95 'eventClassName' => $data['elements']['eventclassname'],
96 'eventName' => StringUtil::normalizeCsv($data['elements']['eventname']),
97 'inherit' => isset($data['elements']['inherit']) ? intval($data['elements']['inherit']) : 0,
98 'listenerClassName' => $data['elements']['listenerclassname'],
99 'listenerName' => isset($data['attributes']['name']) ? $data['attributes']['name'] : '',
100 'niceValue' => $nice,
101 'options' => isset($data['elements']['options']) ? StringUtil::normalizeCsv($data['elements']['options']) : '',
102 'permissions' => isset($data['elements']['permissions']) ? StringUtil::normalizeCsv($data['elements']['permissions']) : ''
103 ];
104 }
105
106 /**
107 * @inheritDoc
108 */
109 protected function import(array $row, array $data) {
110 // if an event listener is updated without a name given, keep the
111 // old automatically assigned name
112 if (!empty($row) && !$data['listenerName']) {
113 unset($data['listenerName']);
114 }
115
116 /** @var EventListener $eventListener */
117 $eventListener = parent::import($row, $data);
118
119 // update event listener name
120 if (!$eventListener->listenerName) {
121 $eventListenerEditor = new EventListenerEditor($eventListener);
122 $eventListenerEditor->update([
123 'listenerName' => EventListener::AUTOMATIC_NAME_PREFIX.$eventListener->listenerID
124 ]);
125
126 $eventListener = new EventListener($eventListener->listenerID);
127 }
128
129 return $eventListener;
130 }
131
132 /**
133 * @inheritDoc
134 */
135 protected function findExistingItem(array $data) {
136 if (!$data['listenerName']) {
137 $sql = "SELECT *
138 FROM wcf".WCF_N."_".$this->tableName."
139 WHERE packageID = ?
140 AND environment = ?
141 AND eventClassName = ?
142 AND eventName = ?
143 AND listenerClassName = ?";
144 $parameters = [
145 $this->installation->getPackageID(),
146 $data['environment'],
147 $data['eventClassName'],
148 $data['eventName'],
149 $data['listenerClassName']
150 ];
151 }
152 else {
153 $sql = "SELECT *
154 FROM wcf".WCF_N."_".$this->tableName."
155 WHERE packageID = ?
156 AND listenerName = ?";
157 $parameters = [
158 $this->installation->getPackageID(),
159 $data['listenerName']
160 ];
161 }
162
163 return [
164 'sql' => $sql,
165 'parameters' => $parameters
166 ];
167 }
168
169 /**
170 * @inheritDoc
171 */
172 public function uninstall() {
173 parent::uninstall();
174
175 // clear cache immediately
176 EventListenerCacheBuilder::getInstance()->reset();
177 }
178
179 /**
180 * @inheritDoc
181 * @since 3.1
182 */
183 public static function getSyncDependencies() {
184 return [];
185 }
186
187 /**
188 * @inheritDoc
189 * @since 5.2
190 */
191 protected function addFormFields(IFormDocument $form) {
192 /** @var FormContainer $dataContainer */
193 $dataContainer = $form->getNodeById('data');
194
195 $dataContainer->appendChildren([
196 TextFormField::create('listenerName')
197 ->label('wcf.acp.pip.eventListener.listenerName')
198 ->description('wcf.acp.pip.eventListener.listenerName.description')
199 ->required()
200 ->addValidator(new FormFieldValidator('format', function(TextFormField $formField) {
201 if (preg_match('~^[a-z][A-z0-9]*$~', $formField->getSaveValue()) !== 1) {
202 $formField->addValidationError(
203 new FormFieldValidationError(
204 'format',
205 'wcf.acp.pip.eventListener.listenerName.error.format'
206 )
207 );
208 }
209 }))
210 ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField $formField) {
211 if (
212 $formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE ||
213 $this->editedEntry->getAttribute('name') !== $formField->getValue()
214 ) {
215 $eventListenerList = new EventListenerList();
216 $eventListenerList->getConditionBuilder()->add('listenerName = ?', [$formField->getValue()]);
217 $eventListenerList->getConditionBuilder()->add('packageID = ?', [$this->installation->getPackageID()]);
218
219 if ($eventListenerList->countObjects() > 0) {
220 $formField->addValidationError(
221 new FormFieldValidationError(
222 'notUnique',
223 'wcf.acp.pip.eventListener.listenerName.error.notUnique'
224 )
225 );
226 }
227 }
228 })),
229
230 ClassNameFormField::create('eventClassName')
231 ->objectProperty('eventclassname')
232 ->label('wcf.acp.pip.eventListener.eventClassName')
233 ->description('wcf.acp.pip.eventListener.eventClassName.description')
234 ->required()
235 ->instantiable(false),
236
237 ItemListFormField::create('eventName')
238 ->objectProperty('eventname')
239 ->label('wcf.acp.pip.eventListener.eventName')
240 ->description('wcf.acp.pip.eventListener.eventName.description')
241 ->required(),
242
243 ClassNameFormField::create('listenerClassName')
244 ->objectProperty('listenerclassname')
245 ->label('wcf.acp.pip.eventListener.listenerClassName')
246 ->required()
247 ->implementedInterface(IParameterizedEventListener::class),
248
249 SingleSelectionFormField::create('environment')
250 ->label('wcf.acp.pip.eventListener.environment')
251 ->description('wcf.acp.pip.eventListener.environment.description')
252 ->options([
253 'admin' => 'admin',
254 'user' => 'user',
255 'all' => 'all',
256 ])
257 ->value('user'),
258
259 BooleanFormField::create('inherit')
260 ->label('wcf.acp.pip.eventListener.inherit')
261 ->description('wcf.acp.pip.eventListener.inherit.description'),
262
263 IntegerFormField::create('niceValue')
264 ->objectProperty('nice')
265 ->label('wcf.acp.pip.eventListener.niceValue')
266 ->description('wcf.acp.pip.eventListener.niceValue.description')
267 ->nullable()
268 ->minimum(-128)
269 ->maximum(127),
270
271 OptionFormField::create()
272 ->description('wcf.acp.pip.eventListener.options.description')
273 ->packageIDs(array_merge(
274 [$this->installation->getPackage()->packageID],
275 array_keys($this->installation->getPackage()->getAllRequiredPackages())
276 )),
277
278 UserGroupOptionFormField::create()
279 ->description('wcf.acp.pip.eventListener.permissions.description')
280 ->packageIDs(array_merge(
281 [$this->installation->getPackage()->packageID],
282 array_keys($this->installation->getPackage()->getAllRequiredPackages())
283 ))
284 ]);
285 }
286
287 /**
288 * @inheritDoc
289 * @since 5.2
290 */
291 protected function fetchElementData(\DOMElement $element, $saveData) {
292 $data = [
293 'eventClassName' => $element->getElementsByTagName('eventclassname')->item(0)->nodeValue,
294 'eventName' => StringUtil::normalizeCsv($element->getElementsByTagName('eventname')->item(0)->nodeValue),
295 'listenerClassName' => $element->getElementsByTagName('listenerclassname')->item(0)->nodeValue,
296 'listenerName' => $element->getAttribute('name'),
297 'packageID' => $this->installation->getPackage()->packageID
298 ];
299
300 foreach (['environment', 'inherit', 'options', 'permissions'] as $optionalElementProperty) {
301 $optionalElement = $element->getElementsByTagName($optionalElementProperty)->item(0);
302 if ($optionalElement !== null) {
303 $data[$optionalElementProperty] = $optionalElement->nodeValue;
304 }
305 else if ($saveData) {
306 switch ($optionalElementProperty) {
307 case 'environment':
308 $data[$optionalElementProperty] = 'user';
309 break;
310
311 case 'inherit':
312 $data[$optionalElementProperty] = 0;
313 break;
314
315 case 'options':
316 case 'permissions':
317 $data[$optionalElementProperty] = '';
318 break;
319 }
320 }
321 }
322
323 $niceValue = $element->getElementsByTagName('nice')->item(0);
324 if ($niceValue !== null) {
325 $data['niceValue'] = $niceValue->nodeValue;
326 }
327 else if ($saveData) {
328 $data['niceValue'] = 0;
329 }
330
331 return $data;
332 }
333
334 /**
335 * @inheritDoc
336 * @since 5.2
337 */
338 public function getElementIdentifier(\DOMElement $element) {
339 return $element->getAttribute('name');
340 }
341
342 /**
343 * @inheritDoc
344 * @since 5.2
345 */
346 protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) {
347 $entryList->setKeys([
348 'listenerName' => 'wcf.acp.pip.eventListener.listenerName',
349 'eventClassName' => 'wcf.acp.pip.eventListener.eventClassName',
350 'eventName' => 'wcf.acp.pip.eventListener.eventName'
351 ]);
352 }
353
354 /**
355 * @inheritDoc
356 * @since 5.2
357 */
358 protected function prepareXmlElement(\DOMDocument $document, IFormDocument $form) {
359 $data = $form->getData()['data'];
360
361 $eventListener = $document->createElement($this->tagName);
362 $eventListener->setAttribute('name', $data['listenerName']);
363
364 $this->appendElementChildren(
365 $eventListener,
366 [
367 'eventclassname',
368 'eventname',
369 'listenerclassname',
370 'environment' => 'user',
371 'inherit' => 0,
372 'nice' => null,
373 'options' => '',
374 'permissions' => ''
375 ],
376 $form
377 );
378
379 return $eventListener;
380 }
11ade432 381}