'activity', 'title' => t('Activity'), 'callback' => 'activity_page', 'access' => user_access('view public activity'), 'weight' => 1, ); $items[] = array( 'path' => 'activity/all', 'title' => t('All activity'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'access' => user_access('view public activity'), ); $items[] = array( 'path' => 'activity/mine', 'title' => t('My activity'), 'access' => $user->uid, 'type' => MENU_LOCAL_TASK, 'access' => user_access('view own activity'), ); $items[] = array( 'path' => 'activity/all/feed', 'title' => t('All activity'), 'callback' => 'activity_feed', 'callback arguments' => array(ACTIVITY_ALL), 'access' => user_access('view public activity'), ); $items[] = array( 'path' => 'admin/settings/activity', 'title' => t('Activity Settings'), 'description' => t('Customize what will display on your users activity page.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('activity_admin_settings'), ); } else { if ($user->uid) { $items[] = array( 'path' => 'activity/'. $user->uid. '/feed', 'title' => t('My activity'), 'callback' => 'activity_feed', 'callback arguments' => array($user->uid), 'type' => MENU_CALLBACK, ); } } return $items; } /** * Admin section * TODO: 1) the defaults aren't available at activity render time unless this * this page has been saved. This is potentially confusing since they * show up in the textfields. */ function activity_admin_settings() { if ($activity_info = activity_get_info()) { foreach ($activity_info as $module => $info) { if (!empty($info)) { $tokens = array(); $token_list = array(); foreach (token_get_list($module) as $name => $token_array) { $token_list = array_merge($token_list, $token_array); } ksort($token_list); foreach ($token_list as $token => $desc) { $tokens[] = '['. $token .']: '. $desc; } if (count($info['types'])) { $form[$module] = array( '#type' => 'fieldset', '#title' => t('Tokens for @name', array('@name' => t($module))), '#collapsible' => true, '#collapsed' => true, '#description' => t('Available tokens') . theme('item_list', $tokens), ); if ($ops = $info['ops']) { if ($roles = $info['roles']) { foreach ($roles as $role_name => $role) { $form[$module][$role_name] = array( '#type' => 'fieldset', '#title' => t('Messages visible to the "%role_name" role.', array('%role_name' => $role['#name'])), '#collapsible' => true, '#collapsed' => false, '#description' => $role['#description'] ? $role['#description'] : '', ); } if ($types = $info['types']) { foreach ($ops as $op_name => $op) { foreach ($info['types'] as $type_name => $type) { foreach ($roles as $role_name => $role) { $token_field = "{$module}_{$type_name}_{$op_name}_{$role_name}"; $form[$module][$role_name][$token_field] = array( '#type' => 'textfield', '#title' => $type. ': '. $op, '#default_value' => variable_get($token_field, $role['#default'] ? $role['#default'] : ''), ); } } } } } } } } } return system_settings_form($form); } else { drupal_set_message(t('No supported modules enabled. Check the !activity_section for supported modules.', array('!activity_section' => l(t('Activity section'), 'admin/build/modules')))); } } /** * API function * * @return an array of module names and metadata from those modules that * implement hook_activity_info. */ function activity_get_info() { foreach (module_implements('activity_info') as $module) { $info[$module] = module_invoke($module, 'activity_info'); } return $info; } /** * API function * Insert an activity record. This gets called by modules wishing to record * their activities. * @param $module The name of the module that is doing the recording, eg. 'node' * @param $type Module's can track more than one type of activity. For example, * the nodeactivity module tracks activities for each content type separately. * $type should be an identifier for the calling module to use. */ function activity_insert($module, $type, $operation, $data, $target_users_roles) { $aid = db_next_id('activity'); db_query("INSERT INTO {activity} (aid, module, type, operation, created, data) VALUES (%d, '%s', '%s', '%s', %d, '%s')", $aid, $module, $type, $operation, time(), serialize($data)); foreach ($target_users_roles as $uid => $role) { db_query("INSERT INTO {activity_targets} (aid, target_uid, target_role) VALUES (%d, %d, '%s')", $aid, $uid, $role); } return $aid; } /** * The API supports: * @param $uids * - a single uid * - an array of uids * - can include the special uid ACTIVITY_ALL * @param $filters * - an array where keys are one of module, type, operation, target_role * - values are arrays of possible values for the keys. * For example: * array('target_role' => 'Author', 'type' => 'Delete') * this would find activity where the author had deleted something. * Example 2: * array('target_role' => array('Requester', 'Requestee')) * This shows that the values can be arrays as well. * @param $limit * The number of results desired * @param $tablesort_headers * An array that determines the sorting of the result set. * */ function activity_get_activity($uids = ACTIVITY_ALL, $filters = NULL, $limit = NULL, $tablesort_headers = NULL) { $wheres = array(); // Build the WHERE clause for user id. if (!is_array($uids)) { $wheres[] = "at.target_uid = %d"; $params[] = $uids; } else { foreach ($uids as $uid) { $nums[] = "%d"; $params[] = $uid; } $wheres[] = 'at.target_uid IN ('. implode(',', $nums). ')'; } // Build sql limiting query to on filtered fields if (!empty($filters) && is_array($filters)) { foreach ($filters as $column => $values) { // Of the possible columns, role is in the at table and all others in the // a table. Prefix the column name with the appropriate table. if ($column == 'target_role') { $column = 'at.target_role'; } else { $column = "a.{$column}"; } if (is_array($values) && count($values) > 1) { foreach ($values as $value) { $strings[] = "'%s'"; $params[] = $value; } $wheres[] = $column. ' IN ('. implode(',', $strings). ')'; } else { $wheres[] = $column. " = '%s'"; // $values is a string with the single value. $params[] = $values; } } } if (count($wheres) > 0) { $where = implode(' AND ', $wheres); $where = "WHERE $where"; } // We always include tablesort_sql in the query so that this API is friendly // to sortable tables. If no headers were passed in, use the default headers. if (empty($tablesort_headers)) { $tablesort_headers = activity_get_tablesort_headers(); $tablesort_headers['a.created']['sort'] = 'desc'; } // Build the sql and do the query. Wrapping it in db_rewrite_sql allows other // modules to impose access restrictions on activity listings. $sql = "SELECT a.*, at.target_uid, at.target_role FROM {activity_targets} at INNER JOIN {activity} a ON a.aid = at.aid $where "; $tablesort_sql = tablesort_sql($tablesort_headers); $sql = db_rewrite_sql("$sql $tablesort_sql", 'at', 'aid', array('uids' => $uids)); if (is_numeric($limit)) { $result = pager_query($sql, $limit, 0, NULL, $params); } else { $result = db_query($sql, $params); } $activity = array(); while ($row = db_fetch_array($result)) { $row['data'] = unserialize($row['data']); $activity[] = $row; } return $activity; } function activity_get_tablesort_headers() { return array( 'at.aid' => array('field' => 'at.aid', 'data' => t('Id')), 'a.module' => array('field' => 'a.module', 'data' => t('Module')), 'a.type' => array('field' => 'a.type', 'data' => t('Type')), 'a.operation' => array('field' => 'a.operation', 'data' => t('Operation')), 'a.created' => array('field' => 'a.created', 'data' => t('Created')), ); } /** * create a block for display * @param op * @param delta * @returns block HTML * * TODO: Add "more activity" link to blocks which goes to activity table page. */ function activity_block($op = 'list', $delta = 0) { global $user, $form_values; if ($op == 'list') { $block['my']['info'] = t("Activity - Mine: show the current user's activity."); $block['all']['info'] = t("Activity - All: show all recent activity"); return $block; } elseif ($op == 'configure') { $form['items'] = array( '#type' => 'select', '#title' => t('Number of items'), '#default_value' => variable_get('activity_block_'. $delta, 5), '#options' =>drupal_map_assoc(range(1, 50)), ); return $form; } elseif ($op == 'save') { variable_set('activity_block_'. $delta, $form_values['items']); } elseif ($op == 'view') { switch ($delta) { case 'my': if (user_access('view own activity')) { // Grab the number of requested activities plus one. We use this one // to determine whether or not to show the "more" link and only display // the correct number of items. $activity = activity_get_activity($user->uid, NULL, variable_get('activity_block_'. $delta, 5) + 1); if ($count = count($activity)) { if ($count > variable_get('activity_block_'. $delta, 5)) { $more_link = '
'. $message. '
'); } } } $channel = array( 'version' => '2.0', 'title' => variable_get('site_name', 'Drupal') .' - '. $feed_title, 'link' => $url, 'description' => variable_get('site_mission', ''), 'language' => $locale, ); // TODO: Figure out what the right namespace should be. $namespaces = array('xmlns:dc="http://purl.org/dc/elements/1.1/"'); $output = "\n"; $output .= "