*
* @param array $page Array of URL segments
* @return bool Was the page handled successfully
*/
function notifier_page_handler ($page) {
gatekeeper();
if (empty($page[0])) {
$page[0] = 'all';
}
$path = elgg_get_plugins_path() . 'notifier/pages/notifier/';
switch ($page[0]) {
case 'popup':
include_once($path . 'popup.php');
break;
case 'subjects':
set_input('guid', $page[1]);
include_once($path . 'subjects.php');
break;
case 'all':
default:
include_once($path . 'list.php');
break;
}
return true;
}
/**
* Create a notification
*
* @param string $hook Hook name
* @param string $type Hook type
* @param bool $result Has the notification been sent
* @param array $params Hook parameters
* @return bool Was the notification handled successfully
*/
function notifier_notification_send($hook, $type, $result, $params) {
$notification = $params['notification'];
/* @var Elgg_Notifications_Notification $notification */
$event = $params['event'];
/* @var Elgg_Notifications_Event $event */
if (!$event) {
// Plugin is calling notify_user() so stop here and let
// the NotificationService handle the notification later.
return false;
}
$ia = elgg_set_ignore_access(true);
$action = $event->getAction();
$object = $event->getObject();
$string = "river:{$action}:{$object->getType()}:{$object->getSubtype()}";
$recipient = $notification->getRecipient();
$actor = $event->getActor();
switch ($object->getType()) {
case 'annotation':
// Get the entity that was annotated
$entity = $object->getEntity();
break;
case 'relationship':
$entity = get_entity($object->guid_two);
break;
default:
if ($object instanceof ElggComment) {
// Use comment's container as notification target
$entity = $object->getContainerEntity();
// Check the action because this isn't necessarily a new comment,
// but e.g. someone being mentioned in a comment
if ($action == 'create') {
$string = "river:comment:{$entity->getType()}:{$entity->getSubtype()}";
}
// TODO How about discussion replies?
} else {
// This covers all other entities
$entity = $object;
}
}
if ($object->getType() == 'annotation' || $object->getType() == 'relationship' || ($object instanceof ElggComment && $action == 'create')) {
// Check if similar notification already exists
$existing = notifier_get_similar($event->getDescription(), $entity, $recipient);
if ($existing) {
// Update the existing notification
$existing->setSubject($actor);
$existing->markUnread();
// time_created must be used because time_updated gets updated
// automatically and it won't therefore match the time_created
// of the object triggering the notification
$existing->time_created = $object->time_created;
return $existing->save();
}
}
// If the river string is not available, fall back to summary or subject
if ($string == elgg_echo($string)) {
if ($notification->summary) {
$string = $notification->summary;
} else {
$string = $notification->subject;
}
}
$note = new ElggNotification();
$note->title = $string;
$note->owner_guid = $recipient->getGUID();
$note->container_guid = $recipient->getGUID();
$note->event = $event->getDescription();
// The notification may be being created later than the event took
// place, so use the original time_created instead of time()
$note->time_created = $object->time_created;
if ($note->save()) {
$note->setSubject($actor);
$note->setTarget($entity);
}
elgg_set_ignore_access($ia);
if ($note) {
return true;
}
}
/**
* Get the count of all unread notifications
*
* @return integer
*/
function notifier_count_unread () {
return notifier_get_unread(array('count' => true));
}
/**
* Get all unread messages for logged in users
*
* @param array $options Options passed to elgg_get_entities_from_metadata
* @return ElggNotification[]
*/
function notifier_get_unread ($options = array()) {
$defaults = array(
'type' => 'object',
'subtype' => 'notification',
'limit' => false,
'owner_guid' => elgg_get_logged_in_user_guid(),
'metadata_name_value_pairs' => array(
'name' => 'status',
'value' => 'unread'
)
);
$options = array_merge($defaults, $options);
return elgg_get_entities_from_metadata($options);
}
/**
* Remove over week old notifications that have been read
*
* @param string $hook Hook name
* @param string $type Hook type
* @param string $return Old stdOut contents
* @param array $params Array containing the time when cron was triggered
* @return void
*/
function notifier_cron ($hook, $type, $return, $params) {
// One week ago
$time = time() - 60 * 60 * 24 * 7;
$options = array(
'type' => 'object',
'subtype' => 'notification',
'wheres' => array("e.time_created < $time"),
'metadata_name_value_pairs' => array(
'name' => 'status',
'value' => 'read'
),
'limit' => false
);
$ia = elgg_set_ignore_access(true);
$notifications = elgg_get_entities_from_metadata($options);
$options['count'] = true;
$count = elgg_get_entities_from_metadata($options);
foreach ($notifications as $notification) {
$notification->delete();
}
echo "Removed $count notifications.
";
elgg_set_ignore_access($ia);
}
/**
* Add view listener to views that may be the targets of notifications
*
* @return void
*/
function notifier_set_view_listener () {
$dbprefix = elgg_get_config('dbprefix');
$types = get_data("SELECT * FROM {$dbprefix}entity_subtypes");
// These subtypes do not have notifications so they can be skipped
$skip = array(
'plugin',
'widget',
'admin_notice',
'notification',
'messages',
'reported_content',
'site_notification'
);
foreach ($types as $type) {
if (in_array($type->subtype, $skip)) {
continue;
}
elgg_extend_view("object/{$type->subtype}", 'notifier/view_listener');
}
// Some manual additions
elgg_extend_view('profile/wrapper', 'notifier/view_listener');
}
/**
* Enable notifier by default for new users according to plugin settings.
*
* We do this using 'create, user' event instead of 'register, user' plugin
* hook so that it affects also users created by an admin.
*
* @param string $event 'create'
* @param string $type 'user'
* @param ElggUser $user The user that was created
* @return boolean
*/
function notifier_enable_for_new_user ($event, $type, $user) {
$personal = (boolean) elgg_get_plugin_setting('enable_personal', 'notifier');
$collections = (boolean) elgg_get_plugin_setting('enable_collections', 'notifier');
if ($personal) {
$prefix = "notification:method:notifier";
$user->$prefix = true;
}
if ($collections) {
/**
* This function is triggered before invite code is checked so it's
* enough just to add the setting. Notifications plugin will take care
* of adding the 'notifynotifier' relationship in case user was invited.
*/
$user->collections_notifications_preferences_notifier = '-1';
}
$user->save();
return true;
}
/**
* Enable notifier as notification method when joining a group.
*
* @param string $event 'join'
* @param string $type 'group'
* @param array $params Array containing ElggUser and ElggGroup
*/
function notifier_enable_for_new_group_member ($event, $type, $params) {
$group = $params['group'];
$user = $params['user'];
$enabled = (boolean) elgg_get_plugin_setting('enable_groups', 'notifier');
if ($enabled) {
if (elgg_instanceof($group, 'group') && elgg_instanceof($user, 'user')) {
add_entity_relationship($user->guid, 'notifynotifier', $group->guid);
}
}
}
/**
* Get existing notifications that match the given parameters.
*
* This can be used when we want to update an old notification.
* E.g. "A likes X" and "B likes X" become "A and B like X".
*
* @param string $event_name String like "action:type:subtype"
* @param ElggEntity $entity Entity being notified about
* @param ElggUser $recipient User being notified
* @return ElggNotification|null
*/
function notifier_get_similar($event_name, $entity, $recipient) {
$db_prefix = elgg_get_config('dbprefix');
$ia = elgg_set_ignore_access(true);
$object_relationship = ElggNotification::HAS_OBJECT;
// Notification (guid_one) has relationship 'hasObject' to target (guid_two)
$options = array(
'type' => 'object',
'subtype' => 'notification',
'owner_guid' => $recipient->guid,
'metadata_name_value_pairs' => array(
'name' => 'event',
'value' => $event_name,
),
'joins' => array(
"JOIN {$db_prefix}entity_relationships er ON e.guid = er.guid_one", // Object relationship
),
'wheres' => array(
"er.guid_two = {$entity->guid}",
"er.relationship = '$object_relationship'",
),
);
$notification = elgg_get_entities_from_metadata($options);
if ($notification) {
$notification = $notification[0];
}
elgg_set_ignore_access($ia);
return $notification;
}
/**
* Mark unread friend notifications as read.
*
* This hook is triggered when user goes to the "friendsof/" page.
*
* @param string $hook Hook name
* @param string $type Hook type
* @param array $return Array containing 'identifier' and 'segments'
* @param array $params This is empty
* @return void
*/
function notifier_read_friends_notification ($hook, $type, $return, $params) {
// Get unread notifications that match the friending event
$options = array(
'metadata_name_value_pairs' => array(
'name' => 'event',
'value' => 'create:relationship:friend',
)
);
$notifications = notifier_get_unread($options);
foreach ($notifications as $note) {
$note->markRead();
}
}
/**
* Create notifications about new relationships
*
* The Elgg 1.9 notifications system does not yet process relationships
* so we create the notifications manually on the 'create', 'relationship'
* event instead.
*
* @param string $event 'create'
* @param string $type 'relationship'
* @param ElggRelationship $object The created relationships
* @return boolean Always returns true
*/
function notifier_relationship_notifications ($event, $type, $object) {
$guid_one = $object->guid_one;
$guid_two = $object->guid_two;
$relationship = $object->relationship;
switch ($relationship) {
case 'friend':
// Notification about a new friend
$actor = get_user($guid_one);
$recipient = get_user($guid_two);
$target = $recipient;
$string = 'friend:notifications:summary';
break;
case 'invited':
// Notification about a group membership invitation
$actor = elgg_get_logged_in_user_entity(); // User who invited
$target = get_entity($guid_one); // The group
$recipient = get_user($guid_two); // The invited user
$string = 'groups:notifications:invitation';
break;
case 'membership_request':
// Notification about a group membership invitation
$actor = get_user($guid_one); // User who requested
$target = get_entity($guid_two); // The group
$recipient = get_user($target->owner_guid); // The group_owner
$string = 'groups:notifications:membership_request';
break;
default;
return true;
}
if (!$actor) {
return true;
}
if (!$recipient) {
return true;
}
if (!$target) {
return true;
}
$ia = elgg_set_ignore_access(true);
$event_name = "create:relationship:{$relationship}";
$note = notifier_get_similar($event_name, $target, $recipient);
if (!$note) {
$note = new ElggNotification();
$note->title = $string;
$note->owner_guid = $recipient->guid;
$note->container_guid = $recipient->guid;
$note->event = $event_name;
$note->save();
$note->setTarget($target);
} else {
// Mark the existing notification as unread
$note->markUnread();
}
$note->setSubject($actor);
elgg_set_ignore_access($ia);
// Returning false would delete the relationship
return true;
}
/**
* Delete notification about a group invitation when user accepts/deletes it
*
* @param string $event 'delete'
* @param string $type 'relationship'
* @param ElggRelationship $object The relationship being deleted
* @return boolean
*/
function notifier_read_group_invitation_notification($event, $type, $object) {
$relationship = $object->relationship;
// Proceed only if the relationship is an invitation
if ($relationship != 'invited' && $relationship != 'membership_request') {
return true;
}
// The group may be hidden, so ignore access
$ia = elgg_set_ignore_access(true);
if ($relationship === 'invited') {
$group = get_entity($object->guid_one);
} else {
$group = get_entity($object->guid_two);
}
elgg_set_ignore_access($ia);
// Proceed only if the invitation is for a group
if (!$group instanceof ElggGroup) {
return true;
}
$dbprefix = elgg_get_config('dbprefix');
$has_object = ElggNotification::HAS_OBJECT;
$options = array(
'joins' => array("JOIN {$dbprefix}entity_relationships er ON e.guid = er.guid_one"),
'wheres' => array("er.relationship = '{$has_object}' AND er.guid_two = {$group->guid}"),
);
// Get unread notifications
$notifications = notifier_get_unread($options);
foreach ($notifications as $note) {
if ($note->event === "create:relationship:$relationship") {
$note->markRead();
}
}
// Returning true means that the relationship deletion can now proceed
return true;
}