Commit | Line | Data |
---|---|---|
11ade432 AE |
1 | <?php |
2 | namespace wcf\system\package\plugin; | |
e0497659 MS |
3 | use wcf\data\event\listener\EventListener; |
4 | use wcf\data\event\listener\EventListenerEditor; | |
efbc545e | 5 | use wcf\data\event\listener\EventListenerList; |
b401cd0d | 6 | use wcf\system\cache\builder\EventListenerCacheBuilder; |
efbc545e MS |
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\event\listener\IParameterizedEventListener; | |
11 | use wcf\system\form\builder\container\FormContainer; | |
12 | use wcf\system\form\builder\field\BooleanFormField; | |
13 | use wcf\system\form\builder\field\ClassNameFormField; | |
14 | use wcf\system\form\builder\field\IntegerFormField; | |
8a53406e | 15 | use wcf\system\form\builder\field\ItemListFormField; |
35d95be3 | 16 | use wcf\system\form\builder\field\option\OptionFormField; |
efbc545e MS |
17 | use wcf\system\form\builder\field\SingleSelectionFormField; |
18 | use wcf\system\form\builder\field\TextFormField; | |
35d95be3 | 19 | use wcf\system\form\builder\field\user\group\option\UserGroupOptionFormField; |
efbc545e MS |
20 | use wcf\system\form\builder\field\validation\FormFieldValidationError; |
21 | use wcf\system\form\builder\field\validation\FormFieldValidator; | |
22 | use wcf\system\form\builder\IFormDocument; | |
11ade432 | 23 | use wcf\system\WCF; |
4f2d3f58 | 24 | use 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 |
34 | class 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 | } |