From 48e011fcc65f775c20373e11f2fb3d6e2831536b Mon Sep 17 00:00:00 2001 From: Marcel Werk Date: Thu, 14 Jul 2011 19:03:02 +0200 Subject: [PATCH] - User Notification Package --- .gitignore | 3 +- .../userNotificationDefaultSettings.tpl | 75 +++ com.woltlab.wcf.notification/cronjobs.xml | 18 + .../eventlistener.xml | 15 + ...rNotificationDefaultSettingsForm.class.php | 277 ++++++++++ .../NotificationConfirmAction.class.php | 138 +++++ .../NotificationDisableAction.class.php | 128 +++++ .../lib/data/user/NotificationUser.class.php | 223 ++++++++ .../notification/FeedNotification.class.php | 40 ++ .../FeedNotificationList.class.php | 45 ++ .../notification/NotificationList.class.php | 80 +++ .../notification/UserNotification.class.php | 26 + .../UserNotificationAction.class.php | 21 + .../UserNotificationEditor.class.php | 21 + .../UserNotificationHandler.class.php | 482 ++++++++++++++++++ .../UserNotificationList.class.php | 21 + .../UserNotificationMessageCompiler.class.php | 19 + ...tUserNotificationConveyableEvent.class.php | 242 +++++++++ .../UserNotificationConveyableEvent.class.php | 143 ++++++ .../event/UserNotificationEvent.class.php | 26 + .../UserNotificationEventAction.class.php | 21 + .../UserNotificationEventEditor.class.php | 21 + .../event/UserNotificationEventList.class.php | 21 + .../message/UserNotificationMessage.class.php | 45 ++ .../UserNotificationMessageEditor.class.php | 36 ++ ...NotificationConveyableObjectType.class.php | 43 ++ ...UserNotificationConveyableObject.class.php | 41 ++ ...NotificationConveyableObjectType.class.php | 64 +++ .../UserNotificationObjectType.class.php | 26 + ...UserNotificationObjectTypeEditor.class.php | 36 ++ ...UserNotificationConveyableObject.class.php | 44 ++ ...NotificationConveyableObjectType.class.php | 53 ++ .../type/UserNotificationObjectType.class.php | 26 + ...UserNotificationObjectTypeAction.class.php | 21 + ...UserNotificationObjectTypeEditor.class.php | 21 + .../UserNotificationObjectTypeList.class.php | 21 + ...ilUserNotificationReceivableType.class.php | 72 +++ .../UserNotificationReceivableType.class.php | 51 ++ .../type/UserNotificationType.class.php | 26 + .../type/UserNotificationTypeAction.class.php | 21 + .../type/UserNotificationTypeEditor.class.php | 21 + .../type/UserNotificationTypeList.class.php | 21 + .../UserNotificationSettingsForm.class.php | 288 +++++++++++ .../page/UserNotificationCountPage.class.php | 57 +++ .../page/UserNotificationFeedPage.class.php | 77 +++ .../lib/page/UserNotificationPage.class.php | 105 ++++ .../cache/CacheBuilderNotifications.class.php | 111 ++++ .../CleanUpNotificationsCronjob.class.php | 74 +++ ...UserMessagesNotificationListener.class.php | 47 ++ ...ateUserPanelNotificationListener.class.php | 35 ++ ...onEventPackageInstallationPlugin.class.php | 88 ++++ com.woltlab.wcf.notification/install.sql | 91 ++++ .../language/de-informal.xml | 69 +++ com.woltlab.wcf.notification/language/de.xml | 69 +++ com.woltlab.wcf.notification/language/en.xml | 70 +++ .../notificationobjecttype.xml | 9 + .../notificationtype.xml | 9 + com.woltlab.wcf.notification/options.xml | 50 ++ com.woltlab.wcf.notification/package.xml | 24 + com.woltlab.wcf.notification/pagelocation.xml | 13 + com.woltlab.wcf.notification/pip.xml | 8 + ...onEventPackageInstallationPlugin.class.php | 118 +++++ ...ectTypePackageInstallationPlugin.class.php | 20 + ...ionTypePackageInstallationPlugin.class.php | 96 ++++ .../templates/userMenuNotificationLink.tpl | 1 + .../templates/userMenuPeriodicalExecuter.tpl | 17 + .../templates/userMessagesNotifications.tpl | 34 ++ .../templates/userNotification.tpl | 95 ++++ .../templates/userNotificationFeedAtom.tpl | 24 + .../templates/userNotificationFeedRss2.tpl | 24 + .../templates/userNotificationSettings.tpl | 82 +++ com.woltlab.wcf.notification/usercpmenu.xml | 15 + com.woltlab.wcf.notification/useroptions.xml | 13 + 73 files changed, 4627 insertions(+), 1 deletion(-) create mode 100644 com.woltlab.wcf.notification/acptemplates/userNotificationDefaultSettings.tpl create mode 100644 com.woltlab.wcf.notification/cronjobs.xml create mode 100644 com.woltlab.wcf.notification/eventlistener.xml create mode 100644 com.woltlab.wcf.notification/files/lib/acp/form/UserNotificationDefaultSettingsForm.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/action/NotificationConfirmAction.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/action/NotificationDisableAction.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/NotificationUser.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotification.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotificationList.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/NotificationList.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotification.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationAction.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationEditor.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationHandler.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationList.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationMessageCompiler.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/event/DefaultUserNotificationConveyableEvent.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationConveyableEvent.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEvent.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventAction.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventEditor.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventList.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessage.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessageEditor.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/AbstractUserNotificationConveyableObjectType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObject.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObjectType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectTypeEditor.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObject.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObjectType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeAction.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeEditor.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeList.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/type/MailUserNotificationReceivableType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationReceivableType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationType.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeAction.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeEditor.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeList.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/form/UserNotificationSettingsForm.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/page/UserNotificationCountPage.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/page/UserNotificationFeedPage.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/page/UserNotificationPage.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/system/cache/CacheBuilderNotifications.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/system/cronjob/CleanUpNotificationsCronjob.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserMessagesNotificationListener.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserPanelNotificationListener.class.php create mode 100644 com.woltlab.wcf.notification/files/lib/system/package/plugin/UserNotificationEventPackageInstallationPlugin.class.php create mode 100644 com.woltlab.wcf.notification/install.sql create mode 100644 com.woltlab.wcf.notification/language/de-informal.xml create mode 100644 com.woltlab.wcf.notification/language/de.xml create mode 100644 com.woltlab.wcf.notification/language/en.xml create mode 100644 com.woltlab.wcf.notification/notificationobjecttype.xml create mode 100644 com.woltlab.wcf.notification/notificationtype.xml create mode 100644 com.woltlab.wcf.notification/options.xml create mode 100644 com.woltlab.wcf.notification/package.xml create mode 100644 com.woltlab.wcf.notification/pagelocation.xml create mode 100644 com.woltlab.wcf.notification/pip.xml create mode 100644 com.woltlab.wcf.notification/pip/NotificationEventPackageInstallationPlugin.class.php create mode 100644 com.woltlab.wcf.notification/pip/NotificationObjectTypePackageInstallationPlugin.class.php create mode 100644 com.woltlab.wcf.notification/pip/NotificationTypePackageInstallationPlugin.class.php create mode 100644 com.woltlab.wcf.notification/templates/userMenuNotificationLink.tpl create mode 100644 com.woltlab.wcf.notification/templates/userMenuPeriodicalExecuter.tpl create mode 100644 com.woltlab.wcf.notification/templates/userMessagesNotifications.tpl create mode 100644 com.woltlab.wcf.notification/templates/userNotification.tpl create mode 100644 com.woltlab.wcf.notification/templates/userNotificationFeedAtom.tpl create mode 100644 com.woltlab.wcf.notification/templates/userNotificationFeedRss2.tpl create mode 100644 com.woltlab.wcf.notification/templates/userNotificationSettings.tpl create mode 100644 com.woltlab.wcf.notification/usercpmenu.xml create mode 100644 com.woltlab.wcf.notification/useroptions.xml diff --git a/.gitignore b/.gitignore index cf5701f52f..798c2fb7ee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ *.sln *.phpproj *.puo -*.suo \ No newline at end of file +*.suo +*.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 index 0000000000..33ce2a3818 --- /dev/null +++ b/com.woltlab.wcf.notification/acptemplates/userNotificationDefaultSettings.tpl @@ -0,0 +1,75 @@ +{include file='header'} + +
+ +
+

{lang}wcf.user.notification.settings{/lang}

+
+
+ +{if $errorField} +

{lang}wcf.global.form.error{/lang}

+{/if} + +{if $success|isset && $success} +

{lang}wcf.user.notification.settings.saved{/lang}

+{/if} + +
+
+
+

{lang}wcf.user.notification.settings{/lang}

+ +
+ + {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} + + + + {foreach from=$notificationTypes key=notificationTypeName item=notificationType} + + {/foreach} + + + + {foreach from=$events key=eventName item=event} + + + {foreach from=$event->supportedNotificationTypes key=supportedType item=supported} + + {/foreach} + + {/foreach} + + {/foreach} +
+
+

{lang}wcf.user.notification.object.type.{$typeName}{/lang}

+
+
+
+ {lang}wcf.user.notification.type.{$notificationTypeName}{/lang} +
+
+

{$event->getTitle()}

+

{$event->getDescription()}

+
+ + +
+
+
+
+ +
+ + + + {@SID_INPUT_TAG} +
+
+ +{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 index 0000000000..dbd9a493c6 --- /dev/null +++ b/com.woltlab.wcf.notification/cronjobs.xml @@ -0,0 +1,18 @@ + + + + + lib/system/cronjob/CleanUpNotificationsCronjob.class.php + Cleans up notifications regularly + */30 + * + * + * + * + 0 + 0 + 0 + 1 + + + diff --git a/com.woltlab.wcf.notification/eventlistener.xml b/com.woltlab.wcf.notification/eventlistener.xml new file mode 100644 index 0000000000..bdab03b491 --- /dev/null +++ b/com.woltlab.wcf.notification/eventlistener.xml @@ -0,0 +1,15 @@ + + + + + StructuredTemplate + shouldDisplay + lib/system/event/listener/StructuredTemplateUserMessagesNotificationListener.class.php + + + StructuredTemplate + shouldDisplay + lib/system/event/listener/StructuredTemplateUserPanelNotificationListener.class.php + + + \ 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 index 0000000000..2177f7c73c --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/acp/form/UserNotificationDefaultSettingsForm.class.php @@ -0,0 +1,277 @@ + + * @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 index 0000000000..49f8a7eb8b --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/action/NotificationConfirmAction.class.php @@ -0,0 +1,138 @@ + + * @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 index 0000000000..15b1891b7e --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/action/NotificationDisableAction.class.php @@ -0,0 +1,128 @@ + + * @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 index 0000000000..932a1a75d1 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/NotificationUser.class.php @@ -0,0 +1,223 @@ + + * @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 + */ + 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 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 index 0000000000..797e5b2f5f --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotification.class.php @@ -0,0 +1,40 @@ + + * @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 index 0000000000..8a4c5fbf15 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/FeedNotificationList.class.php @@ -0,0 +1,45 @@ + + * @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 index 0000000000..0f2a1451e5 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/NotificationList.class.php @@ -0,0 +1,80 @@ + + * @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 + */ + 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 index 0000000000..a23ce7f5b2 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotification.class.php @@ -0,0 +1,26 @@ + + * @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 index 0000000000..6c2a2c5a3a --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationAction.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..27a1148edf --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationEditor.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..77c0a75019 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationHandler.class.php @@ -0,0 +1,482 @@ + + * @package com.woltlab.wcf.user.notification + * @subpackage data.user.notification + * @category Community Framework + */ +class UserNotificationHandler { + /** + * list of available notification object types + * @var array + */ + public static $availableNotificationObjectTypes = null; + + /** + * list of available notification types + * @var array + */ + public static $availableNotificationTypes = null; + + /** + * available notification object package IDs + * @var array + */ + protected static $availableNotificationObjectIDs = null; + + /** + * notification cache data + * @var array + */ + 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 $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 $eventName + * @param string $objectType + * @param mixed $notificationObject + * @param array $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 + */ + 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 + */ + 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 index 0000000000..01753381cc --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationList.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..8c6182b21a --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/UserNotificationMessageCompiler.class.php @@ -0,0 +1,19 @@ + + * @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 index 0000000000..60d0fc41c9 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/DefaultUserNotificationConveyableEvent.class.php @@ -0,0 +1,242 @@ + + * @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 + */ + 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¬ificationID='.$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 index 0000000000..df03bab3ef --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationConveyableEvent.class.php @@ -0,0 +1,143 @@ + + * @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 $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 $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 $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 index 0000000000..3da47db829 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEvent.class.php @@ -0,0 +1,26 @@ + + * @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 index 0000000000..b9b68acc9a --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventAction.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..1efd880125 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventEditor.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..7291baf460 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/event/UserNotificationEventList.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..d2fe43f21a --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessage.class.php @@ -0,0 +1,45 @@ + + * @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 index 0000000000..9eb584052b --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/message/UserNotificationMessageEditor.class.php @@ -0,0 +1,36 @@ + + * @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 index 0000000000..73522e36b3 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/AbstractUserNotificationConveyableObjectType.class.php @@ -0,0 +1,43 @@ + + * @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 index 0000000000..285db334a9 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObject.class.php @@ -0,0 +1,41 @@ + + * @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 index 0000000000..4b25df97c2 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationConveyableObjectType.class.php @@ -0,0 +1,64 @@ + + * @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 $objectIDs + * @return array + */ + public function getObjectsByIDs(array $objectIDs); + + /** + * Get the notification objects + * + * @param mixed $data + * @return array + */ + 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 + */ + 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 index 0000000000..fe443ff342 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectType.class.php @@ -0,0 +1,26 @@ + + * @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 index 0000000000..aef346f4a7 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/UserNotificationObjectTypeEditor.class.php @@ -0,0 +1,36 @@ + + * @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 index 0000000000..7d8511fe28 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObject.class.php @@ -0,0 +1,44 @@ + + * @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 index 0000000000..596888eeb6 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/VoidUserNotificationConveyableObjectType.class.php @@ -0,0 +1,53 @@ + + * @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 index 0000000000..671f53c3ed --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectType.class.php @@ -0,0 +1,26 @@ + + * @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 index 0000000000..ce458e0df3 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeAction.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..320bdb0568 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeEditor.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..370061d5df --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/object/type/UserNotificationObjectTypeList.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..1aaf32b403 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/MailUserNotificationReceivableType.class.php @@ -0,0 +1,72 @@ + + * @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 index 0000000000..4eb8e48cd5 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationReceivableType.class.php @@ -0,0 +1,51 @@ + + * @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 index 0000000000..cd76b2593d --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationType.class.php @@ -0,0 +1,26 @@ + + * @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 index 0000000000..dc117f9960 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeAction.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..b0cefee03b --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeEditor.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..829efbe013 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/data/user/notification/type/UserNotificationTypeList.class.php @@ -0,0 +1,21 @@ + + * @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 index 0000000000..021de0e534 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/form/UserNotificationSettingsForm.class.php @@ -0,0 +1,288 @@ + + * @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 index 0000000000..23f39b8525 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/page/UserNotificationCountPage.class.php @@ -0,0 +1,57 @@ + + * @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 index 0000000000..82072e4eb0 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/page/UserNotificationFeedPage.class.php @@ -0,0 +1,77 @@ + + * @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 index 0000000000..170c6a203b --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/page/UserNotificationPage.class.php @@ -0,0 +1,105 @@ + + * @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 index 0000000000..ab75aec6bc --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/system/cache/CacheBuilderNotifications.class.php @@ -0,0 +1,111 @@ + + * @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 index 0000000000..f3d5ab8698 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/system/cronjob/CleanUpNotificationsCronjob.class.php @@ -0,0 +1,74 @@ + + * @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 index 0000000000..39497272af --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserMessagesNotificationListener.class.php @@ -0,0 +1,47 @@ + + * @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 index 0000000000..b69ab95616 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/system/event/listener/StructuredTemplateUserPanelNotificationListener.class.php @@ -0,0 +1,35 @@ + + * @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 index 0000000000..8b8de31735 --- /dev/null +++ b/com.woltlab.wcf.notification/files/lib/system/package/plugin/UserNotificationEventPackageInstallationPlugin.class.php @@ -0,0 +1,88 @@ + + * @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 index 0000000000..e0fe14d0cc --- /dev/null +++ b/com.woltlab.wcf.notification/install.sql @@ -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 index 0000000000..7748ad721b --- /dev/null +++ b/com.woltlab.wcf.notification/language/de-informal.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + notificationID}&userID={@$user->userID}&token={@$token} + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ohne Bestätigung gelöscht wird. 0 deaktiviert die automatische Löschung unbestätigter Benachrichtigungen.]]> + + + + + + + + + + + + + \ 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 index 0000000000..d4b36bddd7 --- /dev/null +++ b/com.woltlab.wcf.notification/language/de.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + notificationID}&userID={@$user->userID}&token={@$token} + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ohne Bestätigung gelöscht wird. 0 deaktiviert die automatische Löschung unbestätigter Benachrichtigungen.]]> + + + + + + + + + + + + + \ 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 index 0000000000..aae1005c90 --- /dev/null +++ b/com.woltlab.wcf.notification/language/en.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + notificationID}&userID={@$user->userID}&token={@$token} + +]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 index 0000000000..1eac142081 --- /dev/null +++ b/com.woltlab.wcf.notification/notificationobjecttype.xml @@ -0,0 +1,9 @@ + + + + + void + lib/data/user/notification/object/VoidNotificationObjectType.class.php + + + \ 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 index 0000000000..2abb445dcd --- /dev/null +++ b/com.woltlab.wcf.notification/notificationtype.xml @@ -0,0 +1,9 @@ + + + + + mail + lib/data/user/notification/type/MailUserNotificationReceivableType.class.php + + + \ 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 index 0000000000..c61d64fcb0 --- /dev/null +++ b/com.woltlab.wcf.notification/options.xml @@ -0,0 +1,50 @@ + + + + + + user + module_user_notification + + + user.notification + 1 + module_user_notification + + + + + + + + + + + + diff --git a/com.woltlab.wcf.notification/package.xml b/com.woltlab.wcf.notification/package.xml new file mode 100644 index 0000000000..f7ee01560b --- /dev/null +++ b/com.woltlab.wcf.notification/package.xml @@ -0,0 +1,24 @@ + + + + User Notification System + 1.0.0 Alpha 1 + 2010-08-22 + 1 + + + + WoltLab GmbH, Oliver Kliebisch and Tim Düsterhus + http://www.woltlab.com + + + + com.woltlab.wcf + + + + pip.xml + files.tar + install.sql + + \ 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 index 0000000000..20a70ada7b --- /dev/null +++ b/com.woltlab.wcf.notification/pagelocation.xml @@ -0,0 +1,13 @@ + + + + + + module_user_notification + + + + module_user_notification + + + \ 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 index 0000000000..5cf8227c15 --- /dev/null +++ b/com.woltlab.wcf.notification/pip.xml @@ -0,0 +1,8 @@ + + + + wcf\system\package\plugin\UserNotificationEventPackageInstallationPlugin + wcf\system\package\plugin\UserNotificationObjectTypePackageInstallationPlugin + wcf\system\package\plugin\UserNotificationTypePackageInstallationPlugin + + diff --git a/com.woltlab.wcf.notification/pip/NotificationEventPackageInstallationPlugin.class.php b/com.woltlab.wcf.notification/pip/NotificationEventPackageInstallationPlugin.class.php new file mode 100644 index 0000000000..888551e021 --- /dev/null +++ b/com.woltlab.wcf.notification/pip/NotificationEventPackageInstallationPlugin.class.php @@ -0,0 +1,118 @@ + + * @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 index 0000000000..d52de713da --- /dev/null +++ b/com.woltlab.wcf.notification/pip/NotificationObjectTypePackageInstallationPlugin.class.php @@ -0,0 +1,20 @@ + + * @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 index 0000000000..389f6e1af0 --- /dev/null +++ b/com.woltlab.wcf.notification/pip/NotificationTypePackageInstallationPlugin.class.php @@ -0,0 +1,96 @@ + + * @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 index 0000000000..8e4dd6bacb --- /dev/null +++ b/com.woltlab.wcf.notification/templates/userMenuNotificationLink.tpl @@ -0,0 +1 @@ +
  • hasOutstandingNotifications()} class="new"{/if}> {lang}wcf.header.userMenu.userNotifications{/lang}{if $notificationUser->hasOutstandingNotifications()} ({#$notificationUser->hasOutstandingNotifications()}){/if}
  • \ 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 index 0000000000..5b4a65f434 --- /dev/null +++ b/com.woltlab.wcf.notification/templates/userMenuPeriodicalExecuter.tpl @@ -0,0 +1,17 @@ + \ 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 index 0000000000..482ec9ba81 --- /dev/null +++ b/com.woltlab.wcf.notification/templates/userMessagesNotifications.tpl @@ -0,0 +1,34 @@ +
    + +

    {lang}wcf.user.notification.type.userMessages.title{/lang}

    +
      + {foreach from=$notifications item=notification} + {assign var=languageCategory value=$notification->event->languageCategory} + {assign var=eventName value=$notification->eventName} +
    • 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)} +
      + {if $acceptURL} + {lang}{$languageCategory}.{$eventName}.accept{/lang} + {/if} + {if $declineURL} + {lang}{$languageCategory}.{$eventName}.decline{/lang} + {/if} +
      + {/if} +

      {@$notification->messageCache}

      +
    • + {/foreach} +
    +
    + \ 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 index 0000000000..f21e5d9fd4 --- /dev/null +++ b/com.woltlab.wcf.notification/templates/userNotification.tpl @@ -0,0 +1,95 @@ +{include file="documentHeader"} + + {lang}wcf.user.notification.title{/lang} - {lang}wcf.user.usercp{/lang} - {lang}{PAGE_TITLE}{/lang} + + {include file='headInclude' sandbox=false} + {include file='imageViewer'} + + + + + +{include file='header' sandbox=false} + +
    + {include file="userCPHeader"} + +
    +
    +

    {lang}wcf.user.notification.title{/lang}

    + {if $notifications|count} +
    + {pages print=true assign=pagesLinks link="index.php?page=UserNotification&pageNo=%d"|concat:SID_ARG_2ND_NOT_ENCODED} +
    + +
    +
    +

    {lang}wcf.user.notification.stats{/lang}

    +
    +
    +
    + + + + + + + + + + {foreach from=$notifications item=notification} + {assign var=languageCategory value=$notification->event->languageCategory} + {assign var=eventName value=$notification->eventName} + + + + + + {/foreach} + +
    +
    + {lang}wcf.user.notification.icon{/lang} +
    +
    +
    + {lang}wcf.user.notification.text{/lang} +
    +
    +
    + {lang}wcf.user.notification.time{/lang} +
    +
    + + + {@$notification->longOutput} + {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)} +
    + {if $acceptURL} + {lang}{$languageCategory}.{$eventName}.accept{/lang} + {/if} + {if $declineURL} + {lang}{$languageCategory}.{$eventName}.decline{/lang} + {/if} +
    + {/if} +
    + {@$notification->time|time} +
    +
    + + + {else} +

    {lang}wcf.user.notification.noNotifications{/lang}

    + {/if} +
    +
    +
    + +{include file='footer' sandbox=false} + + \ 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 index 0000000000..3e429a4495 --- /dev/null +++ b/com.woltlab.wcf.notification/templates/userNotificationFeedAtom.tpl @@ -0,0 +1,24 @@ + + + {lang}wcf.user.notification.feed.title{/lang} + {@PAGE_URL}/ + {@'c'|gmdate:TIME_NOW} + + + WoltLab Community Framework + + {lang}wcf.user.notification.feed.description{/lang} + + {foreach from=$notifications item=notification} + + <![CDATA[{$notification->shortOutput}]]> + {@PAGE_URL}/index.php?page=UserNotification&notificationID={@$notification->notificationID} + {@'c'|gmdate:$notification->time} + + {$this->user->username} + + longOutput}]]> + + + {/foreach} + \ 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 index 0000000000..c32fcad8c3 --- /dev/null +++ b/com.woltlab.wcf.notification/templates/userNotificationFeedRss2.tpl @@ -0,0 +1,24 @@ + + + + {lang}wcf.user.notification.feed.title{/lang} + {@PAGE_URL}/ + {lang}wcf.user.notification.feed.description{/lang} + + {@'r'|gmdate:TIME_NOW} + {@'r'|gmdate:TIME_NOW} + WoltLab Community Framework {@WCF_VERSION} + 60 + + {foreach from=$notifications item=notification} + + <![CDATA[{$notification->shortOutput}]]> + {$this->user->username} + {@PAGE_URL}/index.php?page=UserNotification + {@PAGE_URL}/index.php?page=UserNotification&notificationID={@$notification->notificationID} + {@'r'|gmdate:$notification->time} + longOutput}]]> + + {/foreach} + + \ 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 index 0000000000..e7699cc1b1 --- /dev/null +++ b/com.woltlab.wcf.notification/templates/userNotificationSettings.tpl @@ -0,0 +1,82 @@ +{include file="documentHeader"} + + {lang}wcf.user.notification.settings{/lang} - {lang}wcf.user.usercp{/lang} - {lang}{PAGE_TITLE}{/lang} + {include file='headInclude' sandbox=false} + + +{include file='header' sandbox=false} + +
    + + {capture append=userMessages} + {if $errorField} +

    {lang}wcf.global.form.error{/lang}

    + {/if} + + {if $success|isset} +

    {lang}wcf.user.edit.success{/lang}

    + {/if} + {/capture} + + {include file="userCPHeader"} + +
    +
    +
    +

    {lang}wcf.user.notification.settings{/lang}

    + +
    + + {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} + + + + {foreach from=$notificationTypes key=notificationTypeName item=notificationType} + + {/foreach} + + + + {foreach from=$events key=eventName item=event} + + + {foreach from=$event->supportedNotificationTypes key=supportedType item=supported} + + {/foreach} + + {/foreach} + + {/foreach} +
    +
    +

    {lang}wcf.user.notification.object.type.{$typeName}{/lang}

    +
    +
    +
    + {lang}wcf.user.notification.type.{$notificationTypeName}{/lang} +
    +
    +

    {$event->getTitle()}

    +

    {$event->getDescription()}

    +
    + +
    +
    +
    +
    +
    + {@SECURITY_TOKEN_INPUT_TAG} + {@SID_INPUT_TAG} + + +
    +
    + +
    + +{include file='footer' sandbox=false} + + \ 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 index 0000000000..c22d41fcf6 --- /dev/null +++ b/com.woltlab.wcf.notification/usercpmenu.xml @@ -0,0 +1,15 @@ + + + + + index.php?form=UserNotificationSettings + wcf.user.usercp.menu.link.settings + module_user_notification + + + index.php?page=UserNotification + wcf.user.usercp.menu.link.management + module_user_notification + + + \ 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 index 0000000000..ff506846f7 --- /dev/null +++ b/com.woltlab.wcf.notification/useroptions.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file -- 2.20.1