activity_ignore)) { $account->activity_ignore = array(); } $form['actions'] = array( '#type' => 'fieldset', '#title' => t('Actions within !site', array('!site' => variable_get('site_name', 'drupal'))), '#description' => t('By default, all of the following actions you perform can be viewed on this site.
Select the activities you would like to exclude people from seeing.'), ); $form['actions']['activity_ignore']['#tree'] = TRUE; // In order to see what actions are being implemented on a given system we // need to query the actions table joining the trigger_assignments. $query = "SELECT a.aid, a.description, ta.hook, ta.op FROM {actions} a INNER JOIN {trigger_assignments} ta ON ta.aid = a.aid WHERE a.type = 'activity'"; $result = db_query($query); while ($row = db_fetch_object($result)) { $module = activity_module_name($row->hook); // The module might be uninstalled and therefore, not exist anymore. if (!empty($module)) { $description = $row->description; // This means we're basically keying off of the aid and not the triggers. We // could possible key off of the triggers instead, and give the user more // fine grained control over (i.e. I want to record my own node inserts but // not my node updates). $form['actions']['activity_ignore'][$row->aid] = array( '#type' => 'radios', '#title' => $description, '#options' => array(1 => t('Record an activity message'), 0 => t('Do not record')), '#default_value' => isset($account->activity_ignore[$row->aid]) ? $account->activity_ignore[$row->aid] : 1, ); } } $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), ); $form['#account'] = $account; return $form; } /** * User settings form submit handler. */ function activity_user_settings_submit($form, &$form_state) { user_save($form['#account'], array('activity_ignore' => $form_state['values']['activity_ignore'])); } /** * Menu callback to administer activity messages. */ function activity_form(&$form_state, $arg1 = NULL, $arg2 = NULL) { if (!empty($form_state['storage']['step'])) { $function = $form_state['storage']['step']; return $function($form_state); } else { if ($arg1 == 'create') { // if we have a specific callback to create a new action, do so return _activity_settings_form_triggers($form_state); } elseif ($arg1 == 'configure') { $form_state['storage']['action'] = $arg2; return _activity_settings_form_messages($form_state); } else { return _activity_settings_actions_list_form(); } } } /** * Form validation controller. */ function activity_form_validate($form, &$form_state) { if (!empty($form_state['values']['step_validate'])) { $function = $form_state['values']['step_validate']; $function($form, $form_state); } } /** * Form submit controller. */ function activity_form_submit($form, &$form_state) { if (empty($form_state['storage'])) { $form_state['storage'] = array(); $form_state['storage']['values'] = array(); } // Call user-defined submit function. if (!empty($form_state['values']['step_submit'])) { $function = $form_state['values']['step_submit']; $function($form, $form_state); } // Store submitted form values, this must happen after function call above to // allow for modifying $form_state['values']. $this_step = $form_state['values']['this_step']; $form_state['storage']['values'][$this_step] = $form_state['values']; // Set up next step. if (!empty($form_state['values']['step_next']) && $form_state['values']['op'] != 'Back') { $form_state['storage']['step'] = $form_state['values']['step_next']; } else if ($form_state['values']['op'] == 'Back') { // Set up previous step $form_state['storage']['step'] = $form_state['values']['step_prev']; } else { // Form complete! $values = $form_state['storage']['values']; $hook = $values['triggers']['hook']; $op = $values['operations']['operation']; $module = activity_module_name($hook) ; $info = call_user_func($module .'_activity_info'); foreach (activity_enabled_languages() as $id => $language) { foreach ($info->objects as $type) { $params[$type .'-pattern-' . $id] = $values['messages'][$type .'-pattern-' . $id]; } $params['everyone-pattern-' . $id] = $values['messages']['everyone-pattern-' . $id]; } $desc = $values['messages']['description']; $update = TRUE; if (!$aid = $values['messages']['aid']) { $aid = NULL; $update = FALSE; } // handle the types, thanks Checkboxes :-D foreach ($values['operations']['activity_types'] as $type => $nice_name) { if (empty($nice_name)) { unset($values['operations']['activity_types'][$type]); } } $params['activity_types'] = $values['operations']['activity_types']; // Save the action include_once('includes/actions.inc'); // In order to save the aid into the actions.parameters column, when a new // template is created, the aid must be generated. Can look into using // {actions_aid} table for this as well. SELECT aid + 1 ORDER BY aid DESC ? if (!$update) { $aid = actions_save('activity_record', 'activity', $params, $desc); } $params['aid'] = $aid; $aid = actions_save('activity_record', 'activity', $params, $desc, $aid); // Save the trigger assignment // @todo: calc weight (last param) $record = new stdClass(); $record->hook = $hook; $record->op = $op; $record->aid = $aid; $record->weight = 1; // try update first drupal_write_record('trigger_assignments', $record, array('aid')); if (!db_affected_rows()) { // ok then insert drupal_write_record('trigger_assignments', $record); } unset($form_state['storage']); $form_state['redirect'] = 'admin/build/activity'; drupal_set_message('Saved.'); } } /** * List activity action that are already present in the system. */ function _activity_settings_actions_list_form() { $form = array(); $sql = "SELECT ta.hook, ta.aid, a.description FROM {trigger_assignments} ta INNER JOIN {actions} a ON a.aid = ta.aid WHERE a.type = 'activity'"; $query = db_query($sql); while ($result = db_fetch_array($query)) { $results[] = $result; } if (!empty($results)) { // since our originating callback is expecting a form $form['activity_actions'] = array( '#type' => 'markup', '#value' => theme('activity_settings_actions_list', $results), ); } else { $form['activity_create'] = array( '#value' => t('There are no Activity Templates created yet. !link', array('!link' => l(t('Create one now.'), 'admin/build/activity/create'))), ); } return $form; } /** * List of modules that implement activity_info(). */ function _activity_settings_form_triggers(&$form_state) { $form = array(); foreach (activity_get_module_info() as $module => $info) { if (!empty($info->hooks)) { foreach ($info->hooks as $hook => $ops) { $options[$hook] = drupal_ucfirst(str_replace('_', ' ', $info->name)); } } } $form['hook'] = array( '#type' => 'radios', '#title' => t('Available Activity Types'), '#options' => $options, '#default_value' => $form_state['storage']['values']['triggers']['hook'], '#required' => TRUE, '#description' => t('Please choose which type of activity you would like to record.'), ); $form['continue'] = array( '#type' => 'submit', '#value' => 'Continue', ); $form['this_step'] = array( '#type' => 'value', '#value' => 'triggers', ); $form['step_next'] = array( '#type' => 'value', '#value' => '_activity_settings_form_operations', ); return $form; } /** * List the ops for the module selected in the _triggers step. */ function _activity_settings_form_operations(&$form_state) { $form = array(); $hook = $form_state['storage']['values']['triggers']['hook']; $module = activity_module_name($hook); foreach (call_user_func($module .'_hook_info') as $module => $trigger_ops) { // Compare ops with our activity_info so that we do not provide a trigger // that activity doesn't support (ex: nodeapi - presave) // @see: node_activity_info() $module_info = activity_get_module_info($module); foreach ($module_info->hooks[$hook] as $op) { $ops[$hook][$op] = $trigger_ops[$hook][$op]; } foreach ($ops as $trigger_name => $hooks) { $options = array(); foreach ($hooks as $op => $runs_when) { $options[$op] = $op .': '. $runs_when['runs when']; } $default_value = isset($form_state['storage']['values']['operations']['operation']) ? $form_state['storage']['values']['operations']['operation'] : ''; $form[$trigger_name]['operation'] = array( '#type' => 'radios', '#title' => t(drupal_ucfirst(str_replace('_', ' ', $trigger_name)) .' Triggers'), '#description' => t('Please choose when you would like to record a message.'), '#options' => $options, '#default_value' => $default_value, '#required' => TRUE, ); } } if (!empty($module_info->type_options)) { if (!empty($form_state['storage']['values']['operations']['activity_types'])) { $default_types = $form_state['storage']['values']['operations']['activity_types']; } else { $default_types = array(); } $form['activity_types'] = array( '#type' => 'checkboxes', '#title' => t('Record for the following types'), '#options' => $module_info->type_options, '#default_value' => $default_types, '#description' => t('If none are selected then these messages will be recorded for all.'), ); } $form['back'] = array( '#type' => 'submit', '#value' => 'Back', ); $form['continue'] = array( '#type' => 'submit', '#value' => 'Continue', ); $form['step_prev'] = array( '#type' => 'value', '#value' => '_activity_settings_form_triggers', ); $form['this_step'] = array( '#type' => 'value', '#value' => 'operations', ); $form['step_next'] = array( '#type' => 'value', '#value' => '_activity_settings_form_messages', ); return $form; } /** * Show the text boxes according to the _triggers and _ops steps. */ function _activity_settings_form_messages(&$form_state) { $form = array(); if (isset($form_state['storage']['step'])) { $hook = $form_state['storage']['values']['triggers']['hook']; $module = activity_module_name($hook); $operation = $form_state['storage']['values']['operations']['operation']; $aid = FALSE; $activity_types = isset($form_state['storage']['values']['operations']['activity_types']) ? $form_state['storage']['values']['operations']['activity_types'] : array(); } else { $aid = $form_state['storage']['action']->aid; // by doing this we assume that an action is not assigned to more than one trigger $result = db_query('SELECT hook, op FROM {trigger_assignments} WHERE aid = %d', $aid); while ($row = db_fetch_array($result)) { $hook = $form_state['storage']['values']['triggers']['hook'] = $row['hook']; $module = activity_module_name($hook); $operation = $form_state['storage']['values']['operations']['operation'] = $row['op']; } } $module_info = activity_get_module_info($module); if (isset($form_state['storage']['action'])) { $default_values = unserialize($form_state['storage']['action']->parameters); } else { $default_values = array(); } // If this came through the multistep process it is already set above. // Otherwise we need to get it from the default values if (!isset($activity_types)){ if (isset($default_values['activity_types'])) { $activity_types = $default_values['activity_types']; } else { $activity_types = array(); } } $form_state['storage']['values']['operations']['activity_types'] = $activity_types; // Filter out false checkboxes. $activity_types = array_filter($activity_types); if (empty($activity_types)) { $types = 'All'; } else { $types = implode(', ' , array_intersect_key($module_info->type_options, $activity_types)); } $form['description'] = array( '#type' => 'textfield', '#title' => t('Description'), '#default_value' => isset($form_state['storage']['action']) ? $form_state['storage']['action']->description : t('Record an activity message when: !module !op types !types', array('!module' => $module, '!op' => $operation, '!types' => $types)), '#maxlength' => '254', '#description' => t('A unique description for this advanced action. This description will be displayed in the interface of modules that integrate with actions, such as Trigger module.'), ); // @todo: extend ->objects to have a description which will fill out the // #description for this form element. Remember to adjust logic everywhere // ->objects is used. foreach (activity_enabled_languages() as $id => $language) { $form["{$id}_fieldset"] = array( '#type' => 'fieldset', '#title' => t('@name messages', array('@name' => $language->name)), '#collapsible' => TRUE, '#collapsed' => $id != language_default('language'), ); foreach ($module_info->objects as $label => $type) { $clean_type = drupal_ucfirst(str_replace('_', ' ', $label)); $form["{$id}_fieldset"][$type .'-pattern-' . $id] = array( '#type' => 'textarea', '#title' => t('!type message', array('!type' => $clean_type)), '#default_value' => isset($default_values[$type .'-pattern-' . $id]) ? $default_values[$type .'-pattern-' . $id] : '', '#description' => t('Using the available tokens, enter a message as how it should appear to the !type of this particular activity.', array('!type' => $clean_type)), ); } $form["{$id}_fieldset"]['everyone-pattern-' . $id] = array( '#type' => 'textarea', '#title' => t('Public message'), '#default_value' => isset($default_values['everyone-pattern-' . $id]) ? $default_values['everyone-pattern-' . $id] : '', '#description' => t('Using the available tokens, enter a message as how it should appear to anyone who is not the author of this particular activity.'), ); } // don't show the back button when editing if (!isset($form_state['storage']['action'])) { $form['back'] = array( '#type' => 'submit', '#value' => 'Back', ); } $form['save'] = array( '#type' => 'submit', '#value' => 'Save', ); $form['step_prev'] = array( '#type' => 'value', '#value' => '_activity_settings_form_operations', ); $form['this_step'] = array( '#type' => 'value', '#value' => 'messages', ); $form['token_help'] = array( '#type' => 'fieldset', '#title' => t('Available tokens'), '#collapsible' => TRUE, '#collapsed' => TRUE, ); $form['token_help']['tokens'] = array( '#type' => 'markup', '#value' => theme('activity_token_help', array_values($module_info->objects)), ); $form['aid'] = array( '#type' => 'value', '#value' => $aid, ); return $form; } /** * Create the form for confirmation of deleting an activity action. * * @ingroup forms * @see activity_actions_delete_form_submit() */ function activity_actions_delete_form(&$form_state, $action) { $form['aid'] = array( '#type' => 'hidden', '#value' => $action->aid, ); $form['delete_existing'] = array( '#type' => 'checkbox', '#default_value' => 0, '#title' => t('Delete Existing Activities for this template'), ); return confirm_form($form, t('Are you sure you want to delete the action %action?', array('%action' => $action->description)), 'admin/build/activity', t('This cannot be undone.'), t('Delete'), t('Cancel') ); } /** * Process activity_actions_delete form submissions. * * Post-deletion operations for activity action deletion. */ function activity_actions_delete_form_submit($form, &$form_state) { $aid = $form_state['values']['aid']; $action = actions_load($aid); actions_delete($aid); watchdog('user', 'Deleted action %aid (%action)', array('%aid' => $aid, '%action' => $action->description)); drupal_set_message(t('Action %action was deleted', array('%action' => $action->description))); $form_state['redirect'] = 'admin/build/activity'; if (!empty($form_state['values']['delete_existing'])) { $batch = array( 'title' => t('Deleting Existing @title', array('@title' => $action->description)), 'operations' => array( array('activity_batch_delete', array($aid)), ), 'file' => drupal_get_path('module', 'activity') . '/activity.admin.inc', ); batch_set($batch); } } /** * Form builder to dispaly settings for activity module */ function activity_settings_form(&$form_state = NULL) { $form['activity_expiration'] = array( '#type' => 'fieldset', '#title' => t('Activity Expiration Settings'), '#element_validate' => array('activity_expire_validate'), ); $form['activity_expiration']['activity_expire'] = array( '#type' => 'select', '#title' => t('Activity log purge'), '#description' => t("Allows you to set a time limit for storing activity records. Select 0 to keep all activity records."), '#options' => drupal_map_assoc(array(0, 3600, 7200, 14400, 21600, 43200, 86400, 604800, 1209600, 2419200, 7257600, 15724800, 31536000), 'format_interval'), '#default_value' => variable_get('activity_expire', 0), ); $form['activity_expiration']['activity_min_count'] = array( '#type' => 'select', '#title' => t('Minimum Activities'), '#description' => t('This is the minimum number activities that the user must have created before deleting any old activities.'), '#options' => drupal_map_assoc(range(0, 200, 10)), '#default_value' => variable_get('activity_min_count', 0), ); // get all the modules and their realms $api_info = activity_get_module_info(); $realms = array(); foreach ($api_info as $module => $info) { foreach ($info->realms as $realm => $nice_name) { $realms[$realm] = t('@module: @name', array('@module' => drupal_ucfirst($module), '@name' => $nice_name)); } } $form['activity_access'] = array( '#type' => 'fieldset', '#title' => t('Activity Access Control'), '#attributes' => array('id' => 'activity-access-fieldset'), ); $form['activity_access']['activity_access_realms'] = array( '#type' => 'checkboxes', '#title' => t('Realms'), '#description' => t('Select the realms for which Activity access records should be recorded. These realms will allow a View to filter in more then just one users Activity.'), '#options' => $realms, '#default_value' => activity_access_realms(), ); $form['activity_access']['activity_access_rebuild'] = array( '#type' => 'submit', '#value' => t('Rebuild Activity Access Table'), '#submit' => array('activity_access_batch_set'), ); // This tells system_settings_form to use array_filter for the checkboxes. $form['array_filter'] = array('#type' => 'value', '#value' => TRUE); return system_settings_form($form); } /** * Element validate callback */ function activity_expire_validate($element, &$form_state) { if (empty($form_state['values']['activity_expire']) && !empty($form_state['values']['activity_min_count'])) { form_set_error($element['activity_expire'], t('You must set a time limit in order to use the minimum count'), $form_state); } } /** * Menu callback -- ask for confirmation of activity deletion */ function activity_delete_confirm(&$form_state, $aid) { $form['aid'] = array( '#type' => 'value', '#value' => $aid, ); return confirm_form($form, t('Are you sure you want to delete this activity?'), $_GET['destination'], t('This action cannot be undone.'), t('Delete'), t('Cancel') ); } /** * Execute activity deletion */ function activity_delete_confirm_submit($form, &$form_state) { if ($form_state['values']['confirm']) { activity_delete($form_state['values']['aid']); } $form_state['redirect'] = ''; } /** * FAPI form submit function for the activity_rebuild button. * * @param array $form * The FAPI form structure array. * * @param array &$form_state * The FAPI form state array. * * @return none */ function activity_access_batch_set($form, &$form_state) { // Address usability concern where the realms arn't set. $submitted_realms = array_filter($form_state['values']['activity_access_realms']); if ($submitted_realms != activity_access_realms()) { variable_set('activity_access_realms', $submitted_realms); } $batch = array( 'title' => t('Rebuilding Activity Access Table'), 'operations' => array( array('activity_access_rebuild_process', array()), ), 'file' => drupal_get_path('module', 'activity') . '/activity.admin.inc', ); batch_set($batch); $form_state['redirect'] = 'admin/settings/activity'; } /** * Batch API processing operation. Rebuilding Access table. * * @param array $context * The batch api context array. * * @return none */ function activity_access_rebuild_process(&$context) { if (!isset($context['sandbox']['last_aid'])) { // Set up the sandbox for the first time. $context['sandbox']['last_aid'] = 0; $context['sandbox']['progress'] = 0; // Activity can be happening on the site. Any Activity happening after this point // will not be rebuilt. This is ok, as in that case, the new Activity will receive // any and all new Activity Access Realms. $context['sandbox']['max'] = db_result(db_query("SELECT COUNT(aid) FROM {activity}")); } // Process 100 Activities at a time. $limit = 100; $activities = db_query_range("SELECT * FROM {activity} WHERE aid > %d ORDER BY aid ASC", $context['sandbox']['last_aid'], 0, $limit); while ($activity = db_fetch_object($activities)) { $grants = activity_get_grants($activity); // Delete existing records. db_query("DELETE FROM {activity_access} WHERE aid = %d", $activity->aid); // Insert new ones. foreach ($grants as $realm => $values) { foreach ($values as $value) { // insert one by one. In D7 we can use the DBTNG to insert multiple $perm = new stdClass(); $perm->aid = $activity->aid; $perm->realm = $realm; $perm->value = $value; drupal_write_record('activity_access', $perm); } } // Update sandbox variables. $context['sandbox']['last_aid'] = $activity->aid; $context['sandbox']['progress']++; } // Check if not finished. if ($context['sandbox']['progress'] < $context['sandbox']['max']) { $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max']; } else { // If finished, delete the sandbox. unset($context['sandbox']); } } /** * Set a batch process to regenerate activity for a specific hook and op pair. * * @param $aid * The actions.aid for this template. * * @return none */ function activity_batch_regenerate($batches) { $batch_set = FALSE; foreach ($batches as $aid => $batch) { $trigger_assignment = db_fetch_object(db_query("SELECT hook, op FROM {trigger_assignments} WHERE aid = '%s'", $aid)); if (!empty($trigger_assignment)) { $batch_set = TRUE; $batch = array( 'title' => t('Regenerating @description Activity', array('@description' => db_result(db_query("SELECT description from {actions} WHERE aid = '%s'", $aid)))), 'operations' => array( array('activity_batch_delete', array($aid)), array('activity_batch_regenerate_step', array($aid, $batch, $trigger_assignment->hook, $trigger_assignment->op)), ), 'file' => drupal_get_path('module', 'activity') . '/activity.admin.inc', ); batch_set($batch); } } if ($batch_set) { batch_process('admin/build/activity'); } else { return ''; } } /** * Batch deletion step. * * @param $aid * The actions.aid for this template. * @param $batch_context * The context array for this batch operation. */ function activity_batch_delete($aid, &$batch_context) { if (!isset($batch_context['sandbox']['last_activity_id'])) { $batch_context['sandbox']['last_activity_id'] = 0; $batch_context['sandbox']['progress'] = 0; $batch_context['sandbox']['max'] = db_result(db_query("SELECT COUNT(aid) FROM {activity} WHERE actions_id = '%s'", $aid)); } $limit = 200; $activity_to_be_deleted = db_query_range("SELECT aid FROM {activity} WHERE aid > %d AND actions_id = '%s' ORDER BY aid ASC", $batch_context['sandbox']['last_activity_id'], $aid, 0, $limit); $activity_ids = array(); while ($row = db_fetch_object($activity_to_be_deleted)) { $activity_ids[] = $row->aid; $batch_context['sandbox']['last_activity_id'] = $row->aid; $batch_context['sandbox']['progress']++; } activity_delete($activity_ids); // Check if not finished. if ($batch_context['sandbox']['progress'] < $batch_context['sandbox']['max']) { $batch_context['finished'] = $batch_context['sandbox']['progress'] / $batch_context['sandbox']['max']; } else { // If finished, delete the sandbox. unset($batch_context['sandbox']); } } /** * Batch regeneration step. * * @param $aid * The actions.aid for this template. * @param $hook * The name of the hook being recorded. * @param $op * The op for the hook being recorded. * @param $batch_context * An array representing the context for the batch operation. */ function activity_batch_regenerate_step($aid, $batch, $hook, $op, &$batch_context) { $module = activity_module_name($hook); $info = activity_get_module_info($module); $load_callback = $info->context_load_callback; $limit = 50; // Set up the sandbox for the regeneration. if (!isset($batch_context['sandbox']['list'])) { $batch_context['sandbox']['list'] = $batch; $batch_context['sandbox']['max'] = count($batch_context['sandbox']['list']); $batch_context['sandbox']['progress'] = 0; $parameters = db_result(db_query("SELECT a.parameters FROM {actions} a WHERE aid = '%s'", $aid)); $batch_context['sandbox']['parameters'] = unserialize($parameters); } $count = 0; foreach ($batch_context['sandbox']['list'] as $key => $event) { $context = array(); if (++$count > $limit) { break; } else { $context = $load_callback($hook, $op, $event['id']); if (!empty($context)) { $context += array( 'created' => $event['created'], 'actor' => $event['actor'], ); activity_record(NULL, $context + $batch_context['sandbox']['parameters']); } $batch_context['sandbox']['progress']++; } unset($batch_context['sandbox']['list'][$key]); } // Check if not finished. if ($batch_context['sandbox']['progress'] < $batch_context['sandbox']['max']) { $batch_context['finished'] = $batch_context['sandbox']['progress'] / $batch_context['sandbox']['max']; } else { // If finished, delete the sandbox. unset($batch_context['sandbox']); } }