<?php /** * Elgg groups plugin * * @package ElggGroups */ elgg_register_event_handler('init', 'system', 'groups_init'); // Ensure this runs after other plugins elgg_register_event_handler('init', 'system', 'groups_fields_setup', 10000); /** * Initialize the groups plugin. */ function groups_init() { elgg_register_library('elgg:groups', elgg_get_plugins_path() . 'groups/lib/groups.php'); // register group entities for search elgg_register_entity_type('group', ''); // Set up the menu $item = new ElggMenuItem('groups', elgg_echo('groups'), 'groups/all'); elgg_register_menu_item('site', $item); // Register a page handler, so we can have nice URLs elgg_register_page_handler('groups', 'groups_page_handler'); // Register URL handlers for groups elgg_register_entity_url_handler('group', 'all', 'groups_url'); elgg_register_plugin_hook_handler('entity:icon:url', 'group', 'groups_icon_url_override'); // Register an icon handler for groups elgg_register_page_handler('groupicon', 'groups_icon_handler'); // Register some actions $action_base = elgg_get_plugins_path() . 'groups/actions/groups'; elgg_register_action("groups/edit", "$action_base/edit.php"); elgg_register_action("groups/delete", "$action_base/delete.php"); elgg_register_action("groups/featured", "$action_base/featured.php", 'admin'); $action_base .= '/membership'; elgg_register_action("groups/invite", "$action_base/invite.php"); elgg_register_action("groups/join", "$action_base/join.php"); elgg_register_action("groups/leave", "$action_base/leave.php"); elgg_register_action("groups/remove", "$action_base/remove.php"); elgg_register_action("groups/killrequest", "$action_base/delete_request.php"); elgg_register_action("groups/killinvitation", "$action_base/delete_invite.php"); elgg_register_action("groups/addtogroup", "$action_base/add.php"); // Add some widgets elgg_register_widget_type('a_users_groups', elgg_echo('groups:widget:membership'), elgg_echo('groups:widgets:description')); // add group activity tool option add_group_tool_option('activity', elgg_echo('groups:enableactivity'), true); elgg_extend_view('groups/tool_latest', 'groups/profile/activity_module'); // add link to owner block elgg_register_plugin_hook_handler('register', 'menu:owner_block', 'groups_activity_owner_block_menu'); // group entity menu elgg_register_plugin_hook_handler('register', 'menu:entity', 'groups_entity_menu_setup'); // group user hover menu elgg_register_plugin_hook_handler('register', 'menu:user_hover', 'groups_user_entity_menu_setup'); // delete and edit annotations for topic replies elgg_register_plugin_hook_handler('register', 'menu:annotation', 'groups_annotation_menu_setup'); //extend some views elgg_extend_view('css/elgg', 'groups/css'); elgg_extend_view('js/elgg', 'groups/js'); // Access permissions elgg_register_plugin_hook_handler('access:collections:write', 'all', 'groups_write_acl_plugin_hook'); //elgg_register_plugin_hook_handler('access:collections:read', 'all', 'groups_read_acl_plugin_hook'); // Register profile menu hook elgg_register_plugin_hook_handler('profile_menu', 'profile', 'forum_profile_menu'); elgg_register_plugin_hook_handler('profile_menu', 'profile', 'activity_profile_menu'); // allow ecml in discussion and profiles elgg_register_plugin_hook_handler('get_views', 'ecml', 'groups_ecml_views_hook'); elgg_register_plugin_hook_handler('get_views', 'ecml', 'groupprofile_ecml_views_hook'); // Register a handler for create groups elgg_register_event_handler('create', 'group', 'groups_create_event_listener'); // Register a handler for delete groups elgg_register_event_handler('delete', 'group', 'groups_delete_event_listener'); elgg_register_event_handler('join', 'group', 'groups_user_join_event_listener'); elgg_register_event_handler('leave', 'group', 'groups_user_leave_event_listener'); elgg_register_event_handler('pagesetup', 'system', 'groups_setup_sidebar_menus'); elgg_register_event_handler('annotate', 'all', 'group_object_notifications'); elgg_register_plugin_hook_handler('access:collections:add_user', 'collection', 'groups_access_collection_override'); elgg_register_event_handler('upgrade', 'system', 'groups_run_upgrades'); } /** * This function loads a set of default fields into the profile, then triggers * a hook letting other plugins to edit add and delete fields. * * Note: This is a system:init event triggered function and is run at a super * low priority to guarantee that it is called after all other plugins have * initialized. */ function groups_fields_setup() { $profile_defaults = array( 'description' => 'longtext', 'briefdescription' => 'text', 'interests' => 'tags', //'website' => 'url', ); $profile_defaults = elgg_trigger_plugin_hook('profile:fields', 'group', NULL, $profile_defaults); elgg_set_config('group', $profile_defaults); // register any tag metadata names foreach ($profile_defaults as $name => $type) { if ($type == 'tags') { elgg_register_tag_metadata_name($name); // only shows up in search but why not just set this in en.php as doing it here // means you cannot override it in a plugin add_translation(get_current_language(), array("tag_names:$name" => elgg_echo("groups:$name"))); } } } /** * Configure the groups sidebar menu. Triggered on page setup * */ function groups_setup_sidebar_menus() { // Get the page owner entity $page_owner = elgg_get_page_owner_entity(); if (elgg_get_context() == 'groups') { if ($page_owner instanceof ElggGroup) { if (elgg_is_logged_in() && $page_owner->canEdit() && !$page_owner->isPublicMembership()) { $url = elgg_get_site_url() . "groups/requests/{$page_owner->getGUID()}"; elgg_register_menu_item('page', array( 'name' => 'membership_requests', 'text' => elgg_echo('groups:membershiprequests'), 'href' => $url, )); } } else { elgg_register_menu_item('page', array( 'name' => 'groups:all', 'text' => elgg_echo('groups:all'), 'href' => 'groups/all', )); $user = elgg_get_logged_in_user_entity(); if ($user) { $url = "groups/owner/$user->username"; $item = new ElggMenuItem('groups:owned', elgg_echo('groups:owned'), $url); elgg_register_menu_item('page', $item); $url = "groups/member/$user->username"; $item = new ElggMenuItem('groups:member', elgg_echo('groups:yours'), $url); elgg_register_menu_item('page', $item); $url = "groups/invitations/$user->username"; $item = new ElggMenuItem('groups:user:invites', elgg_echo('groups:invitations'), $url); elgg_register_menu_item('page', $item); } } } } /** * Groups page handler * * URLs take the form of * All groups: groups/all * User's owned groups: groups/owner/<username> * User's member groups: groups/member/<username> * Group profile: groups/profile/<guid>/<title> * New group: groups/add/<guid> * Edit group: groups/edit/<guid> * Group invitations: groups/invitations/<username> * Invite to group: groups/invite/<guid> * Membership requests: groups/requests/<guid> * Group activity: groups/activity/<guid> * Group members: groups/members/<guid> * * @param array $page Array of url segments for routing * @return bool */ function groups_page_handler($page) { elgg_load_library('elgg:groups'); elgg_push_breadcrumb(elgg_echo('groups'), "groups/all"); switch ($page[0]) { case 'all': groups_handle_all_page(); break; case 'search': groups_search_page(); break; case 'owner': groups_handle_owned_page(); break; case 'member': set_input('username', $page[1]); groups_handle_mine_page(); break; case 'invitations': set_input('username', $page[1]); groups_handle_invitations_page(); break; case 'add': groups_handle_edit_page('add'); break; case 'edit': groups_handle_edit_page('edit', $page[1]); break; case 'profile': groups_handle_profile_page($page[1]); break; case 'activity': groups_handle_activity_page($page[1]); break; case 'members': groups_handle_members_page($page[1]); break; case 'invite': groups_handle_invite_page($page[1]); break; case 'requests': groups_handle_requests_page($page[1]); break; default: return false; } return true; } /** * Handle group icons. * * @param array $page * @return void */ function groups_icon_handler($page) { // The username should be the file we're getting if (isset($page[0])) { set_input('group_guid', $page[0]); } if (isset($page[1])) { set_input('size', $page[1]); } // Include the standard profile index $plugin_dir = elgg_get_plugins_path(); include("$plugin_dir/groups/icon.php"); return true; } /** * Populates the ->getUrl() method for group objects * * @param ElggEntity $entity File entity * @return string File URL */ function groups_url($entity) { $title = elgg_get_friendly_title($entity->name); return "groups/profile/{$entity->guid}/$title"; } /** * Override the default entity icon for groups * * @return string Relative URL */ function groups_icon_url_override($hook, $type, $returnvalue, $params) { $group = $params['entity']; $size = $params['size']; if (isset($group->icontime)) { // return thumbnail $icontime = $group->icontime; return "groupicon/$group->guid/$size/$icontime.jpg"; } return "mod/groups/graphics/default{$size}.gif"; } /** * Add owner block link */ function groups_activity_owner_block_menu($hook, $type, $return, $params) { if (elgg_instanceof($params['entity'], 'group')) { if ($params['entity']->activity_enable != "no") { $url = "groups/activity/{$params['entity']->guid}"; $item = new ElggMenuItem('activity', elgg_echo('groups:activity'), $url); $return[] = $item; } } return $return; } /** * Add links/info to entity menu particular to group entities */ function groups_entity_menu_setup($hook, $type, $return, $params) { if (elgg_in_context('widgets')) { return $return; } $entity = $params['entity']; $handler = elgg_extract('handler', $params, false); if ($handler != 'groups') { return $return; } foreach ($return as $index => $item) { if (in_array($item->getName(), array('access', 'likes', 'edit', 'delete'))) { unset($return[$index]); } } // membership type $membership = $entity->membership; if ($membership == ACCESS_PUBLIC) { $mem = elgg_echo("groups:open"); } else { $mem = elgg_echo("groups:closed"); } $options = array( 'name' => 'membership', 'text' => $mem, 'href' => false, 'priority' => 100, ); $return[] = ElggMenuItem::factory($options); // number of members $num_members = get_group_members($entity->guid, 10, 0, 0, true); $members_string = elgg_echo('groups:member'); $options = array( 'name' => 'members', 'text' => $num_members . ' ' . $members_string, 'href' => false, 'priority' => 200, ); $return[] = ElggMenuItem::factory($options); // feature link if (elgg_is_admin_logged_in()) { if ($entity->featured_group == "yes") { $url = "action/groups/featured?group_guid={$entity->guid}&action_type=unfeature"; $wording = elgg_echo("groups:makeunfeatured"); } else { $url = "action/groups/featured?group_guid={$entity->guid}&action_type=feature"; $wording = elgg_echo("groups:makefeatured"); } $options = array( 'name' => 'feature', 'text' => $wording, 'href' => $url, 'priority' => 300, 'is_action' => true ); $return[] = ElggMenuItem::factory($options); } return $return; } /** * Add a remove user link to user hover menu when the page owner is a group */ function groups_user_entity_menu_setup($hook, $type, $return, $params) { if (elgg_is_logged_in()) { $group = elgg_get_page_owner_entity(); // Check for valid group if (!elgg_instanceof($group, 'group')) { return $return; } $entity = $params['entity']; // Make sure we have a user and that user is a member of the group if (!elgg_instanceof($entity, 'user') || !$group->isMember($entity)) { return $return; } // Add remove link if we can edit the group, and if we're not trying to remove the group owner if ($group->canEdit() && $group->getOwnerGUID() != $entity->guid) { $remove = elgg_view('output/confirmlink', array( 'href' => "action/groups/remove?user_guid={$entity->guid}&group_guid={$group->guid}", 'text' => elgg_echo('groups:removeuser'), )); $options = array( 'name' => 'removeuser', 'text' => $remove, 'priority' => 999, ); $return[] = ElggMenuItem::factory($options); } } return $return; } /** * Add edit and delete links for forum replies */ function groups_annotation_menu_setup($hook, $type, $return, $params) { if (elgg_in_context('widgets')) { return $return; } $annotation = $params['annotation']; if ($annotation->name != 'group_topic_post') { return $return; } if ($annotation->canEdit()) { $url = elgg_http_add_url_query_elements('action/discussion/reply/delete', array( 'annotation_id' => $annotation->id, )); $options = array( 'name' => 'delete', 'href' => $url, 'text' => "<span class=\"elgg-icon elgg-icon-delete\"></span>", 'confirm' => elgg_echo('deleteconfirm'), 'encode_text' => false ); $return[] = ElggMenuItem::factory($options); $url = elgg_http_add_url_query_elements('discussion', array( 'annotation_id' => $annotation->id, )); $options = array( 'name' => 'edit', 'href' => "#edit-annotation-$annotation->id", 'text' => elgg_echo('edit'), 'encode_text' => false, 'rel' => 'toggle', ); $return[] = ElggMenuItem::factory($options); } return $return; } /** * Groups created so create an access list for it */ function groups_create_event_listener($event, $object_type, $object) { $ac_name = elgg_echo('groups:group') . ": " . $object->name; $group_id = create_access_collection($ac_name, $object->guid); if ($group_id) { $object->group_acl = $group_id; } else { // delete group if access creation fails return false; } return true; } /** * Hook to listen to read access control requests and return all the groups you are a member of. */ function groups_read_acl_plugin_hook($hook, $entity_type, $returnvalue, $params) { //error_log("READ: " . var_export($returnvalue)); $user = elgg_get_logged_in_user_entity(); if ($user) { // Not using this because of recursion. // Joining a group automatically add user to ACL, // So just see if they're a member of the ACL. //$membership = get_users_membership($user->guid); $members = get_members_of_access_collection($group->group_acl); print_r($members); exit; if ($membership) { foreach ($membership as $group) $returnvalue[$user->guid][$group->group_acl] = elgg_echo('groups:group') . ": " . $group->name; return $returnvalue; } } } /** * Return the write access for the current group if the user has write access to it. */ function groups_write_acl_plugin_hook($hook, $entity_type, $returnvalue, $params) { $page_owner = elgg_get_page_owner_entity(); $user_guid = $params['user_id']; $user = get_entity($user_guid); if (!$user) { return $returnvalue; } // only insert group access for current group if ($page_owner instanceof ElggGroup) { if ($page_owner->canWriteToContainer($user_guid)) { $returnvalue[$page_owner->group_acl] = elgg_echo('groups:group') . ': ' . $page_owner->name; unset($returnvalue[ACCESS_FRIENDS]); } } else { // if the user owns the group, remove all access collections manually // this won't be a problem once the group itself owns the acl. $groups = elgg_get_entities_from_relationship(array( 'relationship' => 'member', 'relationship_guid' => $user_guid, 'inverse_relationship' => FALSE, 'limit' => 999 )); if ($groups) { foreach ($groups as $group) { unset($returnvalue[$group->group_acl]); } } } return $returnvalue; } /** * Groups deleted, so remove access lists. */ function groups_delete_event_listener($event, $object_type, $object) { delete_access_collection($object->group_acl); return true; } /** * Listens to a group join event and adds a user to the group's access control * */ function groups_user_join_event_listener($event, $object_type, $object) { $group = $object['group']; $user = $object['user']; $acl = $group->group_acl; add_user_to_access_collection($user->guid, $acl); return true; } /** * Make sure users are added to the access collection */ function groups_access_collection_override($hook, $entity_type, $returnvalue, $params) { if (isset($params['collection'])) { if (elgg_instanceof(get_entity($params['collection']->owner_guid), 'group')) { return true; } } } /** * Listens to a group leave event and removes a user from the group's access control * */ function groups_user_leave_event_listener($event, $object_type, $object) { $group = $object['group']; $user = $object['user']; $acl = $group->group_acl; remove_user_from_access_collection($user->guid, $acl); return true; } /** * Grabs groups by invitations * Have to override all access until there's a way override access to getter functions. * * @param int $user_guid The user's guid * @param bool $return_guids Return guids rather than ElggGroup objects * * @return array ElggGroups or guids depending on $return_guids */ function groups_get_invited_groups($user_guid, $return_guids = FALSE) { $ia = elgg_set_ignore_access(TRUE); $groups = elgg_get_entities_from_relationship(array( 'relationship' => 'invited', 'relationship_guid' => $user_guid, 'inverse_relationship' => TRUE, 'limit' => 0, )); elgg_set_ignore_access($ia); if ($return_guids) { $guids = array(); foreach ($groups as $group) { $guids[] = $group->getGUID(); } return $guids; } return $groups; } /** * Join a user to a group, add river event, clean-up invitations * * @param ElggGroup $group * @param ElggUser $user * @return bool */ function groups_join_group($group, $user) { // access ignore so user can be added to access collection of invisible group $ia = elgg_set_ignore_access(TRUE); $result = $group->join($user); elgg_set_ignore_access($ia); if ($result) { // flush user's access info so the collection is added get_access_list($user->guid, 0, true); // Remove any invite or join request flags remove_entity_relationship($group->guid, 'invited', $user->guid); remove_entity_relationship($user->guid, 'membership_request', $group->guid); add_to_river('river/relationship/member/create', 'join', $user->guid, $group->guid); return true; } return false; } /** * Function to use on groups for access. It will house private, loggedin, public, * and the group itself. This is when you don't want other groups or access lists * in the access options available. * * @return array */ function group_access_options($group) { $access_array = array( ACCESS_PRIVATE => 'private', ACCESS_LOGGED_IN => 'logged in users', ACCESS_PUBLIC => 'public', $group->group_acl => 'Group: ' . $group->name, ); return $access_array; } function activity_profile_menu($hook, $entity_type, $return_value, $params) { if ($params['owner'] instanceof ElggGroup) { $return_value[] = array( 'text' => elgg_echo('Activity'), 'href' => "groups/activity/{$params['owner']->getGUID()}" ); } return $return_value; } /** * Parse ECML on group discussion views */ function groups_ecml_views_hook($hook, $entity_type, $return_value, $params) { $return_value['forum/viewposts'] = elgg_echo('groups:ecml:discussion'); return $return_value; } /** * Parse ECML on group profiles */ function groupprofile_ecml_views_hook($hook, $entity_type, $return_value, $params) { $return_value['groups/groupprofile'] = elgg_echo('groups:ecml:groupprofile'); return $return_value; } /** * Discussion * */ elgg_register_event_handler('init', 'system', 'discussion_init'); /** * Initialize the discussion component */ function discussion_init() { elgg_register_library('elgg:discussion', elgg_get_plugins_path() . 'groups/lib/discussion.php'); elgg_register_page_handler('discussion', 'discussion_page_handler'); elgg_register_entity_url_handler('object', 'groupforumtopic', 'discussion_override_topic_url'); // commenting not allowed on discussion topics (use a different annotation) elgg_register_plugin_hook_handler('permissions_check:comment', 'object', 'discussion_comment_override'); $action_base = elgg_get_plugins_path() . 'groups/actions/discussion'; elgg_register_action('discussion/save', "$action_base/save.php"); elgg_register_action('discussion/delete', "$action_base/delete.php"); elgg_register_action('discussion/reply/save', "$action_base/reply/save.php"); elgg_register_action('discussion/reply/delete', "$action_base/reply/delete.php"); // add link to owner block elgg_register_plugin_hook_handler('register', 'menu:owner_block', 'discussion_owner_block_menu'); // Register for search. elgg_register_entity_type('object', 'groupforumtopic'); // because replies are not comments, need of our menu item elgg_register_plugin_hook_handler('register', 'menu:river', 'discussion_add_to_river_menu'); // add the forum tool option add_group_tool_option('forum', elgg_echo('groups:enableforum'), true); elgg_extend_view('groups/tool_latest', 'discussion/group_module'); // notifications register_notification_object('object', 'groupforumtopic', elgg_echo('groupforumtopic:new')); elgg_register_plugin_hook_handler('object:notifications', 'object', 'group_object_notifications_intercept'); elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'groupforumtopic_notify_message'); } /** * Discussion page handler * * URLs take the form of * All topics in site: discussion/all * List topics in forum: discussion/owner/<guid> * View discussion topic: discussion/view/<guid> * Add discussion topic: discussion/add/<guid> * Edit discussion topic: discussion/edit/<guid> * * @param array $page Array of url segments for routing * @return bool */ function discussion_page_handler($page) { elgg_load_library('elgg:discussion'); elgg_push_breadcrumb(elgg_echo('discussion'), 'discussion/all'); switch ($page[0]) { case 'all': discussion_handle_all_page(); break; case 'owner': discussion_handle_list_page($page[1]); break; case 'add': discussion_handle_edit_page('add', $page[1]); break; case 'edit': discussion_handle_edit_page('edit', $page[1]); break; case 'view': discussion_handle_view_page($page[1]); break; default: return false; } return true; } /** * Override the discussion topic url * * @param ElggObject $entity Discussion topic * @return string */ function discussion_override_topic_url($entity) { return 'discussion/view/' . $entity->guid; } /** * We don't want people commenting on topics in the river */ function discussion_comment_override($hook, $type, $return, $params) { if (elgg_instanceof($params['entity'], 'object', 'groupforumtopic')) { return false; } } /** * Add owner block link */ function discussion_owner_block_menu($hook, $type, $return, $params) { if (elgg_instanceof($params['entity'], 'group')) { if ($params['entity']->forum_enable != "no") { $url = "discussion/owner/{$params['entity']->guid}"; $item = new ElggMenuItem('discussion', elgg_echo('discussion:group'), $url); $return[] = $item; } } return $return; } /** * Add the reply button for the river */ function discussion_add_to_river_menu($hook, $type, $return, $params) { if (elgg_is_logged_in() && !elgg_in_context('widgets')) { $item = $params['item']; $object = $item->getObjectEntity(); if (elgg_instanceof($object, 'object', 'groupforumtopic')) { if ($item->annotation_id == 0) { $group = $object->getContainerEntity(); if ($group && ($group->canWriteToContainer() || elgg_is_admin_logged_in())) { $options = array( 'name' => 'reply', 'href' => "#groups-reply-$object->guid", 'text' => elgg_view_icon('speech-bubble'), 'title' => elgg_echo('reply:this'), 'rel' => 'toggle', 'priority' => 50, ); $return[] = ElggMenuItem::factory($options); } } } } return $return; } /** * Event handler for group forum posts * */ function group_object_notifications($event, $object_type, $object) { static $flag; if (!isset($flag)) $flag = 0; if (is_callable('object_notifications')) if ($object instanceof ElggObject) { if ($object->getSubtype() == 'groupforumtopic') { //if ($object->countAnnotations('group_topic_post') > 0) { if ($flag == 0) { $flag = 1; object_notifications($event, $object_type, $object); } //} } } } /** * Intercepts the notification on group topic creation and prevents a notification from going out * (because one will be sent on the annotation) * * @param unknown_type $hook * @param unknown_type $entity_type * @param unknown_type $returnvalue * @param unknown_type $params * @return unknown */ function group_object_notifications_intercept($hook, $entity_type, $returnvalue, $params) { if (isset($params)) { if ($params['event'] == 'create' && $params['object'] instanceof ElggObject) { if ($params['object']->getSubtype() == 'groupforumtopic') { return true; } } } return null; } /** * Returns a more meaningful message * * @param unknown_type $hook * @param unknown_type $entity_type * @param unknown_type $returnvalue * @param unknown_type $params */ function groupforumtopic_notify_message($hook, $entity_type, $returnvalue, $params) { $entity = $params['entity']; $to_entity = $params['to_entity']; $method = $params['method']; if (($entity instanceof ElggEntity) && ($entity->getSubtype() == 'groupforumtopic')) { $descr = $entity->description; $title = $entity->title; $url = $entity->getURL(); $msg = get_input('topicmessage'); if (empty($msg)) $msg = get_input('topic_post'); if (!empty($msg)) $msg = $msg . "\n\n"; else $msg = ''; $owner = get_entity($entity->container_guid); if ($method == 'sms') { return elgg_echo("groupforumtopic:new") . ': ' . $url . " ({$owner->name}: {$title})"; } else { return elgg_get_logged_in_user_entity()->name . ' ' . elgg_echo("groups:viagroups") . ': ' . $title . "\n\n" . $msg . "\n\n" . $entity->getURL(); } } return null; } /** * A simple function to see who can edit a group discussion post * @param the comment $entity * @param user who owns the group $group_owner * @return boolean */ function groups_can_edit_discussion($entity, $group_owner) { //logged in user $user = elgg_get_logged_in_user_guid(); if (($entity->owner_guid == $user) || $group_owner == $user || elgg_is_admin_logged_in()) { return true; } else { return false; } } /** * Process upgrades for the groups plugin */ function groups_run_upgrades() { $path = elgg_get_plugins_path() . 'groups/upgrades/'; $files = elgg_get_upgrade_files($path); foreach ($files as $file) { include "$path{$file}"; } }