<?php /** * Provides links and notifications for using @username mentions * */ elgg_register_event_handler('init', 'system', 'mentions_init'); function mentions_init() { elgg_extend_view('css/elgg', 'css/mentions'); elgg_require_js('mentions/autocomplete'); elgg_extend_view('input/longtext', 'mentions/popup'); elgg_extend_view('input/plaintext', 'mentions/popup'); elgg_register_event_handler('pagesetup', 'system', 'mentions_get_views'); // can't use notification hooks here because of many reasons elgg_register_event_handler('create', 'object', 'mentions_notification_handler'); elgg_register_event_handler('create', 'annotation', 'mentions_notification_handler'); // @todo This will result in multiple notifications for an edited entity so we don't do this //register_elgg_event_handler('update', 'all', 'mentions_notification_handler'); // add option to the personal notifications form elgg_extend_view('notifications/subscriptions/personal', 'mentions/notification_settings'); elgg_register_plugin_hook_handler('action', 'notificationsettings/save', 'mentions_save_settings'); } function mentions_get_regex() { // @todo this won't work for usernames that must be html encoded. // get all chars with unicode 'letter' or 'mark' properties or a number _ or ., // preceeded by @, and possibly surrounded by word boundaries. return '/[\b]?@([\p{L}\p{M}_\.0-9]+)[\b]?/iu'; } function mentions_get_views() { // allow plugins to add additional views to be processed for usernames $views = array('output/longtext'); $views = elgg_trigger_plugin_hook('get_views', 'mentions', null, $views); foreach ($views as $view) { elgg_register_plugin_hook_handler('view', $view, 'mentions_rewrite'); } } /** * Rewrites a view for @username mentions. * * @param string $hook The name of the hook * @param string $type The type of the hook * @param string $content The content of the page * @return string */ function mentions_rewrite($hook, $entity_type, $returnvalue, $params) { $regexp = mentions_get_regex(); $returnvalue = preg_replace_callback($regexp, 'mentions_preg_callback', $returnvalue); return $returnvalue; } /** * Used as a callback fro the preg_replace in mentions_rewrite() * * @param type $matches * @return type str */ function mentions_preg_callback($matches) { $user = get_user_by_username($matches[1]); $period = ''; $icon = ''; // Catch the trailing period when used as punctuation and not a username. if (!$user && substr($matches[1], -1) == '.') { $user = get_user_by_username(rtrim($matches[1], '.')); $period = '.'; } if ($user) { if (elgg_get_plugin_setting('fancy_links', 'mentions')) { $icon = elgg_view('output/img', array( 'src' => $user->getIconURL('topbar'), 'class' => 'pas mentions-user-icon' )); $replace = elgg_view('output/url', array( 'href' => $user->getURL(), 'text' => $icon . $user->name, 'class' => 'mentions-user-link' )); } else { $replace = elgg_view('output/url', array( 'href' => $user->getURL(), 'text' => $user->name, )); } return $replace .= $period; } else { return $matches[0]; } } /** * Catch all create events and scan for @username tags to notify user. * * @param string $event The event name * @param string $event_type The event type * @param ElggData $object The object that was created * @return void */ function mentions_notification_handler($event, $event_type, $object) { // excludes messages - otherwise an endless loop of notifications occur! if (elgg_instanceof($object, 'object', 'messages')) { return; } $type = $object->getType(); $subtype = $object->getSubtype(); $owner = $object->getOwnerEntity(); $fields = array( 'title', 'description', 'value' ); // store the guids of notified users so they only get one notification per creation event $notified_guids = array(); foreach ($fields as $field) { $content = $object->$field; // it's ok in this case if 0 matches == FALSE if (preg_match_all(mentions_get_regex(), $content, $matches)) { // match against the 2nd index since the first is everything foreach ($matches[1] as $username) { $user = get_user_by_username($username); // check for trailing punctuation caught by the regex if (!$user && substr($username, -1) == '.') { $user = get_user_by_username(rtrim($username, '.')); } if (!$user) { continue; } // user must have access to view object/annotation if ($type == 'annotation') { $annotated_entity = $object->getEntity(); if (!$annotated_entity || !has_access_to_entity($annotated_entity, $user)) { continue; } } else { if (!has_access_to_entity($object, $user)) { continue; } } if (!in_array($user->getGUID(), $notified_guids)) { $notified_guids[] = $user->getGUID(); // if they haven't set the notification status default to sending. // Private settings are stored as strings so we check against "0" $notification_setting = elgg_get_plugin_user_setting('notify', $user->getGUID(), 'mentions'); if ($notification_setting === "0") { continue; } $link = $object->getURL(); $type_key = "mentions:notification_types:$type:$subtype"; $type_str = elgg_echo($type_key); if ($type_str == $type_key) { // plugins can add to the list of mention objects by defining // the language string 'mentions:notification_types:<type>:<subtype>' continue; } $subject = elgg_echo('mentions:notification:subject', array($owner->name, $type_str)); $body = elgg_echo('mentions:notification:body', array( $owner->name, $type_str, $link, )); $params = array( 'object' => $object, 'action' => 'mention', ); notify_user($user->getGUID(), $owner->getGUID(), $subject, $body, $params); } } } } } /** * Save mentions-specific info from the notification form * * @param type $hook * @param type $type * @param type $value * @param type $params */ function mentions_save_settings($hook, $type, $value, $params) { $notify = (bool) get_input('mentions_notify'); $user = get_entity(get_input('guid')); if (!elgg_set_plugin_user_setting('notify', $notify, $user->getGUID(), 'mentions')) { register_error(elgg_echo('mentions:settings:failed')); } return; }