2 namespace wcf\system\package\plugin;
3 use wcf\data\object\type\ObjectTypeCache;
4 use wcf\data\user\notification\event\UserNotificationEvent;
5 use wcf\data\user\notification\event\UserNotificationEventEditor;
6 use wcf\data\user\notification\event\UserNotificationEventList;
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\exception\SystemException;
11 use wcf\system\form\builder\container\FormContainer;
12 use wcf\system\form\builder\field\OptionFormField;
13 use wcf\system\form\builder\field\UserGroupOptionFormField;
14 use wcf\system\form\builder\field\validation\FormFieldValidationError;
15 use wcf\system\form\builder\field\validation\FormFieldValidator;
16 use wcf\system\form\builder\field\BooleanFormField;
17 use wcf\system\form\builder\field\ClassNameFormField;
18 use wcf\system\form\builder\field\ItemListFormField;
19 use wcf\system\form\builder\field\SingleSelectionFormField;
20 use wcf\system\form\builder\field\TextFormField;
21 use wcf\system\form\builder\IFormDocument;
22 use wcf\system\user\notification\event\IUserNotificationEvent;
24 use wcf\util\StringUtil;
27 * Installs, updates and deletes user notification events.
29 * @author Matthias Schmidt, Marcel Werk
30 * @copyright 2001-2018 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 UserNotificationEventPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin implements IGuiPackageInstallationPlugin {
35 use TXmlGuiPackageInstallationPlugin;
40 public $className = UserNotificationEventEditor::class;
45 public $tableName = 'user_notification_event';
50 public $tagName = 'event';
56 protected $presetEventIDs = [];
61 protected function handleDelete(array $items) {
62 $sql = "DELETE FROM wcf".WCF_N."_".$this->tableName."
66 $statement = WCF::getDB()->prepareStatement($sql);
67 foreach ($items as $item) {
69 $this->installation->getPackageID(),
70 $this->getObjectTypeID($item['elements']['objecttype']),
71 $item['elements']['name']
79 protected function prepareImport(array $data) {
80 $presetMailNotificationType = 'none';
81 if (isset($data['elements']['presetmailnotificationtype']) && ($data['elements']['presetmailnotificationtype'] == 'instant' || $data['elements']['presetmailnotificationtype'] == 'daily')) {
82 $presetMailNotificationType = $data['elements']['presetmailnotificationtype'];
86 'eventName' => $data['elements']['name'],
87 'className' => $data['elements']['classname'],
88 'objectTypeID' => $this->getObjectTypeID($data['elements']['objecttype']),
89 'permissions' => isset($data['elements']['permissions']) ? StringUtil::normalizeCsv($data['elements']['permissions']) : '',
90 'options' => isset($data['elements']['options']) ? StringUtil::normalizeCsv($data['elements']['options']) : '',
91 'preset' => !empty($data['elements']['preset']) ? 1 : 0,
92 'presetMailNotificationType' => $presetMailNotificationType
99 protected function import(array $row, array $data) {
100 /** @var UserNotificationEvent $event */
101 $event = parent::import($row, $data);
103 if (empty($row) && $data['preset']) {
104 $this->presetEventIDs[$event->eventID] = $data['presetMailNotificationType'];
113 protected function cleanup() {
114 if (empty($this->presetEventIDs)) return;
116 $sql = "INSERT IGNORE INTO wcf".WCF_N."_user_notification_event_to_user
117 (userID, eventID, mailNotificationType)
119 FROM wcf".WCF_N."_user";
120 $statement = WCF::getDB()->prepareStatement($sql);
121 WCF::getDB()->beginTransaction();
122 foreach ($this->presetEventIDs as $eventID => $mailNotificationType) {
123 $statement->execute([$eventID, $mailNotificationType]);
125 WCF::getDB()->commitTransaction();
131 protected function findExistingItem(array $data) {
133 FROM wcf".WCF_N."_".$this->tableName."
134 WHERE objectTypeID = ?
137 $data['objectTypeID'],
143 'parameters' => $parameters
148 * Gets the id of given object type id.
150 * @param string $objectType
152 * @throws SystemException
154 protected function getObjectTypeID($objectType) {
155 // get object type id
156 $sql = "SELECT object_type.objectTypeID
157 FROM wcf".WCF_N."_object_type object_type
158 WHERE object_type.objectType = ?
159 AND object_type.definitionID IN (
161 FROM wcf".WCF_N."_object_type_definition
162 WHERE definitionName = 'com.woltlab.wcf.notification.objectType'
164 $statement = WCF::getDB()->prepareStatement($sql, 1);
165 $statement->execute([$objectType]);
166 $row = $statement->fetchArray();
167 if (empty($row['objectTypeID'])) throw new SystemException("unknown notification object type '".$objectType."' given");
168 return $row['objectTypeID'];
175 public static function getSyncDependencies() {
176 return ['objectType'];
183 public function addFormFields(IFormDocument $form) {
184 /** @var FormContainer $dataContainer */
185 $dataContainer = $form->getNodeById('data');
187 $dataContainer->appendChildren([
188 TextFormField::create('name')
189 ->label('wcf.acp.pip.userNotificationEvent.name')
190 ->description('wcf.acp.pip.userNotificationEvent.name.description')
192 ->addValidator(new FormFieldValidator('format', function(TextFormField $formField) {
193 if (!preg_match('~^[a-z][A-z]+$~', $formField->getValue())) {
194 $formField->addValidationError(
195 new FormFieldValidationError(
197 'wcf.acp.pip.userNotificationEvent.name.error.format'
203 SingleSelectionFormField::create('objectType')
204 ->objectProperty('objecttype')
205 ->label('wcf.acp.pip.userNotificationEvent.objectType')
206 ->description('wcf.acp.pip.userNotificationEvent.objectType.description')
208 ->options(function() {
210 foreach (ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.notification.objectType') as $objectType) {
211 $options[$objectType->objectType] = $objectType->objectType;
218 // validate the uniqueness of the `name` field after knowing that the selected object type is valid
219 ->addValidator(new FormFieldValidator('nameUniqueness', function(SingleSelectionFormField $formField) {
220 /** @var TextFormField $nameField */
221 $nameField = $formField->getDocument()->getNodeById('name');
223 if ($formField->getDocument()->getFormMode() === IFormDocument::FORM_MODE_CREATE || $this->editedEntry->getAttribute('name') !== $nameField->getSaveValue()) {
224 $eventList = new UserNotificationEventList();
225 $eventList->getConditionBuilder()->add('user_notification_event.eventName = ?', [$nameField->getSaveValue()]);
226 $eventList->getConditionBuilder()->add(
227 'user_notification_event.objectTypeID = ?',
228 [ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.notification.objectType', $formField->getSaveValue())->objectTypeID]
231 if ($eventList->countObjects() > 0) {
232 $nameField->addValidationError(
233 new FormFieldValidationError(
235 'wcf.acp.pip.userNotificationEvent.name.error.notUnique'
242 ClassNameFormField::create()
243 ->objectProperty('classname')
245 ->implementedInterface(IUserNotificationEvent::class),
247 BooleanFormField::create('preset')
248 ->label('wcf.acp.pip.userNotificationEvent.preset')
249 ->description('wcf.acp.pip.userNotificationEvent.preset.description'),
251 OptionFormField::create()
252 ->description('wcf.acp.pip.userNotificationEvent.options.description')
253 ->packageIDs(array_merge(
254 [$this->installation->getPackage()->packageID],
255 array_keys($this->installation->getPackage()->getAllRequiredPackages())
258 UserGroupOptionFormField::create()
259 ->description('wcf.acp.pip.userNotificationEvent.permissions.description')
260 ->packageIDs(array_merge(
261 [$this->installation->getPackage()->packageID],
262 array_keys($this->installation->getPackage()->getAllRequiredPackages())
265 SingleSelectionFormField::create('presetMailNotificationType')
266 ->objectProperty('presetmailnotificationtype')
267 ->label('wcf.acp.pip.userNotificationEvent.presetMailNotificationType')
268 ->description('wcf.acp.pip.userNotificationEvent.presetMailNotificationType.description')
271 '' => 'wcf.user.notification.mailNotificationType.none',
272 'daily' => 'wcf.user.notification.mailNotificationType.daily',
273 'instant' => 'wcf.user.notification.mailNotificationType.instant'
282 protected function getElementData(\DOMElement $element, $saveData = false) {
284 'className' => $element->getElementsByTagName('classname')->item(0)->nodeValue,
285 'objectTypeID' => $this->getObjectTypeID($element->getElementsByTagName('objecttype')->item(0)->nodeValue),
286 'eventName' => $element->getElementsByTagName('name')->item(0)->nodeValue,
287 'packageID' => $this->installation->getPackage()->packageID,
291 $options = $element->getElementsByTagName('options')->item(0);
293 $data['options'] = StringUtil::normalizeCsv($options->nodeValue);
296 $permissions = $element->getElementsByTagName('permissions')->item(0);
298 $data['permissions'] = StringUtil::normalizeCsv($permissions->nodeValue);
301 // the presence of a `preset` element is treated as `<preset>1</preset>
302 if ($element->getElementsByTagName('preset')->length === 1) {
306 $presetMailNotificationType = $element->getElementsByTagName('presetmailnotificationtype')->item(0);
307 if ($presetMailNotificationType && in_array($presetMailNotificationType->nodeValue, ['instant', 'daily'])) {
308 $data['presetMailNotificationType'] = $presetMailNotificationType->nodeValue;
318 public function getElementIdentifier(\DOMElement $element) {
320 $element->getElementsByTagName('name')->item(0)->nodeValue . '/' .
321 $element->getElementsByTagName('objecttype')->item(0)->nodeValue
329 protected function setEntryListKeys(IDevtoolsPipEntryList $entryList) {
330 $entryList->setKeys([
331 'name' => 'wcf.acp.pip.userNotificationEvent.name',
332 'className' => 'wcf.acp.pip.userNotificationEvent.className'
340 protected function sortDocument(\DOMDocument $document) {
341 $this->sortImportDelete($document);
343 $compareFunction = function(\DOMElement $element1, \DOMElement $element2) {
344 $objectType1 = $element1->getElementsByTagName('objecttype')->item(0)->nodeValue;
345 $objectType2 = $element2->getElementsByTagName('objecttype')->item(0)->nodeValue;
347 if ($objectType1 !== $objectType2) {
348 return strcmp($objectType1, $objectType2);
352 $element1->getElementsByTagName('name')->item(0)->nodeValue,
353 $element2->getElementsByTagName('name')->item(0)->nodeValue
357 $this->sortChildNodes($document->getElementsByTagName('import'), $compareFunction);
358 $this->sortChildNodes($document->getElementsByTagName('delete'), $compareFunction);
365 protected function writeEntry(\DOMDocument $document, IFormDocument $form) {
366 $data = $form->getData()['data'];
368 $event = $document->createElement($this->tagName);
370 foreach (['name', 'objecttype', 'classname'] as $element) {
372 $document->createElement(
374 (string)$data[$element]
379 foreach (['options', 'permissions', 'preset', 'presetmailnotificationtype'] as $optionalElement) {
380 if (!empty($data[$optionalElement])) {
382 $document->createElement(
384 (string)$data[$optionalElement]
390 $document->getElementsByTagName('import')->item(0)->appendChild($event);