2 namespace wcf\system\package\plugin
;
3 use wcf\data\event\listener\EventListener
;
4 use wcf\data\event\listener\EventListenerEditor
;
5 use wcf\data\event\listener\EventListenerList
;
6 use wcf\system\cache\builder\EventListenerCacheBuilder
;
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
;
15 use wcf\system\form\builder\field\ItemListFormField
;
16 use wcf\system\form\builder\field\option\OptionFormField
;
17 use wcf\system\form\builder\field\SingleSelectionFormField
;
18 use wcf\system\form\builder\field\TextFormField
;
19 use wcf\system\form\builder\field\user\group\option\UserGroupOptionFormField
;
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
;
24 use wcf\util\StringUtil
;
27 * Installs, updates and deletes event listeners.
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
34 class EventListenerPackageInstallationPlugin
extends AbstractXMLPackageInstallationPlugin
implements IGuiPackageInstallationPlugin
{
35 use TXmlGuiPackageInstallationPlugin
;
40 public $className = EventListenerEditor
::class;
45 public $tagName = 'eventlistener';
50 protected function handleDelete(array $items) {
51 $sql = "DELETE FROM wcf".WCF_N
."_".$this->tableName
."
54 AND eventClassName = ?
57 AND listenerClassName = ?";
58 $legacyStatement = WCF
::getDB()->prepareStatement($sql);
60 $sql = "DELETE FROM wcf".WCF_N
."_".$this->tableName
."
62 AND listenerName = ?";
63 $statement = WCF
::getDB()->prepareStatement($sql);
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']
78 $this->installation
->getPackageID(),
79 $item['attributes']['name']
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;
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']) : ''
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']);
116 /** @var EventListener $eventListener */
117 $eventListener = parent
::import($row, $data);
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
126 $eventListener = new EventListener($eventListener->listenerID
);
129 return $eventListener;
135 protected function findExistingItem(array $data) {
136 if (!$data['listenerName']) {
138 FROM wcf".WCF_N
."_".$this->tableName
."
141 AND eventClassName = ?
143 AND listenerClassName = ?";
145 $this->installation
->getPackageID(),
146 $data['environment'],
147 $data['eventClassName'],
149 $data['listenerClassName']
154 FROM wcf".WCF_N
."_".$this->tableName
."
156 AND listenerName = ?";
158 $this->installation
->getPackageID(),
159 $data['listenerName']
165 'parameters' => $parameters
172 public function uninstall() {
175 // clear cache immediately
176 EventListenerCacheBuilder
::getInstance()->reset();
183 public static function getSyncDependencies() {
191 protected function addFormFields(IFormDocument
$form) {
192 /** @var FormContainer $dataContainer */
193 $dataContainer = $form->getNodeById('data');
195 $dataContainer->appendChildren([
196 TextFormField
::create('listenerName')
197 ->label('wcf.acp.pip.eventListener.listenerName')
198 ->description('wcf.acp.pip.eventListener.listenerName.description')
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(
205 'wcf.acp.pip.eventListener.listenerName.error.format'
210 ->addValidator(new FormFieldValidator('uniqueness', function(TextFormField
$formField) {
212 $formField->getDocument()->getFormMode() === IFormDocument
::FORM_MODE_CREATE ||
213 $this->editedEntry
->getAttribute('name') !== $formField->getValue()
215 $eventListenerList = new EventListenerList();
216 $eventListenerList->getConditionBuilder()->add('listenerName = ?', [$formField->getValue()]);
217 $eventListenerList->getConditionBuilder()->add('packageID = ?', [$this->installation
->getPackageID()]);
219 if ($eventListenerList->countObjects() > 0) {
220 $formField->addValidationError(
221 new FormFieldValidationError(
223 'wcf.acp.pip.eventListener.listenerName.error.notUnique'
230 ClassNameFormField
::create('eventClassName')
231 ->objectProperty('eventclassname')
232 ->label('wcf.acp.pip.eventListener.eventClassName')
233 ->description('wcf.acp.pip.eventListener.eventClassName.description')
235 ->instantiable(false),
237 ItemListFormField
::create('eventName')
238 ->objectProperty('eventname')
239 ->label('wcf.acp.pip.eventListener.eventName')
240 ->description('wcf.acp.pip.eventListener.eventName.description')
243 ClassNameFormField
::create('listenerClassName')
244 ->objectProperty('listenerclassname')
245 ->label('wcf.acp.pip.eventListener.listenerClassName')
247 ->implementedInterface(IParameterizedEventListener
::class),
249 SingleSelectionFormField
::create('environment')
250 ->label('wcf.acp.pip.eventListener.environment')
251 ->description('wcf.acp.pip.eventListener.environment.description')
259 BooleanFormField
::create('inherit')
260 ->label('wcf.acp.pip.eventListener.inherit')
261 ->description('wcf.acp.pip.eventListener.inherit.description'),
263 IntegerFormField
::create('niceValue')
264 ->objectProperty('nice')
265 ->label('wcf.acp.pip.eventListener.niceValue')
266 ->description('wcf.acp.pip.eventListener.niceValue.description')
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())
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())
291 protected function fetchElementData(\DOMElement
$element, $saveData) {
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
300 foreach (['environment', 'inherit', 'options', 'permissions'] as $optionalElementProperty) {
301 $optionalElement = $element->getElementsByTagName($optionalElementProperty)->item(0);
302 if ($optionalElement !== null) {
303 $data[$optionalElementProperty] = $optionalElement->nodeValue
;
305 else if ($saveData) {
306 switch ($optionalElementProperty) {
308 $data[$optionalElementProperty] = 'user';
312 $data[$optionalElementProperty] = 0;
317 $data[$optionalElementProperty] = '';
323 $niceValue = $element->getElementsByTagName('nice')->item(0);
324 if ($niceValue !== null) {
325 $data['niceValue'] = $niceValue->nodeValue
;
327 else if ($saveData) {
328 $data['niceValue'] = 0;
338 public function getElementIdentifier(\DOMElement
$element) {
339 return $element->getAttribute('name');
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'
358 protected function prepareXmlElement(\DOMDocument
$document, IFormDocument
$form) {
359 $data = $form->getData()['data'];
361 $eventListener = $document->createElement($this->tagName
);
362 $eventListener->setAttribute('name', $data['listenerName']);
364 $this->appendElementChildren(
370 'environment' => 'user',
379 return $eventListener;