theme configuration page.', array('@url' => url('admin/build/themes'))); break; } } } function og_menu() { // Anon users should be able to get to the join page $items['og/subscribe/%node'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'og_subscribe', 'page arguments' => array(2), 'access callback' => 'node_access', 'access arguments' => array('view', 2), 'title' => 'Join group' ); $items['og/opml'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'og_opml', 'access callback' => 'user_is_logged_in', 'title' => 'OPML', ); $items['og/unsubscribe/%node/%user'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('og_confirm_unsubscribe', 2, 3), 'access callback' => 'og_menu_access_unsubscribe', 'access arguments' => array(2, 3), 'title' => 'Leave group', ); $items['og/approve/%node/%user/%'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'og_approve', 'page arguments' => array(2, 3, 4), 'access callback' => 'og_is_group_admin', 'access arguments' => array(2), 'title' => 'Approve membership request' ); $items['og/deny/%node/%user/%'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'og_deny', 'page arguments' => array(2, 3, 4), 'access callback' => 'og_is_group_admin', 'access arguments' => array(2), 'title' => 'Deny membership request', ); $items['og/create_admin/%node/%user'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('og_create_admin_confirm', 2, 3), 'access callback' => 'og_is_group_admin', 'access arguments' => array(2), 'title' => 'Create group administrator', ); $items['og/delete_admin/%node/%user'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'drupal_get_form', 'page arguments' => array('og_remove_admin_confirm', 2, 3), 'access callback' => 'og_is_group_admin', 'access arguments' => array(2), 'title' => 'Delete group administrator', ); // members only and group may not be invite-only or closed $items['og/invite/%node'] = array( 'page callback' => 'drupal_get_form', 'page arguments' => array('og_invite_form', 2), 'access callback' => 'og_menu_access_invite', 'access arguments' => array(2), 'title' => 'Send invitation', 'type' => MENU_CALLBACK, ); $items["og/manage/%node"] = array( 'page callback' => 'og_manage', 'page arguments' => array(2), 'access callback' => 'og_is_group_member', 'access arguments' => array(2, FALSE), 'title' => 'Manage membership', 'type' => MENU_CALLBACK, ); $items['og/activity'] = array( 'title' => 'Group activity', 'page callback' => 'og_page_activity', 'access arguments' => array('administer organic groups'), 'weight' => 4, 'type' => MENU_LOCAL_TASK, ); $items['admin/og'] = array( 'title' => 'Organic groups', 'description' => 'Administer the suite of Organic groups modules.', 'position' => 'right', 'weight' => -5, 'page callback' => 'system_admin_menu_block_page', 'access arguments' => array('administer site configuration'), 'file' => 'system.admin.inc', 'file path' => drupal_get_path('module', 'system'), ); $items['admin/og/og'] = array( 'page callback' => 'drupal_get_form', 'page arguments' => array('og_admin_settings'), 'title' => 'Organic groups configuration', 'access arguments' => array('administer site configuration'), 'description' => 'Configure the main Organic groups module (og).', 'file' => 'og.admin.inc', 'weight' => -5, ); // group admin only $items['og/users/%node/add_user'] = array( 'page callback' => 'drupal_get_form', 'title' => 'Add members', 'page arguments' => array('og_add_users', 2), 'type' => MENU_LOCAL_TASK, 'weight' => 5, 'access callback' => 'og_is_group_admin', 'access arguments' => array(2), ); // Broadcast tab on group node. $items['node/%node/broadcast'] = array( 'title' => 'Broadcast', 'page callback' => 'drupal_get_form', 'page arguments' => array('og_broadcast_form', 1), 'access callback' => 'og_broadcast_access', 'access arguments' => array(1), 'type' => MENU_LOCAL_TASK, 'weight' => 7 ); return $items; } function og_menu_alter(&$menu) { // If og_access is disabled, we at least add back the edit tab for group admins to edit their posts. $menu['node/%node/edit']['access callback'] = 'og_menu_access_node_edit'; $menu['node/%node/edit']['access arguments'] = array(1); } function og_menu_access_node_edit($node) { // Am I a group admin for this group post? if (!module_exists('og_access') && isset($node->og_groups)) { foreach ($node->og_groups as $gid) { if (og_is_group_admin(node_load($gid))) { return TRUE; } } } // Am I group admin for this group node? if (!module_exists('og_access') && og_is_group_admin($node)) { return TRUE; } // Since the group admin tests failed, check access as usual. return node_access('update', $node); } function og_menu_access_unsubscribe($group_node, $account) { global $user; // Unsubscribee must already be a member or pending member. if (!og_is_group_member($group_node, FALSE, $account->uid)) { // Check pending as well. $subs = og_get_subscriptions($account->uid, 0); foreach ($subs as $key => $sub) { if ($group_node->nid == $key) { $is_member = TRUE; break; } } if (empty($is_member)) { return FALSE; } } // Only admins can remove another member if ($account->uid != $user->uid && !og_is_group_admin($group_node)) { return FALSE; } // Regular users may not unsubscribe from CLOSED groups. if ($group_node->og_selective == OG_CLOSED && !og_is_group_admin($group_node)) { return FALSE; } // Group manager may not unsubscribe if ($group_node->uid == $account->uid) { return FALSE; } // Protect private groups. if (!node_access('view', $group_node)) { return FALSE; } return TRUE; } function og_menu_access_invite($node) { return og_is_group_member($node) && ($node->og_selective < OG_INVITE_ONLY || og_is_group_admin($node)); } function og_menu_access_picture($gid) { return og_is_picture() && og_is_group_member($gid) ? TRUE : FALSE; } /** * Check a user's membership in a group. * * @param gid * An integer or a node object representing the group node. * @param $include_admins * Whether or not site admins are considered members. * @param $uid * Pass a user id, or pass NULL in order to check current user. */ function og_is_group_member($gid, $include_admins = TRUE, $uid = NULL) { if ($uid) { $user = user_load(array('uid' => $uid)); } else { global $user; // Adventurous modules can cause us to arrive here before og_init() has fired. // See http://drupal.org/node/285696 if (!isset($user->og_groups)) { $user = user_load(array('uid' => $uid)); } } // Allow caller to pass in a full $node. Used by menu items. if (is_object($gid)) { $gid = $gid->nid; } $groups = array_keys($user->og_groups); if ($include_admins) { return user_access('administer nodes', $user) || in_array($gid, $groups) ? TRUE : FALSE; } else { return in_array($gid, $groups) ? TRUE : FALSE; } } /** * Determine whether user can act as a group administrator for a given group. * * @param string $node * A group node object. * @param string $account * A user account object. If not supplied, the current user is assumed. * @return boolean */ function og_is_group_admin($node, $account = NULL) { if (is_null($account)) { $account = $GLOBALS['user']; } return og_is_group_type($node->type) && (user_access('administer nodes', $account) || !empty($account->og_groups[$node->nid]['is_admin'])); } // An implementation of hook_theme(). function og_theme() { return array( 'opml_icon' => array('arguments' => array('url')), 'og_format_subscriber_status' => array('arguments' => array('group')), 'og_mission' => array('template' => 'og-mission', 'arguments' => array('form' => NULL), 'path' => drupal_get_path('module', 'og'). '/theme'), ); } /** * Simplify $mission variable for the template */ function og_preprocess_og_mission(&$variables) { $variables['mission'] = $variables['form']['#value']; } /** * Make group context available to javascript for ad tags and analytics. * * @return void **/ function og_preprocess_page(&$variables) { if ($group_node = og_get_group_context()) { $data = array('og' => array('group_context' => array( 'nid' => $group_node->nid, 'title' => $group_node->title, 'type' => $group_node->type, ))); drupal_add_js($data, 'setting'); $variables['scripts'] = drupal_get_js(); $variables['body_classes'] .= " og-context og-context-$group_node->nid"; } } /** * Enrich non group nodes with the list og groups that the node belongs to. * * @return void **/ function og_preprocess_node(&$variables) { $og_links = array(); $node = $variables['node']; // TODO: _both is not present during node preview and when you remove all audiences. // So group links don't curently show then. if (og_is_group_post_type($node->type) && !empty($node->og_groups_both)) { $current_groups = og_node_groups_distinguish($node->og_groups_both, FALSE); foreach ($current_groups['accessible'] as $gid => $item) { $og_links['og_'. $gid] = array('title' => $item['title'], 'href' => "node/$gid"); } $variables['og_links']['view'] = theme('links', $og_links); $variables['og_links']['raw'] = $og_links; array_unshift($variables['template_files'], 'node-og-group-post'); } elseif (og_is_group_type($node->type)) { // This looks awful on a group node unset($variables['submitted']); array_unshift($variables['template_files'], 'node-og-group'); } } function og_theme_registry_alter(&$variables) { // Check for og provided templates just before we use the default node.tpl.php array_splice($variables['node']['theme paths'], 1, 0, drupal_get_path('module', 'og')); } function og_init() { // we have to perform a load in order to assure that the $user->og_groups bits are present. global $user; if ($user->uid) { $user = user_load(array('uid' => $user->uid)); } else { $user->og_groups = array(); } drupal_add_css(drupal_get_path('module', 'og'). '/og.css'); // Set group context and language if needed. if ($node = og_determine_context()) { og_set_language($node); // In D7, restrict use of $_SESSION if this lands http://drupal.org/node/201122. if ($user->uid || variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED) { $_SESSION['og_last'] = $node; } } } /** * Set the language for the page based on group's language. Will have no effect * if user has set a personal language. * @param string $node * A group node object. */ function og_set_language($node) { if ($node->og_language) { $map = language_list(); $og_language = $map[$node->og_language]; $user_language = user_preferred_language($account, $og_language); if ($og_language == $user_language) { $GLOBALS['language'] = $og_language; } } } /** * Implementation of hook_perm(). */ function og_perm() { return array('administer organic groups'); } /** * Implementation of hook_og(). */ function og_og($op, $gid, $uid, $args) { if (module_exists('rules')) { if (in_array($op, array('user insert', 'user delete'))) { $op = str_replace(' ', '_', $op); rules_invoke_event('og_'. $op, $uid, $gid); } // Pending member was approved. elseif ($op = 'user update' && $args['is_active']) { rules_invoke_event('og_user_approved', $uid, $gid); } } } /** * Set group context based on URL. Modules may override what we set here. * Set a custom theme based on what group is being displayed (if any). * Be smart about selecting the 'active' group for ambiguous urls like node/$nid * TODOL see http://api.drupal.org/api/function/menu_get_object/6 */ function og_determine_context() { global $custom_theme; $group_node = NULL; // a node object containing the 'active' group for this request // Recognize context when handling a request from a Views ajax block. See views_ajax(). $path = isset($_REQUEST['view_path']) ? $_REQUEST['view_path'] : NULL; if (arg(0, $path) == 'og' && is_numeric(arg(2, $path))) { $group_node = og_set_theme(arg(2, $path)); } elseif (arg(0, $path) == 'node' && is_numeric(arg(1, $path))) { $group_node = og_set_theme(arg(1, $path)); } elseif (arg(0, $path) == 'node' && arg(1, $path) == 'add' && arg(2, $path) == 'book' && arg(3, $path) == 'parent') { $group_node = og_set_theme(arg(4, $path)); $_REQUEST['edit']['og_groups'][] = $group_node->nid; // checks the right box on node form } elseif (arg(0, $path) == 'node' && arg(1, $path) == 'add' && isset($_REQUEST['gids'])) { $gid = intval(current($_REQUEST['gids'])); $group_node = node_load($gid); $custom_theme = $group_node->og_theme; } elseif (arg(0, $path) == 'comment' && in_array(arg(1, $path), array('edit', 'delete')) && is_numeric(arg(2, $path))) { $nid = db_result(db_query('SELECT nid FROM {comments} WHERE cid = %d', arg(2, $path))); $group_node = og_set_theme($nid); } elseif (arg(0, $path) == 'comment' && is_numeric(arg(2, $path))) { // comment/reply/cid/nid URL pattern. $group_node = og_set_theme(arg(2, $path)); } elseif (arg(0, $path) == 'comment' && is_numeric(arg(2, $path))) { // comment/reply/cid/nid URL pattern. $group_node = og_set_theme(arg(2, $path)); } return og_set_group_context($group_node); } /** * API function for getting the group context (if any) for the current request. Used * for things like setting current theme and breadcrumbs. This context is set during og_determine_context() * * @return $node object */ function og_get_group_context() { return og_set_group_context(); } /** * API function for setting the group context for the current request. Modules may set this as needed. * This context is originally set during og_determine_context(). * * @return $node object */ function og_set_group_context($group_node = NULL) { static $stored_group_node; if (is_object($group_node) && og_is_group_type($group_node->type)) { $stored_group_node = $group_node; } return $stored_group_node; } function og_set_theme($nid) { global $custom_theme, $user; $node = node_load(intval($nid)); if (og_is_group_type($node->type)) { if (!$custom_theme) { $custom_theme = $node->og_theme; } return $node; } elseif (isset($node->og_groups)) { switch (count($node->og_groups)) { case 0: return NULL; case 1: $group_node = node_load(current($node->og_groups)); if (!$custom_theme) { $custom_theme = $group_node->og_theme; } break; default: // Node is in multiple groups. Preference goes to the group we showed on the prior page view (if any), // Then to a group the current user is a member of. if (isset($_SESSION['og_last']) && in_array($_SESSION['og_last']->nid, $node->og_groups)) { $group_node = node_load($_SESSION['og_last']->nid); if (!$custom_theme) { $custom_theme = $group_node->og_theme; } } else { $groups = array(); // intersect the node's groups with the user's groups if ($user->uid) { $groups = array_intersect($node->og_groups, array_keys($user->og_groups)); } // no user is logged in, or none of the node's groups are the user's groups if (empty($groups)) { $groups = $node->og_groups; } // use array_shift and not [0] because array_intersect preserves keys $group_node = node_load(array_shift($groups)); if (!$custom_theme) { $custom_theme = $group_node->og_theme; } } } return $group_node; } } /** * Admins may broadcast messages to all their members. * * @ingroup forms * @param $node * The group node. */ function og_broadcast_form($form_state, $node) { drupal_set_title(t('Send message to %group', array('%group' => $node->title))); if (!empty($form_state['post'])) { drupal_set_message(t('Your message will be sent to all members of this group.')); } $form['subject'] = array( '#type' => 'textfield', '#title' => t('Subject'), '#size' => 70, '#maxlength' => 250, '#description' => t('Enter a subject for your message.'), '#required' => TRUE, ); $form['body'] = array( '#type' => 'textarea', '#title' => t('Body'), '#rows' => 5, '#cols' => 90, '#description' => t('Enter a body for your message.'), '#required' => TRUE ); $form['send'] = array('#type' => 'submit', '#value' => t('Send message')); $form['gid'] = array('#type' => 'value', '#value' => $node->nid); return $form; } function og_broadcast_form_submit($form, &$form_state) { global $user; $sql = og_list_users_sql(1); $result = db_query($sql, $form_state['values']['gid']); $recipients = array(); while ($row = db_fetch_object($result)) { $recipients[] = $row->uid; } $node = node_load($form_state['values']['gid']); $variables = array( '@group' => $node->title, '@subject' => $form_state['values']['subject'], '@body' => $form_state['values']['body'], '@site' => variable_get('site_name', 'drupal'), '!url_group' => url("node/$node->nid", array('absolute' => TRUE)), '!url_unsubscribe' => url("og/unsubscribe/$node->nid/$user->uid", array('absolute' => TRUE)) ); $message = array( 'from' => $user, 'subject' => $form_state['values']['subject'], 'body' => _og_mail_text('og_admin_email_body', $variables) ); // Send notifications to each member; Sending an array of recipients implies // that this is a bulk message. module_invoke_all('og', 'user broadcast', $node->nid, $recipients, $message); drupal_set_message(format_plural(count($recipients), '1 message queued for delivery.', '@count messages queued for delivery.')); } function og_manage($group_node) { global $user; $bc = og_get_breadcrumb($group_node); drupal_set_breadcrumb($bc); return drupal_get_form('og_manage_form', $group_node); } function og_manage_form($form_state, $group) { global $user; // avoid double messages on form submit if (!$form_state['post']) { // group manager can't leave if ($group->og_selective == OG_CLOSED) { drupal_set_message(t('You may not leave this group because it is a closed group. You should request removal from a group administrator.')); } elseif ($group->uid == $user->uid) { drupal_set_message(t('You may not leave this group because you are its owner. A site administrator can assign ownership to another user and then you may leave.')); } else { $links[] = l(t('Leave this group'), "og/unsubscribe/$group->nid/$user->uid"); $form['unsubscribe'] = array('#value' => theme('item_list', $links)); } } $form['gid'] = array('#type' => 'value', '#value' => $group->nid); return $form; } function og_manage_form_submit($form, &$form_state) { global $user; $passed_values = $form_state['values']; unset($passed_values['gid'], $passed_values['op'], $passed_values['form_id'], $passed_values['form_build_id'], $passed_values['form_token']); og_save_subscription($form_state['values']['gid'], $user->uid, $passed_values); drupal_set_message(t('Membership saved.')); } /** * Low level function for managing membership * * @param $gid node ID of a group * @param $uid user ID of user * @param $args an array with details of this membership. Recognized array keys are: is_active, is_admin, created. Other values are passed to hook implementations. */ function og_save_subscription($gid, $uid, $args = array()) { $sql = "SELECT COUNT(*) FROM {og_uid} WHERE nid = %d AND uid = %d"; $cnt = db_result(db_query($sql, $gid, $uid)); $time = time(); $subscription = array( 'nid' => $gid, 'uid' => $uid, 'created' => isset($args['created']) ? $args['created'] : $time, 'changed' => $time ); unset($args['created']); $subscription += $args; if ($cnt == 0) { drupal_write_record('og_uid', $subscription); module_invoke_all('og', 'user insert', $gid, $uid, $args); } else { drupal_write_record('og_uid', $subscription, array('nid', 'uid')); module_invoke_all('og', 'user update', $gid, $uid, $args); } } function og_delete_subscription($gid, $uid){ $sql = "DELETE FROM {og_uid} WHERE nid = %d AND uid = %d"; db_query($sql, $gid, $uid); module_invoke_all('og', 'user delete', $gid, $uid, array()); } function og_approve($node, $account, $token) { if (!og_check_token($token, $node->nid)) { drupal_set_message(t('Bad token. You seem to have followed an invalid link.'), 'error'); drupal_access_denied(); return; } if (og_is_group_member($node, FALSE, $account->uid)) { drupal_set_message(t('!name already approved to group %group.', array('!name' => theme('username', $account), '%group' => $node->title)), 'error'); return ''; } else { og_save_subscription($node->nid, $account->uid, array('is_active' => 1)); drupal_set_message(t('Membership request approved.')); $variables = array( '@title' => $node->title, '!group_url'=> url("node/$node->nid", array('absolute' => TRUE)) ); $message = array( 'subject' => _og_mail_text('og_approve_user_subject', $variables), 'body' => _og_mail_text('og_approve_user_body', $variables) ); module_invoke_all('og', 'user approve', $node->nid, $uid, $message); drupal_goto("node/$node->nid"); } } function og_deny($node, $account, $token) { if (!og_check_token($token, $node->nid)) { drupal_set_message(t('Bad token. You seem to have followed an invalid link.'), 'error'); drupal_access_denied(); return; } og_delete_subscription($node->nid, $account->uid); drupal_set_message(t('Membership request denied.')); $variables = array( '@title' => $node->title, '!group_url' => url("node/$node->nid", array('absolute' => TRUE)) ); $message = array( 'subject' => _og_mail_text('og_deny_user_subject', $variables), 'body' => _og_mail_text('og_deny_user_body', $variables) ); module_invoke_all('og', 'user deny', $node->nid, $uid, $message); drupal_goto("node/$node->nid"); } /** * OG create admin form */ function og_create_admin_confirm($form_state, $node, $account) { $form['node'] = array('#type' => 'value', '#value' => $node); $form['account'] = array('#type' => 'value', '#value' => $account); return confirm_form($form, t('Are you sure you want to make %name a group administrator for the group %title?', array('%name' => $account->name, '%title' => $node->title)), "og/users/$node->nid", ' ', t('Confirm'), t('Cancel')); } /** * Confirm og create admin form */ function og_create_admin_confirm_submit($form, &$form_state) { $account = $form_state['values']['account']; $node = $form_state['values']['node']; og_save_subscription($node->nid, $account->uid, array('is_admin' => 1)); drupal_set_message(t('%name was promoted to group administrator.', array('%name' => $account->name))); $variables = array( '@group' => $node->title, '!group_url' => url("node/$node->nid", array('absolute' => TRUE)), '@username' => $account->name ); $message = array( 'subject' => _og_mail_text('og_new_admin_subject', $variables), 'body' => _og_mail_text('og_new_admin_body', $variables) ); module_invoke_all('og', 'admin create', $node->nid, $account->uid, $message); $form_state['#redirect'] = "og/users/$node->nid"; } /** * OG remove admin form */ function og_remove_admin_confirm($form_state, $node, $account) { $form['gid'] = array('#type' => 'value', '#value' => $node->nid); $form['account'] = array('#type' => 'value', '#value' => $account); return confirm_form($form, t('Are you sure you want to remove %name as a group administrator for the group %title?', array('%name' => $account->name, '%title' => $node->title)), "og/users/$node->nid", ' ', t('Remove'), t('Cancel') ); } /** * Confirm og remove admin form */ function og_remove_admin_confirm_submit($form, &$form_state) { $account = $form_state['values']['account']; $gid = $form_state['values']['gid']; og_save_subscription($gid, $account->uid, array('is_admin' => 0)); drupal_set_message(t('%name is no longer a group administrator.', array('%name' => $account->name))); $form_state['redirect'] = "og/users/$gid"; } function og_invite_form($form_state, $node) { $bc = og_get_breadcrumb($node); drupal_set_breadcrumb($bc); $max = variable_get('og_email_max', 10); $form['mails'] = array( '#type' => 'textarea', '#title' => t('Email addresses or usernames'), '#description' => t('Enter up to %max email addresses or usernames. Separate multiple addresses by commas or new lines. Each person will receive an invitation message from you.', array('%max' => $max)) ); $form['pmessage'] = array( '#type' => 'textarea', '#title' => t('Personal message'), '#description' => t('Optional. Enter a message which will become part of the invitation email.') ); $form['op'] = array('#type' => 'submit', '#value' => t('Send invitation')); $form['gid'] = array ('#type' => 'value', '#value' => $node->nid); return $form; } // TODOL Use #element_validate as per http://drupal.org/node/144132#element-validate function og_invite_form_validate($form, &$form_state) { global $user; $max = variable_get('og_email_max', 10); $mails = $form_state['values']['mails']; $mails = str_replace("\n", ',', $mails); $emails = explode(',', $mails); if (count($emails) > $max) { form_set_error('mails', t('You may not specify more than %max email addresses or usernames.', array('%max' => $max))); } elseif (in_array($user->mail, $emails)) { form_set_error('mails', t('You may not invite yourself - @self.', array('@self' => $user->mail))); } else { $valid_emails = array(); $bad = array(); foreach ($emails as $email) { $email = trim($email); if (empty($email)) { continue; } if (valid_email_address($email)) { $valid_emails[] = $email; } else { $account = user_load(array('name' => check_plain($email))); if ($account->mail) { $valid_emails[] = $account->mail; } else { $bad[] = $email; } } } if (count($bad)) { form_set_error('mails', t('Invalid email address or username: @value.', array('@value' => implode(', ', $bad)))); } else { // Store valid e-mails so we don't have to go through that looping again on submit $form_state['valid_emails'] = $valid_emails; } } } function og_invite_form_submit($form, &$form_state) { $emails = $form_state['valid_emails']; $node = node_load($form_state['values']['gid']); $variables = array( '@group' => $node->title, '@description' => $node->og_description, '@site' => variable_get('site_name', 'drupal'), '!group_url' => url("og/subscribe/$node->nid", array('absolute' => TRUE)), '@body' => $form_state['values']['pmessage'], ); global $user; $from = $user->mail; foreach ($emails as $mail) { drupal_mail('og', 'invite_user', $mail, $GLOBALS['language'], $variables, $from); } drupal_set_message(format_plural(count($emails), '1 invitation sent.', '@count invitations sent.')); } /** * Implementation of hook_mail(). */ function og_mail($key, &$message, $params) { $language = $message['language']; $message['subject'] .= _og_mail_text($key .'_subject', $params, $language); $message['body'][] = _og_mail_text($key .'_body', $params, $language); } function og_subscribe($node, $uid = NULL) { global $user; if (is_null($uid)) { if ($user->uid) { $account = $user; } else { drupal_set_message(t('In order to join this group, you must login or register a new account. After you have successfully done so, you will need to request membership again.')); drupal_goto('user'); } } else { $account = user_load(array('uid' => $uid)); } if ($node->og_selective >= OG_INVITE_ONLY || $node->status == 0 || !og_is_group_type($node->type)) { drupal_access_denied(); exit(); } // Only admins can add another member. if ($account->uid != $user->uid && !og_is_group_admin($node)) { drupal_access_denied(); exit(); } // User is already a member of this group, redirect to group homepage. else if (isset($account->og_groups[$node->nid])) { drupal_set_message(t('@user is already a member the group @group.', array('@user' => $account->name, '@group' => $node->title))); drupal_goto('node/'. $node->nid); } else { return drupal_get_form('og_confirm_subscribe', $node->nid, $node, $account); } } /** * Confirm og membership form */ function og_confirm_subscribe($form_state, $gid, $node, $account) { $form['gid'] = array('#type' => 'value', '#value' => $gid); $form['account'] = array('#type' => 'value', '#value' => $account); if ($node->og_selective == OG_MODERATED) { $form['request'] = array( '#type' => 'textarea', '#title' => t('Additional details'), '#description' => t('Add any detail which will help an administrator decide whether to approve or deny your membership request.') ); } else { $form['request'] = array( '#type' => 'value', '#value' => '', ); } return confirm_form($form, t('Are you sure you want to join the group %title?', array('%title' => $node->title)), 'node/'. $node->nid, ' ', t('Join'), t('Cancel')); } /** * Confirm og membership submit handler */ function og_confirm_subscribe_submit($form, &$form_state) { $return = og_subscribe_user($form_state['values']['gid'], $form_state['values']['account'], $form_state['values']['request']); if (!empty($return['message'])) { drupal_set_message($return['message']); } $form_state['redirect'] = 'node/'. $form_state['values']['gid']; } /** * Create a new membership for a given user to given group. Edits to membership should * go through og_save_subscription(). No access control since this is an API function. * * @return string 'approval', 'subscribed' or 'rejected' depending on the group's configuration. **/ function og_subscribe_user($gid, $account, $request = NULL) { // moderated groups must approve all members (selective=1) $node = node_load($gid); switch ($node->og_selective) { case OG_MODERATED: $admins = array(); og_save_subscription($gid, $account->uid, array('is_active' => 0)); $sql = og_list_users_sql(1, 1, NULL); $res = db_query($sql, $node->nid); $admins = array(); while ($row = db_fetch_object($res)) { $admins[] = $row->uid; } if (!empty($admins)) { // Prepend user's request text with standard cruft. Should be a // variable but that's a bit annoying. if ($request) { $request = t("\n\nPersonal message from @name:\n------------------\n\n@request", array('@name' => $account->name, '@request' => $request)); } $variables = array( '@group' => $node->title, '@username' => $account->name, '!approve_url' => url("og/approve/$node->nid/$account->uid", array('absolute' => TRUE)), '!group_url' => url("og/users/$node->nid", array('absolute' => TRUE)), '@request' => $request, ); $message = array( 'subject' => _og_mail_text('og_request_user_subject', $variables), 'body' => _og_mail_text('og_request_user_body', $variables) . $request ); // Send notifications to each admin; Sending an array of recipients // implies that this is a bulk message. module_invoke_all('og', 'user request', $gid, $admins, $message); } $return_value = array('type' => 'approval', 'message' => t('Membership request to the %group group awaits approval by an administrator.', array('%group' => $node->title))); break; case OG_OPEN: og_save_subscription($gid, $account->uid, array('is_active' => 1)); $return_value = array('type' => 'subscribed', 'message' => t('You are now a member of the %group.', array('%group' => $node->title))); break; case OG_CLOSED: case OG_INVITE_ONLY: // admins can add members to these groups, but others can't. if (og_is_group_admin($node)) { og_save_subscription($gid, $account->uid, array('is_active' => 1)); } else { $return_value = array('type' => 'rejected', 'message' => t('Membership request to the %group group was rejected, only group administrators can add users to this group.', array('%group' => $node->title))); } } return $return_value; } /** * Confirm og unsubscription form */ function og_confirm_unsubscribe($form_state, $group_node, $account) { $form['group_node'] = array('#type' => 'value', '#value' => $group_node); $form['account'] = array('#type' => 'value', '#value' => $account); return confirm_form($form, t('Are you sure you want to remove !name from the group %title?', array('!name' => theme('username', $account), '%title' => $group_node->title)), 'og/users/'. $group_node->nid, ' ', t('Remove'), t('Cancel')); } /** * Confirm og unsubscription submit handler */ function og_confirm_unsubscribe_submit($form, &$form_state) { global $user; $group_node = $form_state['values']['group_node']; $account = $form_state['values']['account']; og_delete_subscription($group_node->nid, $account->uid); // If needed, reload user object to reflect unsubscribed group. if ($user->uid == $account->uid) { og_get_subscriptions($account->uid, 1, TRUE); // Clear cache. $user = user_load(array('uid' => $user->uid)); } drupal_set_message(t('%user removed from %group.', array('%user' => $account->name, '%group' => $group_node->title))); // Determine where to go next. GHP if accessible, or else site front page. $form_state['redirect'] = node_access('view', $group_node) ? "node/". $group_node->nid : ''; } /** * Load all memberships for a given user. * * Since a user's memberships are loaded into $user object, this function is only occasionally * useful to get group memberships for users other than the current user. Even * then, it often makes sense to call user_load() instead of this function. * * @return array **/ function og_get_subscriptions($uid, $min_is_active = 1, $reset = FALSE) { static $subscriptions = array(); if ($reset) { unset($subscriptions[$uid]); } if (!isset($subscriptions[$uid][$min_is_active])) { list($types, $in) = og_get_sql_args(); array_unshift($types, $min_is_active); array_unshift($types, $uid); $sql = "SELECT n.title, n.type, n.status, ou.* FROM {og_uid} ou INNER JOIN {node} n ON ou.nid = n.nid WHERE ou.uid = %d AND ou.is_active >= %d AND n.type $in ORDER BY n.title"; $result = db_query($sql, $types); while ($row = db_fetch_array($result)) { $subscriptions[$uid][$min_is_active][$row['nid']] = $row; } if (!isset($subscriptions[$uid][$min_is_active])) { $subscriptions[$uid][$min_is_active] = array(); } } return $subscriptions[$uid][$min_is_active]; } function og_list_users_sql($min_is_active = 1, $min_is_admin = 0, $orderby='u.name ASC', $count = FALSE) { $order = ''; if ($count) { $fields = 'COUNT(*)'; } else { $fields = "u.uid, u.name, u.mail, u.picture, ou.*"; if ($orderby) { $order = "ORDER BY $orderby"; } } return "SELECT $fields FROM {og_uid} ou INNER JOIN {users} u ON ou.uid = u.uid WHERE ou.nid = %d AND u.status > 0 AND ou.is_active >= $min_is_active AND ou.is_admin >= $min_is_admin $order"; } function og_add_users($form_state, $group_node) { $form['og_names'] = array( '#type' => 'textarea', '#title' => t('List of users'), '#rows' => 5, '#cols' => 70, // No autocomplete b/c user_autocomplete can't handle commas like taxonomy. pls improve core. // '#autocomplete_path' => 'user/autocomplete', '#description' => t('Add one or more usernames in order to associate users with this group. Multiple usernames should be separated by a comma.'), '#element_validate' => array('og_add_users_og_names_validate'), ); $form['op'] = array('#type' => 'submit', '#value' => t('Add users')); $form['gid'] = array('#type' => 'value', '#value' => $group_node->nid); return $form; } // An #element_validate handler function og_add_users_og_names_validate($form, $form_state) { $names = explode(',', $form_state['values']['og_names']); foreach ($names as $name) { $account = user_load(array('name' => trim($name))); if (isset($account->uid)) { $accounts[] = $account; $uids[] = $account->uid; } else { $bad[] = check_plain($name); $err = TRUE; } } if (isset($err)) { form_set_error('og_names', format_plural(count($bad), 'Unrecognized name: %bad.', 'Unrecognized names: %bad.', array('%bad' => implode(', ', $bad)))); } } function og_add_users_submit($form, &$form_state) { // Safest option is to do a select, filter existing members, then insert. $names = explode(',', $form_state['values']['og_names']); foreach ($names as $name) { $account = user_load(array('name' => trim($name))); if ($account->uid) { $accounts[] = $account; } } foreach ($accounts as $account) { og_save_subscription($form_state['values']['gid'], $account->uid, array('is_active' => 1)); } drupal_set_message(format_plural(count($accounts), '1 user added to the group.', '@count users added to the group.')); } // TODOL: use Views for opml page. function og_opml() { $output = "\n"; $output .= "\n"; $output .= "\n"; $output .= ''. check_plain(variable_get('site_name', 'Drupal')) ."\n"; $output .= ''. gmdate('r') ."\n"; $output .= "\n"; $output .= "\n"; global $user; foreach ($user->og_groups as $gid => $group) { $output .= ' TRUE)) ."\" />\n"; } $output .= "\n"; $output .= "\n"; drupal_set_header('Content-Type: text/xml; charset=utf-8'); print $output; } function og_page_activity() { $sql = "SELECT oga.group_nid, node_group_nid.title, node_group_nid.uid, u.name, COUNT(*) as ncount, MAX(n.created) as ncreated, SUM(ncs.comment_count) as ccount, MAX(last_comment_timestamp) AS lct FROM {node} n INNER JOIN {og_ancestry} oga ON n.nid = oga.nid LEFT JOIN {node_comment_statistics} ncs ON n.nid = ncs.nid INNER JOIN {node} node_group_nid ON oga.group_nid = node_group_nid.nid INNER JOIN {users} u ON node_group_nid.uid = u.uid WHERE n.status = 1 GROUP BY oga.group_nid, node_group_nid.title, node_group_nid.uid, u.name"; $header = array( array('data' => t('Title'), 'field' => 'node_group_nid.title'), array('data' => t('Manager'), 'field' => 'u.name'), array('data' => t('Posts'), 'field' => 'ncount'), array('data' => t('Comments'), 'field' => 'ccount'), array('data' => t('Age'), 'field' => 'node_group_nid.created'), array('data' => t('Last comment'), 'field' => 'lct', 'sort' => 'asc'), ); $result = db_query($sql. tablesort_sql($header)); while ($row = db_fetch_object($result)) { $rows[] = array( l($row->title, "node/$row->group_nid"), theme('username', $row), $row->ncount, $row->ccount, format_interval(time()-$row->ncreated), format_interval(time()-$row->lct), ); } if (!isset($rows)) { $rows[] = array(array('data' => t('No groups available.'), 'colspan' => 6)); } return theme('table', $header, $rows); } /** * When you view a group, you really see some facts about the group in a block and then * lists of nodes affiliated with that group (requires og_views module). The node list is provided by the View of * your choice (see the variable 'og_home_page_view'). If you use og_panels.module and the group has defined * a default home page, then that page becomes the presentation of the GHP. * * @return void * Add changes to $node->content by reference. **/ // function og_view_group(&$node, $teaser = FALSE, $page = FALSE) { if ($teaser || !$page) { $node->content['og_description'] = array( '#type' => 'item', '#title' => t('Description'), '#value' => $node->og_description, ); } else { // See http://drupal.org/files/issues/bc-fixup-204415-50.patch for an alternate way $bc = og_get_breadcrumb($node); array_pop($bc); drupal_set_breadcrumb($bc); unset($node->content['body']); $node->content['og_mission'] = array( '#value' => $node->body, // node_prepare() already ran check_markup() '#node' => $node, '#weight' => -3, '#theme' => 'og_mission', ); } } function og_home_empty($node) { global $user; $dest = drupal_get_destination(); if (og_is_group_member($node->nid)) { $msg = t('No posts in this group.'); } else { $msg = t('No public posts in this group.'); if (!$user->uid) { $msg .= ' '. t('You must register or login and become a member in order to post messages, and view any private posts.', array('!register' => url("user/register", array('query' => $dest)), '!login' => url("user/login", array('query' => $dest)))); } // TODOL: hide this from pending members too elseif ($node->og_selective < OG_INVITE_ONLY) { $msg .= ' '. t('Consider joining this group in order to view its posts.', array('!url' => url("og/subscribe/$node->nid", array('query' => $dest)))); } } drupal_set_message($msg); } function og_selective_map() { return array( OG_OPEN => t('Open'), OG_MODERATED => t('Moderated'), OG_INVITE_ONLY => t('Invite only'), OG_CLOSED => t('Closed'), ); } /** * Adds standard fields for any node configured to be a group node. * * @param object $node */ function og_group_form($node, $form_state) { global $user; // Set the default values for a new item. By using += rather than =, we // only overwrite array keys that have not yet been set. It's safe to use // on both an empty array, and an incoming array with full or partial data. $node = (array)$node; $node += array( 'og_description' => NULL, 'og_theme' => NULL, 'og_language' => NULL, 'nid' => NULL, ); $node = (object)$node; $form['og_description'] = array( '#type' => 'textfield', '#title' => t('Description'), '#default_value' => $node->og_description, '#size' => 70, '#maxlength' => 150, '#required' => TRUE, '#description' => t('A brief description for the group details block and the group directory.'), '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_description') : -4, ); $default = isset($node->nid) ? $node->og_selective : OG_OPEN; $options = array( t('Open - membership requests are accepted immediately.'), t('Moderated - membership requests must be approved.'), t('Invite only - membership must be created by an administrator.'), t('Closed - membership is exclusively managed by an administrator.'), ); $form['og_selective'] = array( '#type' => 'radios', '#title' => t('Membership requests'), '#required' => TRUE, '#default_value' => $default, '#options' => $options, '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_selective') : 0, '#description' => t('How should membership requests be handled in this group? When you select closed, users will not be able to join or leave.') ); // registration checkbox // get the visibility for normal users $visibility = variable_get('og_visibility_registration', OG_REGISTRATION_CHOOSE_FALSE); // admin can always choose registration checkbox - get right default if (user_access('administer nodes')) { $visibility = in_array($visibility, array(OG_REGISTRATION_NEVER, OG_REGISTRATION_CHOOSE_FALSE)) ? OG_REGISTRATION_CHOOSE_FALSE : OG_REGISTRATION_CHOOSE_TRUE; } $default = FALSE; switch ($visibility) { case OG_REGISTRATION_NEVER: $form['og_register'] = array('#type' => 'value', '#value' => 0); break; case OG_REGISTRATION_ALWAYS: $form['og_register'] = array('#type' => 'value', '#value' => 1); break; case OG_REGISTRATION_CHOOSE_TRUE: $default = TRUE; // fall through case OG_REGISTRATION_CHOOSE_FALSE: $form['og_register'] = array( '#type' => 'checkbox', '#title' => t('Registration form'), '#default_value' => $node->nid ? $node->og_register : $default, '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_register') : 0, '#description' =>t('May users join this group during registration? If checked, a corresponding checkbox will be added to the registration form.'), ); break; } // directory checkbox $visibility = variable_get('og_visibility_directory', OG_DIRECTORY_CHOOSE_TRUE); // override for admins - get right default if (user_access('administer nodes')) { $visibility = in_array($visibility, array(OG_DIRECTORY_NEVER, OG_DIRECTORY_CHOOSE_FALSE)) ? OG_DIRECTORY_CHOOSE_FALSE : OG_DIRECTORY_CHOOSE_TRUE; } $default = FALSE; switch ($visibility) { case OG_DIRECTORY_NEVER: $form['og_directory'] = array('#type' => 'value', '#value' => 0); break; case OG_DIRECTORY_ALWAYS: $form['og_directory'] = array('#type' => 'value', '#value' => 1); break; case OG_DIRECTORY_CHOOSE_TRUE: $default = TRUE; // fall through case OG_DIRECTORY_CHOOSE_FALSE: $form['og_directory'] = array( '#type' => 'checkbox', '#title' => t('List in groups directory'), '#default_value' => $node->nid ? $node->og_directory : $default, '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_directory') : 0, '#description' => t('Should this group appear on the list of groups page (requires OG Views module)? Disabled if the group is set to private group.', array('@url' => url('og')))); break; } if (module_exists('locale') && $languages = locale_language_list()) { if (count($languages) > 1) { $form['og_language'] = array( '#type' => 'radios', '#title' => t('Group language'), '#default_value' => $node->og_language, '#options' => array('' => t('Language neutral')) + $languages, '#weight' => module_exists('content') ? content_extra_field_weight($node->type, 'og_language') : 0, '#description' => t('Selecting a different locale will change the interface language for all group pages and emails. Users who have chosen a preferred language always see their chosen language.'), ); } } if ($theme_form = system_theme_select_form(t('Selecting a different theme will change the look and feel of the group.'), isset($form_state['values']['theme']) ? $form_state['values']['theme'] : $node->og_theme, 2)) { $theme_form['themes']['#weight'] = 8; $form += $theme_form; } return $form; } // Returns all the group affiliations for a given node. function og_get_node_groups($node) { $groups = array(); if (!og_is_group_type($node->type)) { $result = og_get_node_groups_result($node->nid); while ($row = db_fetch_object($result)) { $groups[$row->group_nid] = $row->title; } return $groups; } } // The query for the get_node_groups function. Is reused in og.views.inc function og_get_node_groups_result($nid) { // We do not run db_rewrite_sql() here since we need to know about groups that the user cannot access as well (i.e. node edit). $sql = "SELECT oga.group_nid, n.title FROM {node} n INNER JOIN {og_ancestry} oga ON n.nid = oga.group_nid WHERE oga.nid = %d"; return db_query($sql, $nid); } function og_presave_group(&$node) { if (!empty($node->og_groups_inaccessible)) { // Add the inaccessible groups which did not show in Audience selector $node->og_groups = (array)$node->og_groups + $node->og_groups_inaccessible; } /** * Change $node->theme to $node->og_theme so it matches node_load(). The node form uses $theme, not $og_theme. * If author chose the default theme, then '' is written to DB and group follows changes made by site admin. */ if (isset($node->theme)) { $node->og_theme = $node->theme; } else { $node->og_theme = NULL; } // Normalize og_groups array if (isset($node->og_groups)) { $node->og_groups = array_filter($node->og_groups); $node->og_groups = array_keys($node->og_groups); } // Support devel module's bulk node generation. // Affiliate group posts with group(s). Also populate special group fields. if (isset($node->devel_generate)) { og_devel_generate($node); } } function og_devel_generate(&$node) { if (og_is_group_type($node->type)) { $node->og_selective = rand(0,3); $can_join = $node->og_selective <= OG_MODERATED; $open_join = $node->og_selective < OG_MODERATED; $node->og_register = $can_join ? rand(0,1) : FALSE; $node->og_directory = $can_join ? rand(0,1) :FALSE; $node->og_private = !$open_join ? rand(0,1) : FALSE; // Ignored if og_access not installed. $node->og_description = devel_create_greeking(rand(1, 20), TRUE); $node->og_notification = rand(0,1); $node->og_language = NULL; if (module_exists('locale') && $languages = locale_language_list()) { if (count($languages) > 1) { $node->og_language = array_rand($languages); } } } elseif (og_is_group_post_type($node->type)) { $types = og_get_types('group'); $placeholders = db_placeholders($types, 'varchar'); $sql = "SELECT nid FROM {node} WHERE type IN ($placeholders) AND status = 1 ORDER BY RAND()"; $result = db_query_range($sql, $types, 0, rand(1,4)); while ($row = db_fetch_object($result)) { $node->og_groups[] = $row->nid; } $node->og_public = rand(0,1); // Ignored if og_access not installed. } } function og_load_group(&$node) { $sql = 'SELECT * FROM {og} WHERE nid = %d'; $result = db_query($sql, $node->nid); $node = (object) array_merge((array)$node, (array)db_fetch_array($result)); } function og_insert_group($node) { drupal_write_record('og', $node); } function og_update_group($node) { // If an existing node becomes a group, then a row may not be present in {og} table. $sql = "SELECT nid FROM {og} WHERE nid = %d"; if (db_result(db_query($sql, $node->nid))) { drupal_write_record('og', $node, array('nid')); } else { og_insert_group($node); } } // Return a breadcrumb array for a given groupnode. function og_get_breadcrumb($group_node) { $bc[] = l(t('Home'), ""); if (module_exists('og_views')) { $bc[] = l(t('Groups'), "og"); } $bc[] = l($group_node->title, "node/$group_node->nid"); return $bc; } /** * Implementation of hook_nodeapi(). * */ function og_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { global $user; switch ($op) { case 'view': $group_node = og_get_group_context(); if ($group_node && $page && !empty($node->og_groups)) { $bc = og_get_breadcrumb($group_node); drupal_set_breadcrumb($bc); } if (og_is_group_type($node->type)) { og_view_group($node, $teaser, $page); } break; case 'load': if (og_is_group_type($node->type)) { og_load_group($node); } elseif ($grps = og_get_node_groups($node)) { // TODO: Refactor so we don't need 2 arrays. $node->og_groups = drupal_map_assoc(array_keys($grps)); $node->og_groups_both = $grps; $public = db_result(db_query_range("SELECT is_public FROM {og_ancestry} WHERE nid = %d", $node->nid, 0, 1)); $node->og_public = $public ? TRUE : FALSE; } else { $node->og_groups = $node->og_groups_both = array(); } break; case 'validate': // Ensure that a group is selected if groups are required. needed when // author has no groups. In other cases, fapi does the validation. if (og_is_group_post_type($node->type) && variable_get('og_audience_required', FALSE) && !user_access('administer nodes')) { if (!isset($node->og_groups)) { form_set_error('title', t('You must join a group before posting on this web site.', array('@join' => url('og')))); } } break; case 'presave': og_presave_group($node); break; case 'delete': $sql = "DELETE FROM {og} WHERE nid = %d"; db_query($sql, $node->nid); $sql = "DELETE FROM {og_ancestry} WHERE nid = %d"; db_query($sql, $node->nid); $sql = "DELETE FROM {og_uid} WHERE nid = %d"; db_query($sql, $node->nid); break; case 'insert': if (og_is_group_type($node->type)) { og_insert_group($node); // Make sure the node owner is a full powered member. og_save_subscription($node->nid, $node->uid, array('is_active' => 1, 'is_admin' => 1)); // Load new group into $user->og_groups so that author can get redirected to the new group if ($node->uid == $user->uid) { $user->og_groups = og_get_subscriptions($node->uid, 1, TRUE); } $account = user_load(array('uid' => $node->uid)); $variables = array( '@group' => $node->title, '!group_url' => url("node/$node->nid", array('absolute' => TRUE)), '@username' => $account->name, '!invite_url' => url("og/invite/$node->nid", array('absolute' => TRUE)) ); $message = array( 'subject' => _og_mail_text('og_new_admin_subject', $variables), 'body' => _og_mail_text('og_new_admin_body', $variables) ); // Skip the alert if we are auto-generating nodes. if (empty($node->devel_generate)) { // Alert the user that they are now the admin of the group. module_invoke_all('og', 'admin new', $node->nid, $account->uid, $message); } } else { og_save_ancestry($node); } break; case 'update': if (og_is_group_type($node->type)) { og_update_group($node); // make sure the node owner is a full powered member og_save_subscription($node->nid, $node->uid, array('is_active' => 1, 'is_admin' => 1)); // load new group into $user->og_groups so that author can get redirected to the new group if ($node->uid == $user->uid) { $user->og_groups = og_get_subscriptions($user->uid, 1, TRUE); } } else { og_save_ancestry($node); } break; case 'search result': // Similar code in og_preprocess_node() $current_groups['accessible'] = array(); if ($node->og_groups) { $current_groups = og_node_groups_distinguish($node->og_groups_both, FALSE); } return format_plural(count($current_groups['accessible']), '1 group', '@count groups'); // TODOL: bad formatting. commented out. // foreach ($current_groups['accessible'] as $gid => $item) { // $og_links['og_'. $gid] = array('title' => $item['title'], 'href' => "node/$gid"); // } // return theme('links', $og_links, array('class' => 'groups links')); break; case 'rss item': if ($node->og_groups) { foreach ($node->og_groups_both as $gid => $title) { // TODO: should be absolute link. core bug. $append['og_links'] = array('title' => $title, 'href' => "node/$gid"); $ret[] = array('key' => 'group', 'value' => check_plain($title), 'attributes' => array( 'domain' => url("node/$gid", array('absolute' => TRUE)), 'xmlns' => 'http://drupal.org/project/og', ), ); } $node->body .= '
'. theme('links', $append). '
'; $node->teaser .= '
'. theme('links', $append). '
'; return $ret; } break; } } function og_msgid_server() { global $base_url; if ($dir = str_replace("/", ".", substr(strchr(str_replace("http://", "", $base_url), "/"), 1))) { $at = "@$dir.". $_SERVER['SERVER_NAME']; } else { $at = '@'. $_SERVER['SERVER_NAME']; } return strtolower($at); } function og_form_alter(&$form, &$form_state, $form_id) { // Add audience selection to node forms if (isset($form['#node']) && $form_id == $form['#node']->type .'_node_form') { $node = $form['#node']; if (og_is_group_type($node->type)) { $form = array_merge($form, og_group_form($node, $form_state)); // Don't trample on custom label. if ($form['body_field']['body']['#title'] == t('Body')) { $form['body_field']['body']['#title'] = t('Mission statement'); $form['body_field']['body']['#description'] = t('A welcome greeting for your group home page. Consider listing the group objectives and mission.'); } $form['author']['name']['#title'] = t('Group manager'); $form['options']['sticky']['#title'] = t('Sticky at top of group home page and other lists.'); } elseif (og_is_group_post_type($node->type)) { if ($group_node = og_get_group_context()) { $bc = og_get_breadcrumb($group_node); if (isset($node->nid)) { $bc[] = l($node->title, "node/$node->nid"); } drupal_set_breadcrumb($bc); } og_form_add_og_audience($form, $form_state); } } } /** * Implementation of hook_content_extra_fields. */ function og_content_extra_fields($type_name) { $extras = array(); if (og_is_group_post_type($type_name)) { $extra['og_nodeapi'] = array( 'label' => t('Groups'), 'description' => module_exists('og_access') ? t('OG audience & Public checkbox.') : t('OG audience.'), 'weight' => 0, ); } elseif (og_is_group_type($type_name)) { $extra['og_description'] = array( 'label' => t('Description'), 'description' => t('Group description.'), 'weight' => -4, ); $extra['og_selective'] = array( 'label' => t('Membership requests'), 'description' => t('Handling of group membership requests.'), ); $extra['og_register'] = array( 'label' => t('Registration form'), 'description' => t('Checkbox for visibility on registration form.'), ); $extra['og_directory'] = array( 'label' => t('List in groups directory'), 'description' => t('Checkbox for visibility in the groups directory.'), ); if (module_exists('locale') && $languages = locale_language_list()) { if (count($languages) > 1) { $extra['og_language'] = array( 'label' => t('Group language'), 'description' => t('The default interface language for this group.'), ); } } } return $extra; } function og_form_node_type_form_alter(&$form, &$form_state) { // Built in content types do not allow changes to type machine name. if (isset($form['identity']['type']['#default_value'])) { $usage = variable_get('og_content_type_usage_'. $form['identity']['type']['#default_value'], 'omitted'); } else { $usage = variable_get('og_content_type_usage_'. $form['identity']['type']['#value'], 'omitted'); } // Persist $usage so that we can rebuild node access as needed. $form['old_og_content_type_usage'] = array( '#type' => 'value', '#value' => $usage, ); // We push to the front so we can unset() variables before they are saved. array_unshift($form['#submit'], 'og_node_type_form_submit'); $options = og_types_map(); $form['og'] = array( '#type' => 'fieldset', '#title' => t('Organic groups'), '#collapsible' => TRUE, '#collapsed' => TRUE, '#access' => user_access('administer organic groups'), ); $form['og']['og_content_type_usage'] = array( '#type' => 'radios', '#title' => t('Organic groups usage'), '#default_value' => $usage, '#options' => $options, '#description' => t('Specify how organic groups should treat nodes of this type. Nodes may behave as a group, as group posts, or may not participate in organic groups at all.'), ); } // Add option to migrate messages before deleting a group. // TODO: add option to move memberships as well function og_form_node_delete_confirm_alter(&$form, &$form_state) { $node = node_load($form['nid']['#value']); if (og_is_group_type($node->type)) { og_node_delete_group_form($form); } elseif (og_is_group_post_type($node->type)) { og_node_delete_nongroup_form($form); } } // Rebuild node access if Usage has changed function og_node_type_form_submit($form, &$form_state) { $type = $form_state['values']['type']; $var = 'og_content_type_usage'; $new = $form_state['values'][$var]; $old = $form_state['values']['old_'. $var]; if ($new != $old) { node_access_needs_rebuild(); } // Prevent this old variable from being saved to DB. unset($form_state['values']['old_'. $var]); } // Form_alter() the node_delete form for a group function og_node_delete_group_form(&$form) { $options[OG_DELETE_NOTHING] = t('Do nothing.'); $options[OG_DELETE_ORPHANS] = t("Delete all group posts which don't also belong to another group."); if (user_access('administer nodes')) { $options[OG_DELETE_MOVE_NODES] = t('Move all group posts to the group listed below.'); $options[OG_DELETE_MOVE_NODES_MEMBERSHIPS] = t('Move all group posts and memberships to the group listed below.'); } $form['verb'] = array( '#type' => 'radios', '#title' => t('Group posts'), '#options' => $options, '#default_value' => OG_DELETE_NOTHING, '#weight' => -1, '#description' => t('In addition to deleting this group, you choose how to disposition the posts and memberships within it.') ); if (user_access('administer nodes')) { $options = og_all_groups_options(); unset($options[$form['nid']['#value']]); $form['target'] = array( '#type' => 'select', '#title' => t('Target group'), '#default_value' => 0, '#options' => $options, '#weight' => 0, '#description' => t('If you chose Move all group posts above, specify a destination group.'), ); // Register a submit handlers for moving child nodes and memberships. // Memberships should move before the group node is deleted. // Group nodes wait until afterwards so that our custom redirect works. array_unshift($form['#submit'], 'og_node_delete_move_memberships'); $form['#submit'][] = 'og_node_delete_confirm_submit'; } $form['actions']['submit']['#value'] = t('Delete group'); } // Alter the node_delete form for a non-group. // Redirect back to group home page after a delete. function og_node_delete_nongroup_form(&$form) { if ($groupnode = og_get_group_context()) { $form['#redirect'] = "node/$groupnode->nid"; } } // Return all node ids belonging to a group. No access control. // If you are retrieving for displaying, you may want to use an embedded View instead of this function. function og_group_child_nids($group_nid) { $result = db_query('SELECT oga.nid FROM {og_ancestry} oga WHERE oga.group_nid = %d', $group_nid); $child_nids = array(); while ($row = db_fetch_object($result)) { $child_nids[] = $row->nid; } return $child_nids; } // Submit handler for node delete form. Handles deletes to group nodes. function og_node_delete_confirm_submit($form, &$form_state) { $deleted_group_nid = $form_state['values']['nid']; $target_group_nid = $form_state['values']['target']; $delete_orphans = $form_state['values']['verb'] == OG_DELETE_ORPHANS; $move_children = $form_state['values']['verb'] >= OG_DELETE_MOVE_NODES; $count = 0; foreach (og_group_child_nids($deleted_group_nid) as $child_nid) { $node = node_load($child_nid); unset($node->og_groups[$deleted_group_nid]); if ($move_children) { // there is an array_unique() in og_save_ancestry which guards against duplicates so don't worry here. $node->og_groups[$target_group_nid] = $target_group_nid; } if ($delete_orphans && count($node->og_groups) == 0) { node_delete($node->nid); } else { node_save($node); } $count++; } if ($delete_orphans) { drupal_set_message(format_plural($count, 'Deleted 1 orphan post.', 'Deleted @count orphan posts.')); } elseif ($move_children) { drupal_set_message(format_plural($count, 'Moved 1 orphan post.', 'Moved @count orphan posts.')); } if ($move_children) { $form_state['redirect'] = 'node/'. $target_group_nid; } } // Submit handler for group node delete form. // Move memberships to target group after a deletion of a group node. No access control. function og_node_delete_move_memberships($form, &$form_state) { if ($form_state['values']['verb'] == OG_DELETE_MOVE_NODES_MEMBERSHIPS) { $deleted_group_nid = $form_state['values']['nid']; $target_group_nid = $form_state['values']['target']; $sql = og_list_users_sql(); $result = db_query($sql, $deleted_group_nid); $count = 0; while ($row = db_fetch_object($result)) { og_save_subscription($target_group_nid, $row->uid, array('is_active' => 1)); $count++; } drupal_set_message(format_plural($count, 'Moved 1 membership.', 'Moved @count memberships.')); } } // Return an array containing all groups - suitable for a form item. function og_all_groups_options() { list($types, $in) = og_get_sql_args(); $sql = "SELECT n.title, n.nid FROM {node} n WHERE n.type $in AND n.status = 1 ORDER BY n.title ASC"; $result = db_query($sql, $types); while ($row = db_fetch_object($result)) { $options[$row->nid] = $row->title; } return isset($options) ? $options : array(); } /** * Iterate over a set of groups and separate out those that are inaccessible to the current user. * Don't return groups of which the current user is a member, unless $exclude_joined=FALSE * is passed. Used in og_form_add_og_audience() and node.tpl.php. * * @return array * A two element array containing 'accessible' and 'inaccessible' keys. **/ function og_node_groups_distinguish($groups_names_both, $exclude_joined = TRUE) { global $user; $current_groups = array('accessible' => array(), 'inaccessible' => array()); if (empty($groups_names_both)) { // Do nothing. } else { $placeholders = db_placeholders($groups_names_both); $sql = 'SELECT n.nid FROM {node} n WHERE n.nid IN ('. $placeholders. ')'; $result = db_query(db_rewrite_sql($sql), array_keys($groups_names_both)); while ($row = db_fetch_object($result)) { $current_groups['accessible'][$row->nid]['title'] = $groups_names_both[$row->nid]; } foreach ($groups_names_both as $gid => $title) { if (!in_array($gid, array_keys($current_groups['accessible']))) { $current_groups['inaccessible'][$gid] = $gid; } } if ($exclude_joined) { // Don't return groups that the user has already joined (default). $current_groups['accessible'] = array_diff_assoc($current_groups['accessible'], $user->og_groups); } } return $current_groups; } /** * Helper method to add OG audience fields to a given form. This is * lives in a separate function from og_form_alter() so it can be shared * by other OG contrib modules. */ function og_form_add_og_audience(&$form, &$form_state) { global $user; // Determine the selected groups if FAPI doesn't tell us. if (isset($_GET['gids'])) { $gids = $_GET['gids']; } $node = $form['#node']; $required = variable_get('og_audience_required', 0) && !user_access('administer nodes'); $is_optgroup = FALSE; // Determine the list of groups that are shown. // Start by collecting all groups that the user is a member of. $subs = og_get_subscriptions($user->uid); $options = array(); foreach ($subs as $key => $val) { $options[$key] = $val['title']; } if (user_access('administer nodes')) { // Node admins see all of groups. $all = og_all_groups_options(); $other = array_diff_assoc($all, $options); // Use an optgroup if admin is not a member of all groups. if ($other) { $options = array( t('My groups') => $options, t('Other groups') => $other, ); $is_optgroup = TRUE; } else { $options = $all; } } else { // Classify those groups which the node already has but the author does not. if (!isset($node->og_groups_both)) { $node->og_groups_both = array(); } $current_groups = og_node_groups_distinguish($node->og_groups_both); // Put inaccessible groups in the $form so that they can persist. See og_presave_group() and og_access_alter_nongroup_form() in og_access.module $form['og_invisible']['og_groups_inaccessible'] = array('#type' => 'value', '#value' => $current_groups['inaccessible']); // Add the accessible groups that they node already belongs to. if ($current_groups['accessible']) { // Use an optgroup to distinguish between my memberships and additional groups in the Audience select. // There is code below which assumes that $options does not have optgroups but that code is within a $simple check // So we are OK as long as $simple does not apply to node edits. // NOTE: If you form_alter the audience element, beware that it can sometimes be an optgroup. foreach ($current_groups['accessible'] as $key => $val) { $other[$key] = $val['title']; } $options = array( t('My groups') => $options, t('Other groups') => $other, ); $is_optgroup = TRUE; } } // Show read only item if we are non-admin, and in simple mode (i.e. non-checkboxes) and at least one group is in querystring $simple = !user_access('administer organic groups') && !variable_get('og_audience_checkboxes', TRUE) && count($gids); // determine value of audience multi-select if (count($options) == 1 && $required) { $gids = array_keys($options); $gid = $gids[0]; $groups = array($gid); // also show read only mode if user has 1 option and we are in required mode $simple = TRUE; } elseif (!empty($gids)) { // populate field from the querystring if sent $groups = $gids; if (!user_access('administer nodes') && $simple) { // filter out any groups where author is not a member. we cannot rely on fapi to do this when in simple mode. $groups = array_intersect($gids, array_keys($options)); } } elseif (isset($node->og_groups)) { $groups = $node->og_groups; } else { $groups = array(); } // This is only used by og_access module right now. $form['og_initial_groups'] = array( '#type' => 'value', '#value' => $groups, ); if (module_exists('content')) { $form['og_nodeapi']['#weight'] = content_extra_field_weight($form['#node']->type, 'og_nodeapi'); } // Emit the audience form element. if ($simple) { // 'simple' mode. read only. if (count($groups)) { foreach ($groups as $gid) { $titles[] = $options[$gid]; $item_value = implode(', ', $titles); } $form['og_nodeapi']['visible']['og_groups_visible'] = array( '#type' => 'item', '#title' => t('Audience'), '#value' => $item_value ); $assoc_groups = drupal_map_assoc($groups); // this 'value' element persists the audience value during submit process $form['og_nodeapi']['invisible']['og_groups'] = array('#type' => 'value', '#value' => $assoc_groups); } } elseif ($cnt = count($options, COUNT_RECURSIVE)) { // show multi-select. if less than 20 choices, use checkboxes. $type = $cnt >= 20 || $is_optgroup ? 'select' : 'checkboxes'; $form['og_nodeapi']['visible']['og_groups'] = array( '#type' => $type, '#title' => t('Audience'), '#attributes' => array('class' => 'og-audience'), '#options' => $options, '#required' => $required, '#description' => format_plural(count($options), 'Show this post in this group.', 'Show this post in these groups.'), '#default_value' => $groups ? $groups : array(), '#required' => $required, '#multiple' => TRUE); } else if ($required) { form_set_error('title', t('You must join a group before posting a %type.', array('@join' => url('og'), '%type' => node_get_types('name', $node->type)))); } } /** * Define all OG message strings. * Modelled after user.module */ function _og_mail_text($messageid, $variables = array(), $language = NULL) { $langcode = isset($language) ? $language->language : NULL; // Check if an admin setting overrides the default string. if ($admin_setting = variable_get('og_'. $messageid, FALSE)) { return strtr($admin_setting, $variables); } // No override, return with default strings. else { switch ($messageid) { case 'og_new_node_subject': return t("@group: '@title' at @site", $variables, $langcode); case 'og_new_node_body': return t("@type '@subject' by @username\n\n@node_teaser\n\n!read_more: !content_url\nPost reply: !reply_url\n\n--\nYou are subscribed from the group '@group' at @site.\nTo manage your subscription, visit !group_url", $variables, $langcode); case 'og_admin_email_subject': return $variables['@subject']; case 'og_admin_email_body': return t("@body\n\n--\nThis message was sent by an administrator in the '@group' group at @site. To visit this group, browse to !url_group. To unsubscribe from this group, visit !url_unsubscribe", $variables, $langcode); case 'og_approve_user_subject': return t("Membership request approved for '@title'", $variables, $langcode); case 'og_approve_user_body': return t("You may now post messages in this group located at !group_url", $variables, $langcode); case 'og_deny_user_subject': return t("Membership request denied for '@title'", $variables, $langcode); case 'og_deny_user_body': return t("Sorry, your membership request was denied.", $variables, $langcode); // These emails have no 'og' prefix as they come through hook_mail(). case 'invite_user_subject': return t("Invitation to join the group '@group' at @site", $variables, $langcode); case 'invite_user_body': return t("Hi. I'm a member of '@group' and I welcome you to join this group as well. Please see the link and message below.\n\n@group\n@description\nJoin: !group_url\n@body", $variables, $langcode); case 'og_request_user_subject': return t("Membership request for '@group' from '@username'", $variables, $langcode); case 'og_request_user_body': return t("To instantly approve this request, visit !approve_url.\nYou may deny this request or manage members at !group_url. \n\nPersonal message from @username:\n------------------\n\n@request", $variables, $langcode); case 'og_new_admin_subject': return t("You are now an administrator for the group '@group'", $variables, $langcode); case 'og_new_admin_body': return t("@username, you are now an administrator for the group '@group'.\n\nYou can administer this group by logging in here:\n !group_url", $variables, $langcode); } } } // Helper function for queries that need all group types. function og_get_sql_args() { if ($types = og_get_types('group')) { $in = 'IN ('. db_placeholders($types, "varchar"). ')'; } else { $in = 'IS NULL'; } return array($types, $in); } function og_user($op, $edit, &$account, $category = NULL) { global $user; switch ($op) { case 'register': $options = array(); list($types, $in) = og_get_sql_args(); // If groups are passed on querystring, just use them. if (isset($_REQUEST['gids']) && $gids = $_REQUEST['gids']) { $default_value = $gids; foreach ($gids as $gid) { $nids[] = (int)$gid; } $in_nids = db_placeholders($nids); $sql = "SELECT n.nid, n.title, o.* FROM {node} n INNER JOIN {og} o ON n.nid = o.nid WHERE n.type $in AND n.status = 1 AND n.nid IN ($in_nids) ORDER BY n.title"; $result = db_query(db_rewrite_sql($sql), array_merge($types, $nids)); } else { $default_value = array(); // perhaps this should be a View $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, o.* FROM {node} n INNER JOIN {og} o ON n.nid = o.nid WHERE n.type $in AND n.status = 1 AND o.og_register = 1 ORDER BY n.title"), $types); } while ($group = db_fetch_object($result)) { $options[$group->nid] = ''. t('Join %name.', array('%name' => $group->title)). "\n"; if ($group->selective) { $options[$group->nid] .= ' '. t('(approval needed)'); } } if (count($options)) { $form['og_register'] = array('#type' => 'fieldset', '#title' => t('Groups')); $form['og_register']['og_register'] = array( '#type' => 'checkboxes', '#options' => $options, '#default_value' => $default_value, ); return $form; } case 'insert': if (isset($edit['og_register'])) { foreach (array_keys(array_filter($edit['og_register'])) as $gid) { $return = og_subscribe_user($gid, $account); if (!empty($return['message'])) { drupal_set_message($return['message']); } } } break; case 'delete': $sql = 'DELETE FROM {og_uid} WHERE uid=%d'; db_query($sql, $account->uid); break; case 'load': $account->og_groups = og_get_subscriptions($account->uid); break; case 'view': if ($account->og_groups) { foreach ($account->og_groups as $key => $val) { $links[$key] = l($val['title'], "node/$key") . theme('og_format_subscriber_status', $val); } $account->content['summary']['groups'] = array( '#type' => 'item', '#title' => t('Groups'), '#value' => theme('item_list', $links), // Not working in 6 // '#theme' => 'item_list', '#attributes' => array('class' => 'og_groups'), // Only show list of groups to self and admins. TOOD: Make this configurable or doable via Views. '#access' => $account->uid == $user->uid || user_access('administer organic groups'), ); } break; } } function og_save_ancestry($node) { if (og_is_group_post_type($node->type)) { $sql = "DELETE FROM {og_ancestry} WHERE nid = %d"; db_query($sql, $node->nid); if (isset($node->og_groups)) { $node->og_groups = array_unique($node->og_groups); foreach ($node->og_groups as $gid) { // TODOL: rename is_public to og_public in DB. $ancestry = array( 'nid' => $node->nid, 'group_nid' => $gid, 'is_public' => $node->og_public, ); drupal_write_record('og_ancestry', $ancestry); } } } } /** * An implementation of hook_node_type. Automatically update admin preferences when node type is renamed or removed. */ function og_node_type($op, $info) { switch ($op) { case 'delete': variable_del('og_content_type_usage_'. $info->type); break; case 'update': $usage = variable_get('og_content_type_usage_'. $info->type, 'omitted'); variable_set('og_content_type_usage_'. $info->type, $usage); if (isset($info->old_type)) { variable_del('og_content_type_usage_'. $info->old_type); } } } function og_types_map() { $usages = array( 'group' => t('Group node'), 'omitted' => t('May not be posted into a group.'), 'group_post_standard' => t('Standard group post (typically only author may edit).') ); if (module_exists('og_access')) { $usages['group_post_wiki'] = t('Wiki group post (any group member may edit).'); } return $usages; } // Return all content types which meet a specified usage. function og_get_types($usage) { $types = node_get_types(); foreach ($types as $type) { if ($usage == 'group_post') { if (!og_is_omitted_type($type->type) && !og_is_group_type($type->type)) { $return[$usage][] = $type->type; } } else { $type_usage = variable_get('og_content_type_usage_'. $type->type, 'omitted'); $return[$type_usage][] = $type->type; } } return isset($return[$usage]) ? $return[$usage] : array(); } // returns TRUE if node type lets all subscribers edit the node. function og_is_wiki_type($type) { $usage = variable_get('og_content_type_usage_'. $type, 'omitted'); return strpos($usage, 'wiki') ? TRUE : FALSE; } // returns TRUE if node type can be posted into a group. function og_is_group_post_type($type) { $usage = variable_get('og_content_type_usage_'. $type, 'omitted'); return strpos($usage, 'group_post') !== FALSE ? TRUE : FALSE; } function og_is_omitted_type($type) { return variable_get('og_content_type_usage_'. $type, 'omitted') == 'omitted'; } /** * API function for determining whether a given node type has been designated * by admin to behave as a group node (i.e. a container). * * @param string $type * @return boolean */ function og_is_group_type($type) { return variable_get('og_content_type_usage_'. $type, 'omitted') == 'group'; } /** * Implementation of hook_block(). */ function og_block($op = 'list', $delta = 0, $edit = array()) { if ($op == 'list') { $blocks[0]['info'] = t('Group details'); $blocks[0]['cache'] = BLOCK_NO_CACHE; // $blocks[1] used to be the album block. We do not change the numbers to not confuse people who update. // $blocks[2] used to be the group members block. This is now served by Views. We do not change the numbers to not confuse people who update. $blocks[3]['info'] = t('New groups'); $blocks[3]['cache'] = BLOCK_CACHE_PER_USER; // Now provided by og_views. Please don't reuse this number 4 // $blocks[4]['info'] = t('My groups'); $blocks[5]['info'] = t('Group notifications'); $blocks[5]['cache'] = BLOCK_NO_CACHE; // Now provided by og_views. Please don't reuse this number 6 // $blocks[6]['info'] = t('Group network'); // Auto-enable the group blocks for fresh installations. // TODOL: In order for this to work, you must rehash blocks during install which has been problematic. // $blocks[0]['status'] = 1; // $blocks[0]['region'] = 'left'; // $blocks[0]['weight'] = -2; // $blocks[5]['status'] = 1; // $blocks[5]['region'] = 'left'; // $blocks[5]['weight'] = -1; return $blocks; } elseif ($op == 'view') { switch ($delta) { case 0: return og_block_details(); case 3: return og_block_new(); case 5: return og_block_notifications(); } } elseif ($op == 'configure') { switch ($delta) { case 2: $items['og_block_cnt'] = array( '#type' => 'textfield', '#title' => t('Maximum number of members to show'), '#default_value' => variable_get("og_block_cnt_$delta", 10), '#size' => 5, ); $items['og_block_subscribers_is_admin'] = array( '#type' => 'checkboxes', '#title' => t('Group roles'), '#default_value' => variable_get("og_block_subscribers_is_admin", array('members', 'admins')), '#options' => array( 'members' => t('Standard members'), 'admins' => t('Administrators') ), '#description' => t('You may specify which types of group members appear in the listing.'), ); return $items; case 3: return array('og_block_cnt' => array('#type' => 'textfield', '#title' => t('Maximum number of groups to show'), '#default_value' => variable_get("og_block_cnt_$delta", 10), '#size' => 5, '#maxlength' => 255)); } } elseif ($op == 'save') { switch ($delta) { case 2: if (isset($edit['og_block_subscribers_is_admin'])) { variable_set("og_block_subscribers_is_admin", array_filter($edit['og_block_subscribers_is_admin'])); } if (isset($edit['og_block_cnt'])) { variable_set("og_block_cnt_$delta", $edit['og_block_cnt']); } break; case 3: if (isset($edit['og_block_cnt'])) { variable_set("og_block_cnt_$delta", $edit['og_block_cnt']); } break; } } } function og_block_notifications() { global $user; if ($groupnode = og_get_group_context()) { $content = t('This group offers a subscription option.', array('@subscription' => url('og/manage/'. $groupnode->nid))); $block['content'] = $content; $block['subject'] = t('Group notifications'); return $block; } } /** * Return code that emits an XML icon. TODOL: this opml icon belongs in theme.inc */ function theme_opml_icon($url) { if ($image = theme('image', drupal_get_path('module', 'og'). '/images/opml-icon-16x16.png', t('OPML feed'), t('OPML feed'))) { return ''. $image. ''; } } function og_block_new() { list($types, $in) = og_get_sql_args(); $sql = "SELECT COUNT(*) FROM {node} n INNER JOIN {og} og ON n.nid = og.nid WHERE og.og_directory=1 AND n.type $in AND n.status = 1"; $cnt = db_result(db_query(db_rewrite_sql($sql), $types)); if ($cnt > 0) { $max = variable_get('og_block_cnt_3', 10); $sql = "SELECT n.nid, n.title FROM {node} n INNER JOIN {og} og ON n.nid = og.nid WHERE n.status = 1 AND n.type $in AND og.og_directory=1 ORDER BY nid DESC"; $result = db_query_range(db_rewrite_sql($sql), $types, 0, $max); $output = node_title_list($result); if ($cnt > $max) { $output .= ''; } $block['subject'] = t('New groups'); $block['content'] = $output; return $block; } } function og_block_details() { global $user; // Only display group details if we have a group context. if (($node = og_get_group_context()) && node_access('view', $node)) { list($txt, $subscription) = og_subscriber_count_link($node); if ($subscription == 'active' || user_access('administer nodes')) { $links = module_invoke_all('og_create_links', $node); // We want to open this up for OG_INVITE_ONLY but we need to handle invitation workflow much better. See http://drupal.org/node/170332 if ($node->og_selective < OG_INVITE_ONLY) { $links['invite'] = l(t('Invite friend'), "og/invite/$node->nid"); } $links['subscribers'] = $txt; $links['manager'] = t('Manager: !name', array('!name' => theme('username', $node))); // Site admins get a Join link if they are not yet subscribed. $subscribe = isset($subscription) && og_is_group_member($node->nid, FALSE) ? l(t('My membership'), "og/manage/$node->nid") : og_subscribe_link($node); if(isset($subscribe)) { $links['my_membership'] = $subscribe; } } elseif ($subscription == 'requested') { $links['approval'] = t('Your membership request awaits approval.'); $links['delete'] = l(t('Delete request'), "og/unsubscribe/$node->nid/$user->uid", array('query' => 'destination=og')); } elseif (!$user->uid) { $dest = drupal_get_destination(); $links['must_login'] = t('You must register/login in order to post into this group.', array('!register' => url("user/register", array('query' => $dest)), '!login' => url("user/login", array('query' => $dest)))); } elseif ($node->og_selective < OG_INVITE_ONLY) { $links['subscribe'] = og_subscribe_link($node); } elseif ($node->og_selective == OG_INVITE_ONLY) { $links['closed'] = t('This is an invite only group. The group administrators add/remove members as needed.'); } elseif ($node->og_selective == OG_CLOSED) { $links['closed'] = t('This is a closed group. The group administrators add/remove members as needed.'); } // Modify these links by reference. If you want control of the whole block, see og_block_details(). drupal_alter('og_links', $links, $node); $block['content'] = theme('item_list', $links); $block['subject'] = l($node->title, "node/$node->nid"); return $block; } } /** * Determine the number of active and pending members and the current user's membership state. * * @return array * An array containing two strings. One for the number of members and another containing 'active' or 'requested' */ function og_subscriber_count_link($node) { global $user; $result = db_query(og_list_users_sql(0, 0, NULL), $node->nid); $cntpending = $cntall = 0; $subscription = ''; while ($row = db_fetch_object($result)) { $cntall++; if ($row->is_active == 0) { $cntpending++; } if ($row->uid == $user->uid) { if ($row->is_active) { $subscription = 'active'; } else { $subscription = 'requested'; } } } $txt = format_plural($cntall-$cntpending, '1 member', '@count members'); // The hyperlinked version of this text is supplied by og_views.module in alter hook. $txt .= $cntpending ? " ($cntpending)" : ''; return array($txt, $subscription); } function og_subscribe_link($node) { if ($node->og_selective == OG_MODERATED) { $txt = t('Request membership'); } elseif ($node->og_selective == OG_OPEN) { $txt = t('Join'); } if(isset($txt)) return l($txt, "og/subscribe/$node->nid", array('attributes' => array('rel' => 'nofollow'), 'query' => drupal_get_destination())); } // $group is an object containing a group node. // TODO: Make this a proper menu function og_og_create_links($group) { global $user; $post_types = og_get_types('group_post'); $types = node_get_types(); foreach ($post_types as $post_type) { // We used to check for node_access(create) but then node admins would get a false positive and see node types that they could not create. // When this becomes a proper menu in D6, we get sorting for free $type_name = $types[$post_type]->name; $type_url_str = str_replace('_', '-', $post_type); if (module_invoke($types[$post_type]->module, 'access', 'create', $post_type, $user)) { $links["create_$post_type"] = l(t('Create !type', array('!type' => $type_name)), "node/add/$type_url_str", array( 'title' => t('Add a new !type in this group.', array('!type' => $type_name)), 'query' => "gids[]=$group->nid", )); } } return isset($links) ? $links : array(); } function og_is_picture() { return variable_get('user_pictures', 0); } // TODOL: maybe use a custom theme('mark') here? // Mark the current user's membership in a given group if it is pending. function theme_og_format_subscriber_status($group) { if (!$group['is_active']) { return ' '. t('(pending approval)'); } } /** * Implementation of hook_xmlrpc(). /* * */ function og_xmlrpc() { module_load_include('inc', 'og', 'og_xmlrpc'); return array( array( 'og.subscribe_user', 'og_xmlrpc_subscribe_user', array('struct', 'string', 'string', 'int', 'int'), t('Add a user to a group.')), array( 'og.getAllSubscribers', 'og_xmlrpc_get_all_subscribers', array('array', 'string', 'string', 'int', 'int', 'int'), t('All members for a given group.')), array( 'og.getUserGroups', 'og_xmlrpc_get_user_groups', array('array', 'string', 'string', 'int'), t('Retrieve the group memberships for a given user.')), ); } /** * Implementation of hook_token_list() for og specific tokens /* */ function og_token_list($type = 'all') { if ($type == 'node' || $type == 'all') { $tokens['node']['ogname'] = t('Title of top group'); $tokens['node']['ogname-raw'] = t('Unfiltered title of top group. WARNING - raw user input.'); $tokens['node']['og-id'] = t('ID of top group'); return $tokens; } } /** * Implementation of hook_token_values() for og specific tokens */ function og_token_values($type, $object = NULL) { switch ($type) { case 'node': // Set some defaults. $values['ogname'] = ''; $values['ogname-raw'] = ''; $values['og-id'] = ''; if (is_array($object->og_groups)) { $gids = array_filter($object->og_groups); foreach ($gids as $gid) { $title = db_result(db_query("SELECT title FROM {node} WHERE nid = %d", $gid)); $values['ogname'] = check_plain($title); $values['ogname-raw'] = $title; $values['og-id'] = $gid; break; } return $values; } break; } return $values; } function og_readme() { global $base_path; // this link has to work when clean urls are disabled and drupal in subdir. $href = drupal_get_path('module', 'og'). '/README.txt'; $link = "". t('README file'). ''; return $link; } /** * Get a private token used to protect links from spoofing - CSRF. */ function og_get_token($nid) { return drupal_get_token($nid); } /** * Check to see if a token value matches the specified node. */ function og_check_token($token, $seed) { return drupal_get_token($seed) == $token; } /** * Access callback: og_notifications (or similar) is required for the broadcast * tab. Override menu item to amend. */ function og_broadcast_access($node) { return og_is_group_admin($node) && module_exists('og_notifications'); }