- User Notification Package
authorMarcel Werk <burntime@woltlab.com>
Thu, 14 Jul 2011 17:03:02 +0000 (19:03 +0200)
committerMarcel Werk <burntime@woltlab.com>
Thu, 14 Jul 2011 17:03:02 +0000 (19:03 +0200)
73 files changed:
.gitignore
com.woltlab.wcf.notification/acptemplates/userNotificationDefaultSettings.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/cronjobs.xml [new file with mode: 0644]
com.woltlab.wcf.notification/eventlistener.xml [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/acp/form/UserNotificationDefaultSettingsForm.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/action/NotificationConfirmAction.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/action/NotificationDisableAction.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/NotificationUser.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotification.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotificationList.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/NotificationList.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotification.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationAction.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationEditor.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationHandler.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationList.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationMessageCompiler.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/event/DefaultUserNotificationConveyableEvent.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationConveyableEvent.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEvent.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventAction.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventEditor.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventList.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessage.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessageEditor.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/AbstractUserNotificationConveyableObjectType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObject.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObjectType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectTypeEditor.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObject.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObjectType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeAction.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeEditor.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeList.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/type/MailUserNotificationReceivableType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationReceivableType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationType.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeAction.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeEditor.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeList.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/form/UserNotificationSettingsForm.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/page/UserNotificationCountPage.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/page/UserNotificationFeedPage.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/page/UserNotificationPage.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/system/cache/CacheBuilderNotifications.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/system/cronjob/CleanUpNotificationsCronjob.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserMessagesNotificationListener.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserPanelNotificationListener.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/files/lib/system/package/plugin/UserNotificationEventPackageInstallationPlugin.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/install.sql [new file with mode: 0644]
com.woltlab.wcf.notification/language/de-informal.xml [new file with mode: 0644]
com.woltlab.wcf.notification/language/de.xml [new file with mode: 0644]
com.woltlab.wcf.notification/language/en.xml [new file with mode: 0644]
com.woltlab.wcf.notification/notificationobjecttype.xml [new file with mode: 0644]
com.woltlab.wcf.notification/notificationtype.xml [new file with mode: 0644]
com.woltlab.wcf.notification/options.xml [new file with mode: 0644]
com.woltlab.wcf.notification/package.xml [new file with mode: 0644]
com.woltlab.wcf.notification/pagelocation.xml [new file with mode: 0644]
com.woltlab.wcf.notification/pip.xml [new file with mode: 0644]
com.woltlab.wcf.notification/pip/NotificationEventPackageInstallationPlugin.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/pip/NotificationObjectTypePackageInstallationPlugin.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/pip/NotificationTypePackageInstallationPlugin.class.php [new file with mode: 0644]
com.woltlab.wcf.notification/templates/userMenuNotificationLink.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/templates/userMenuPeriodicalExecuter.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/templates/userMessagesNotifications.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/templates/userNotification.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/templates/userNotificationFeedAtom.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/templates/userNotificationFeedRss2.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/templates/userNotificationSettings.tpl [new file with mode: 0644]
com.woltlab.wcf.notification/usercpmenu.xml [new file with mode: 0644]
com.woltlab.wcf.notification/useroptions.xml [new file with mode: 0644]

index cf5701f52fac356aefc182e85e0787ba4a1b8648..798c2fb7ee3e67154bca58590a2de086e1472eba 100644 (file)
@@ -2,4 +2,5 @@
 *.sln\r
 *.phpproj\r
 *.puo\r
-*.suo
\ No newline at end of file
+*.suo\r
+*.project
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/acptemplates/userNotificationDefaultSettings.tpl b/com.woltlab.wcf.notification/acptemplates/userNotificationDefaultSettings.tpl
new file mode 100644 (file)
index 0000000..33ce2a3
--- /dev/null
@@ -0,0 +1,75 @@
+{include file='header'}
+
+<div class="mainHeadline">
+       <img src="{@RELATIVE_WCF_DIR}icon/infoL.png" alt="" />
+       <div class="headlineContainer">
+               <h2>{lang}wcf.user.notification.settings{/lang}</h2>
+       </div>
+</div>
+
+{if $errorField}
+       <p class="error">{lang}wcf.global.form.error{/lang}</p>
+{/if}
+
+{if $success|isset && $success}
+       <p class="success">{lang}wcf.user.notification.settings.saved{/lang}</p>
+{/if}
+
+<form method="post" action="index.php?form=UserNotificationDefaultSettings">
+<div class="border tabMenuContent">
+                       <div class="container-1">
+                               <h3 class="subHeadline">{lang}wcf.user.notification.settings{/lang}</h3>
+
+                               <div class="border borderMarginRemove">
+                                       <table class="tableList">
+                                       {foreach from=$notificationObjectTypes key=typeName item=data}
+                                               {assign var=objectType value=$data.object}
+                                               {assign var=events value=$data.events}
+                                               {cycle name="eventRows" values='container-1,container-2' print=false advance=false reset=true}
+                                               <thead>
+                                                       <tr class="tableHead">
+                                                               <th class="columnObjectType">
+                                                                       <div>
+                                                                               <h3 class="emptyHead">{lang}wcf.user.notification.object.type.{$typeName}{/lang}</h3>
+                                                                       </div>
+                                                               </th>
+                                                               {foreach from=$notificationTypes key=notificationTypeName item=notificationType}
+                                                                       <th class="columnNotificationType columnIcon">
+                                                                               <div>
+                                                                                       <span class="emptyHead"><img src="{@RELATIVE_WCF_DIR}icon/{$notificationType->getIcon()}S.png" alt="{lang}wcf.user.notification.type.{$notificationTypeName}{/lang}" title="{lang}wcf.user.notification.type.{$notificationTypeName}{/lang}" /></span>
+                                                                               </div>
+                                                                       </th>
+                                                               {/foreach}
+                                                       </tr>
+                                               </thead>
+                                               <tbody>
+                                                       {foreach from=$events key=eventName item=event}
+                                                               <tr class="{cycle name="eventRows"}">
+                                                                       <td class="columnEvent columnText">
+                                                                               <p title="{$event->getDescription()}">{$event->getTitle()}</p>
+                                                                               <p class="smallFont">{$event->getDescription()}</p>
+                                                                       </td>
+                                                                       {foreach from=$event->supportedNotificationTypes key=supportedType item=supported}
+                                                                       <td class="columnNotificationType columnIcon">
+                                                                               <input value="1" type="checkbox" name="activeEventNotifications[{$typeName}][{$eventName}][{$supportedType}][enabled]"{if !$supported} disabled="disabled"{/if}{if $supported && $activeEventNotifications[$typeName][$eventName][$supportedType]['enabled']} checked="checked"{/if} />
+                                                                               <input value="1" type="checkbox" name="activeEventNotifications[{$typeName}][{$eventName}][{$supportedType}][canBeDisabled]"{if !$supported} disabled="disabled"{/if}{if $supported && $activeEventNotifications[$typeName][$eventName][$supportedType]['canBeDisabled']} checked="checked"{/if} />
+                                                                       </td>
+                                                                       {/foreach}
+                                                               </tr>
+                                                       {/foreach}
+                                               </tbody>
+                                       {/foreach}
+                                       </table>
+                               </div>
+                       </div>
+               </div>
+
+       <div class="formSubmit">
+               <input type="submit" accesskey="s" value="{lang}wcf.global.button.submit{/lang}" />
+               <input type="reset" accesskey="r" value="{lang}wcf.global.button.reset{/lang}" />
+               <input type="hidden" name="packageID" value="{@PACKAGE_ID}" />
+               {@SID_INPUT_TAG}
+       </div>
+</form>
+
+{include file='footer'}
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/cronjobs.xml b/com.woltlab.wcf.notification/cronjobs.xml
new file mode 100644 (file)
index 0000000..dbd9a49
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/cronjobs.xsd">
+       <import>
+               <cronjob>
+                       <classPath>lib/system/cronjob/CleanUpNotificationsCronjob.class.php</classPath>
+                       <description>Cleans up notifications regularly</description>
+                       <startMinute>*/30</startMinute>
+                       <startHour>*</startHour>
+                       <startDom>*</startDom>
+                       <startMonth>*</startMonth>
+                       <startDow>*</startDow>
+                       <execMultiple>0</execMultiple>
+                       <canbeedited>0</canbeedited>
+                       <canbedisabled>0</canbedisabled>
+                       <active>1</active>
+               </cronjob>
+       </import>
+</data>
diff --git a/com.woltlab.wcf.notification/eventlistener.xml b/com.woltlab.wcf.notification/eventlistener.xml
new file mode 100644 (file)
index 0000000..bdab03b
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/event-listener.xsd">
+       <import>
+               <eventlistener>
+                       <eventClassName>StructuredTemplate</eventClassName>
+                       <eventName>shouldDisplay</eventName>
+                       <listenerClassFile>lib/system/event/listener/StructuredTemplateUserMessagesNotificationListener.class.php</listenerClassFile>
+               </eventlistener>
+               <eventlistener>
+                       <eventClassName>StructuredTemplate</eventClassName>
+                       <eventName>shouldDisplay</eventName>
+                       <listenerClassFile>lib/system/event/listener/StructuredTemplateUserPanelNotificationListener.class.php</listenerClassFile>
+               </eventlistener>
+       </import>
+</data>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/acp/form/UserNotificationDefaultSettingsForm.class.php b/com.woltlab.wcf.notification/files/lib/acp/form/UserNotificationDefaultSettingsForm.class.php
new file mode 100644 (file)
index 0000000..2177f7c
--- /dev/null
@@ -0,0 +1,277 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/acp/form/ACPForm.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationHandler.class.php');
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+
+/**
+ * Shows the notification settings form.
+ *
+ * @author     Tim Düsterhus
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage form
+ * @category   Community Framework
+ */
+class UserNotificationDefaultSettingsForm extends ACPForm {
+       /**
+        * @see AbstractPage::$templateName
+        */
+       public $templateName = 'userNotificationDefaultSettings';
+
+       /**
+        * An array holding all available notification object types
+        *
+        * @var array
+        */
+       public $notificationObjectTypes = array();
+
+       /**
+        * An array holding all available notification types
+        *
+        * @var array
+        */
+       public $notificationTypes = array();
+
+       /**
+        * The activated event notifications in the form
+        *
+        * @var array
+        */
+       public $activeEventNotifications = array();
+
+       /**
+        * The notification user object of the current user
+        *
+        * @var NotificationUser
+        */
+       public $notificationUser = null;
+
+       /**
+        * @see Page::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+
+               $this->notificationUser = new NotificationUser(null, WCF::getUser());
+               $this->notificationObjectTypes = NotificationHandler::getAvailableNotificationObjectTypes();
+               $this->notificationTypes = NotificationHandler::getAvailableNotificationTypes();
+       }
+
+       /**
+        * @see Form::readFormParameters()
+        */
+       public function readFormParameters() {
+               parent::readFormParameters();
+
+               if (isset($_POST['activeEventNotifications']) && is_array($_POST['activeEventNotifications'])) $this->activeEventNotifications = $_POST['activeEventNotifications'];
+       }
+
+       /**
+        * @see Form::validate()
+        */
+       public function validate() {
+               parent::validate();
+
+               // validate checked options and unset wrong settings
+               foreach ($this->activeEventNotifications as $objectType => $events) {
+                       if (!isset($this->notificationObjectTypes[$objectType])) {
+                               unset ($this->activeEventNotifications[$objectType]);
+                               continue;
+                       }
+
+                       foreach ($events as $eventName => $notificationTypes) {
+                               if (!isset($this->notificationObjectTypes[$objectType]['events'][$eventName])) {
+                                       unset ($this->activeEventNotifications[$objectType][$eventName]);
+                                       continue;
+                               }
+
+                               foreach ($notificationTypes as $notificationType => $value) {
+                                       if (!isset($this->notificationTypes[$notificationType])) {
+                                               unset ($this->activeEventNotifications[$objectType][$eventName][$notificationType]);
+                                               continue;
+                                       }
+                               }
+                       }
+               }
+
+               $settings = array();
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_user_notification_event_settings
+                       WHERE packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+               $result = WCF::getDB()->sendQuery($sql);
+               while($row = WCF::getDB()->fetchArray($result)) {
+                       $settings[$row['objectType']][$row['eventName']][$row['notificationType']] = $row;
+               }
+
+               // add default values
+               foreach ($this->notificationObjectTypes as $name => $objectType) {
+                       if (isset($objectType['events'])) {
+                               foreach ($objectType['events'] as $eventName => $event) {
+                                       foreach ($this->notificationTypes as $typeName => $type) {
+                                               if (!isset($this->activeEventNotifications[$name][$eventName][$typeName]['canBeDisabled'])) {
+                                                       $this->activeEventNotifications[$name][$eventName][$typeName]['canBeDisabled'] = 0;
+                                               }
+                                               if (!isset($this->activeEventNotifications[$name][$eventName][$typeName]['enabled'])) {
+                                                       $this->activeEventNotifications[$name][$eventName][$typeName]['enabled'] = 0;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @see Page::readData()
+        */
+       public function readData() {
+               parent::readData();
+
+               $settings = array();
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_user_notification_event_settings
+                       WHERE packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+               $result = WCF::getDB()->sendQuery($sql);
+               while($row = WCF::getDB()->fetchArray($result)) {
+                       $settings[$row['objectType']][$row['eventName']][$row['notificationType']] = $row;
+               }
+
+               if (!count($_POST)) {
+                       foreach ($this->notificationObjectTypes as $name => $objectType) {
+                               if (isset($objectType['events'])) {
+                                       foreach ($objectType['events'] as $eventName => $event) {
+                                               foreach ($this->notificationTypes as $typeName => $type) {
+                                                       if (isset($settings[$name][$eventName][$typeName]['canBeDisabled'])) {
+                                                               $this->activeEventNotifications[$name][$eventName][$typeName]['canBeDisabled'] = $settings[$name][$eventName][$typeName]['canBeDisabled'];
+                                                       }
+                                                       else {
+                                                               $this->activeEventNotifications[$name][$eventName][$typeName]['canBeDisabled'] = 1;
+                                                       }
+                                                       if (isset($settings[$name][$eventName][$typeName]['enabled'])) {
+                                                               $this->activeEventNotifications[$name][$eventName][$typeName]['enabled'] = $settings[$name][$eventName][$typeName]['enabled'];
+                                                       }
+                                                       else {
+                                                               $this->activeEventNotifications[$name][$eventName][$typeName]['enabled'] = 1;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               if ($this->notificationTypes) {
+                       // calculate compatibility map
+                       foreach ($this->notificationObjectTypes as $name => $objectType) {
+                               if (!isset($objectType['events']) || !is_array($objectType['events'])) {
+                                       unset ($this->notificationObjectTypes[$name]);
+                                       continue;
+                               }
+                               foreach ($objectType['events'] as $eventName => $event) {
+                                       $this->notificationObjectTypes[$name]['events'][$eventName]->supportedNotificationTypes = array();
+                                       foreach ($this->notificationTypes as $typeName => $type) {
+                                               $this->notificationObjectTypes[$name]['events'][$eventName]->supportedNotificationTypes[$typeName] = $event->supportsNotificationType($type);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @see Page::assignVariables()
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+
+               WCF::getTPL()->assign(array(
+                       'notificationObjectTypes' => $this->notificationObjectTypes,
+                       'notificationTypes' => $this->notificationTypes,
+                       'activeEventNotifications' => $this->activeEventNotifications,
+                       'user' => $this->notificationUser
+               ));
+       }
+
+       /**
+        * @see Page::show()
+        */
+       public function show() {
+               // check module
+               if (!MODULE_USER_NOTIFICATION) {
+                       throw new IllegalLinkException();
+               }
+
+               // check permission
+               /*if (!WCF::getUser()->userID) {
+                       throw new PermissionDeniedException();
+               }
+
+               // set active user cp menu item
+               UserCPMenu::getInstance()->setActiveMenuItem('wcf.user.usercp.menu.link.settings.notification');
+*/
+               // show form
+               parent::show();
+       }
+
+       /**
+        * @see Form::save()
+        */
+       public function save() {
+               parent::save();
+
+               // delete old data but only from this dependency tree
+               $sql = "DELETE FROM     wcf".WCF_N."_user_notification_event_settings
+                       WHERE           packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+               WCF::getDB()->sendQuery($sql);
+
+               if ($this->notificationTypes) {
+                       // calculate compatibility map
+                       foreach ($this->notificationObjectTypes as $name => $objectType) {
+                               if (!isset($objectType['events']) || !is_array($objectType['events'])) {
+                                       unset ($this->notificationObjectTypes[$name]);
+                                       continue;
+                               }
+                               foreach ($objectType['events'] as $eventName => $event) {
+                                       $this->notificationObjectTypes[$name]['events'][$eventName]->supportedNotificationTypes = array();
+                                       foreach ($this->notificationTypes as $typeName => $type) {
+                                               $this->notificationObjectTypes[$name]['events'][$eventName]->supportedNotificationTypes[$typeName] = $event->supportsNotificationType($type);
+                                       }
+                               }
+                       }
+               }
+
+               // prepare new data
+               $inserts = '';
+               foreach ($this->activeEventNotifications as $objectType => $events) {
+                       foreach ($events as $eventName => $notificationTypes) {
+                               foreach ($notificationTypes as $notificationType => $data) {
+                                       if ($data['enabled'] && $data['canBeDisabled']) continue;
+                                       if (!$this->notificationObjectTypes[$objectType]['events'][$eventName]->supportedNotificationTypes[$notificationType]) continue;
+                                       $objectTypeObject = NotificationHandler::getNotificationObjectTypeObject($objectType);
+                                       if (!empty($inserts)) $inserts .= ',';
+                                       $inserts .= "(".$objectTypeObject->getPackageID().",
+                                       '".escapeString($objectType)."',
+                                       '".escapeString($eventName)."',
+                                       '".escapeString($notificationType)."',
+                                       ".(!$data['enabled'] || $data['canBeDisabled'] ? "1" : "0").",
+                                       ".($data['enabled'] ? "1" : "0").")";
+                               }
+                       }
+
+               }
+
+               if (!empty($inserts)) {
+                       $sql = "INSERT INTO     wcf".WCF_N."_user_notification_event_settings
+                                               (packageID, objectType, eventName, notificationType, canBeDisabled, enabled)
+                                               VALUES
+                                               ".$inserts;
+                       WCF::getDB()->sendQuery($sql);
+               }
+
+               $this->saved();
+
+               // show success message
+               WCF::getTPL()->assign('success', true);
+       }
+
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/action/NotificationConfirmAction.class.php b/com.woltlab.wcf.notification/files/lib/action/NotificationConfirmAction.class.php
new file mode 100644 (file)
index 0000000..49f8a7e
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/action/AbstractSecureAction.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationEditor.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationList.class.php');
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+
+/**
+ * This action handles default confirmations of notifications
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage action
+ * @category   Community Framework
+ */
+class NotificationConfirmAction extends AbstractSecureAction {
+       /**
+        * The id of the notification
+        *
+        * @var integer
+        */
+       public $notificationID = 0;
+
+       /**
+        * The notification editor object
+        *
+        * @var NotificationEditor
+        */
+       public $notification = null;
+
+       /**
+        * The url for redirection (non-ajax)
+        *
+        * @var string
+        */
+       public $url = null;
+
+       /**
+        * @see Action::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+
+               if (!MODULE_USER_NOTIFICATION) {
+                       throw new IllegalLinkException();
+               }
+
+               if (isset($_REQUEST['notificationID']))  {
+                       $this->notificationID = intval($_REQUEST['notificationID']);
+
+                       $this->notification = new NotificationEditor($this->notificationID);
+                       // validate notification ID
+                       if (!$this->notification->notificationID) {
+                               throw new IllegalLinkException();
+                       }
+                       $this->notification->userID = explode(',', $this->notification->userID);
+                       // validate user
+                       if (!in_array(WCF::getUser()->userID, $this->notification->userID)) {
+                               throw new PermissionDeniedException();
+                       }
+               }
+
+               if (isset($_REQUEST['url'])) $this->url = rawurldecode(StringUtil::trim($_REQUEST['url']));
+       }
+
+       /**
+        * @see Action::execute()
+        */
+       public function execute() {
+               parent::execute();
+
+               // get user
+               $user = new NotificationUser(null, WCF::getUser(), false);
+
+               // mark single notification
+               if ($this->notificationID) {
+                       if ($this->notification->confirmed) {
+                               throw new NamedUserException(WCF::getLanguage()->get('wcf.user.notification.error.alreadyAccepted'));
+                       }
+
+                       $this->notification->markConfirmed();
+               }
+               // mark all outstanding notifications
+               else {
+                       if ($user->hasOutstandingNotifications()) {
+                               // get outstanding notifications
+                               $notificationList = new NotificationList();
+                               $notificationList->sqlConditions = "notification.userID = ".$user->userID;
+                               $notificationList->readObjects();
+
+                               $notifications = $notificationList->getObjects();
+                               $notificationIDArray = array();
+
+                               foreach ($notifications as $notification) {
+                                       if (!$notification->confirmed && !$notification->acceptURL) {
+                                               $notificationIDArray[] = $notification->notificationID;
+                                       }
+                               }
+
+                               NotificationEditor::markAllConfirmed($notificationIDArray, array(WCF::getUser()->userID));
+                       }
+               }
+
+               // recalculate flags
+               $user->recalculateOutstandingNotifications();
+
+               $this->executed();
+       }
+
+       /**
+        * @see Action::executed()
+        */
+       public function executed() {
+               parent::executed();
+
+               if (!isset($_REQUEST['ajax'])) {
+                       $this->checkURL();
+                       HeaderUtil::redirect($this->url);
+               }
+       }
+
+       /**
+        * Gets the redirect url.
+        */
+       protected function checkURL() {
+               if (empty($this->url)) {
+                       $this->url = 'index.php'.SID_ARG_1ST;
+               }
+               // append missing session id
+               else if (SID_ARG_1ST != '' && !preg_match('/(?:&|\?)s=[a-z0-9]{40}/', $this->url)) {
+                       if (StringUtil::indexOf($this->url, '?') !== false) $this->url .= SID_ARG_2ND_NOT_ENCODED;
+                       else $this->url .= SID_ARG_1ST;
+               }
+       }
+}
+?>
diff --git a/com.woltlab.wcf.notification/files/lib/action/NotificationDisableAction.class.php b/com.woltlab.wcf.notification/files/lib/action/NotificationDisableAction.class.php
new file mode 100644 (file)
index 0000000..15b1891
--- /dev/null
@@ -0,0 +1,128 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/action/AbstractAction.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/Notification.class.php');
+require_once(WCF_DIR.'lib/data/user/UserEditor.class.php');
+
+/**
+ * This action disables notifications
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage action
+ * @category   Community Framework
+ */
+class NotificationDisableAction extends AbstractAction {
+       /**
+        * The id of the notification
+        *
+        * @var integer
+        */
+       public $notificationID = 0;
+
+       /**
+        * The notification object
+        *
+        * @var Notification
+        */
+       public $notification = null;
+
+       /**
+        * The user editor object
+        *
+        * @var UserEditor
+        */
+       public $user = null;
+
+       /**
+        * The mail token
+        *
+        * @var string
+        */
+       public $token = '';
+
+       /**
+        * @see Action::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+
+               if (!MODULE_USER_NOTIFICATION) {
+                       throw new IllegalLinkException();
+               }
+
+               if (isset($_REQUEST['notificationID']))  {
+                       $this->notificationID = intval($_REQUEST['notificationID']);
+
+                       $this->notification = new Notification($this->notificationID);
+                       // validate notification ID
+                       if (!$this->notification->notificationID) {
+                               throw new IllegalLinkException();
+                       }                       
+               }
+               
+               if (isset($_REQUEST['userID'])) {
+                       $this->user = new UserEditor(intval($_REQUEST['userID']));
+
+                       // validate user ID
+                       if (!$this->user->userID) {
+                               throw new IllegalLinkException();
+                       }
+               }
+
+               if (isset($_REQUEST['token'])) {
+                       $this->token = StringUtil::trim($_REQUEST['token']);
+
+                       // validate token
+                       if (empty($this->token) || $this->user->notificationMailToken != $this->token) {
+                               throw new IllegalLinkException();
+                       }
+               }
+
+               if (!$this->user || !$this->notification || !$this->token) {
+                       throw new IllegalLinkException();
+               }
+       }
+
+       /**
+        * @see Action::execute()
+        */
+       public function execute() {
+               parent::execute();
+
+               // delete old entry if present
+               $sql = "DELETE FROM     wcf".WCF_N."_user_notification_event_to_user
+                       WHERE           objectType = '".escapeString($this->notification->objectType)."'
+                       AND             eventName = '".escapeString($this->notification->eventName)."'
+                       AND             notificationType = 'mail'
+                       AND             enabled = 1
+                       AND             packageID = ".$this->notification->packageID."
+                       AND             userID = ".$this->user->userID;
+               WCF::getDB()->sendQuery($sql);
+
+               // insert new setting for this type
+               $sql = "INSERT INTO     wcf".WCF_N."_user_notification_event_to_user
+                                       (userID, packageID, objectType, eventName, notificationType, enabled)
+                       VALUES          (".$this->user->userID.",
+                                       ".$this->notification->packageID.",
+                                       '".escapeString($this->notification->objectType)."',
+                                       '".escapeString($this->notification->eventName)."',
+                                       'mail',
+                                       0)";
+               WCF::getDB()->sendQuery($sql);
+
+               $this->executed();
+
+               WCF::getTPL()->assign(array(
+                       'url' => 'index.php'.SID_ARG_1ST,
+                       'message' => WCF::getLanguage()->get('wcf.user.notification.type.mail.disabled'),
+                       'wait' => 5
+               ));
+               WCF::getTPL()->display('redirect');
+               exit;                
+       }
+
+}
+?>
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/NotificationUser.class.php b/com.woltlab.wcf.notification/files/lib/data/user/NotificationUser.class.php
new file mode 100644 (file)
index 0000000..932a1a7
--- /dev/null
@@ -0,0 +1,223 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/system/session/UserSession.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationHandler.class.php');
+
+/**
+ * Represents a user in the notifications system
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage data.user
+ * @category   Community Framework
+ */
+class NotificationUser extends UserSession {
+       /**
+        * True if the user's notification settings should be loaded
+        *
+        * @var         boolean
+        */
+       protected $loadSettings = true;
+
+       /**
+        * Constructs a new NotificationUser object
+        *
+        * @param       integer         $userID
+        * @param       mixed           $row
+        * @param       boolean         $loadSettings
+        * @see         UserSession::__construct
+        */
+       public function __construct($userID, $row = null, $loadSettings = true) {
+               if (is_object($row)) {
+                       $row = $row->data;
+               }
+               $this->loadSettings = $loadSettings;
+
+               parent::__construct($userID, $row);
+       }
+
+       /**
+        * @see DatabaseObject::__construct
+        */
+       protected function handleData($data) {
+               if ($data['userID'] && $this->loadSettings) {
+                       // load notification settings
+                       $sql = "SELECT  objectType, eventName, notificationType, enabled
+                               FROM    wcf".WCF_N."_user_notification_event_to_user
+                               WHERE   userID = ".$data['userID']."
+                               AND     packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+                       $result = WCF::getDB()->sendQuery($sql);
+
+                       $data['eventNotificationSettings'] = array();
+                       while ($row = WCF::getDB()->fetchArray($result)) {
+                               $data['eventNotificationSettings'][$row['objectType']][$row['eventName']][$row['notificationType']] = $row['enabled'];
+                       }
+               }
+               if (isset($data['notificationFlags'])) {
+                       $data['notificationFlags'] = unserialize($data['notificationFlags']);
+               }
+               parent::handleData($data);
+       }
+
+       /**
+        * Returns an array of notification types which are activated for the given
+        * event for this user
+        *
+        * @param       string          $eventName
+        * @param       string          $objectType
+        * @return      array<mixed>
+        */
+       public function getEventNotificationTypes($eventName, $objectType) {
+               $notificationTypes = array();
+               if (isset($this->data['eventNotificationSettings'][$objectType][$eventName])) {
+                       foreach ($this->data['eventNotificationSettings'][$objectType][$eventName] as $typeName => $enabled) {
+                               if ($enabled) {
+                                       $notificationTypeObject = null;
+                                       try {
+                                               $notificationTypeObject = NotificationHandler::getNotificationTypeObject($typeName);
+                                       }
+                                       catch (SystemException $ex) {
+                                               continue;
+                                       }
+                                       $notificationTypes[] = $notificationTypeObject;
+                               }
+                       }
+               }
+               else {
+                       $objectTypes = NotificationHandler::getAvailableNotificationObjectTypes();
+                       $notificationTypeObject = null;
+                       try {
+                               $notificationTypeObject = NotificationHandler::getNotificationTypeObject($objectTypes[$objectType]['events'][$eventName]->defaultNotificationType);
+                               $notificationTypes[] = $notificationTypeObject;
+                       }
+                       catch (SystemException $ex) {
+                               // do nothing
+                       }
+               }
+
+               return $notificationTypes;
+       }
+
+       /**
+        * Returns true if there are any unseen notifications for this user
+        *
+        * @return      integer
+        */
+       public function hasOutstandingNotifications() {
+               $count = 0;
+
+               foreach (explode(',', NotificationHandler::getAvailablePackageIDs()) as $packageID) {
+                       if (isset($this->data['notificationFlags'][$packageID]) && $this->data['notificationFlags'][$packageID] > 0) {
+                               $count += $this->data['notificationFlags'][$packageID];
+                       }
+               }
+
+               return $count;
+       }
+
+       /**
+        * Increases the notification counter for a notification object type
+        *
+        * @param       integer         $packageID
+        * @param       integer         $count
+        */
+       public function addOutstandingNotification($packageID, $count = 1) {
+               // validation
+               if ($count < 1) return;
+
+               if (isset($this->data['notificationFlags'][$packageID])) {
+                       $this->data['notificationFlags'][$packageID] += $count;
+               }
+               else $this->data['notificationFlags'][$packageID] = $count;
+
+               $editor = $this->getEditor();
+               $editor->updateFields(array('notificationFlags' => serialize($this->data['notificationFlags'])));
+               Session::resetSessions(array($this->userID), true, false);
+       }
+
+       /**
+        * Decreases the notification counter for a notification object type
+        *
+        * @param       integer         $packageID
+        * @param       integer         $count
+        */
+       public function removeOutstandingNotification($packageID, $count = 1) {
+               // validation
+               if ($count < 1) return;
+
+               if (isset($this->data['notificationFlags'][$packageID])) {
+                       $this->data['notificationFlags'][$packageID] -= $count;
+                       // "thread" security
+                       if ($this->data['notificationFlags'][$packageID] <= 0) {
+                               unset($this->data['notificationFlags'][$packageID]);
+                       }
+               }
+
+               $editor = $this->getEditor();
+               $editor->updateFields(array('notificationFlags' => serialize($this->data['notificationFlags'])));
+               Session::resetSessions(array($this->userID), true, false);
+       }
+
+       /**
+        * Recalculates all notification flags
+        */
+       public function recalculateOutstandingNotifications() {
+               self::recalculateUserNotificationFlags(array($this->userID));
+       }
+
+       /**
+        * Bulk processes a number of users and recalculates their notification flags
+        *
+        * @param       array<integer>          userIDs
+        */
+       public static function recalculateUserNotificationFlags($userIDs = array()) {
+               if (!count($userIDs)) return;
+               $sql = "SELECT          COUNT(*) AS count, n.packageID, u.userID
+                       FROM            wcf".WCF_N."_user_notification n
+                       LEFT JOIN
+                               wcf".WCF_N."_user_notification_to_user u
+                               ON n.notificationID = u.notificationID
+                       WHERE           u.userID IN(".implode(',', $userIDs).")
+                       AND             u.confirmed = 0
+                       GROUP BY        n.packageID, u.userID";
+               $result = WCF::getDB()->sendQuery($sql);
+
+               $notificationFlags = array();
+               while ($row = WCF::getDB()->fetchArray($result)) {
+                       $notificationFlags[$row['userID']][$row['packageID']] = $row['count'];
+               }
+
+               $inserts = '';
+               // add empty data and prepare inserts
+               foreach ($userIDs as $userID) {
+                       if (!isset($notificationFlags[$userID])) {
+                               $notificationFlags[$userID] = array();
+                       }
+
+                       if (!empty($inserts)) $inserts .= ',';
+                       $inserts .= "(".$userID.", '".escapeString(serialize($notificationFlags[$userID]))."')";
+               }
+
+               if (!empty($inserts)) {
+                       $sql = "INSERT INTO             wcf".WCF_N."_user
+                                                       (userID, notificationFlags)
+                               VALUES                          ".$inserts."
+                               ON DUPLICATE KEY UPDATE notificationFlags = VALUES(notificationFlags)";
+                       WCF::getDB()->sendQuery($sql);
+               }
+
+               Session::resetSessions($userIDs, true, false);
+       }
+
+       /**
+        * @see User::getEditor()
+        */
+       public function getEditor() {
+               // saves another query
+               require_once(WCF_DIR.'lib/data/user/UserEditor.class.php');
+               return new UserEditor(null, array_merge(get_object_vars($this), $this->data));
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotification.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotification.class.php
new file mode 100644 (file)
index 0000000..797e5b2
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/Notification.class.php');
+
+/**
+ * Represents a notification in a rss or atom feed
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class FeedNotification extends Notification {
+       /**
+        * @see Notification::handleData()         
+        */
+       protected function handleData($data) {
+               // escape CDATA
+               $data['shortOutput'] = StringUtil::escapeCDATA(StringUtil::stripHTML($data['shortOutput']));
+               $data['mediumOutput'] = StringUtil::escapeCDATA($data['mediumOutput']);
+               $data['longOutput'] = StringUtil::escapeCDATA($data['longOutput']);
+               if (isset($data['messageCache'])) $data['messageCache'] = StringUtil::escapeCDATA($data['messageCache']);
+
+               parent::handleData($data);
+       }
+
+       /**
+        * @see Notification::parseURLsCallback()
+        */
+       protected function parseURLsCallback($match) {
+               $url = parent::parseURLsCallback($match);
+
+               $url = FileUtil::addTrailingSlash(PAGE_URL).$url;
+
+               return $url;
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotificationList.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotificationList.class.php
new file mode 100644 (file)
index 0000000..8a4c5fb
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/NotificationList.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/FeedNotification.class.php');
+
+/**
+ * Represents a list of feed notifications
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class FeedNotificationList extends NotificationList {
+       /**
+        * @see DatabaseObjectList::readObjects()
+        */
+       public function readObjects() {
+               // get ids
+               $notificationIDArray = $objectIDArray = $objects = array();
+               $notificationObjectTypes = NotificationHandler::getAvailableNotificationObjectTypes();
+               $sqlConditions = $this->sqlConditions;
+               if (!empty($sqlConditions)) $sqlConditions .= " AND ";
+               $sqlConditions .= "notification.packageID IN(".NotificationHandler::getAvailablePackageIDs().")";
+               $sql = "SELECT           ".$this->sqlSelects." notification.*, GROUP_CONCAT(u.userID SEPARATOR ',') AS userID
+                       FROM    wcf".WCF_N."_user_notification notification
+                       LEFT JOIN
+                               wcf".WCF_N."_user_notification_to_user u
+                               ON notification.notificationID = u.notificationID
+                       ".$this->sqlJoins."
+                       WHERE ".$sqlConditions."
+                       GROUP BY notification.notificationID
+                       ".(!empty($this->sqlOrderBy) ? "ORDER BY ".$this->sqlOrderBy : '');
+               $result = WCF::getDB()->sendQuery($sql, $this->sqlLimit, $this->sqlOffset);
+               while ($row = WCF::getDB()->fetchArray()) {
+                       if (!isset($notificationObjectTypes[$row['objectType']]['events'][$row['eventName']])) continue;
+                       $row['event'] = $notificationObjectTypes[$row['objectType']]['events'][$row['eventName']];
+                       $this->notifications[] = new FeedNotification(null, $row);
+               }
+
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/NotificationList.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/NotificationList.class.php
new file mode 100644 (file)
index 0000000..0f2a145
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/Notification.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationHandler.class.php');
+require_once(WCF_DIR.'lib/data/DatabaseObjectList.class.php');
+
+/**
+ * Represents a list of notifications
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class NotificationList extends DatabaseObjectList {
+       // system
+       public $sqlOrderBy = 'notification.time DESC';
+
+       /**
+        * list of notifications
+        *
+        * @var array<Notification>
+        */
+       public $notifications = array();
+
+       /**
+        * @see DatabaseObjectList::countObjects()
+        */
+       public function countObjects() {
+               $sqlConditions = $this->sqlConditions;
+               if (!empty($sqlConditions)) $sqlConditions .= " AND ";
+               $sqlConditions .= "notification.packageID IN(".NotificationHandler::getAvailablePackageIDs().")";
+               $sql = "SELECT  COUNT(*) AS count
+                       FROM    wcf".WCF_N."_user_notification notification
+                       LEFT JOIN
+                               wcf".WCF_N."_user_notification_to_user u
+                               ON notification.notificationID = u.notificationID
+                       WHERE ".$sqlConditions;
+               $row = WCF::getDB()->getFirstRow($sql);
+               return $row['count'];
+       }
+
+       /**
+        * @see DatabaseObjectList::readObjects()
+        */
+       public function readObjects() {
+               // get ids
+               $notificationIDArray = $objectIDArray = $objects = array();
+               $notificationObjectTypes = NotificationHandler::getAvailableNotificationObjectTypes();
+               $sqlConditions = $this->sqlConditions;
+               if (!empty($sqlConditions)) $sqlConditions .= " AND ";
+               $sqlConditions .= "notification.packageID IN(".NotificationHandler::getAvailablePackageIDs().")";
+               $sql = "SELECT           ".$this->sqlSelects." notification.*, GROUP_CONCAT(u.userID SEPARATOR ',') AS userID
+                       FROM    wcf".WCF_N."_user_notification notification
+                       LEFT JOIN
+                               wcf".WCF_N."_user_notification_to_user u
+                               ON notification.notificationID = u.notificationID
+                       ".$this->sqlJoins."
+                       WHERE ".$sqlConditions."
+                       GROUP BY notification.notificationID
+                       ".(!empty($this->sqlOrderBy) ? "ORDER BY ".$this->sqlOrderBy : '');
+               $result = WCF::getDB()->sendQuery($sql, $this->sqlLimit, $this->sqlOffset);
+               while ($row = WCF::getDB()->fetchArray()) {
+                       if (!isset($notificationObjectTypes[$row['objectType']]['events'][$row['eventName']])) continue;
+                       $row['event'] = clone $notificationObjectTypes[$row['objectType']]['events'][$row['eventName']];
+                       $this->notifications[] = new Notification(null, $row);
+               }
+
+       }
+
+       /**
+        * @see DatabaseObjectList::getObjects()
+        */
+       public function getObjects() {
+               return $this->notifications;
+       }
+}
+?>
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotification.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotification.class.php
new file mode 100644 (file)
index 0000000..a23ce7f
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+namespace wcf\data\user\notification;
+use wcf\data\DatabaseObject;
+
+/**
+ * Represents a user notification.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class UserNotification extends DatabaseObject {
+       /**
+        * @see DatabaseObject::$databaseTableName
+        */
+       protected static $databaseTableName = 'user_notification';
+       
+       /**
+        * @see DatabaseObject::$databaseTableIndexName
+        */
+       protected static $databaseTableIndexName = 'notificationID';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationAction.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationAction.class.php
new file mode 100644 (file)
index 0000000..6c2a2c5
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes user notification-related actions.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class UserNotificationAction extends AbstractDatabaseObjectAction {
+       /**
+        * @see AbstractDatabaseObjectAction::$className
+        */
+       protected $className = 'wcf\data\user\notification\UserNotificationEditor';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationEditor.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationEditor.class.php
new file mode 100644 (file)
index 0000000..27a1148
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit user notifications.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class UserNotificationEditor extends DatabaseObjectEditor {
+       /**
+        * @see DatabaseObjectEditor::$baseClass
+        */
+       protected static $baseClass = 'wcf\data\user\notification\UserNotification';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationHandler.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationHandler.class.php
new file mode 100644 (file)
index 0000000..77c0a75
--- /dev/null
@@ -0,0 +1,482 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/UserNotificationEditor.class.php');
+
+/**
+ * The core class of the notification system
+ *
+ * @author     Marcel Werk, Tim Duesterhus, Oliver Kliebisch
+ * @copyright  2009-2011 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class UserNotificationHandler {
+       /**
+        * list of available notification object types
+        * @var array<UserNotificationObjectType>
+        */
+       public static $availableNotificationObjectTypes = null;
+
+       /**
+        * list of available notification types
+        * @var array<UserNotificationType>
+        */
+       public static $availableNotificationTypes = null;
+
+       /**
+        * available notification object package IDs
+        * @var array<integer>
+        */
+       protected static $availableNotificationObjectIDs = null;
+
+       /**
+        * notification cache data
+        * @var array<mixed>
+        */
+       protected static $cacheData = null;
+
+       /**
+        * The reference user object for the permission check
+        * @var User
+        */
+       protected static $user = null;
+
+       /**
+        * Triggers a notification event
+        *
+        * @param       string          $eventName
+        * @param       string          $objectType
+        * @param       mixed           $notificationObject
+        * @param       integer         $recipientUserID
+        * @param       array<mixed>    $additionalData
+        */
+       public static function fireEvent($eventName, $objectType, $notificationObject, $recipientUserID, $additionalData = array()) {
+               if (!MODULE_USER_NOTIFICATION) return;
+               // get recipient and apply it to the handler
+               $recipient = new NotificationUser($recipientUserID);
+               if ($recipient->userID == 0) return;
+               self::setUser($recipient);
+
+               $objectTypes = self::getAvailableNotificationObjectTypes();
+               if (isset($objectTypes[$objectType]['events'][$eventName])) {
+                       $objectTypeObject = $objectTypes[$objectType]['object'];
+                       $eventObject = $objectTypes[$objectType]['events'][$eventName];
+
+                       // save memory
+                       unset ($objectTypes);
+
+                       // fetches the notification object if $notificationObject isn't already one
+                       if (!$notificationObject instanceof UserNotificationConveyableObject) {
+                               $notificationObject = $objectTypeObject->getObjects($notificationObject);
+                               $notificationObject = current($notificationObject);
+                       }
+
+                       if (!$notificationObject) {
+                               throw new SystemException("Unable to fetch the notification object with the type '".$objectType."'", 11000);
+                       }
+                       $eventObject->setObject($notificationObject, $additionalData);
+
+                       // get notification types
+                       $notificationTypes = $recipient->getEventNotificationTypes($eventName, $objectType);
+
+                       // switch to recipient language
+                       // enable recipient language
+                       $languages = array(0 => WCF::getLanguage(), WCF::getLanguage()->getLanguageID() => WCF::getLanguage());
+                       if (!isset($languages[$recipient->languageID])) $languages[$recipient->languageID] = new Language($recipient->languageID);
+                       $languages[$recipient->languageID]->setLocale();
+                       $eventObject->setLanguage($languages[$recipient->languageID]);
+
+                       // prepare messages and send notification messages
+                       $notification = UserNotificationEditor::create(array(
+                               'userID' => $recipient->userID,
+                               'packageID' => $objectTypeObject->getPackageID(),
+                               'objectType' => $objectType,
+                               'objectID' => $notificationObject->getObjectID(),
+                               'eventName' => $eventName,
+                               'time' => TIME_NOW,
+                               'shortOutput' => $eventObject->getShortOutput($eventName),
+                               'mediumOutput' => $eventObject->getMediumOutput($eventName),
+                               'longOutput' => $eventObject->getOutput($eventName),
+                               'additionalData' => $additionalData
+                       ));
+                       
+                       foreach ($notificationTypes as $notificationType) {
+                               if ($eventObject->supportsNotificationType($notificationType) == false) {
+                                       continue;
+                               }
+
+                               $notificationType->send($recipient, $eventObject, $notification);
+                       }
+
+                       // add notification flag
+                       $recipient->addOutstandingNotification(self::getNotificationObjectTypeObject($notification->objectType)->getPackageID());
+
+                       // enable user language
+                       WCF::getLanguage()->setLocale();
+               }
+       }
+
+       /**
+        * Revokes an event and all its messages if possible
+        *
+        * @param       array<string>   $eventName
+        * @param       string          $objectType
+        * @param       mixed           $notificationObject
+        * @param       array<mixed>    $additionalData
+        */
+       public static function revokeEvent($eventNames, $objectType, $notificationObjects, $additionalData = array()) {
+               $objectTypes = self::getAvailableNotificationObjectTypes();
+               $objectTypeObject = $objectTypes[$objectType]['object'];
+               $eventObjectArray = array();
+               foreach ($eventNames as $key => $eventName) {
+                       if (isset($objectTypes[$objectType]['events'][$eventName])) {
+                               $eventObjectArray[$eventName] = $objectTypes[$objectType]['events'][$eventName];
+                       }
+                       else {
+                               unset($eventNames[$key]);
+                       }
+               }
+
+               unset($objectTypes);
+
+               // fetches the notification objects
+               $notificationObjects = $objectTypeObject->getObjects($notificationObjects);
+
+               if (!count($notificationObjects)) {
+                       throw new SystemException("Unable to fetch the notification object with the type '".$objectType."'", 11000);
+               }
+               $objectIDArray = array();
+               foreach ($notificationObjects as $notificationObject) {
+                       $objectIDArray[] = $notificationObject->getObjectID();
+               }
+
+               $sql = "SELECT  n.*,
+                               GROUP_CONCAT(u.userID SEPARATOR ',') AS userID
+                       FROM    wcf".WCF_N."_user_notification n
+                       LEFT JOIN
+                               wcf".WCF_N."_user_notification_to_user u
+                               ON n.notificationID = u.notificationID
+                       WHERE   objectType = '".escapeString($objectType)."'
+                       AND     packageID = ".$objectTypeObject->getPackageID()."
+                       AND     objectID IN(".implode(',', $objectIDArray).")
+                       AND     eventName IN('".implode("','", array_map('escapeString', $eventNames))."')";
+               $result = WCF::getDB()->getResultList($sql);
+               if ($result[0]['notificationID'] === null) return;
+               $affectedUserIDs = array();
+               $notificationIDArray = array();
+               foreach ($result as $row) {
+                       $notification = new UserNotificationEditor(null, $row);
+                       $notificationIDArray[] = $notification->notificationID;
+                       $row['userID'] = explode(',', $row['userID']);
+                       $recipients = array();
+                       foreach ($row['userID'] as $userID) {
+                               // could we do it better?
+                               $recipients[] = new UserNotificationUser($userID, null, false);
+                       }
+
+                       $eventObject = $eventObjectArray[$notification->eventName];
+                       $notificationObject = $notificationObjects[$notification->objectID];
+                       $eventObject->setObject($notificationObject, $additionalData);
+                       foreach ($recipients as $recipient) {
+                               $notificationTypeArray = UserNotificationHandler::getAvailableNotificationObjectTypes();
+                               foreach ($notificationTypeArray as $notificationType) {
+                                       $notificationTypeObject = null;
+                                       try {
+                                               $notificationTypeObject = self::getNotificationTypeObject($notificationType);
+                                               if (!$notificationTypeObject) continue;
+                                       }
+                                       catch (SystemException $ex) {
+                                               // notification object might be missing but that would be no error
+                                               continue;
+                                       }
+                                       // revoke messages if supported
+                                       $notificationTypeObject->revoke($recipient, $eventObject, $notification);
+                               }
+
+                               $affectedUserIDs[] = $recipient->userID;
+                       }
+               }
+
+               // delete notification data
+               UserNotificationEditor::deleteAll($notificationIDArray);
+
+               // update user flags
+               UserNotificationUser::recalculateUserNotificationFlags($affectedUserIDs);
+       }
+
+       /**
+        * Gets notification objects by their ids.
+        *
+        * @param       string          $objectType
+        * @param       mixed           $objectID
+        * @return      mixed
+        */
+       public static function getNotificationObjectByID($objectType, $objectID) {
+               // get notification object type object
+               $typeObject = null;
+               try {
+                       $typeObject = self::getNotificationObjectTypeObject($objectType);
+               }
+               catch (SystemException $e) {
+                       return null;
+               }
+
+               // get notification objects
+               return $typeObject->getObjectByID($objectID);
+       }
+
+       /**
+        * Returns the object of a notification object type.
+        *
+        * @param       string          $objectType
+        * @return      NotificationObjectType
+        */
+       public static function getNotificationObjectTypeObject($objectType) {
+               $types = self::getAvailableNotificationObjectTypes();
+               if (!isset($types[$objectType])) {
+                       throw new SystemException("Unknown notification object type '".$objectType."'", 11000);
+               }
+
+               return $types[$objectType]['object'];
+       }
+
+       /**
+        * Returns the object of a notification type.
+        *
+        * @param       string          $notificationType
+        * @return      NotificationType
+        */
+       public static function getNotificationTypeObject($notificationType) {
+               $types = self::getAvailableNotificationTypes();
+               if (!isset($types[$notificationType])) {
+                       throw new SystemException("Unknown notification object type '".$notificationType."'", 11000);
+               }
+
+               return $types[$notificationType];
+       }
+
+       /**
+        * Returns a list of available notification object types.
+        *
+        * @return      array<NotificationObjectType>
+        */
+       public static function getAvailableNotificationObjectTypes() {
+               if (self::$availableNotificationObjectTypes === null) {
+                       self::loadCache();
+                       $types = self::$cacheData['objectTypes'];
+                       foreach ($types as $type) {
+                               // check options and modules
+                               if (!empty($type['options'])) {
+                                       $options = explode(',', StringUtil::toUpperCase($type['options']));
+                                       foreach ($options as $option) {
+                                               if (!defined($option) || !constant($option))
+                                                               continue 2;
+                                       }
+                               }
+
+                               // check permissions
+                               if (!empty($type['permissions'])) {
+                                       $permissions = explode(',', $type['permissions']);
+                                       foreach ($permissions as $permission) {
+                                               if (!self::getUser()->getPermission($permission))
+                                                               continue 2;
+                                       }
+                               }
+
+                               // get path to class file
+                               if (empty($type['packageDir'])) {
+                                       $path = WCF_DIR;
+                               }
+                               else {
+                                       $path = FileUtil::getRealPath(WCF_DIR.$type['packageDir']);
+                               }
+                               $path .= $type['classFile'];
+
+                               // include class file
+                               if (!class_exists($type['className'])) {
+                                       if (!file_exists($path)) {
+                                               throw new SystemException("Unable to find class file '".$path."'", 11000);
+                                       }
+                                       require_once($path);
+                               }
+
+                               // instance object
+                               if (!class_exists($type['className'])) {
+                                       throw new SystemException("Unable to find class '".$type['className']."'", 11001);
+                               }
+                               self::$availableNotificationObjectTypes[$type['objectType']]['object'] = new $type['className'];
+                       }
+
+                       $events = self::$cacheData['events'];
+                       foreach ($events as $event) {
+                               // check options and modules
+                               if (!empty($event['options'])) {
+                                       $options = explode(',', StringUtil::toUpperCase($event['options']));
+                                       foreach ($options as $option) {
+                                               if (!defined($option) || !constant($option))
+                                                               continue 2;
+                                       }
+                               }
+
+                               // check permissions
+                               if (!empty($event['permissions'])) {
+                                       $permissions = explode(',', $event['permissions']);
+                                       foreach ($permissions as $permission) {
+                                               if (!self::getUser()->getPermission($permission))
+                                                               continue 2;
+                                       }
+                               }
+
+                               // get path to class file
+                               if (empty($event['packageDir'])) {
+                                       $path = WCF_DIR;
+                               }
+                               else {
+                                       $path = FileUtil::getRealPath(WCF_DIR.$event['packageDir']);
+                               }
+                               $path .= $event['classFile'];
+
+                               // include class file
+                               if (!class_exists($event['className'])) {
+                                       if (!file_exists($path)) {
+                                               // load default event
+                                               require_once(WCF_DIR.'lib/data/user/notification/event/DefaultNotificationEvent.class.php');
+                                               $event['className'] = 'DefaultNotificationEvent';
+                                               //throw new SystemException("Unable to find class file '".$path."'", 11000);
+                                       }
+                                       else require_once($path);
+                               }
+
+                               // instance object
+                               if (!class_exists($event['className'])) {
+                                       throw new SystemException("Unable to find class '".$event['className']."'", 11001);
+                               }
+                               self::$availableNotificationObjectTypes[$event['objectType']]['events'][$event['eventName']] = new $event['className']($event);
+                       }
+               }
+
+               return self::$availableNotificationObjectTypes;
+       }
+
+       /**
+        * Returns a list of available notification types
+        *
+        * @return      array<NotificationType>
+        */
+       public static function getAvailableNotificationTypes() {
+               if (self::$availableNotificationTypes === null) {
+                       self::loadCache();
+                       $types = self::$cacheData['notificationTypes'];
+                       foreach ($types as $type) {
+                               // check options and modules
+                               if (!empty($type['options'])) {
+                                       $options = explode(',', StringUtil::toUpperCase($type['options']));
+                                       foreach ($options as $option) {
+                                               if (!defined($option) || !constant($option))
+                                                               continue 2;
+                                       }
+                               }
+
+                               // check permissions
+                               if (!empty($type['permissions'])) {
+                                       $permissions = explode(',', $type['permissions']);
+                                       foreach ($permissions as $permission) {
+                                               if (!self::getUser()->getPermission($permission))
+                                                               continue 2;
+                                       }
+                               }
+
+                               // get path to class file
+                               if (empty($type['packageDir'])) {
+                                       $path = WCF_DIR;
+                               }
+                               else {
+                                       $path = FileUtil::getRealPath(WCF_DIR.$type['packageDir']);
+                               }
+                               $path .= $type['classFile'];
+
+                               // include class file
+                               if (!class_exists($type['className'])) {
+                                       if (!file_exists($path)) {
+                                               throw new SystemException("Unable to find class file '".$path."'", 11000);
+                                       }
+                                       require_once($path);
+                               }
+
+                               // instance object
+                               if (!class_exists($type['className'])) {
+                                       throw new SystemException("Unable to find class '".$type['className']."'", 11001);
+                               }
+                               self::$availableNotificationTypes[$type['notificationType']] = new $type['className'];
+                       }
+               }
+
+               return self::$availableNotificationTypes;
+       }
+
+       /**
+        * Returns a concatenized string of available package IDs
+        *
+        * @return string
+        */
+       public static function getAvailablePackageIDs() {
+               if (self::$availableNotificationObjectIDs === null) {
+                       $objectTypeObjects = self::getAvailableNotificationObjectTypes();
+
+                       self::$availableNotificationObjectIDs = array();
+                       if ($objectTypeObjects) {
+                               foreach ($objectTypeObjects as $objectType) {
+                                       if ($packageID = $objectType['object']->getPackageID()) {
+                                               self::$availableNotificationObjectIDs[] = $packageID;
+                                       }
+
+                                       $additionalPackageIDs = $objectType['object']->getAdditionalPackageIDs();
+                                       if (!empty($additionalPackageIDs)) {
+                                               self::$availableNotificationObjectIDs = array_merge($additionalPackageIDs, self::$availableNotificationObjectIDs);
+                                       }
+                               }
+                       }
+
+                       array_unique(self::$availableNotificationObjectIDs);
+               }
+
+               return count(self::$availableNotificationObjectIDs) ? '0,'.implode(',', self::$availableNotificationObjectIDs) : '0';
+       }
+
+       /**
+        * Sets the user for permission checks
+        *
+        * @param UserSession $user
+        */
+       public static function setUser(UserSession $user) {
+               self::$user = $user;
+               self::$availableNotificationObjectTypes = self::$availableNotificationTypes = self::$availableNotificationObjectIDs = null;
+       }
+
+       /**
+        * Returns the user for permission checks
+        *
+        * @return UserSession
+        */
+       public static function getUser() {
+               if (self::$user === null || !self::$user instanceof UserSession) {
+                       self::$user = WCF::getUser();
+               }
+
+               return self::$user;
+       }
+
+       /**
+        * Loads the notification cache
+        */
+       protected static function loadCache() {
+               if (self::$cacheData === null) {
+                       WCF::getCache()->addResource('notifications-'.PACKAGE_ID, WCF_DIR.'cache/cache.notifications-'.PACKAGE_ID.'.php', WCF_DIR.'lib/system/cache/CacheBuilderNotifications.class.php');
+                       self::$cacheData = WCF::getCache()->get('notifications-'.PACKAGE_ID);
+               }
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationList.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationList.class.php
new file mode 100644 (file)
index 0000000..0175338
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of user notifications.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class UserNotificationList extends DatabaseObjectList {
+       /**
+        * @see DatabaseObjectList::$className
+        */
+       public $className = 'wcf\data\user\notification\UserNotification';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationMessageCompiler.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationMessageCompiler.class.php
new file mode 100644 (file)
index 0000000..8c6182b
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/system/template/TemplateScriptingCompiler.class.php');
+
+/**
+ * Compiles template scripting in notification messages.
+ *
+ * @author     Marcel Werk, Oliver Kliebisch
+ * @copyright  2009-2011 WoltLab GmbH, Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class UserNotificationMessageCompiler extends TemplateScriptingCompiler {
+       public $rightDelimiter = '}}';
+       public $leftDelimiter = '{{';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/event/DefaultUserNotificationConveyableEvent.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/DefaultUserNotificationConveyableEvent.class.php
new file mode 100644 (file)
index 0000000..60d0fc4
--- /dev/null
@@ -0,0 +1,242 @@
+<?php
+// wcf import
+require_once(WCF_DIR.'lib/data/user/notification/event/UserNotificationConveyableEvent.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/event/UserNotificationEvent.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/object/UserNotificationConveyableObject.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/UserNotification.class.php');
+
+/**
+ * This is the default and simple notification event class if you don't need further customization
+ *
+ * @author     Marcel Werk, Oliver Kliebisch
+ * @copyright  2009-2010 WoltLab GmbH, Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.event
+ * @category   Community Framework
+ */
+class DefaultUserNotificationConveyableEvent implements UserNotificationConveyableEvent {
+       /**
+        * notification event
+        * 
+        * @var UserNotificationEvent
+        */
+       protected $event = null;
+       
+       /**
+        * The language object of the recipient user's language
+        *
+        * @var Language
+        */
+       protected $language = null;
+
+       /**
+        * The notification object
+        *
+        * @var UserNotificationConveyableObject
+        */
+       protected $object = null;
+
+       /**
+        * Additional data for this event
+        *
+        * @var array<mixed>
+        */
+       public $additionalData = array();
+
+       /**
+        * Creates a new DefaultUserNotificationConveyableEvent object.
+        * 
+        * @param       UserNotificationEvent           $event 
+        */
+       public function __construct(UserNotificationEvent $event) {
+               $this->event = $event;
+       }
+       
+       /**
+        * @see UserNotificationConveyableEvent::initialize()
+        */
+       public function initialize(&$data) {
+               return;
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getMessage()
+        */
+       public function getMessage(NotificationType $notificationType, $additionalVariables = array()) {
+               return $this->getLanguageVariable($this->event->languageCategory.'.'.$this->getEventName().'.'.$notificationType->getName(), $additionalVariables);
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getShortOutput()
+        */
+       public function getShortOutput() {
+                return $this->getLanguageVariable($this->event->languageCategory.'.'.$this->getEventName().'.short');
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getMediumOutput()
+        */
+       public function getMediumOutput() {
+                return $this->getLanguageVariable($this->event->languageCategory.'.'.$this->getEventName().'.medium');
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getShortOutput()
+        */
+       public function getOutput() {
+               return $this->getLanguageVariable($this->event->languageCategory.'.'.$this->getEventName());
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getTitle()
+        */
+       public function getTitle() {
+                return $this->getLanguageVariable($this->event->languageCategory.'.'.$this->getEventName().'.title');
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getDescription()
+        */
+       public function getDescription() {
+                return $this->getLanguageVariable($this->event->languageCategory.'.'.$this->getEventName().'.description');
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getLanguageVariable()
+        */
+       public function getLanguageVariable($var, $additionalVariables = array()) {
+               return $this->getLanguage()->getDynamicVariable($var, array_merge($additionalVariables,
+                        array(
+                               'event' => $this
+                        )
+                ));
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::supportsNotificationType()
+        */
+       public function supportsNotificationType(NotificationType $notificationType) {
+               // returns true if the language variable exists. By using the
+               // static getter, errors are avoided
+               return ($this->getLanguage()->get($this->event->languageCategory.'.'.$this->getEventName().'.'.$notificationType->getName()) != $this->event->languageCategory.'.'.$this->getEventName().'.'.$notificationType->getName());
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getLanguage()
+        */
+       public function getLanguage() {
+               if ($this->language === null || !$this->language instanceof Language) {
+                       $this->language = WCF::getLanguage();
+               }
+
+               return $this->language;
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::setLanguage()
+        */
+       public function setLanguage(Language $language) {
+               $this->language = $language;
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getObject()
+        */
+       public function getObject() {
+               return $this->object;
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::setObject()
+        */
+       public function setObject(UserNotificationConveyableObject $object, $additionalData = array()) {
+               $this->object = $object;
+               $this->additionalData = $additionalData;
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getEventName()
+        */
+       public function getEventName() {
+               return $this->event->eventName;
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getIcon()
+        */
+       public function getIcon() {
+               return $this->event->icon;
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getAcceptURL()
+        */
+       public function getAcceptURL(UserNotification $notification) {
+               if ($this->event->requiresConfirmation) {
+                       if ($this->event->acceptURL) {
+                               return $this->parseURL($this->event->acceptURL, $notification);
+                       }
+                       else {
+                               return $this->parseURL('index.php?action=NotificationConfirm&notificationID='.$notification->notificationID, $notification);
+                       }
+               }
+               else {
+                       return '';
+               }
+       }
+
+       /**
+        * @see UserNotificationConveyableEvent::getDeclineURL()
+        */
+       public function getDeclineURL(UserNotification $notification) {
+               if ($this->event->requiresConfirmation && $this->event->declineURL) {
+                       return $this->parseURL($this->event->declineURL, $notification);
+               }
+               else {
+                       return '';
+               }
+       }
+
+       /**
+        * Parses accept and decline URLs
+        *
+        * @param       string                  $url
+        * @param       UserNotification        $notification
+        * @return      string
+        */
+       protected function parseURL($url, UserNotification $notification) {
+               $url = str_replace('%u', $notification->userID, $url);
+               $url = str_replace('%o', $notification->objectID, $url);
+               $url = str_replace('%n', $notification->notificationID, $url);
+
+               // append request uri
+               if (WCF::getSession()->requestMethod == 'GET') {
+                       if (StringUtil::indexOf($url, '?') !== false) {
+                               $url .= '&';
+                       }
+                       else {
+                               $url .= '?';
+                       }
+                       preg_match('/index\.php.*/is', WCF::getSession()->requestURI, $requestURI);
+
+                       $url .= 'url='.rawurlencode($requestURI[0]);
+               }
+
+               // append security token
+               $url .= '&t='.SECURITY_TOKEN;
+
+               // append session id
+               if (SID != '' && !preg_match('/(?:&|\?)s=[a-z0-9]{40}/', $url)) {
+                       if (StringUtil::indexOf($url, '?') !== false) {
+                               $url .= SID_ARG_2ND_NOT_ENCODED;
+                       }
+                       else {
+                               $url .= SID_ARG_1ST;
+                       }
+               }
+
+               return $url;
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationConveyableEvent.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationConveyableEvent.class.php
new file mode 100644 (file)
index 0000000..df03bab
--- /dev/null
@@ -0,0 +1,143 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/UserNotification.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/type/NotificationType.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/object/UserNotificationConveyableObject.class.php');
+
+/**
+ * This interface should be implemented by every event which is fired by the notification system
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.event
+ * @category   Community Framework
+ */
+interface UserNotificationConveyableEvent {
+       /**
+        * Callback function for event based data manipulation for notifications
+        *
+        * @param       array<mixed>     $data
+        */
+       public function initialize(&$data);
+
+       /**
+        * Returns the message for this notification event.
+        *
+        * @param       NotificationType        $notificationType
+        * @return      string
+        */
+       public function getMessage(NotificationType $notificationType);
+
+       /**
+        * Returns the short output for this notification event
+        *
+        * @return      string
+        */
+       public function getShortOutput();
+
+       /**
+        * Returns the medium output for this notification event
+        *
+        * @return      string
+        */
+       public function getMediumOutput();
+
+       /**
+        * Returns the full output for this notification event
+        *
+        * @return      string
+        */
+       public function getOutput();
+
+       /**
+        * Returns the human-readable title of this event
+        *
+        * @return      string
+        */
+       public function getTitle();
+
+       /**
+        * Returns the human-readable description of this event
+        *
+        * @return      string
+        */
+       public function getDescription();
+
+       /**
+        * Returns the icon of this event
+        *
+        * @return      string
+        */
+       public function getIcon();
+
+       /**
+        *
+        * @param       string          $var
+        * @param       array<mixed>    $additionalVariables
+        */
+       public function getLanguageVariable($var, $additionalVariables = array());
+
+       /**
+        * Returns true if this event supports the given notification type
+        *
+        * @param       NotificationType        $notificationType
+        * @return      boolean
+        */
+       public function supportsNotificationType(NotificationType $notificationType);
+
+       /**
+        * Sets the recipient user's language
+        *
+        * @param       Language        $language
+        */
+       public function setLanguage(Language $language);
+
+       /**
+        * Returns the recipient user's language
+        *
+        * @return      Language
+        */
+       public function getLanguage();
+
+       /**
+        * Sets the object for the event
+        *
+        * @param       UserNotificationConveyableObject        $object
+        * @param       array<mixed>                            $additionalData
+        */
+       public function setObject(UserNotificationConveyableObject $object, $additionalData = array());
+
+       /**
+        * Returns the object of this event
+        *
+        * @return      NotificationObject
+        */
+       public function getObject();
+
+       /**
+        * Returns the name of this event
+        *
+        * @deprecated
+        * @return      string
+        */
+       public function getEventName();
+
+       /**
+        * Returns the URL for accepting the notification
+        *
+        * @param       UserNotification    $notification
+        * @return      string
+        */
+       public function getAcceptURL(UserNotification $notification);
+
+       /**
+        * Returns the URL for declining the notification
+        *
+        * @param       UserNotification    $notification
+        * @return      string
+        */
+       public function getDeclineURL(UserNotification $notification);
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEvent.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEvent.class.php
new file mode 100644 (file)
index 0000000..3da47db
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+namespace wcf\data\user\notification\event;
+use wcf\data\DatabaseObject;
+
+/**
+ * Represents a user notification event.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.event
+ * @category   Community Framework
+ */
+class UserNotificationEvent extends DatabaseObject {
+       /**
+        * @see DatabaseObject::$databaseTableName
+        */
+       protected static $databaseTableName = 'user_notification_event';
+       
+       /**
+        * @see DatabaseObject::$databaseTableIndexName
+        */
+       protected static $databaseTableIndexName = 'eventID';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventAction.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventAction.class.php
new file mode 100644 (file)
index 0000000..b9b68ac
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\event;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes user notification event-related actions.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.event
+ * @category   Community Framework
+ */
+class UserNotificationEventAction extends AbstractDatabaseObjectAction {
+       /**
+        * @see AbstractDatabaseObjectAction::$className
+        */
+       protected $className = 'wcf\data\user\notification\event\UserNotificationEventEditor';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventEditor.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventEditor.class.php
new file mode 100644 (file)
index 0000000..1efd880
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\event;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit user notification events.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.event
+ * @category   Community Framework
+ */
+class UserNotificationEventEditor extends DatabaseObjectEditor {
+       /**
+        * @see DatabaseObjectEditor::$baseClass
+        */
+       protected static $baseClass = 'wcf\data\user\notification\event\UserNotificationEvent';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventList.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventList.class.php
new file mode 100644 (file)
index 0000000..7291baf
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\event;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of user notification events.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.event
+ * @category   Community Framework
+ */
+class UserNotificationEventList extends DatabaseObjectList {
+       /**
+        * @see DatabaseObjectList::$className
+        */
+       public $className = 'wcf\data\user\notification\event\UserNotificationEvent';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessage.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessage.class.php
new file mode 100644 (file)
index 0000000..d2fe43f
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/DatabaseObject.class.php');
+
+/**
+ * Represents a notification message.
+ *
+ * @author     Marcel Werk
+ * @copyright  2009-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.message
+ * @category   Community Framework
+ */
+class UserNotificationMessage extends DatabaseObject {
+       /**
+        * @see DatabaseObject::$databaseTableName
+        */
+       public $databaseTableName = 'user_notification_message';
+       
+       /**
+        * @see DatabaseObject::$databaseIndexName
+        */
+       public $databaseIndexName = 'messageID';
+       
+       /**
+        * Returns a notification message based on the given parameters.
+        *
+        * @param       integer         $notificationID
+        * @param       string          $notificationType
+        * @return      UserNotificationMessage
+        */
+       public static function getMessage($notificationID, $notificationType) {
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_user_notification_message
+                       WHERE   notificationID = ".$notificationID."
+                               AND notificationType = '".escapeString($notificationType)."'";
+               if (($row = WCF::getDB()->getFirstRow($sql)) === false) {
+                       $row = array();
+               }
+               
+               return new UserNotificationMessage(null, $row);
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessageEditor.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessageEditor.class.php
new file mode 100644 (file)
index 0000000..9eb5840
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/DatabaseObjectEditor.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/message/UserNotificationMessage.class.php');
+
+/**
+ * Extends the notification message object with functions to create, update and delete messages.
+ *
+ * @author     Marcel Werk
+ * @copyright  2009-2010 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.message
+ * @category   Community Framework
+ */
+class UserNotificationMessageEditor extends DatabaseObjectEditor {
+       /**
+        * @see DatabaseObjectEditor::$baseClass
+        */
+       protected static $baseClass = 'UserNotificationMessage';
+       
+       /**
+        * @see EditableObject::create()
+        */
+       public static function create(array $parameters = array()) {
+               return self::__create('wcf'.WCF_N.'_user_notification_message', 'messageID', 'UserNotificationMessage', $parameters);
+       }
+       
+       /**
+        * @see EditableObject::deleteAll()
+        */
+       public static function deleteAll(array $messageIDs = array()) {
+               return self::__deleteAll('wcf'.WCF_N.'_user_notification_message', 'messageID', $messageIDs);
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/AbstractUserNotificationConveyableObjectType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/AbstractUserNotificationConveyableObjectType.class.php
new file mode 100644 (file)
index 0000000..73522e3
--- /dev/null
@@ -0,0 +1,43 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/object/UserNotificationConveyableObjectType.class.php');
+
+/**
+ * A default implementation for NotificationObjectType to provide access to NotificationObjects
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.object
+ * @category   Community Framework
+ */
+abstract class AbstractUserNotificationConveyableObjectType implements UserNotificationConveyableObjectType {
+       /**
+        * @see NotficiationObjectType::getObjects()
+        */
+       public function getObjects($data) {
+               $objectArray = array();
+               if (is_int($data) || is_string($data)) {
+                       $object = $this->getObjectByID($data);
+                       if ($object) $objectArray[$object->getObjectID()] = $object;
+               }
+               else if (is_array($data)) {
+                       $objectArray = $this->getObjectsByIDArray($data);
+               }
+               else if (is_object($data)) {
+                       $object = $this->getObjectByObject($data);
+                       if ($object) $objectArray[$object->getObjectID()] = $object;
+               }
+
+               return $objectArray;
+       }
+
+       /**
+        * @see NotficiationObjectType::getAdditionalPackageIDs()
+        */
+       public function getAdditionalPackageIDs() {
+               return array();
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObject.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObject.class.php
new file mode 100644 (file)
index 0000000..285db33
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * This interface should be implemented by every object which is part of a notification
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.object
+ * @category   Community Framework
+ */
+interface UserNotificationConveyableObject {
+       /**
+        * Returns the ID of this object.
+        *
+        * @return      string
+        */
+       public function getObjectID();
+
+       /**
+        * Returns the title of this object.
+        *
+        * @return      string
+        */
+       public function getTitle();
+
+       /**
+        * Returns the url of this object.
+        *
+        * @return      string
+        */
+       public function getURL();
+
+       /**
+        * Returns the icon of this object.
+        *
+        * @return      string
+        */
+       public function getIcon();
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObjectType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObjectType.class.php
new file mode 100644 (file)
index 0000000..4b25df9
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/object/UserNotificationConveyableObject.class.php');
+
+/**
+ * This interface defines the basic methods every notification object type should implement.
+ *
+ * @author     Marcel Werk, Oliver Kliebisch
+ * @copyright  2009-2011 WoltLab GmbH, Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.object
+ * @category   Community Framework
+ */
+interface UserNotificationConveyableObjectType {
+       /**
+        * Get the notification object by its ID
+        *
+        * @param       integer         $objectID
+        * @return      UserNotificationConveyableObject
+        */
+       public function getObjectByID($objectID);
+
+       /**
+        * Get the notification object by using its parent object
+        *
+        * @param       object          $object
+        * @return      UserNotificationConveyableObject
+        */
+       public function getObjectByObject($object);
+
+       /**
+        * Get the notification objects by their IDs
+        *
+        * @param       array<integer>          $objectIDs
+        * @return      array<UserNotificationConveyableObject>
+        */
+       public function getObjectsByIDs(array $objectIDs);
+
+       /**
+        * Get the notification objects
+        *
+        * @param       mixed           $data
+        * @return      array<UserNotificationConveyableObject>
+        */
+       public function getObjects($data);
+
+       /**
+        * Returns the package ID of the object's package
+        * It does not return the package ID of the oject type package
+        *
+        * @return integer
+        */
+       public function getPackageID();
+
+       /**
+        * Returns additional packageIDs.
+        * This can be used for multiple combined dependency trees
+        *
+        * @return array<integer>
+        */
+       public function getAdditionalPackageIDs();
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectType.class.php
new file mode 100644 (file)
index 0000000..fe443ff
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/DatabaseObject.class.php');
+
+/**
+ * Represents a notification object type.
+ *
+ * @author     Marcel Werk
+ * @copyright  2009-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.object
+ * @category   Community Framework
+ */
+class UserNotificationObjectType extends DatabaseObject {
+       /**
+        * @see DatabaseObject::$databaseTableName
+        */
+       public $databaseTableName = 'user_notification_object_type';
+       
+       /**
+        * @see DatabaseObject::$databaseIndexName
+        */
+       public $databaseIndexName = 'objectTypeID';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectTypeEditor.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectTypeEditor.class.php
new file mode 100644 (file)
index 0000000..aef346f
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/DatabaseObjectEditor.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/object/UserNotificationObjectType.class.php');
+
+/**
+ * Extends the notification object type object with functions to create, update and delete messages.
+ *
+ * @author     Marcel Werk
+ * @copyright  2009-2010 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.object
+ * @category   Community Framework
+ */
+class UserNotificationObjectTypeEditor extends DatabaseObjectEditor {
+       /**
+        * @see DatabaseObjectEditor::$baseClass
+        */
+       protected static $baseClass = 'UserNotificationObjectType';
+       
+       /**
+        * @see EditableObject::create()
+        */
+       public static function create(array $parameters = array()) {
+               return self::__create('wcf'.WCF_N.'_user_notification_object_type', 'objectTypeID', 'UserNotificationObjectType', $parameters);
+       }
+       
+       /**
+        * @see EditableObject::deleteAll()
+        */
+       public static function deleteAll(array $objectTypeIDs = array()) {
+               return self::__deleteAll('wcf'.WCF_N.'_user_notification_object_type', 'objectTypeID', $$objectTypeIDs);
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObject.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObject.class.php
new file mode 100644 (file)
index 0000000..7d8511f
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/object/UserNotificationConveyableObject.class.php');
+
+/**
+ * This dummy object provides the usage of object-unbound notifications
+ *
+ * @author     Marcel Werk, Oliver Kliebisch
+ * @copyright  2009-2011 WoltLab GmbH, Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.object
+ * @category   Community Framework
+ */
+class VoidUserNotificationConveyableObject implements UserNotificationConveyableObject {
+       /**
+        * @see UserNotificationConveyableObject::getIcon()
+        */
+       public function getIcon() {
+               return '';
+       }
+
+       /**
+        * @see UserNotificationConveyableObject::getObjectID()
+        */
+       public function getObjectID() {
+               return 0;
+       }
+
+       /**
+        * @see UserNotificationConveyableObject::getTitle()
+        */
+       public function getTitle() {
+               return '';
+       }
+
+       /**
+        * @see UserNotificationConveyableObject::getURL()
+        */
+       public function getURL() {
+               return '';
+       }
+}
+?>
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObjectType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObjectType.class.php
new file mode 100644 (file)
index 0000000..596888e
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/object/AbstractUserNotificationConveyableObjectType.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/object/VoidUserNotificationConveyableObject.class.php');
+
+/**
+ * A notification object type for general unbound notifications
+ *
+ * @author     Marcel Werk, Oliver Kliebisch
+ * @copyright  2009-2011 WoltLab GmbH, Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.object
+ * @category   Community Framework
+ */
+class VoidUserNotificationConveyableObjectType extends AbstractUserNotificationConveyableObjectType {
+       /**
+        * @see UserNotificationConveyableObjectType::getObjectByID()
+        */
+       public function getObjectByID($objectID) {
+              return new VoidUserNotificationConveyableObject();
+       }
+
+       /**
+        * @see UserNotificationConveyableObjectType::getObjectByObject()
+        */
+       public function getObjectByObject($object) {
+               return new VoidUserNotificationConveyableObject();
+       }
+
+       /**
+        * @see UserNotificationConveyableObjectType::getObjectsByIDs()
+        */
+       public function getObjectsByIDs(array $objectIDs) {
+               return array(0 => new VoidUserNotificationConveyableObject());
+       }
+
+       /**
+        * @see UserNotificationConveyableObjectType::getPackageID()
+        */
+       public function getPackageID() {                
+               // void notifications are bound directly to the notification package
+               return WCF::getPackageID('com.woltlab.wcf.user.notification');
+       }
+
+       /**
+        * @see UserNotificationConveyableObjectType::getObjects()
+        */
+       public function getObjects($data) {
+               return array(0 => new VoidUserNotificationConveyableObject());
+       }
+}
+?>
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectType.class.php
new file mode 100644 (file)
index 0000000..671f53c
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+namespace wcf\data\user\notification\object\type;
+use wcf\data\DatabaseObject;
+
+/**
+ * Represents a user notification object type.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.object.type
+ * @category   Community Framework
+ */
+class UserNotificationObjectType extends DatabaseObject {
+       /**
+        * @see DatabaseObject::$databaseTableName
+        */
+       protected static $databaseTableName = 'user_notification_object_type';
+       
+       /**
+        * @see DatabaseObject::$databaseTableIndexName
+        */
+       protected static $databaseTableIndexName = 'objectTypeID';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeAction.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeAction.class.php
new file mode 100644 (file)
index 0000000..ce458e0
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\object\type;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes user notification object type-related actions.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.object.type
+ * @category   Community Framework
+ */
+class UserNotificationObjectTypeAction extends AbstractDatabaseObjectAction {
+       /**
+        * @see AbstractDatabaseObjectAction::$className
+        */
+       protected $className = 'wcf\data\user\notification\object\type\UserNotificationObjectTypeEditor';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeEditor.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeEditor.class.php
new file mode 100644 (file)
index 0000000..320bdb0
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\object\type;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit user notification object types.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.object.type
+ * @category   Community Framework
+ */
+class UserNotificationObjectTypeEditor extends DatabaseObjectEditor {
+       /**
+        * @see DatabaseObjectEditor::$baseClass
+        */
+       protected static $baseClass = 'wcf\data\user\notification\object\type\UserNotificationObjectType';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeList.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeList.class.php
new file mode 100644 (file)
index 0000000..370061d
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\object\type;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of user notification object types.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification
+ * @category   Community Framework
+ */
+class UserNotificationObjectTypeList extends DatabaseObjectList {
+       /**
+        * @see DatabaseObjectList::$className
+        */
+       public $className = 'wcf\data\user\notification\object\type\UserNotificationObjectType';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/type/MailUserNotificationReceivableType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/MailUserNotificationReceivableType.class.php
new file mode 100644 (file)
index 0000000..1aaf32b
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/notification/type/UserNotificationReceivableType.class.php');
+require_once(WCF_DIR.'lib/data/mail/Mail.class.php');
+
+/**
+ * A notification type for sending mail notifications.
+ *
+ * @author     Marcel Werk, Oliver Kliebisch
+ * @copyright  2009-2011 WoltLab GmbH, Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.type
+ * @category   Community Framework
+ */
+class MailUserNotificationReceivableType implements UserNotificationReceivableType {
+       /**
+        * @see UserNotificationReceivableType::send()
+        */
+       public function send(User $user, UserNotificationConveyableEvent $event, UserNotificationEditor $notification) {
+               // get messages
+               $message = $event->getMessage($this, array(
+                       'user' => $user,
+                       'pageURL' => FileUtil::addTrailingSlash(PAGE_URL)
+               ));
+
+               // append notification mail footer
+               $token = $user->notificationMailToken;
+               if (!$token) {
+                       // generate token if not present
+                       $token = StringUtil::substring(StringUtil::getHash(serialize(array($user->userID, StringUtil::getRandomID()))), 0, 20);
+                       $editor = $user->getEditor();
+                       $editor->updateOptions(array('notificationMailToken' => $token));
+               }
+               $message .= '\n'.$event->getLanguage()->getDynamicVariable('wcf.user.notification.type.mail.footer',  array(
+                       'user' => $user,
+                       'pageURL' => FileUtil::addTrailingSlash(PAGE_URL),
+                       'token' => $token,
+                       'notification' => $notification
+               ));
+
+               // Use short output as mail subject and strip its HTML
+               $shortMessage = StringUtil::stripHTML($notification->shortOutput);
+
+               // build mail
+               $mail = new Mail(array($user->username => $user->email), $event->getLanguageVariable('wcf.user.notification.type.mail.subject', array('title' => $shortMessage)), $message);
+               $mail->send();
+       }
+
+       /**
+        * @see UserNotificationReceivableType::revoke()
+        */
+       public function send(User $user, UserNotificationConveyableEvent $event, UserNotificationEditor $notification) {
+               // unsupported
+               return;
+       }
+
+       /**
+        * @see UserNotificationReceivableType::getName()
+        */
+       public function getName() {
+               return 'mail';
+       }
+
+       /**
+        * @see UserNotificationReceivableType::getIcon()
+        */
+       public function getIcon() {
+               return 'email';
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationReceivableType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationReceivableType.class.php
new file mode 100644 (file)
index 0000000..4eb8e48
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/user/User.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/event/UserNotificationConveyableEvent.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/UserNotificationEditor.class.php');
+
+/**
+ * This interface should be implemented by every notification type
+ *
+ * @author     Marcel Werk, Oliver Kliebisch
+ * @copyright  2009-2011 WoltLab GmbH, Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.user.notification
+ * @subpackage data.user.notification.type
+ * @category   Community Framework
+ */
+interface UserNotificationReceivableType {
+       /**
+        * Sends the notification using this notification transport type
+        *
+        * @param       User                                    $user
+        * @param       UserNotificationConveyableEvent         $event
+        * @param       UserNotificationEditor                  $notification
+        */
+       public function send(User $user, UserNotificationConveyableEvent $event, UserNotificationEditor $notification);
+
+       /**
+        * Tries to revoke the notification. This might not be applicable for
+        * all notification types
+        *
+        * @param       User                                    $user
+        * @param       UserNotificationConveyableEvent         $event
+        * @param       UserNotificationEditor                  $notification
+        */
+       public function revoke(User $user, UserNotificationConveyableEvent $event, UserNotificationEditor $notification);
+
+       /**
+        * Returns the name of the notification type
+        *
+        * @return string
+        */
+       public function getName();
+
+       /**
+        * Returns the icon of the notification type
+        *
+        * @return string
+        */
+       public function getIcon();
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationType.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationType.class.php
new file mode 100644 (file)
index 0000000..cd76b25
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+namespace wcf\data\user\notification\type;
+use wcf\data\DatabaseObject;
+
+/**
+ * Represents a user notification type.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.type
+ * @category   Community Framework
+ */
+class UserNotificationType extends DatabaseObject {
+       /**
+        * @see DatabaseObject::$databaseTableName
+        */
+       protected static $databaseTableName = 'user_notification_type';
+       
+       /**
+        * @see DatabaseObject::$databaseTableIndexName
+        */
+       protected static $databaseTableIndexName = 'notificationTypeID';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeAction.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeAction.class.php
new file mode 100644 (file)
index 0000000..dc117f9
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\type;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes user notification type-related actions.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.type
+ * @category   Community Framework
+ */
+class UserNotificationTypeAction extends AbstractDatabaseObjectAction {
+       /**
+        * @see AbstractDatabaseObjectAction::$className
+        */
+       protected $className = 'wcf\data\user\notification\type\UserNotificationTypeEditor';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeEditor.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeEditor.class.php
new file mode 100644 (file)
index 0000000..b0cefee
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\type;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit user notification types.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.type
+ * @category   Community Framework
+ */
+class UserNotificationTypeEditor extends DatabaseObjectEditor {
+       /**
+        * @see DatabaseObjectEditor::$baseClass
+        */
+       protected static $baseClass = 'wcf\data\user\notification\type\UserNotificationType';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeList.class.php b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeList.class.php
new file mode 100644 (file)
index 0000000..829efbe
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\data\user\notification\type;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of user notification types.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage data.user.notification.type
+ * @category   Community Framework
+ */
+class UserNotificationTypeList extends DatabaseObjectList {
+       /**
+        * @see DatabaseObjectList::$className
+        */
+       public $className = 'wcf\data\user\notification\type\UserNotificationType';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/form/UserNotificationSettingsForm.class.php b/com.woltlab.wcf.notification/files/lib/form/UserNotificationSettingsForm.class.php
new file mode 100644 (file)
index 0000000..021de0e
--- /dev/null
@@ -0,0 +1,288 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/form/AbstractSecureForm.class.php');
+require_once(WCF_DIR.'lib/page/util/menu/UserCPMenu.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationHandler.class.php');
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+
+/**
+ * Shows the notification settings form.
+ *
+ * @author     Tim Düsterhus, Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage form
+ * @category   Community Framework
+ */
+class UserNotificationSettingsForm extends AbstractSecureForm {
+       /**
+        * @see AbstractPage::$templateName
+        */
+       public $templateName = 'userNotificationSettings';
+
+       /**
+        * An array holding all available notification object types
+        *
+        * @var array
+        */
+       public $notificationObjectTypes = array();
+
+       /**
+        * An array holding all available notification types
+        *
+        * @var array
+        */
+       public $notificationTypes = array();
+
+       /**
+        * The activated event notifications in the form
+        *
+        * @var array
+        */
+       public $activeEventNotifications = array();
+
+       /**
+        * The notification user object of the current user
+        *
+        * @var NotificationUser
+        */
+       public $notificationUser = null;
+
+       /**
+        * @see Page::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+
+               $this->notificationUser = new NotificationUser(null, WCF::getUser());
+               $this->notificationObjectTypes = NotificationHandler::getAvailableNotificationObjectTypes();
+               $this->notificationTypes = NotificationHandler::getAvailableNotificationTypes();
+       }
+
+       /**
+        * @see Form::readFormParameters()
+        */
+       public function readFormParameters() {
+               parent::readFormParameters();
+
+               if (isset($_POST['activeEventNotifications']) && is_array($_POST['activeEventNotifications'])) $this->activeEventNotifications = $_POST['activeEventNotifications'];
+       }
+
+       /**
+        * @see Form::validate()
+        */
+       public function validate() {
+               parent::validate();
+
+               // validate checked options and unset wrong settings
+               foreach ($this->activeEventNotifications as $objectType => $events) {
+                       if (!isset($this->notificationObjectTypes[$objectType])) {
+                               unset ($this->activeEventNotifications[$objectType]);
+                               continue;
+                       }
+
+                       foreach ($events as $eventName => $notificationTypes) {
+                               if (!isset($this->notificationObjectTypes[$objectType]['events'][$eventName])) {
+                                       unset ($this->activeEventNotifications[$objectType][$eventName]);
+                                       continue;
+                               }
+
+                               foreach ($notificationTypes as $notificationType => $value) {
+                                       if (!isset($this->notificationTypes[$notificationType])) {
+                                               unset ($this->activeEventNotifications[$objectType][$eventName][$notificationType]);
+                                               continue;
+                                       }
+                               }
+                       }
+               }
+
+               $settings = array();
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_user_notification_event_settings
+                       WHERE packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+               $result = WCF::getDB()->sendQuery($sql);
+               while($row = WCF::getDB()->fetchArray($result)) {
+                       $settings[$row['objectType']][$row['eventName']][$row['notificationType']] = $row;
+               }
+
+               // add default values
+               foreach ($this->notificationObjectTypes as $name => $objectType) {
+                       if (isset($objectType['events'])) {
+                               foreach ($objectType['events'] as $eventName => $event) {
+                                       foreach ($this->notificationTypes as $typeName => $type) {
+                                               if (!isset($this->activeEventNotifications[$name][$eventName]) || !isset($this->activeEventNotifications[$name][$eventName][$typeName])) {
+                                                       $this->activeEventNotifications[$name][$eventName][$typeName] = 0;
+                                               }
+                                               if (isset($settings[$name][$eventName][$typeName]) && !$settings[$name][$eventName][$typeName]['canBeDisabled']) {
+                                                       $this->activeEventNotifications[$name][$eventName][$typeName] = 1;
+                                               }
+                                               else if (isset($settings[$name][$eventName][$typeName]) && !$settings[$name][$eventName][$typeName]['enabled']) {
+                                                       $this->activeEventNotifications[$name][$eventName][$typeName] = 0;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @see Page::readData()
+        */
+       public function readData() {
+               parent::readData();
+
+               $settings = array();
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_user_notification_event_settings
+                       WHERE packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+               $result = WCF::getDB()->sendQuery($sql);
+               while($row = WCF::getDB()->fetchArray($result)) {
+                       $settings[$row['objectType']][$row['eventName']][$row['notificationType']] = $row;
+               }
+
+               if ($this->notificationTypes) {
+                       // calculate compatibility map
+                       foreach ($this->notificationObjectTypes as $name => $objectType) {
+                               if (!isset($objectType['events']) || !is_array($objectType['events'])) {
+                                       unset ($this->notificationObjectTypes[$name]);
+                                       continue;
+                               }
+                               foreach ($objectType['events'] as $eventName => $event) {
+                                       $this->notificationObjectTypes[$name]['events'][$eventName]->supportedNotificationTypes = array();
+                                       foreach ($this->notificationTypes as $typeName => $type) {
+                                               $adminSettings = true;
+                                               if (isset($settings[$name][$eventName][$typeName]) && !$settings[$name][$eventName][$typeName]['canBeDisabled']) $adminSettings = false;
+                                               else if (isset($settings[$name][$eventName][$typeName]) && !$settings[$name][$eventName][$typeName]['enabled']) $adminSettings = false;
+                                               $this->notificationObjectTypes[$name]['events'][$eventName]->supportedNotificationTypes[$typeName] = $event->supportsNotificationType($type) && $adminSettings;
+                                       }
+                               }
+                       }
+               }
+
+               if (!count($_POST)) {
+                       foreach ($this->notificationObjectTypes as $name => $objectType) {
+                               if (isset($objectType['events'])) {
+                                       foreach ($objectType['events'] as $eventName => $event) {
+                                               foreach ($this->notificationTypes as $typeName => $type) {
+                                                       if (isset($this->notificationUser->eventNotificationSettings[$name][$eventName][$typeName])) {
+                                                               $this->activeEventNotifications[$name][$eventName][$typeName] = $this->notificationUser->eventNotificationSettings[$name][$eventName][$typeName];
+                                                       }
+                                                       else {
+                                                               if ($typeName == $event->defaultNotificationType) {
+                                                                       $this->activeEventNotifications[$name][$eventName][$typeName] = 1;
+                                                               }
+                                                               else {
+                                                                       $this->activeEventNotifications[$name][$eventName][$typeName] = 0;
+                                                               }
+                                                       }
+                                                       if (isset($settings[$name][$eventName][$typeName]) && !$settings[$name][$eventName][$typeName]['canBeDisabled']) {
+                                                               $this->activeEventNotifications[$name][$eventName][$typeName] = 1;
+                                                       }
+                                                       else if (isset($settings[$name][$eventName][$typeName]) && !$settings[$name][$eventName][$typeName]['enabled']) {
+                                                               $this->activeEventNotifications[$name][$eventName][$typeName] = 0;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * @see Page::assignVariables()
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+
+               WCF::getTPL()->assign(array(
+                       'notificationObjectTypes' => $this->notificationObjectTypes,
+                       'notificationTypes' => $this->notificationTypes,
+                       'activeEventNotifications' => $this->activeEventNotifications,
+                       'user' => $this->notificationUser
+               ));
+       }
+
+       /**
+        * @see Page::show()
+        */
+       public function show() {
+               // check module
+               if (!MODULE_USER_NOTIFICATION) {
+                       throw new IllegalLinkException();
+               }
+
+               // check permission
+               if (!WCF::getUser()->userID) {
+                       throw new PermissionDeniedException();
+               }
+
+               // set active user cp menu item
+               UserCPMenu::getInstance()->setActiveMenuItem('wcf.user.usercp.menu.link.settings.notification');
+
+               // show form
+               parent::show();
+       }
+
+       /**
+        * @see Form::save()
+        */
+       public function save() {
+               parent::save();
+
+               // delete old data but only from this dependency tree
+               $sql = "DELETE FROM     wcf".WCF_N."_user_notification_event_to_user
+                       WHERE           userID = ".WCF::getUser()->userID."
+                       AND             packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+               WCF::getDB()->sendQuery($sql);
+
+               $settings = array();
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_user_notification_event_settings
+                       WHERE packageID IN (".NotificationHandler::getAvailablePackageIDs().")";
+               $result = WCF::getDB()->sendQuery($sql);
+               while($row = WCF::getDB()->fetchArray($result)) {
+                       $settings[$row['objectType']][$row['eventName']][$row['notificationType']] = $row;
+               }
+
+               // prepare new data
+               $inserts = '';
+               foreach ($this->activeEventNotifications as $objectType => $events) {
+                       foreach ($events as $eventName => $notificationTypes) {
+                               foreach ($notificationTypes as $notificationType => $enabled) {
+                                       if (isset($settings[$objectType][$eventName][$notificationType]) && !$settings[$objectType][$eventName][$notificationType]['enabled']) {
+                                               $enabled = false;
+                                       }
+                                       else if (isset($settings[$objectType][$eventName][$notificationType]) && !$settings[$objectType][$eventName][$notificationType]['canBeDisabled']) {
+                                               $enabled = true;
+                                       }
+
+                                       $objectTypeObject = NotificationHandler::getNotificationObjectTypeObject($objectType);
+                                       if (!empty($inserts)) $inserts .= ',';
+                                       $inserts .= "(".WCF::getUser()->userID.",
+                                       ".$objectTypeObject->getPackageID().",
+                                       '".escapeString($objectType)."',
+                                       '".escapeString($eventName)."',
+                                       '".escapeString($notificationType)."',
+                                       ".($enabled ? "1" : "0").")";
+                               }
+                       }
+
+               }
+
+               if (!empty($inserts)) {
+                       $sql = "INSERT INTO     wcf".WCF_N."_user_notification_event_to_user
+                                               (userID, packageID, objectType, eventName, notificationType, enabled)
+                                               VALUES
+                                               ".$inserts;
+                       WCF::getDB()->sendQuery($sql);
+               }
+
+               $this->saved();
+
+               // show success message
+               WCF::getTPL()->assign('success', true);
+       }
+
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/page/UserNotificationCountPage.class.php b/com.woltlab.wcf.notification/files/lib/page/UserNotificationCountPage.class.php
new file mode 100644 (file)
index 0000000..23f39b8
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/page/AbstractPage.class.php');
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+
+/**
+ * Outputs the number of outstanding notifications
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage page
+ * @category   Community Framework
+ */
+class UserNotificationCountPage extends AbstractPage {
+       /**
+        * The notification user object
+        *
+        * @var NotificationUser
+        */
+       public $user = null;
+
+       /**
+        * @see AbstractPage::construct()
+        */
+       public function __construct() {
+               WCF::getSession()->disableUpdate();
+
+               parent::__construct();
+       }
+
+       /**
+        * @see Page::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+
+               if (!MODULE_USER_NOTIFICATION || !WCF::getUser()->userID) {
+                       throw new IllegalLinkException();
+               }
+
+               $this->user = new NotificationUser(null, WCF::getUser(), false);
+       }
+
+       /**
+        * @see Page::show()
+        */
+       public function show() {
+               parent::show();
+
+               HeaderUtil::sendHeaders();
+               echo $this->user->hasOutstandingNotifications();
+               exit;
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/page/UserNotificationFeedPage.class.php b/com.woltlab.wcf.notification/files/lib/page/UserNotificationFeedPage.class.php
new file mode 100644 (file)
index 0000000..82072e4
--- /dev/null
@@ -0,0 +1,77 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/page/AbstractFeedPage.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/FeedNotificationList.class.php');
+
+/**
+ * Prints a list of notification in a rss or atom feed
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage page
+ * @category   Community Framework
+ */
+class UserNotificationFeedPage extends AbstractFeedPage {
+       /**
+        * list of notifications
+        *
+        * @var FeedNotificationList
+        */
+       public $notificationList = null;
+
+       /**
+        * @see Page::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+
+               // check user login (only cookie login supported by now)
+               if (WCF::getUser()->userID == 0) {
+                       throw new PermissionDeniedException();
+               }
+
+               // get notifications
+               $this->notificationList = new FeedNotificationList();
+               $this->notificationList->sqlConditions .= 'u.userID = '.WCF::getUser()->userID;
+               $this->notificationList->sqlConditions .= ' AND notification.time > '.($this->hours ? (TIME_NOW - $this->hours * 3600) : (TIME_NOW - 30 * 86400));
+       }
+
+       /**
+        * @see Page::readData()
+        */
+       public function readData() {
+               parent::readData();
+
+               $this->notificationList->sqlLimit = $this->limit;
+               $this->notificationList->readObjects();
+       }
+
+       /**
+        * @see Page::assignVariables()
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+
+               WCF::getTPL()->assign(array(
+                       'notifications' => $this->notificationList->getObjects()
+               ));
+       }
+
+       /**
+        * @see Page::show()
+        */
+       public function show() {
+               if (!MODULE_USER_NOTIFICATION) {
+                       throw new IllegalLinkException();
+               }
+
+               parent::show();
+
+               // send content
+               WCF::getTPL()->display(($this->format == 'atom' ? 'userNotificationFeedAtom' : 'userNotificationFeedRss2'), false);
+               exit;
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/page/UserNotificationPage.class.php b/com.woltlab.wcf.notification/files/lib/page/UserNotificationPage.class.php
new file mode 100644 (file)
index 0000000..170c6a2
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/page/MultipleLinkPage.class.php');
+require_once(WCF_DIR.'lib/page/util/menu/UserCPMenu.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationList.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationEditor.class.php');
+
+/**
+ * Shows the user notification center
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage page
+ * @category   Community Framework
+ */
+class UserNotificationPage extends MultipleLinkPage {
+       // system
+       public $templateName = 'userNotification';
+       public $defaultSortField = 'time';
+
+       /**
+        * The list of this users notifications
+        *
+        * @var NotificationList
+        */
+       public $notificationList = null;
+
+       /**
+        * @see Page::readParameters()
+        */
+       public function readParameters() {
+               parent::readParameters();
+
+               if (!MODULE_USER_NOTIFICATION) {
+                       throw new IllegalLinkException();
+               }
+
+               $this->notificationList = new NotificationList();
+               $this->notificationList->sqlConditions = "u.userID = ".WCF::getUser()->userID;
+       }
+
+       /**
+        * @see Page::readData()
+        */
+       public function readData() {
+               parent::readData();
+
+               $this->notificationList->sqlSelects = "u.*,";
+               $this->notificationList->sqlLimit = $this->itemsPerPage;
+               $this->notificationList->sqlOffset = $this->itemsPerPage * ($this->pageNo - 1);
+               $this->notificationList->sqlOrderBy = "notification.time DESC";
+               $this->notificationList->readObjects();
+
+               // mark certain notifications as confirmed by now
+               $notificationIDArray = array();
+               foreach($this->notificationList->getObjects() as $notification) {
+                       if (!$notification->confirmed && !$notification->event->acceptURL) {
+                               $notificationIDArray[] = $notification->notificationID;
+                       }
+               }
+
+               if (count($notificationIDArray)) {
+                       NotificationEditor::markAllConfirmed($notificationIDArray, array(WCF::getUser()->userID));
+                       $user = new NotificationUser(null, WCF::getUser(), false);
+                       $user->recalculateOutstandingNotifications();
+               }
+       }
+
+       /**
+        * @see Page::assignVariables()
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+
+               WCF::getTPL()->assign(array(
+                       'notifications' => $this->notificationList->getObjects()
+               ));
+       }
+
+       /**
+        * @see MultipleLinkPage::countItems()
+        */
+       public function countItems() {
+               parent::countItems();
+
+               return $this->notificationList->countObjects();
+       }
+
+       /**
+        * @see Page::show()
+        */
+       public function show() {
+               if (!WCF::getUser()->userID) {
+                       throw new PermissionDeniedException();
+               }
+
+               // set active tab
+               UserCPMenu::getInstance()->setActiveMenuItem('wcf.user.usercp.menu.link.management.notification');
+
+               parent::show();
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/system/cache/CacheBuilderNotifications.class.php b/com.woltlab.wcf.notification/files/lib/system/cache/CacheBuilderNotifications.class.php
new file mode 100644 (file)
index 0000000..ab75aec
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+require_once(WCF_DIR.'lib/system/cache/CacheBuilder.class.php');
+
+/**
+ * Caches notifications related data
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage system.cache
+ * @category   Community Framework
+ */
+class CacheBuilderNotifications implements CacheBuilder {
+       /**
+        * @see CacheBuilder::getData()
+        */
+       public function getData($cacheResource) {
+               list($cache, $packageID) = explode('-', $cacheResource['cache']);
+
+               // initialize arrays
+               $data = array();
+               $data['objectTypes'] = array();
+               $data['events'] = array();
+               $data['notificationTypes'] = array();                
+
+               /* object types */
+               // get type ids
+               $typeIDArray = array();
+               $sql = "SELECT          objectType, objectTypeID
+                       FROM            wcf".WCF_N."_user_notification_object_type object_type,
+                                       wcf".WCF_N."_package_dependency package_dependency
+                       WHERE           object_type.packageID = package_dependency.dependency
+                                       AND package_dependency.packageID = ".$packageID."
+                       ORDER BY        package_dependency.priority";
+               $result = WCF::getDB()->sendQuery($sql);
+               while ($row = WCF::getDB()->fetchArray($result)) {
+                       $typeIDArray[$row['objectType']] = $row['objectTypeID'];
+               }
+
+               if (count($typeIDArray) > 0) {
+                       $sql = "SELECT          object_type.*, package.packageDir
+                               FROM            wcf".WCF_N."_user_notification_object_type object_type
+                               LEFT JOIN       wcf".WCF_N."_package package
+                               ON              (package.packageID = object_type.packageID)
+                               WHERE           object_type.objectTypeID IN (".implode(',', $typeIDArray).")";
+                       $result = WCF::getDB()->sendQuery($sql);
+                       while ($row = WCF::getDB()->fetchArray($result)) {
+                               $row['className'] = StringUtil::getClassName($row['classFile']);
+                               $data['objectTypes'][] = $row;
+                       }
+               }
+
+               /* events */
+               // get event ids
+               $eventIDArray = array();
+               $sql = "SELECT          eventName, eventID
+                       FROM            wcf".WCF_N."_user_notification_event event,
+                                       wcf".WCF_N."_package_dependency package_dependency
+                       WHERE           event.packageID = package_dependency.dependency
+                                       AND package_dependency.packageID = ".$packageID."
+                       ORDER BY        package_dependency.priority";
+               $result = WCF::getDB()->sendQuery($sql);
+               while ($row = WCF::getDB()->fetchArray($result)) {
+                       $eventIDArray[$row['eventName']] = $row['eventID'];
+               }
+
+               if (count($eventIDArray) > 0) {
+                       $sql = "SELECT          event.*, package.packageDir
+                               FROM            wcf".WCF_N."_user_notification_event event
+                               LEFT JOIN       wcf".WCF_N."_package package
+                               ON              (package.packageID = event.packageID)
+                               WHERE           event.eventID IN (".implode(',', $eventIDArray).")";
+                       $result = WCF::getDB()->sendQuery($sql);
+                       while ($row = WCF::getDB()->fetchArray($result)) {
+                               $row['className'] = StringUtil::getClassName($row['classFile']);
+                               $data['events'][] = $row;                                
+                       }
+               }
+
+               /* notification types */
+               // get notification type ids
+               $notificationTypeIDArray = array();
+               $sql = "SELECT          notificationType, notificationTypeID
+                       FROM            wcf".WCF_N."_user_notification_type notification_type,
+                                       wcf".WCF_N."_package_dependency package_dependency
+                       WHERE           notification_type.packageID = package_dependency.dependency
+                                       AND package_dependency.packageID = ".$packageID."
+                       ORDER BY        package_dependency.priority";
+               $result = WCF::getDB()->sendQuery($sql);
+               while ($row = WCF::getDB()->fetchArray($result)) {
+                       $notificationTypeIDArray[$row['notificationType']] = $row['notificationTypeID'];
+               }
+
+               if (count($notificationTypeIDArray) > 0) {
+                       $sql = "SELECT          notification_type.*, package.packageDir
+                               FROM            wcf".WCF_N."_user_notification_type notification_type
+                               LEFT JOIN       wcf".WCF_N."_package package
+                               ON              (package.packageID = notification_type.packageID)
+                               WHERE           notification_type.notificationTypeID IN (".implode(',', $notificationTypeIDArray).")";
+                       $result = WCF::getDB()->sendQuery($sql);
+                       while ($row = WCF::getDB()->fetchArray($result)) {
+                               $row['className'] = StringUtil::getClassName($row['classFile']);
+                               $data['notificationTypes'][] = $row;
+                       }
+               }
+
+               return $data;
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/system/cronjob/CleanUpNotificationsCronjob.class.php b/com.woltlab.wcf.notification/files/lib/system/cronjob/CleanUpNotificationsCronjob.class.php
new file mode 100644 (file)
index 0000000..f3d5ab8
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/data/cronjobs/Cronjob.class.php');
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationEditor.class.php');
+
+/**
+ * Clears outdated notifications
+ *
+ * @author     Oliver Kliebisch
+ * @copyright  2009-2010 Oliver Kliebisch
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.community.wcf.user.notification
+ * @subpackage system.cronjob
+ * @category   Community Framework
+ */
+class CleanUpNotificationsCronjob implements Cronjob {
+       /**
+        * @see Cronjob::execute()
+        */
+       public function execute($data) {
+               if (!MODULE_USER_NOTIFICATION) return;
+
+               if (USER_NOTIFICATION_LIFETIME > -1) {
+                       $sql = "SELECT          notificationID
+                               FROM            wcf".WCF_N."_user_notification
+                               WHERE           confirmationTime < ".(TIME_NOW - 3600 * USER_NOTIFICATION_LIFETIME)."
+                               AND             confirmationTime <> 0
+                               AND             confirmed = 1";
+                       $result = WCF::getDB()->sendQuery($sql);
+
+                       $notificationIDArray = array();
+                       while ($row = WCF::getDB()->fetchArray($result)) {
+                               $notificationIDArray[] = $row['notificationID'];
+                       }
+
+                       if (count($notificationIDArray)) {
+                               NotificationEditor::deleteAll($notificationIDArray);
+                       }
+               }
+
+               if (USER_NOTIFICATION_LIFETIME_UNCONFIRMED > 0) {
+                       // get affected users
+                       $sql = "SELECT          user_notification.notificationID, user_notification.userID
+                               FROM            wcf".WCF_N."_user_notification user_notification
+                               WHERE           time < ".(TIME_NOW - 3600 * USER_NOTIFICATION_LIFETIME_UNCONFIRMED)."
+                               AND             confirmed = 0";
+                       $result = WCF::getDB()->sendQuery($sql);
+
+                       $userIDs = array();
+                       $notificationIDArray = array();
+                       while ($row = WCF::getDB()->fetchArray($result)) {
+                               $notificationIDArray[] = $row['notificationID'];
+                               if ($row['userID'] && !isset($userIDs[$row['userID']])) {
+                                       $userIDs[$row['userID']] = $row['userID'];
+                               }
+                       }
+
+                       if (count($notificationIDArray)) {
+                               NotificationEditor::deleteAll($notificationIDArray);
+                       }
+
+                       // update affected users
+                       NotificationUser::recalculateUserNotificationFlags($userIDs);
+               }
+
+               // optimize tables to save some memory (mysql only)
+               if (WCF::getDB()->getDBType() == 'MySQLDatabase' || WCF::getDB()->getDBType() == 'MySQLiDatabase' || WCF::getDB()->getDBType() == 'MySQLPDODatabase') {
+                       $sql = "OPTIMIZE TABLE  wcf".WCF_N."_user_notification, wcf".WCF_N."_user_notification_message";
+                       WCF::getDB()->registerShutdownUpdate($sql);
+               }
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserMessagesNotificationListener.class.php b/com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserMessagesNotificationListener.class.php
new file mode 100644 (file)
index 0000000..3949727
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/system/event/EventListener.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationList.class.php');
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+
+/**
+ * Shows outstanding user messages notifications
+ *
+ * @author      Oliver Kliebisch
+ * @copyright   2009-2010 Oliver Kliebisch
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     com.woltlab.community.wcf.user.notification
+ * @subpackage  system.event.listener
+ * @category    Community Framework
+ */
+class StructuredTemplateUserMessagesNotificationListener implements EventListener {
+       /**
+        * @see EventListener::execute()
+        */
+       public function execute($eventObj, $className, $eventName) {
+               // do nothing for guests and spiders or if module is deactivated
+               if (WCF::getUser()->userID == 0 || !MODULE_USER_NOTIFICATION) return;
+
+               $user = new NotificationUser(null, WCF::getUser(), false);
+               if (!$user->hasOutstandingNotifications()) return;
+
+               // get outstanding notifications
+               $notificationList = new NotificationList();
+               $notificationList->sqlSelects .= "notification_message.*,";
+               $notificationList->sqlJoins .= "INNER JOIN      wcf".WCF_N."_user_notification_message notification_message
+                                               ON              (notification_message.notificationID = notification.notificationID
+                                               AND             notification_message.notificationType = 'userMessages')";
+               $notificationList->sqlConditions = "    u.userID = ".$user->userID."
+                                               AND     u.confirmed = 0";
+               $notificationList->readObjects();
+
+               $notifications = $notificationList->getObjects();
+
+               if (!count($notifications)) return;
+               WCF::getTPL()->assign('notifications', $notifications);
+               WCF::getTPL()->append(array(
+                       'userMessages' => WCF::getTPL()->fetch('userMessagesNotifications')
+               ));
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserPanelNotificationListener.class.php b/com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserPanelNotificationListener.class.php
new file mode 100644 (file)
index 0000000..b69ab95
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/system/event/EventListener.class.php');
+require_once(WCF_DIR.'lib/data/user/notification/NotificationList.class.php');
+require_once(WCF_DIR.'lib/data/user/NotificationUser.class.php');
+
+/**
+ * Shows the user panel notification link
+ *
+ * @author      Oliver Kliebisch
+ * @copyright   2009-2010 Oliver Kliebisch
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     com.woltlab.community.wcf.user.notification
+ * @subpackage  system.event.listener
+ * @category    Community Framework
+ */
+class StructuredTemplateUserPanelNotificationListener implements EventListener {
+       /**
+        * @see EventListener::execute()
+        */
+       public function execute($eventObj, $className, $eventName) {
+               // do nothing for guests and spiders or if module is deactivated
+               if (!USER_NOTIFICATION_USER_MENU_LINK_ACTIVE || WCF::getUser()->userID == 0 || !MODULE_USER_NOTIFICATION) return;
+
+               $user = new NotificationUser(null, WCF::getUser(), false);
+
+               WCF::getTPL()->assign('notificationUser', $user);
+
+               WCF::getTPL()->append(array(
+                       'additionalUserMenuItems' => WCF::getTPL()->fetch('userMenuNotificationLink'),
+                       'additionalHeaderContents' => USER_NOTIFICATION_USER_MENU_LINK_AUTOREFRESH ? WCF::getTPL()->fetch('userMenuPeriodicalExecuter') : ''
+               ));
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/files/lib/system/package/plugin/UserNotificationEventPackageInstallationPlugin.class.php b/com.woltlab.wcf.notification/files/lib/system/package/plugin/UserNotificationEventPackageInstallationPlugin.class.php
new file mode 100644 (file)
index 0000000..8b8de31
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+namespace wcf\system\package\plugin;
+use wcf\system\cache\CacheHandler;
+use wcf\system\WCF;
+
+/**
+ * This PIP installs, updates or deletes user notification events.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2011 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf.notification
+ * @subpackage system.package.plugin
+ * @category   Community Framework
+ */
+class UserNotificationEventPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+       /**
+        * @see AbstractXMLPackageInstallationPlugin::$className
+        */
+       public $className = 'wcf\data\user\notification\event\UserNotificationEventEditor';
+       
+       /**
+        * @see AbstractXMLPackageInstallationPlugin::$tableName
+        */
+       public $tableName = 'user_notification_event';
+       
+       /**
+        * @see AbstractXMLPackageInstallationPlugin::$tagName
+        */     
+       public $tagName = 'event';
+       
+       /**
+        * @see AbstractXMLPackageInstallationPlugin::handleDelete()
+        */
+       protected function handleDelete(array $items) {
+               $sql = "DELETE FROM     wcf".WCF_N."_".$this->tableName."
+                       WHERE           packageID = ?
+                                       AND eventName = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               foreach ($items as $item) {
+                       $statement->execute(array(
+                               $this->installation->getPackageID(),
+                               $item['elements']['name']
+                       ));
+               }
+       }
+       
+       /**
+        * @see AbstractXMLPackageInstallationPlugin::prepareImport()
+        */
+       protected function prepareImport(array $data) {
+               $objectTypeID = 0;
+               $defaultNotificationTypeID = 0;
+               
+               return array(
+                       'eventName' => $data['elements']['name'],
+                       'className' => $data['elements']['classname'],
+                       'objectTypeID' => $objectTypeID,
+                       'defaultNotificationTypeID' => $defaultNotificationTypeID,
+                       'languageCategory' => (isset($data['elements']['languagecategory']) ? $data['elements']['languagecategory'] : ''),
+                       'requiresConfirmation' => (isset($data['elements']['requiresconfirmation']) ? intval($data['elements']['requiresconfirmation']) : 0),
+                       'acceptURL' => (isset($data['elements']['accepturl']) ? $data['elements']['accepturl'] : ''),
+                       'declineURL' => (isset($data['elements']['declineurl']) ? $data['elements']['declineurl'] : ''),
+                       'permissions' => (isset($data['elements']['permissions']) ? $data['elements']['permissions'] : ''),
+                       'options' => (isset($data['elements']['options']) ? $data['elements']['options'] : '')
+               );
+       }
+       
+       /**
+        * @see AbstractXMLPackageInstallationPlugin::findExistingItem()
+        */
+       protected function findExistingItem(array $data) {
+               $sql = "SELECT  *
+                       FROM    wcf".WCF_N."_".$this->tableName."
+                       WHERE   packageID = ?
+                               AND eventName = ?";
+               $parameters = array(
+                       $this->installation->getPackageID(),
+                       $data['eventName']
+               );
+               
+               return array(
+                       'sql' => $sql,
+                       'parameters' => $parameters
+               );
+       }
+}
+?>
diff --git a/com.woltlab.wcf.notification/install.sql b/com.woltlab.wcf.notification/install.sql
new file mode 100644 (file)
index 0000000..e0fe14d
--- /dev/null
@@ -0,0 +1,91 @@
+-- notifications
+DROP TABLE IF EXISTS wcf1_user_notification;
+CREATE TABLE wcf1_user_notification (
+       notificationID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       packageID INT(10) NOT NULL,
+       eventID INT(10) NOT NULL,
+       objectID INT(10) NOT NULL DEFAULT 0,
+       time INT(10) NOT NULL DEFAULT 0,
+       shortOutput VARCHAR(255) DEFAULT NULL,
+       mediumOutput TEXT,
+       longOutput TEXT,
+       additionalData TEXT
+);
+
+-- notification recipients
+DROP TABLE IF EXISTS wcf1_user_notification_to_user;
+CREATE TABLE wcf1_user_notification_to_user (
+       notificationID INT(10) NOT NULL,
+       userID INT(10) NOT NULL,
+       confirmed TINYINT(1) NOT NULL,
+       confirmationTime INT(10) NOT NULL DEFAULT 0,
+       UNIQUE KEY notificationID (notificationID, userID)
+);
+
+-- events that create notifications
+DROP TABLE IF EXISTS wcf1_user_notification_event;
+CREATE TABLE wcf1_user_notification_event (
+       eventID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       packageID INT(10) NOT NULL,
+       eventName VARCHAR(255) NOT NULL DEFAULT '',
+       objectTypeID INT(10) NOT NULL,
+       className VARCHAR(255) NOT NULL DEFAULT '',
+       languageCategory VARCHAR(255) NOT NULL,
+       defaultNotificationTypeID INT(10) NULL,
+       requiresConfirmation TINYINT(1) NOT NULL DEFAULT 0,
+       acceptURL VARCHAR(255) NOT NULL DEFAULT '',
+       declineURL VARCHAR(255) NOT NULL DEFAULT '',
+       permissions TEXT,
+       options TEXT,  
+       UNIQUE KEY packageID (packageID, eventName)
+);
+
+-- user configuration for events
+DROP TABLE IF EXISTS wcf1_user_notification_event_to_user;
+CREATE TABLE wcf1_user_notification_event_to_user (
+       userID INT(10) NOT NULL,
+       eventID INT(10) NOT NULL,
+       notificationTypeID INT(10) NOT NULL,
+       enabled TINYINT(1) NOT NULL DEFAULT 0
+);
+
+-- objects that create notifications
+DROP TABLE IF EXISTS wcf1_user_notification_object_type;
+CREATE TABLE wcf1_user_notification_object_type (
+       objectTypeID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       packageID INT(10) NOT NULL,
+       objectType VARCHAR(255) NOT NULL,
+       className VARCHAR(255) NOT NULL,  
+       permissions TEXT,
+       options TEXT,
+       UNIQUE KEY packageID (packageID, objectType)
+);
+
+-- notification types (pm, mail, ...)
+DROP TABLE IF EXISTS wcf1_user_notification_type;
+CREATE TABLE wcf1_user_notification_type (
+       notificationTypeID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       packageID INT(10) NOT NULL,
+       notificationType VARCHAR(255) NOT NULL,
+       className VARCHAR(255) NOT NULL,
+       permissions TEXT,
+       options TEXT  
+);
+
+ALTER TABLE wcf1_user_notification ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_user_notification ADD FOREIGN KEY (eventID) REFERENCES wcf1_user_notification_event (eventID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_user_notification_to_user ADD FOREIGN KEY (notificationID) REFERENCES wcf1_user_notification (notificationID) ON DELETE CASCADE;
+ALTER TABLE wcf1_user_notification_to_user ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_user_notification_event ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+ALTER TABLE wcf1_user_notification_event ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_user_notification_object_type (objectTypeID) ON DELETE CASCADE;
+ALTER TABLE wcf1_user_notification_event ADD FOREIGN KEY (defaultNotificationTypeID) REFERENCES wcf1_user_notification_type (notificationTypeID) ON DELETE SET NULL;
+
+ALTER TABLE wcf1_user_notification_event_to_user ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
+ALTER TABLE wcf1_user_notification_event_to_user ADD FOREIGN KEY (eventID) REFERENCES wcf1_user_notification_event (eventID) ON DELETE CASCADE;
+ALTER TABLE wcf1_user_notification_event_to_user ADD FOREIGN KEY (notificationTypeID) REFERENCES wcf1_user_notification_type (notificationTypeID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_user_notification_object_type ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_user_notification_type ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/language/de-informal.xml b/com.woltlab.wcf.notification/language/de-informal.xml
new file mode 100644 (file)
index 0000000..7748ad7
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/language.xsd" languagecode="de-informal">
+       <category name="wcf.user.notification">
+               <item name="wcf.user.notification.title"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.user.notification.stats"><![CDATA[Insgesamt {#$items} Benachrichtigung{if $items != 1}en{/if}]]></item>
+               <item name="wcf.user.notification.noNotifications"><![CDATA[Es liegen momentan keine Benachrichtigungen vor.]]></item>
+               <item name="wcf.user.notification.icon"><![CDATA[Icon]]></item>
+               <item name="wcf.user.notification.text"><![CDATA[Nachricht]]></item>
+               <item name="wcf.user.notification.time"><![CDATA[Zeit]]></item>
+               <item name="wcf.user.notification.confirmAll"><![CDATA[Alle bestätigen]]></item>
+               <item name="wcf.user.notification.settings"><![CDATA[Benachrichtigungseinstellungen]]></item>
+               <item name="wcf.user.notification.settings.saved"><![CDATA[Die Einstellungen wurden erfolgreich gespeichert.]]></item>
+               <item name="wcf.user.notification.location.settings"><![CDATA[Benachrichtigungseinstellungen]]></item>
+               <item name="wcf.user.notification.location.overview"><![CDATA[Benachrichtigungsübersicht]]></item>
+               <item name="wcf.user.notification.type.userMessages"><![CDATA[Infobox]]></item>
+               <item name="wcf.user.notification.type.userMessages.title"><![CDATA[Es liegen folgende neue Benachrichtigungen vor:]]></item>
+               <item name="wcf.user.notification.type.mail"><![CDATA[E-Mail]]></item>
+               <item name="wcf.user.notification.type.mail.subject"><![CDATA[{@$title}]]></item>
+               <item name="wcf.user.notification.type.mail.footer"><![CDATA[
+
+Du kannst die Einstellungen für Deine Benachrichtigungen auf {lang}{@PAGE_TITLE}{/lang} unter folgender URL detailliert konfigurieren:
+{@$pageURL}index.php?form=UserNotificationSettings
+Möchtest Du in Zukunft diese Benachrichtigung per Mail nicht mehr erhalten, rufen diesen Link auf, um diese zu deaktivieren: {@$pageURL}index.php?action=NotificationDisable&notificationID={@$notification->notificationID}&userID={@$user->userID}&token={@$token}
+
+]]></item>
+               <item name="wcf.user.notification.type.mail.disabled"><![CDATA[Du hast die Benachrichtung für dieses Ereignis per E-Mail deaktiviert. Du kannst die Benachrichtigung in Deinen Profileinstellungen jederzeit erneut aktivieren.]]></item>
+               <item name="wcf.user.notification.feed"><![CDATA[Neuste Benachrichtigungen]]></item>
+               <item name="wcf.user.notification.feed.title"><![CDATA[Benachrichtigungen abonnieren]]></item>
+               <item name="wcf.user.notification.feed.description"><![CDATA[Die neusten Benachrichtigungen von "{lang}{PAGE_TITLE}{/lang}"]]></item>
+               <item name="wcf.user.notification.error.alreadyAccepted"><![CDATA[Du hast diese Benachrichtigung bereits bestätigt.]]></item>
+               <item name="wcf.user.notification.object.type.void"><![CDATA[Allgemein]]></item>
+       </category>
+       <category name="wcf.user.usercp">
+               <item name="wcf.user.usercp.menu.link.settings.notification"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.user.usercp.menu.link.management.notification"><![CDATA[Benachrichtigungen]]></item>
+       </category>
+       <category name="wcf.header.userMenu">
+               <item name="wcf.header.userMenu.userNotifications"><![CDATA[Benachrichtigungen]]></item>
+       </category>
+       <category name="wcf.acp.option">
+               <item name="wcf.acp.option.module_user_notification"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.acp.option.module_user_notification.description"><![CDATA[Aktiviert das Benutzerbenachrichtigungssystem.]]></item>
+               <item name="wcf.acp.option.category.user.notification"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.acp.option.category.user.notification.description"><![CDATA[]]></item>
+               <item name="wcf.acp.option.category.user.notification.general"><![CDATA[Allgemein]]></item>
+               <item name="wcf.acp.option.category.user.notification.general.description"><![CDATA[]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_active"><![CDATA[Link im Benutzermenü anzeigen]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_active.description"><![CDATA[Ist diese Option aktiviert, wird ein Link zur Nachrichtenübersicht im Seitenkopf angezeigt.]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh"><![CDATA[Link im Benutzermenü automatisch aktualisieren]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh.description"><![CDATA[Ist diese Option aktiviert, aktualisiert sich die Anzeige der Nachrichtenübersicht im Seitenkopf regelmäßig mittels AJAX. Die Aktivierung dieser Funktion verursacht eine höhere Serverlast.]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh_intervall"><![CDATA[Aktualisierungsintervall [Sekunden]]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh_intervall.description"><![CDATA[Wie häufig soll sich der Link zur Nachrichtenübersicht aktualisieren?]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime"><![CDATA[Lebenspanne bestätigter Benachrichtigungen [Stunden]]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime.description"><![CDATA[Gib an, wie lange eine Benachrichtigung nach Bestätigung aufbewahrt werden sollen. -1 deaktiviert die automatische Löschung von Benachrichtigungen.]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime_unconfirmed"><![CDATA[Lebenspanne unbestätigter Benachrichtigungen [Stunden]]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime_unconfirmed.description"><![CDATA[Gib an, wie lange eine Benachrichtigung aufbewahrt werden soll, bevor sie auch <strong>ohne Bestätigung</strong> gelöscht wird. 0 deaktiviert die automatische Löschung unbestätigter Benachrichtigungen.]]></item>
+       </category>
+       <category name="wcf.acp.package">
+               <item name="wcf.acp.package.step.install.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Benachrichtigungsobjekte werden installiert ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Benachrichtigungsobjekte werden deinstalliert ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Benachrichtigungsobjekte werden aktualisiert ...]]></item>
+               <item name="wcf.acp.package.step.install.NotificationTypePackageInstallationPlugin"><![CDATA[Benachrichtigungstypen werden installiert ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationTypePackageInstallationPlugin"><![CDATA[Benachrichtigungstypen werden deinstalliert ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationTypePackageInstallationPlugin"><![CDATA[Benachrichtigungstypen werden aktualisiert ...]]></item>
+               <item name="wcf.acp.package.step.install.NotificationEventPackageInstallationPlugin"><![CDATA[Benachrichtigungsereignisse werden installiert ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationEventPackageInstallationPlugin"><![CDATA[Benachrichtigungsereignisse werden deinstalliert ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationEventPackageInstallationPlugin"><![CDATA[Benachrichtigungsereignisse werden aktualisiert ...]]></item>
+       </category>
+</language>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/language/de.xml b/com.woltlab.wcf.notification/language/de.xml
new file mode 100644 (file)
index 0000000..d4b36bd
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/language.xsd" languagecode="de">
+       <category name="wcf.user.notification">
+               <item name="wcf.user.notification.title"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.user.notification.stats"><![CDATA[Insgesamt {#$items} Benachrichtigung{if $items != 1}en{/if}]]></item>
+               <item name="wcf.user.notification.noNotifications"><![CDATA[Es liegen momentan keine Benachrichtigungen vor.]]></item>
+               <item name="wcf.user.notification.icon"><![CDATA[Icon]]></item>
+               <item name="wcf.user.notification.text"><![CDATA[Nachricht]]></item>
+               <item name="wcf.user.notification.time"><![CDATA[Zeit]]></item>
+               <item name="wcf.user.notification.confirmAll"><![CDATA[Alle bestätigen]]></item>
+               <item name="wcf.user.notification.settings"><![CDATA[Benachrichtigungseinstellungen]]></item>
+               <item name="wcf.user.notification.settings.saved"><![CDATA[Die Einstellungen wurden erfolgreich gespeichert.]]></item>
+               <item name="wcf.user.notification.location.settings"><![CDATA[Benachrichtigungseinstellungen]]></item>
+               <item name="wcf.user.notification.location.overview"><![CDATA[Benachrichtigungsübersicht]]></item>
+               <item name="wcf.user.notification.type.userMessages"><![CDATA[Infobox]]></item>                
+               <item name="wcf.user.notification.type.userMessages.title"><![CDATA[Es liegen folgende neue Benachrichtigungen vor:]]></item>
+               <item name="wcf.user.notification.type.mail"><![CDATA[E-Mail]]></item>
+               <item name="wcf.user.notification.type.mail.subject"><![CDATA[{@$title}]]></item>
+               <item name="wcf.user.notification.type.mail.footer"><![CDATA[
+
+Sie können die Einstellungen für Ihre Benachrichtigungen auf {lang}{@PAGE_TITLE}{/lang} unter folgender URL detailliert konfigurieren:
+{@$pageURL}index.php?form=UserNotificationSettings
+Möchten Sie in Zukunft diese Benachrichtigung per Mail nicht mehr erhalten, rufen Sie diesen Link auf, um diese zu deaktivieren: {@$pageURL}index.php?action=NotificationDisable&notificationID={@$notification->notificationID}&userID={@$user->userID}&token={@$token}
+
+]]></item>
+               <item name="wcf.user.notification.type.mail.disabled"><![CDATA[Sie haben die Benachrichtung für dieses Ereignis per E-Mail deaktiviert. Sie können die Benachrichtigung in Ihren Profileinstellungen jederzeit erneut aktivieren.]]></item>
+               <item name="wcf.user.notification.feed"><![CDATA[Neuste Benachrichtigungen]]></item>
+               <item name="wcf.user.notification.feed.title"><![CDATA[Benachrichtigungen abonnieren]]></item>
+               <item name="wcf.user.notification.feed.description"><![CDATA[Die neusten Benachrichtigungen von "{lang}{PAGE_TITLE}{/lang}"]]></item>
+               <item name="wcf.user.notification.error.alreadyAccepted"><![CDATA[Sie haben diese Benachrichtigung bereits bestätigt.]]></item>
+               <item name="wcf.user.notification.object.type.void"><![CDATA[Allgemein]]></item>
+       </category>
+       <category name="wcf.user.usercp">
+               <item name="wcf.user.usercp.menu.link.settings.notification"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.user.usercp.menu.link.management.notification"><![CDATA[Benachrichtigungen]]></item>
+       </category>
+       <category name="wcf.header.userMenu">
+               <item name="wcf.header.userMenu.userNotifications"><![CDATA[Benachrichtigungen]]></item>
+       </category>
+       <category name="wcf.acp.option">
+               <item name="wcf.acp.option.module_user_notification"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.acp.option.module_user_notification.description"><![CDATA[Aktiviert das Benutzerbenachrichtigungssystem.]]></item>
+               <item name="wcf.acp.option.category.user.notification"><![CDATA[Benachrichtigungen]]></item>
+               <item name="wcf.acp.option.category.user.notification.description"><![CDATA[]]></item>
+               <item name="wcf.acp.option.category.user.notification.general"><![CDATA[Allgemein]]></item>
+               <item name="wcf.acp.option.category.user.notification.general.description"><![CDATA[]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_active"><![CDATA[Link im Benutzermenü anzeigen]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_active.description"><![CDATA[Ist diese Option aktiviert, wird ein Link zur Nachrichtenübersicht im Seitenkopf angezeigt.]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh"><![CDATA[Link im Benutzermenü automatisch aktualisieren]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh.description"><![CDATA[Ist diese Option aktiviert, aktualisiert sich die Anzeige der Nachrichtenübersicht im Seitenkopf regelmäßig mittels AJAX. Die Aktivierung dieser Funktion verursacht eine höhere Serverlast.]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh_intervall"><![CDATA[Aktualisierungsintervall [Sekunden]]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh_intervall.description"><![CDATA[Wie häufig soll sich der Link zur Nachrichtenübersicht aktualisieren?]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime"><![CDATA[Lebenspanne bestätigter Benachrichtigungen [Stunden]]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime.description"><![CDATA[Geben Sie an, wie lange eine Benachrichtigung nach Bestätigung aufbewahrt werden sollen. -1 deaktiviert die automatische Löschung von Benachrichtigungen.]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime_unconfirmed"><![CDATA[Lebenspanne unbestätigter Benachrichtigungen [Stunden]]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime_unconfirmed.description"><![CDATA[Geben Sie an, wie lange eine Benachrichtigung aufbewahrt werden soll, bevor sie auch <strong>ohne Bestätigung</strong> gelöscht wird. 0 deaktiviert die automatische Löschung unbestätigter Benachrichtigungen.]]></item>
+       </category>
+       <category name="wcf.acp.package">
+               <item name="wcf.acp.package.step.install.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Benachrichtigungsobjekte werden installiert ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Benachrichtigungsobjekte werden deinstalliert ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Benachrichtigungsobjekte werden aktualisiert ...]]></item>
+               <item name="wcf.acp.package.step.install.NotificationTypePackageInstallationPlugin"><![CDATA[Benachrichtigungstypen werden installiert ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationTypePackageInstallationPlugin"><![CDATA[Benachrichtigungstypen werden deinstalliert ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationTypePackageInstallationPlugin"><![CDATA[Benachrichtigungstypen werden aktualisiert ...]]></item>
+               <item name="wcf.acp.package.step.install.NotificationEventPackageInstallationPlugin"><![CDATA[Benachrichtigungsereignisse werden installiert ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationEventPackageInstallationPlugin"><![CDATA[Benachrichtigungsereignisse werden deinstalliert ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationEventPackageInstallationPlugin"><![CDATA[Benachrichtigungsereignisse werden aktualisiert ...]]></item>
+       </category>
+</language>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/language/en.xml b/com.woltlab.wcf.notification/language/en.xml
new file mode 100644 (file)
index 0000000..aae1005
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/language.xsd" languagecode="en">
+       <category name="wcf.user.notification">
+               <item name="wcf.user.notification.title"><![CDATA[Notifications]]></item>
+               <item name="wcf.user.notification.stats"><![CDATA[Overall {#$items} notification{if $items != 1}s{/if}]]></item>
+               <item name="wcf.user.notification.noNotifications"><![CDATA[There are no notifications at the moment.]]></item>
+               <item name="wcf.user.notification.icon"><![CDATA[Icon]]></item>
+               <item name="wcf.user.notification.text"><![CDATA[Message]]></item>
+               <item name="wcf.user.notification.time"><![CDATA[Time]]></item>
+               <item name="wcf.user.notification.confirmAll"><![CDATA[Confirm all]]></item>
+               <item name="wcf.user.notification.settings"><![CDATA[Notification settings]]></item>
+               <item name="wcf.user.notification.settings.saved"><![CDATA[The settings were successfully saved.]]></item>
+               <item name="wcf.user.notification.location.settings"><![CDATA[Notification settings]]></item>
+               <item name="wcf.user.notification.location.overview"><![CDATA[Notification overview]]></item>
+               <item name="wcf.user.notification.type.userMessages"><![CDATA[Infobox]]></item>
+               <item name="wcf.user.notification.type.userMessages.title"><![CDATA[You have the following new notifications:]]></item>
+               <item name="wcf.user.notification.type.mail"><![CDATA[E-Mail]]></item>
+               <item name="wcf.user.notification.type.mail.subject"><![CDATA[{@$title}]]></item>
+               <item name="wcf.user.notification.type.mail.footer"><![CDATA[
+
+
+You can change your personal notification settings on {lang}{@PAGE_TITLE}{/lang} under the following URL:
+{@$pageURL}index.php?form=UserNotificationSettings
+If you do not want to recevive this mail notification in future just follow this link: {@$pageURL}index.php?action=NotificationDisable&notificationID={@$notification->notificationID}&userID={@$user->userID}&token={@$token}
+
+]]></item>
+               <item name="wcf.user.notification.type.mail.disabled"><![CDATA[You have disabled the mail notification successfully. You can reactive this notification in your profile settings at any time.]]></item>
+               <item name="wcf.user.notification.feed"><![CDATA[Newest notifications]]></item>
+               <item name="wcf.user.notification.feed.title"><![CDATA[Subcribe to notifications]]></item>
+               <item name="wcf.user.notification.feed.description"><![CDATA[Your newest notifications on "{lang}{PAGE_TITLE}{/lang}"]]></item>
+               <item name="wcf.user.notification.error.alreadyAccepted"><![CDATA[You have already confirmed this notification.]]></item>
+               <item name="wcf.user.notification.object.type.void"><![CDATA[General]]></item>
+       </category>
+       <category name="wcf.user.usercp">
+               <item name="wcf.user.usercp.menu.link.settings.notification"><![CDATA[Notifications]]></item>
+               <item name="wcf.user.usercp.menu.link.management.notification"><![CDATA[Notifications]]></item>
+       </category>
+       <category name="wcf.header.userMenu">
+               <item name="wcf.header.userMenu.userNotifications"><![CDATA[Notifications]]></item>
+       </category>
+       <category name="wcf.acp.option">
+               <item name="wcf.acp.option.module_user_notification"><![CDATA[Notifications]]></item>
+               <item name="wcf.acp.option.module_user_notification.description"><![CDATA[Activates the user notification system.]]></item>
+               <item name="wcf.acp.option.category.user.notification"><![CDATA[Notifications]]></item>
+               <item name="wcf.acp.option.category.user.notification.description"><![CDATA[]]></item>
+               <item name="wcf.acp.option.category.user.notification.general"><![CDATA[General]]></item>
+               <item name="wcf.acp.option.category.user.notification.general.description"><![CDATA[]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_active"><![CDATA[Show user menu link]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_active.description"><![CDATA[If this option is activated, a link to the user notification overview will be displayed in each user's user panel.]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh"><![CDATA[Automatic user menu link refresh]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh.description"><![CDATA[If this option is activated, the user menu link will refresh itself automatically using AJAX. This feature increases the server load.]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh_intervall"><![CDATA[Refresh intervall [Seconds]]]></item>
+               <item name="wcf.acp.option.user_notification_user_menu_link_autorefresh_intervall.description"><![CDATA[How often shall the link refresh itself?]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime"><![CDATA[Lifetime of confirmed notifications [Hours]]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime.description"><![CDATA[Enter for how many hours confirmed notifications should be kept before deletion. -1 deactivates the automatic deletion of old confirmed notifications.]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime_unconfirmed"><![CDATA[Lifetime of unconfirmed notifications [Hours]]]></item>
+               <item name="wcf.acp.option.user_notification_lifetime_unconfirmed.description"><![CDATA[Enter for how many hours unconfirmed notifications should be kept before deletion. 0 deactivates the automatic deletion of notifications.]]></item>
+       </category>
+       <category name="wcf.acp.package">
+               <item name="wcf.acp.package.step.install.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Installing notification objects ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Uninstalling notification objects ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationObjectTypePackageInstallationPlugin"><![CDATA[Updating notification objects ...]]></item>
+               <item name="wcf.acp.package.step.install.NotificationTypePackageInstallationPlugin"><![CDATA[Installing notification types ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationTypePackageInstallationPlugin"><![CDATA[Uninstalling notification types ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationTypePackageInstallationPlugin"><![CDATA[Updating notification types ...]]></item>
+               <item name="wcf.acp.package.step.install.NotificationEventPackageInstallationPlugin"><![CDATA[Installing notification events ...]]></item>
+               <item name="wcf.acp.package.step.uninstall.NotificationEventPackageInstallationPlugin"><![CDATA[Uninstalling notification events ...]]></item>
+               <item name="wcf.acp.package.step.update.NotificationEventPackageInstallationPlugin"><![CDATA[Updating notification events ...]]></item>
+       </category>
+</language>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/notificationobjecttype.xml b/com.woltlab.wcf.notification/notificationobjecttype.xml
new file mode 100644 (file)
index 0000000..1eac142
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/notification-object-type.xsd">
+       <import>
+               <type>
+                       <name>void</name>
+                       <classfile>lib/data/user/notification/object/VoidNotificationObjectType.class.php</classfile>                        
+               </type>
+       </import>
+</data>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/notificationtype.xml b/com.woltlab.wcf.notification/notificationtype.xml
new file mode 100644 (file)
index 0000000..2abb445
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/notification-type.xsd">
+       <import>
+               <type>
+                       <name>mail</name>
+                       <classfile>lib/data/user/notification/type/MailUserNotificationReceivableType.class.php</classfile>
+               </type>
+       </import>
+</data>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/options.xml b/com.woltlab.wcf.notification/options.xml
new file mode 100644 (file)
index 0000000..c61d64f
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/options.xsd">
+       <import>
+               <categories>
+                       <category name="user.notification">
+                               <parent>user</parent>
+                               <options>module_user_notification</options>
+                       </category>
+                       <category name="user.notification.general">
+                               <parent>user.notification</parent>
+                               <showorder>1</showorder>
+                               <options>module_user_notification</options>
+                       </category>
+               </categories>
+               <options>
+                       <option name="module_user_notification">
+                               <categoryname>module.user</categoryname>
+                               <optiontype>boolean</optiontype>
+                               <defaultvalue>1</defaultvalue>
+                       </option>
+                       <option name="user_notification_user_menu_link_active">
+                               <categoryname>user.notification.general</categoryname>
+                               <optiontype>boolean</optiontype>
+                               <defaultvalue>1</defaultvalue>
+                               <enableoptions>user_notification_user_menu_link_autorefresh</enableoptions>
+                       </option>
+                       <option name="user_notification_user_menu_link_autorefresh">
+                               <categoryname>user.notification.general</categoryname>
+                               <optiontype>boolean</optiontype>
+                               <defaultvalue>0</defaultvalue>
+                               <enableoptions>user_notification_user_menu_link_autorefresh_intervall</enableoptions>
+                       </option>
+                       <option name="user_notification_user_menu_link_autorefresh_intervall">
+                               <categoryname>user.notification.general</categoryname>
+                               <optiontype>integer</optiontype>
+                               <defaultvalue>30</defaultvalue>
+                       </option>
+                       <option name="user_notification_lifetime">
+                               <categoryname>user.notification.general</categoryname>
+                               <optiontype>integer</optiontype>
+                               <defaultvalue>48</defaultvalue>
+                       </option>
+                       <option name="user_notification_lifetime_unconfirmed">
+                               <categoryname>user.notification.general</categoryname>
+                               <optiontype>integer</optiontype>
+                               <defaultvalue>0</defaultvalue>
+                       </option>
+               </options>
+       </import>
+</data>
diff --git a/com.woltlab.wcf.notification/package.xml b/com.woltlab.wcf.notification/package.xml
new file mode 100644 (file)
index 0000000..f7ee015
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package name="com.woltlab.wcf.user.notification" xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/package.xsd">
+       <packageinformation>
+               <packagename>User Notification System</packagename>             
+               <version>1.0.0 Alpha 1</version>
+               <date>2010-08-22</date>
+               <isunique>1</isunique>
+       </packageinformation>
+
+       <authorinformation>
+               <author>WoltLab GmbH, Oliver Kliebisch and Tim Düsterhus</author>
+               <authorurl>http://www.woltlab.com</authorurl>
+       </authorinformation>
+       
+       <requiredpackages>
+               <requiredpackage minversion="2.0.0 Alpha 1">com.woltlab.wcf</requiredpackage>
+       </requiredpackages>
+
+       <instructions type="install">
+               <instruction type="packageinstallationplugins">pip.xml</instruction>
+               <instruction type="files">files.tar</instruction>
+               <instruction type="sql">install.sql</instruction>
+       </instructions>
+</package>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/pagelocation.xml b/com.woltlab.wcf.notification/pagelocation.xml
new file mode 100644 (file)
index 0000000..20a70ad
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/page-location.xsd">  
+       <import>
+               <pagelocation name="wcf.user.notification.location.settings">
+                       <pattern><![CDATA[index\.php\?form=UserNotificationSettings]]></pattern>
+                       <options>module_user_notification</options>
+               </pagelocation>
+               <pagelocation name="wcf.user.notification.location.overview">
+                       <pattern><![CDATA[index\.php\?page=UserNotification$]]></pattern>
+                       <options>module_user_notification</options>
+               </pagelocation>         
+       </import>
+</data>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/pip.xml b/com.woltlab.wcf.notification/pip.xml
new file mode 100644 (file)
index 0000000..5cf8227
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/pip.xsd">
+       <import>
+               <pip name="userNotificationEvent">wcf\system\package\plugin\UserNotificationEventPackageInstallationPlugin</pip>
+               <pip name="userNotificationObjectType">wcf\system\package\plugin\UserNotificationObjectTypePackageInstallationPlugin</pip>
+               <pip name="userNotificationType">wcf\system\package\plugin\UserNotificationTypePackageInstallationPlugin</pip>
+       </import>
+</data>
diff --git a/com.woltlab.wcf.notification/pip/NotificationEventPackageInstallationPlugin.class.php b/com.woltlab.wcf.notification/pip/NotificationEventPackageInstallationPlugin.class.php
new file mode 100644 (file)
index 0000000..888551e
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/acp/package/plugin/AbstractXMLPackageInstallationPlugin.class.php');
+
+/**
+ * Install, updates and uninstalls notification events
+ *
+ * @author      Oliver Kliebisch
+ * @copyright   2009-2010 Oliver Kliebisch
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     com.woltlab.community.wcf.user.notification
+ * @subpackage  acp.package.plugin
+ * @category    Community Framework
+ */
+class NotificationEventPackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+       public $tagName = 'notificationevent';
+       public $tableName = 'user_notification_event';
+
+       /**
+        * @see PackageInstallationPlugin::install()
+        */
+       public function install() {
+               parent::install();
+
+               if (!$xml = $this->getXML()) {
+                       return;
+               }
+
+               // Create an array with the data blocks (import or delete) from the xml file.
+               $notificationEventXML = $xml->getElementTree('data');
+
+               // Loop through the array and install or uninstall items.
+               foreach ($notificationEventXML['children'] as $key => $block) {
+                       if (count($block['children'])) {
+                               // Handle the import instructions
+                               if ($block['name'] == 'import') {
+                                       // Loop through items and create or update them.
+                                       foreach ($block['children'] as $notificationEvent) {
+                                               // Extract item properties.
+                                               foreach ($notificationEvent['children'] as $child) {
+                                                       if (!isset($child['cdata'])) continue;
+                                                       $notificationEvent[$child['name']] = $child['cdata'];
+                                               }
+
+                                               // default values
+                                               $name = $objectType = $classFile = $languageCategory = $defaultNotificationType = $icon = $acceptURL = $declineURL = $permissions = $options = '';
+                                               $requiresConfirmation = 0;
+
+                                               // get values
+                                               if (isset($notificationEvent['name'])) $name = $notificationEvent['name'];
+                                               if (isset($notificationEvent['objecttype'])) $objectType = $notificationEvent['objecttype'];
+                                               if (isset($notificationEvent['classfile'])) $classFile = $notificationEvent['classfile'];
+                                               if (isset($notificationEvent['languagecategory'])) $languageCategory = $notificationEvent['languagecategory'];
+                                               if (isset($notificationEvent['defaultnotificationtype'])) $defaultNotificationType = $notificationEvent['defaultnotificationtype'];
+                                               if (isset($notificationEvent['icon'])) $icon = $notificationEvent['icon'];
+                                               if (isset($notificationEvent['requiresconfirmation'])) $requiresConfirmation = $notificationEvent['requiresconfirmation'] ? 1 : 0;
+                                               if (isset($notificationEvent['accepturl'])) $acceptURL = $notificationEvent['accepturl'];
+                                               if (isset($notificationEvent['declineurl'])) $declineURL = $notificationEvent['declineurl'];
+                                               if (isset($notificationEvent['permissions'])) $permissions = $notificationEvent['permissions'];
+                                               if (isset($notificationEvent['options'])) $options = $notificationEvent['options'];
+
+                                               // insert items
+                                               $sql = "INSERT INTO                     wcf".WCF_N."_".$this->tableName."
+                                                                                       (packageID, eventName, objectType, classFile, languageCategory, defaultNotificationType, icon, requiresConfirmation, acceptURL, declineURL, permissions, options)
+                                                       VALUES                          (".$this->installation->getPackageID().",
+                                                                                       '".escapeString($name)."',
+                                                                                       '".escapeString($objectType)."',
+                                                                                       '".escapeString($classFile)."',
+                                                                                       '".escapeString($languageCategory)."',
+                                                                                       '".escapeString($defaultNotificationType)."',
+                                                                                       '".escapeString($icon)."',
+                                                                                       '".escapeString($requiresConfirmation)."',
+                                                                                       '".escapeString($acceptURL)."',
+                                                                                       '".escapeString($declineURL)."',
+                                                                                       '".escapeString($permissions)."',
+                                                                                       '".escapeString($options)."')
+                                                       ON DUPLICATE KEY UPDATE         objectType = VALUES(objectType),
+                                                                                       classFile = VALUES(classFile),
+                                                                                       languageCategory = VALUES(languageCategory),
+                                                                                       defaultNotificationType = VALUES(defaultNotificationType),
+                                                                                       icon = VALUES(icon),
+                                                                                       requiresConfirmation = VALUES(requiresConfirmation),
+                                                                                       acceptURL = VALUES(acceptURL),
+                                                                                       declineURL = VALUES(declineURL),
+                                                                                       permissions = VALUES(permissions),
+                                                                                       options = VALUES(options)";
+                                               WCF::getDB()->sendQuery($sql);
+                                       }
+                               }
+                               // Handle the delete instructions.
+                               else if ($block['name'] == 'delete' && $this->installation->getAction() == 'update') {
+                                       // Loop through items and delete them.
+                                       $nameArray = array();
+                                       foreach ($block['children'] as $notificationEvent) {
+                                               // Extract item properties.
+                                               foreach ($notificationEvent['children'] as $child) {
+                                                       if (!isset($child['cdata'])) continue;
+                                                       $notificationEvent[$child['name']] = $child['cdata'];
+                                               }
+
+                                               if (empty($notificationEvent['name'])) {
+                                                       throw new SystemException("Required 'name' attribute for notification event is missing", 13023);
+                                               }
+                                               $nameArray[] = $notificationEvent['name'];
+                                       }
+                                       if (count($nameArray)) {
+                                               $sql = "DELETE FROM     wcf".WCF_N."_".$this->tableName."
+                                                       WHERE           packageID = ".$this->installation->getPackageID()."
+                                                                       AND eventName IN ('".implode("','", array_map('escapeString', $nameArray))."')";
+                                               WCF::getDB()->sendQuery($sql);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/pip/NotificationObjectTypePackageInstallationPlugin.class.php b/com.woltlab.wcf.notification/pip/NotificationObjectTypePackageInstallationPlugin.class.php
new file mode 100644 (file)
index 0000000..d52de71
--- /dev/null
@@ -0,0 +1,20 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/acp/package/plugin/NotificationTypePackageInstallationPlugin.class.php');
+
+/**
+ * Install, updates and uninstalls notification object types
+ *
+ * @author      Oliver Kliebisch
+ * @copyright   2009-2010 Oliver Kliebisch
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     com.woltlab.community.wcf.user.notification
+ * @subpackage  acp.package.plugin
+ * @category    Community Framework
+ */
+class NotificationObjectTypePackageInstallationPlugin extends NotificationTypePackageInstallationPlugin {
+       public $tagName = 'notificationobjecttype';
+       public $tableName = 'user_notification_object_type';
+       public $fieldName = 'objectType';
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/pip/NotificationTypePackageInstallationPlugin.class.php b/com.woltlab.wcf.notification/pip/NotificationTypePackageInstallationPlugin.class.php
new file mode 100644 (file)
index 0000000..389f6e1
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+// wcf imports
+require_once(WCF_DIR.'lib/acp/package/plugin/AbstractXMLPackageInstallationPlugin.class.php');
+
+/**
+ * Install, updates and uninstalls notification types
+ *
+ * @author      Oliver Kliebisch
+ * @copyright   2009-2010 Oliver Kliebisch
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package     com.woltlab.community.wcf.user.notification
+ * @subpackage  acp.package.plugin
+ * @category    Community Framework
+ */
+class NotificationTypePackageInstallationPlugin extends AbstractXMLPackageInstallationPlugin {
+       public $tagName = 'notificationtype';
+       public $tableName = 'user_notification_type';
+       public $fieldName = 'notificationType';
+
+       /**
+        * @see PackageInstallationPlugin::install()
+        */
+       public function install() {
+               parent::install();
+
+               if (!$xml = $this->getXML()) {
+                       return;
+               }
+
+               // Create an array with the data blocks (import or delete) from the xml file.
+               $notificationTypeXML = $xml->getElementTree('data');
+
+               // Loop through the array and install or uninstall items.
+               foreach ($notificationTypeXML['children'] as $key => $block) {
+                       if (count($block['children'])) {
+                               // Handle the import instructions
+                               if ($block['name'] == 'import') {
+                                       // Loop through items and create or update them.
+                                       foreach ($block['children'] as $notificationType) {
+                                               // Extract item properties.
+                                               foreach ($notificationType['children'] as $child) {
+                                                       if (!isset($child['cdata'])) continue;
+                                                       $notificationType[$child['name']] = $child['cdata'];
+                                               }
+
+                                               // default values
+                                               $name = $classFile = $permissions = $options = '';
+
+                                               // get values
+                                               if (isset($notificationType['name'])) $name = $notificationType['name'];
+                                               if (isset($notificationType['classfile'])) $classFile = $notificationType['classfile'];
+                                               if (isset($notificationType['permissions'])) $permissions = $notificationType['permissions'];
+                                               if (isset($notificationType['options'])) $options = $notificationType['options'];
+
+                                               // insert items
+                                               $sql = "INSERT INTO                     wcf".WCF_N."_".$this->tableName."
+                                                                                       (packageID, ".$this->fieldName.", classFile, permissions, options)
+                                                       VALUES                          (".$this->installation->getPackageID().",
+                                                                                       '".escapeString($name)."',
+                                                                                       '".escapeString($classFile)."',
+                                                                                       '".escapeString($permissions)."',
+                                                                                       '".escapeString($options)."')
+                                                       ON DUPLICATE KEY UPDATE         classFile = VALUES(classFile),
+                                                                                       permissions = VALUES(permissions),
+                                                                                       options = VALUES(options)";
+                                               WCF::getDB()->sendQuery($sql);
+                                       }
+                               }
+                               // Handle the delete instructions.
+                               else if ($block['name'] == 'delete' && $this->installation->getAction() == 'update') {
+                                       // Loop through items and delete them.
+                                       $nameArray = array();
+                                       foreach ($block['children'] as $notificationType) {
+                                               // Extract item properties.
+                                               foreach ($notificationType['children'] as $child) {
+                                                       if (!isset($child['cdata'])) continue;
+                                                       $notificationType[$child['name']] = $child['cdata'];
+                                               }
+
+                                               if (empty($notificationType['name'])) {
+                                                       throw new SystemException("Required 'name' attribute for ".$this->fieldName." is missing", 13023);
+                                               }
+                                               $nameArray[] = $notificationType['name'];
+                                       }
+                                       if (count($nameArray)) {
+                                               $sql = "DELETE FROM     wcf".WCF_N."_".$this->tableName."
+                                                       WHERE           packageID = ".$this->installation->getPackageID()."
+                                                                       AND ".$this->fieldName." IN ('".implode("','", array_map('escapeString', $nameArray))."')";
+                                               WCF::getDB()->sendQuery($sql);
+                                       }
+                               }
+                       }
+               }
+       }
+}
+?>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/templates/userMenuNotificationLink.tpl b/com.woltlab.wcf.notification/templates/userMenuNotificationLink.tpl
new file mode 100644 (file)
index 0000000..8e4dd6b
--- /dev/null
@@ -0,0 +1 @@
+<li id="userMenuNotificationOverview"{if $notificationUser->hasOutstandingNotifications()} class="new"{/if}><a href="index.php?page=UserNotification{@SID_ARG_2ND}"><img src="{icon}infoS.png{/icon}" alt="" /> <span>{lang}wcf.header.userMenu.userNotifications{/lang}{if $notificationUser->hasOutstandingNotifications()} ({#$notificationUser->hasOutstandingNotifications()}){/if}</span></a></li>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/templates/userMenuPeriodicalExecuter.tpl b/com.woltlab.wcf.notification/templates/userMenuPeriodicalExecuter.tpl
new file mode 100644 (file)
index 0000000..5b4a65f
--- /dev/null
@@ -0,0 +1,17 @@
+<script type="text/javascript">
+//<![CDATA[
+       new PeriodicalExecuter(function() {
+               new Ajax.Request('index.php?page=UserNotificationCount'+SID_ARG_2ND, {
+                                       method: 'get',
+                                       onSuccess: function(transport) {
+                                               outstandingNotifications = parseInt(transport.responseText);
+                                               if (outstandingNotifications > 0) {
+                                                       $('userMenuNotificationOverview').addClassName('new');
+                                                       $('userMenuNotificationOverview').down('span').update('{lang}wcf.header.userMenu.userNotifications{/lang} (' + outstandingNotifications + ')');
+                                                       new Effect.Pulsate('userMenuNotificationOverview', { pulses: 2, duration: 3.0 });
+                                               }
+                                       }
+               });
+       }, {USER_NOTIFICATION_USER_MENU_LINK_AUTOREFRESH_INTERVALL});
+//]]>
+</script>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/templates/userMessagesNotifications.tpl b/com.woltlab.wcf.notification/templates/userMessagesNotifications.tpl
new file mode 100644 (file)
index 0000000..482ec9b
--- /dev/null
@@ -0,0 +1,34 @@
+                       <div class="info deletable" id="outstandingNotificationsContainer">
+                               <a href="index.php?action=NotificationConfirm{if $this->session->requestMethod == 'GET'}&amp;url={$this->session->requestURI|rawurlencode}{/if}&amp;t={@SECURITY_TOKEN}{@SID_ARG_2ND}" class="close deleteButton"><img src="{icon}closeS.png{/icon}" alt="" title="{lang}wcf.user.notification.confirmAll{/lang}" longdesc="" /></a>
+                               <p>{lang}wcf.user.notification.type.userMessages.title{/lang}</p>
+                               <ul class="itemList">
+                                       {foreach from=$notifications item=notification}
+                                               {assign var=languageCategory value=$notification->event->languageCategory}
+                                               {assign var=eventName value=$notification->eventName}
+                                               <li class="deletable"{if $notification->event->icon} style="list-style-image:url('{icon}{$notification->event->icon}S.png{/icon}');"{/if}>
+                                                       {if $notification->event->requiresConfirmation}
+                                                               {assign var=acceptURL value=$notification->event->getAcceptURL($notification)}
+                                                               {assign var=declineURL value=$notification->event->getDeclineURL($notification)}
+                                                               <div class="buttons">
+                                                                       {if $acceptURL}
+                                                                       <a href="{@$acceptURL}" class="deleteButton" title="{lang}{$languageCategory}.{$eventName}.accept{/lang}"><img src="{icon}checkS.png{/icon}" alt="{lang}{$languageCategory}.{$eventName}.accept{/lang}" longdesc="{if $declineURL}{lang}{$languageCategory}.{$eventName}.accept.sure{/lang}{/if}" /></a>
+                                                                       {/if}
+                                                                       {if $declineURL}
+                                                                       <a href="{@$declineURL}" class="deleteButton" title="{lang}{$languageCategory}.{$eventName}.decline{/lang}"><img src="{icon}deleteS.png{/icon}" alt="{lang}{$languageCategory}.{$eventName}.decline{/lang}" longdesc="{lang}{$languageCategory}.{$eventName}.decline.sure{/lang}" /></a>
+                                                                       {/if}
+                                                               </div>
+                                                       {/if}
+                                                       <p class="itemListTitle">{@$notification->messageCache}</p>
+                                               </li>
+                                       {/foreach}
+                               </ul>
+                       </div>
+                       <script type="text/javascript">
+                               //<![CDATA[
+                               document.observe('wcf:inlineDelete', function() {
+                                       if ($('outstandingNotificationsContainer') && !$('outstandingNotificationsContainer').down('li')) {
+                                               inlineDelete($('outstandingNotificationsContainer').down('.close'));
+                                       }
+                               });
+                               //]]>
+                       </script>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/templates/userNotification.tpl b/com.woltlab.wcf.notification/templates/userNotification.tpl
new file mode 100644 (file)
index 0000000..f21e5d9
--- /dev/null
@@ -0,0 +1,95 @@
+{include file="documentHeader"}
+<head>
+       <title>{lang}wcf.user.notification.title{/lang} - {lang}wcf.user.usercp{/lang} - {lang}{PAGE_TITLE}{/lang}</title>
+
+       {include file='headInclude' sandbox=false}
+       {include file='imageViewer'}
+       <script type="text/javascript" src="{@RELATIVE_WCF_DIR}js/MultiPagesLinks.class.js"></script>
+       <link rel="alternate" type="application/rss+xml" href="index.php?page=UserNotificationFeed&amp;format=rss2" title="{lang}wcf.user.notification.feed{/lang} (RSS2)" />
+       <link rel="alternate" type="application/atom+xml" href="index.php?page=UserNotificationFeed&amp;format=atom" title="{lang}wcf.user.notification.feed{/lang} (Atom)" />
+</head>
+<body{if $templateName|isset} id="tpl{$templateName|ucfirst}"{/if}>
+{include file='header' sandbox=false}
+
+<div id="main">
+       {include file="userCPHeader"}
+
+       <div class="border tabMenuContent">
+               <div class="container-1">
+                       <h3 class="subHeadline">{lang}wcf.user.notification.title{/lang}</h3>
+                       {if $notifications|count}
+                               <div class="contentHeader">
+                                       {pages print=true assign=pagesLinks link="index.php?page=UserNotification&pageNo=%d"|concat:SID_ARG_2ND_NOT_ENCODED}
+                               </div>
+
+                               <div class="border titleBarPanel">
+                                       <div class="containerHead">
+                                               <h4>{lang}wcf.user.notification.stats{/lang}</h4>
+                                       </div>
+                               </div>
+                               <div class="border borderMarginRemove">
+                                       <table class="tableList">
+                                               <thead>
+                                                       <tr class="tableHead">
+                                                               <th class="columnIcon">
+                                                                               <div>
+                                                                                       <span class="emptyHead">{lang}wcf.user.notification.icon{/lang}</span>
+                                                                               </div>
+                                                               </th>
+                                                               <th class="columnText">
+                                                                               <div>
+                                                                                       <span class="emptyHead">{lang}wcf.user.notification.text{/lang}</span>
+                                                                               </div>
+                                                               </th>
+                                                               <th class="columnText">
+                                                                               <div>
+                                                                                       <span class="emptyHead">{lang}wcf.user.notification.time{/lang}</span>
+                                                                               </div>
+                                                               </th>
+                                                       </tr>
+                                               </thead>
+                                               <tbody>
+                                                       {foreach from=$notifications item=notification}
+                                                               {assign var=languageCategory value=$notification->event->languageCategory}
+                                                               {assign var=eventName value=$notification->eventName}
+                                                               <tr class="{cycle values='container-1,container-2'}">
+                                                                       <td class="columnIcon">
+                                                                               <img src="{icon}{$notification->event->icon}M.png{/icon}" alt="" />
+                                                                       </td>
+                                                                       <td class="columnText{if !$notification->confirmed} new{/if}">
+                                                                               <span>{@$notification->longOutput}</span>
+                                                                               {if !$notification->confirmed && $notification->event->requiresConfirmation && $notification->event->acceptURL}
+                                                                               {assign var=acceptURL value=$notification->event->getAcceptURL($notification)}
+                                                                               {assign var=declineURL value=$notification->event->getDeclineURL($notification)}
+                                                                               <div class="buttons" style="float: right;">
+                                                                                       {if $acceptURL}
+                                                                                               <a href="{@$acceptURL}" title="{lang}{$languageCategory}.{$eventName}.accept{/lang}"><img src="{icon}checkS.png{/icon}" alt="{lang}{$languageCategory}.{$eventName}.accept{/lang}" {if $declineURL}onclick="return confirm('{lang}{$languageCategory}.{$eventName}.accept.sure{/lang}');"{/if} /></a>
+                                                                                       {/if}
+                                                                                       {if $declineURL}
+                                                                                               <a href="{@$declineURL}" title="{lang}{$languageCategory}.{$eventName}.decline{/lang}"><img src="{icon}deleteS.png{/icon}" alt="{lang}{$languageCategory}.{$eventName}.decline{/lang}" onclick="return confirm('{lang}{$languageCategory}.{$eventName}.accept.sure{/lang}');" /></a>
+                                                                                       {/if}
+                                                                               </div>
+                                                                               {/if}
+                                                                       </td>
+                                                                       <td class="columnText">
+                                                                               <span>{@$notification->time|time}</span>
+                                                                       </td>
+                                                               </tr>
+                                                       {/foreach}
+                                               </tbody>
+                                       </table>
+                                </div>
+
+                               <div class="contentFooter">
+                                       {@$pagesLinks}
+                               </div>
+                       {else}
+                               <p>{lang}wcf.user.notification.noNotifications{/lang}</p>
+                       {/if}
+               </div>
+       </div>
+</div>
+
+{include file='footer' sandbox=false}
+</body>
+</html>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/templates/userNotificationFeedAtom.tpl b/com.woltlab.wcf.notification/templates/userNotificationFeedAtom.tpl
new file mode 100644 (file)
index 0000000..3e429a4
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="{@CHARSET}"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+       <title>{lang}wcf.user.notification.feed.title{/lang}</title>
+       <id>{@PAGE_URL}/</id>
+       <updated>{@'c'|gmdate:TIME_NOW}</updated>
+       <link href="{@PAGE_URL}/" />
+       <generator uri="http://www.woltlab.com/" version="{@WCF_VERSION}">
+               WoltLab Community Framework
+       </generator>
+       <subtitle>{lang}wcf.user.notification.feed.description{/lang}</subtitle>
+
+       {foreach from=$notifications item=notification}
+               <entry>
+                       <title><![CDATA[{$notification->shortOutput}]]></title>
+                       <id>{@PAGE_URL}/index.php?page=UserNotification&amp;notificationID={@$notification->notificationID}</id>
+                       <updated>{@'c'|gmdate:$notification->time}</updated>
+                       <author>
+                               <name>{$this->user->username}</name>
+                       </author>
+                       <content type="html"><![CDATA[{@$notification->longOutput}]]></content>
+                       <link href="{@PAGE_URL}/index.php?page=UserNotification&amp;notificationID={@$notification->notificationID}" />
+               </entry>
+       {/foreach}
+</feed>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/templates/userNotificationFeedRss2.tpl b/com.woltlab.wcf.notification/templates/userNotificationFeedRss2.tpl
new file mode 100644 (file)
index 0000000..c32fcad
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="{@CHARSET}"?>
+<rss version="2.0">
+       <channel>
+               <title>{lang}wcf.user.notification.feed.title{/lang}</title>
+               <link>{@PAGE_URL}/</link>
+               <description>{lang}wcf.user.notification.feed.description{/lang}</description>
+
+               <pubDate>{@'r'|gmdate:TIME_NOW}</pubDate>
+               <lastBuildDate>{@'r'|gmdate:TIME_NOW}</lastBuildDate>
+               <generator>WoltLab Community Framework {@WCF_VERSION}</generator>
+               <ttl>60</ttl>
+
+               {foreach from=$notifications item=notification}
+                       <item>
+                               <title><![CDATA[{$notification->shortOutput}]]></title>
+                               <author>{$this->user->username}</author>
+                               <link>{@PAGE_URL}/index.php?page=UserNotification</link>
+                               <guid>{@PAGE_URL}/index.php?page=UserNotification&amp;notificationID={@$notification->notificationID}</guid>
+                               <pubDate>{@'r'|gmdate:$notification->time}</pubDate>
+                               <description><![CDATA[{@$notification->longOutput}]]></description>
+                       </item>
+               {/foreach}
+       </channel>
+</rss>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/templates/userNotificationSettings.tpl b/com.woltlab.wcf.notification/templates/userNotificationSettings.tpl
new file mode 100644 (file)
index 0000000..e7699cc
--- /dev/null
@@ -0,0 +1,82 @@
+{include file="documentHeader"}
+<head>
+       <title>{lang}wcf.user.notification.settings{/lang} - {lang}wcf.user.usercp{/lang} - {lang}{PAGE_TITLE}{/lang}</title>
+       {include file='headInclude' sandbox=false}
+</head>
+<body{if $templateName|isset} id="tpl{$templateName|ucfirst}"{/if}>
+{include file='header' sandbox=false}
+
+<div id="main">
+
+       {capture append=userMessages}
+               {if $errorField}
+                       <p class="error">{lang}wcf.global.form.error{/lang}</p>
+               {/if}
+
+               {if $success|isset}
+                       <p class="success">{lang}wcf.user.edit.success{/lang}</p>
+               {/if}
+       {/capture}
+
+       {include file="userCPHeader"}
+
+       <form method="post" action="index.php?form=UserNotificationSettings">
+               <div class="border tabMenuContent">
+                       <div class="container-1">
+                               <h3 class="subHeadline">{lang}wcf.user.notification.settings{/lang}</h3>
+
+                               <div class="border borderMarginRemove">
+                                       <table class="tableList">
+                                       {foreach from=$notificationObjectTypes key=typeName item=data}
+                                               {assign var=objectType value=$data.object}
+                                               {assign var=events value=$data.events}
+                                               {cycle name="eventRows" values='container-1,container-2' print=false advance=false reset=true}
+                                               <thead>
+                                                       <tr class="tableHead">
+                                                               <th class="columnObjectType">
+                                                                       <div>
+                                                                               <h3 class="emptyHead">{lang}wcf.user.notification.object.type.{$typeName}{/lang}</h3>
+                                                                       </div>
+                                                               </th>
+                                                               {foreach from=$notificationTypes key=notificationTypeName item=notificationType}
+                                                                       <th class="columnNotificationType columnIcon">
+                                                                               <div>
+                                                                                       <span class="emptyHead"><img src="{icon}{$notificationType->getIcon()}S.png{/icon}" alt="{lang}wcf.user.notification.type.{$notificationTypeName}{/lang}" title="{lang}wcf.user.notification.type.{$notificationTypeName}{/lang}" /></span>
+                                                                               </div>
+                                                                       </th>
+                                                               {/foreach}
+                                                       </tr>
+                                               </thead>
+                                               <tbody>
+                                                       {foreach from=$events key=eventName item=event}
+                                                               <tr class="{cycle name="eventRows"}">
+                                                                       <td class="columnEvent columnText">
+                                                                               <p title="{$event->getDescription()}">{$event->getTitle()}</p>
+                                                                               <p class="smallFont">{$event->getDescription()}</p>
+                                                                       </td>
+                                                                       {foreach from=$event->supportedNotificationTypes key=supportedType item=supported}
+                                                                       <td class="columnNotificationType columnIcon">
+                                                                               <input type="checkbox" name="activeEventNotifications[{$typeName}][{$eventName}][{$supportedType}]"{if !$supported} disabled="disabled"{/if}{if $activeEventNotifications[$typeName][$eventName][$supportedType]} checked="checked"{/if} />
+                                                                       </td>
+                                                                       {/foreach}
+                                                               </tr>
+                                                       {/foreach}
+                                               </tbody>
+                                       {/foreach}
+                                       </table>
+                               </div>
+                       </div>
+               </div>
+               <div class="formSubmit">
+                       {@SECURITY_TOKEN_INPUT_TAG}
+                       {@SID_INPUT_TAG}
+                       <input type="submit" accesskey="s" value="{lang}wcf.global.button.submit{/lang}" />
+                       <input type="reset" accesskey="r" value="{lang}wcf.global.button.reset{/lang}" />
+               </div>
+       </form>
+
+</div>
+
+{include file='footer' sandbox=false}
+</body>
+</html>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/usercpmenu.xml b/com.woltlab.wcf.notification/usercpmenu.xml
new file mode 100644 (file)
index 0000000..c22d41f
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/usercp-menu.xsd">
+       <import>
+               <usercpmenuitem name="wcf.user.usercp.menu.link.settings.notification">                 
+                       <link>index.php?form=UserNotificationSettings</link>
+                       <parent>wcf.user.usercp.menu.link.settings</parent>
+                       <options>module_user_notification</options>
+               </usercpmenuitem>
+               <usercpmenuitem name="wcf.user.usercp.menu.link.management.notification">
+                       <link>index.php?page=UserNotification</link>
+                       <parent>wcf.user.usercp.menu.link.management</parent>
+                       <options>module_user_notification</options>
+               </usercpmenuitem>
+       </import>
+</data>
\ No newline at end of file
diff --git a/com.woltlab.wcf.notification/useroptions.xml b/com.woltlab.wcf.notification/useroptions.xml
new file mode 100644 (file)
index 0000000..ff50684
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com/XSD/useroptions.xsd">
+       <import>
+               <options>
+                       <option name="notificationMailToken">
+                               <categoryname>hidden</categoryname>
+                               <optiontype>text</optiontype>
+                               <editable>4</editable>
+                               <visible>4</visible>
+                       </option>
+               </options>
+       </import>
+</data>
\ No newline at end of file