type])) { $entity->content['type'] = array( '#markup' => '
Log type: ' . $log_types[$entity->type] . '
', '#weight' => -101, ); } } /** * Implements hook_form_alter(). */ function farm_log_form_alter(&$form, &$form_state, $form_id) { // If this is a log form, attempt to prepopulate name and reference fields. if ($form_id == 'log_form') { farm_log_prepopulate_log_form_name($form); farm_log_prepopulate_log_form_references($form); } } /** * Implements hook_entity_presave(). */ function farm_log_entity_presave($entity, $type) { // When a log entity is being saved, populate the geometry field from areas. if ($type == 'log') { farm_log_populate_geometry($entity); } } /** * Implements hook_action_info(). */ function farm_log_action_info() { return array( 'farm_log_asset_action' => array( 'type' => 'farm_asset', 'label' => t('Add log'), 'configurable' => TRUE, 'triggers' => array('any'), 'aggregate' => TRUE, ), 'farm_log_assign_action' => array( 'type' => 'log', 'label' => t('Assign'), 'configurable' => TRUE, 'triggers' => array('any'), ), ); } /** * Configuration form for farm_log_asset_action. * * @param array $context * The context passed into the action form function. * @param array $form_state * The form state passed into the action form function. * * @return array * Returns a form array. */ function farm_log_asset_action_form(array $context, array $form_state) { // Load a list of all available log types. $log_types = log_types(); // Get information about field instances on logs. $field_instances = field_info_instances('log'); // Build a list of log type options. $log_type_options = array(); foreach ($log_types as $log_type => $info) { // If this log type does not have field_farm_asset, skip it. if (empty($field_instances[$log_type]['field_farm_asset'])) { continue; } // Add the log type to the list of options. $log_type_options[$log_type] = $info->label; } // Log type select list. $form['log_type'] = array( '#type' => 'select', '#title' => t('Log type'), '#description' => t('What type of log would you like to create?'), '#options' => $log_type_options, '#required' => TRUE, ); // Return the form. return $form; } /** * Submit handler for farm_log_asset_action configuration form. * * @param array $form * The form array. * @param array $form_state * The form state array. * * @return array * Returns an array that will end up in the action's context. */ function farm_log_asset_action_submit(array $form, array $form_state) { // Start to build the context array. $context = array(); // Add the log type to the context. $context['log_type'] = $form_state['values']['log_type']; // Return the context array. return $context; } /** * Action function for farm_log_asset_action. * * Sends the user to a new log form with the selected assets referenced. * * @param array $assets * An array of asset entities. * @param array $context * Array with parameters for this action. */ function farm_log_asset_action(array $assets, $context = array()) { // Redirect to the log creation form for the selected type, with the asset // IDs passed as query parameters. $options = array( 'query' => array( 'farm_asset' => array_keys($assets), ), ); drupal_goto('log/add/' . $context['log_type'], $options); } /** * Log assign action configuration form. * * @param array $context * The context passed into the action form function. * @param array $form_state * The form state passed into the action form function. * * @return array * Returns a form array. */ function farm_log_assign_action_form(array $context, array $form_state) { // Generate a list of users. Only include users with farm roles. $user_options = array(); $roles = farm_access_roles(); $query = db_query('SELECT u.uid, u.name FROM {users} u LEFT JOIN {users_roles} ur ON u.uid = ur.uid LEFT JOIN {role} r ON ur.rid = r.rid WHERE r.name IN (:roles)', array(':roles' => $roles)); $records = $query->fetchAll(); foreach ($records as $record) { $user_options[$record->uid] = $record->name; } // Display a multi-select list. $form['users'] = array( '#type' => 'select', '#title' => t('Assign log(s) to'), '#description' => t('Select people to assign these logs to.'), '#options' => $user_options, '#multiple' => TRUE, ); // Add a checkbox for appending the users instead of overwriting them. $form['operation'] = array( '#type' => 'radios', '#title' => t('Append or Replace'), '#description' => t('Select "Append" if you want to add users to the logs, but keep existing assignments. Select "Replace" if you want to replace existing assignments with the ones specified above.'), '#options' => array( 'append' => t('Append'), 'replace' => t('Replace'), ), '#default_value' => 'append', ); // Return the form. return $form; } /** * Log assign action configuration form submit. * * @param array $form * The form array. * @param array $form_state * The form state array. * * @return array * Returns an array that will end up in the action's context. */ function farm_log_assign_action_submit(array $form, array $form_state) { return array( 'users' => $form_state['values']['users'], 'operation' => $form_state['values']['operation'], ); } /** * Action function for farm_log_assign_action. * * Assigns a log to one or more people. * * @param Log $log * The log entity object. * @param array $context * Array with parameters for this action. */ function farm_log_assign_action(Log $log, $context = array()) { // If the operation is invalid, bail. if (!in_array($context['operation'], array('append', 'replace'))) { drupal_set_message('Invalid operation.'); return; } // If the operation is 'append', and there are no users, bail. if ($context['operation'] == 'append' && empty($context['users'])) { return; } // Create an entity wrapper for the log. $log_wrapper = entity_metadata_wrapper('log', $log); // If the owner field doesn't exist, bail. if (!isset($log_wrapper->field_farm_log_owner)) { return; } // Keep track of users that are already assigned. $existing_users = array(); // If we are appending, load existing owner IDs. if ($context['operation'] == 'append' && !empty($log_wrapper->field_farm_log_owner)) { foreach ($log_wrapper->field_farm_log_owner->getIterator() as $delta => $user_wrapper) { $existing_users[] = $user_wrapper->uid->value(); } } // Or, if we are replacing, clear out the existing owner IDs. elseif ($context['operation'] == 'replace') { $log_wrapper->field_farm_log_owner = array(); } // Assume that we are not going to save the log. $save = FALSE; // Iterate through the users. foreach ($context['users'] as $uid) { // If the ID is empty, skip it. if (empty($uid)) { continue; } // Load the user. $user = user_load($uid); // if the user didn't load, skip it. if (empty($user)) { continue; } // If the user is already referenced in the log, skip it. if (in_array($user->uid, $existing_users)) { continue; } // Add the user ID to the array of existing users so we don't accidentally // add the same one more than once. Shouldn't happen, but be defensive. $existing_users[] = $user->uid; // Add the user to the log's owner field. $log_wrapper->field_farm_log_owner[] = $user; // We will save the log. $save = TRUE; } // If we should save the log, then save it. if ($save) { $log_wrapper->save(); } } /** * Helper function for creating log categories. Terms will only be added if * they don't already exist. * * @param array $categories * An array of strings that will be added as terms to the Farm Log Categories * vocabulary. */ function farm_log_categories_create($categories) { // If the categories is not an array, bail. if (!is_array($categories)) { return; } // Define the vocabulary machine name. $vocabulary_machine_name = 'farm_log_categories'; // Load the vocabulary. $vocabulary = taxonomy_vocabulary_machine_name_load($vocabulary_machine_name); // If the vocabulary doesn't exist, bail. if (empty($vocabulary->vid)) { return; } // Iterate through the categories. foreach ($categories as $category) { // First, check to see if the term already exists. If it does, skip it. $terms = taxonomy_get_term_by_name($category, $vocabulary_machine_name); if (!empty($terms)) { continue; } // Translate the category name. $term_name = t($category); // Create the new term. $term = new stdClass(); $term->name = $term_name; $term->vid = $vocabulary->vid; taxonomy_term_save($term); } // Always reset the categories to alphabetical order. /** * @see taxonomy_vocabulary_confirm_reset_alphabetical_submit() */ db_update('taxonomy_term_data') ->fields(array('weight' => 0)) ->condition('vid', $vocabulary->vid) ->execute(); } /** * Create log categories on behalf of all modules that provide them. */ function farm_log_categories_create_all() { $categories = module_invoke_all('farm_log_categories'); if (!empty($categories)) { farm_log_categories_create($categories); } } /** * Helper function for populating a log's geometry from an area reference field. * * @param Entity $entity * The entity to act upon. * * @see farm_log_entity_movement_presave(). */ function farm_log_populate_geometry($entity) { // Define the area field name. $area_field = 'field_farm_area'; // If the log doesn't have an area reference field, bail. if (!isset($entity->{$area_field})) { return; } // If a geometry is already defined, bail. if (!empty($entity->field_farm_geofield[LANGUAGE_NONE][0]['geom'])) { return; } // Load the area(s) referenced by the area reference field. $area_ids = array(); if (!empty($entity->{$area_field}[LANGUAGE_NONE])) { foreach ($entity->{$area_field}[LANGUAGE_NONE] as $area_reference) { if (!empty($area_reference['tid'])) { $area_ids[] = $area_reference['tid']; } } } // Extract geometries from the areas. $geoms = farm_area_extract_geoms($area_ids); // Populate the geofield. farm_map_geofield_populate($entity, $geoms); } /** * Helper function for populating the name field in log forms. * * @param array $form * The form array to modify, passed by reference. */ function farm_log_prepopulate_log_form_name(&$form) { // If the GET parameter isn't set, bail. $params = drupal_get_query_parameters(); if (empty($params['name'])) { return; } // If the log form name field already has a default value, bail. if (!empty($form['name']['#default_value'])) { return; } // Set the name in the form. $form['name']['#default_value'] = $params['name']; } /** * Implements hook_farm_log_prepopulate_reference_fields(). */ function farm_log_farm_log_prepopulate_reference_fields($log_type) { // Define the fields that will be prepopulated in log forms. return array( 'field_farm_asset' => array( 'entity_type' => 'farm_asset', 'url_param' => 'farm_asset', ), 'field_farm_area' => array( 'entity_type' => 'taxonomy_term', 'url_param' => 'farm_area', ), 'field_farm_log_category' => array( 'entity_type' => 'taxonomy_term', 'lookup' => TRUE, 'vocabulary' => 'farm_log_categories', 'url_param' => 'category', ), 'field_farm_log_owner' => array( 'entity_type' => 'user', ), ); } /** * Helper function for populating entity reference fields in log forms. * * @param array $form * The form array to modify, passed by reference. */ function farm_log_prepopulate_log_form_references(&$form) { // Get the log type from the form. Bail if none. if (empty($form['#bundle'])) { return; } $log_type = $form['#bundle']; // Ask modules for information about fields that should be prepopulated for // this log type. $fields = module_invoke_all('farm_log_prepopulate_reference_fields', $log_type); // Allow modules to alter the field information. drupal_alter('farm_log_prepopulate_reference_fields', $fields, $log_type); // Populate the fields. foreach ($fields as $field => $info) { // Start with an empty array of IDs. $ids = array(); // If the field does not exist on the log, skip it. if (!isset($form[$field])) { continue; } // If a URL param is available, get a list of entity IDs from it. if (!empty($info['url_param'])) { // Get query parameters. $params = drupal_get_query_parameters(); // If the URL param is set, pull the IDs out. if (!empty($params[$info['url_param']])) { $ids = $params[$info['url_param']]; } } // Or, if the entity type is 'user', load the ID from the current user. elseif ($info['entity_type'] == 'user') { global $user; if (!empty($user->uid)) { $ids[] = $user->uid; } } // Ensure that the IDs are an array. if (!is_array($ids)) { $ids = array($ids); } // Allow modules to add log categories. if ($field == 'field_farm_log_category' && !empty($form['#entity'])) { $categories = module_invoke_all('farm_log_categories_populate', $form['#entity']); if (!empty($categories)) { foreach ($categories as $category) { if (is_string($category)) { $ids[] = t($category); } } } } // If there are no IDs, skip. if (empty($ids)) { continue; } // Look up taxonomy term IDs, if necessary. if ($info['entity_type'] == 'taxonomy_term' && !empty($info['lookup']) && !empty($info['vocabulary'])) { $term_names = $ids; $ids = array(); foreach ($term_names as $name) { $terms = taxonomy_get_term_by_name($name, $info['vocabulary']); $term = reset($terms); if (!empty($term->tid)) { $ids[] = $term->tid; } } } // Prepopulate with the farm_fields helper function. farm_fields_prepopulate_entityreference($form, $info['entity_type'], $field, $ids); } } /** * Helper function for building a select query of logs related to assets. * * This builds on top of the more general farm_log_query() function. * * @see farm_log_query() * * @param int|string $asset_id * The asset id to search for. This can either be a specific id, or a field * alias string from another query (ie: 'mytable.assetid'). For an example * of field alias string usage, see the Views field handler code in * farm_movement_handler_relationship_location::query(). If this is omitted, * the asset reference table will still be joined in, but no further * filtering will be done. * @param int $time * Unix timestamp limiter. Only logs before this time will be included. * Defaults to the current time. Set to 0 to load the absolute last. * @param bool $done * Whether or not to only show logs that are marked as "done". Defaults to * TRUE. * @param bool $single * Whether or not to limit the query to a single result. Defaults to TRUE. * * @return \SelectQuery * Returns a SelectQuery object. */ function farm_log_asset_query($asset_id = NULL, $time = REQUEST_TIME, $done = TRUE, $single = TRUE) { /** * Please read the comments in farm_log_query() to understand how this works, * and to be aware of the limitations and responsibilities we have in this * function with regard to sanitizing query inputs. */ // Start with a query from the farm_log_query() helper function. $query = farm_log_query($time, $done, $single); // Ensure $asset_id is valid, because it will be used directly in the query // string. This is defensive code. See note about farm_log_query() above. if (!is_numeric($asset_id) || $asset_id < 0) { $asset_id = db_escape_field($asset_id); } // Join in asset reference field. Use an inner join to exclude logs that do // not have any asset references. $query->innerJoin('field_data_field_farm_asset', 'ss_fdffa', "ss_fdffa.entity_type = 'log' AND ss_fdffa.entity_id = ss_log.id AND ss_fdffa.deleted = 0"); // If an asset ID is specified, only include logs that reference it. if (!empty($asset_id)) { $query->where('ss_fdffa.field_farm_asset_target_id = ' . $asset_id); } // Return the query object. return $query; } /** * Helper function for building a select query of logs. * * This function is used by other modules to build queries and Views handlers * that need to find the most recent log in a specific context. * * Modules can use this to generate a base query, and then add their own * modifications on top of that. * * @param int $time * Unix timestamp limiter. Only logs before this time will be included. * Defaults to the current time. Set to 0 to load the absolute last. * @param bool $done * Whether or not to only show logs that are marked as "done". Defaults to * TRUE. * @param bool $single * Whether or not to limit the query to a single result. Defaults to TRUE. * * @return \SelectQuery * Returns a SelectQuery object. */ function farm_log_query($time = REQUEST_TIME, $done = TRUE, $single = TRUE) { /** * This query may be used as a sub-query join in a Views handler via the * views_join_subquery class (for an example see: * farm_movement_handler_relationship_location). When a sub-query is added * via views_join_subquery, it is not possible to use query arguments in the * sub-query itself. So we cannot use the query::condition() method, or any * other methods that take query arguments separately and perform sanitation * on them. Thus, it is the responsibility of this function to sanitize any * inputs and use them directly in the SQL. * * We use the "ss_" prefix on aliases to avoid potential name conflicts when * this query is used as a sub-select inside another query. */ // Ensure $time is valid, because it may be used directly in the query // string. This is defensive code. See note about views_join_subquery above. if (!is_numeric($time) || $time < 0) { $time = REQUEST_TIME; } // Build a query to find a log that references an asset. $query = db_select('log', 'ss_log'); // If $time is not zero, limit to only logs before it. This allows the // absolute last log to be found by setting $time to zero. if ($time !== 0) { $query->where('ss_log.timestamp <= ' . $time); } // If only "done" movement logs should be included, add a filter. if ($done) { $query->where('ss_log.done = 1'); } // Order by timestamp and then log id, descending. $query->orderBy('ss_log.timestamp', 'DESC'); $query->orderBy('ss_log.id', 'DESC'); // Limit the query to a single result (the first one), if desired. if (!empty($single)) { $query->range(0, 1); } // Return the query object. return $query; } /** * Helper function for creating a farm log. * * @param string $type * The log type machine name. * @param string $name * Optionally specify a name for the new log. * @param int $timestamp * The timestamp of the log (defaults to REQUEST_TIME). * @param bool $done * Boolean indicating whether or not the log is done. * @param array $assets * An array of assets to reference in the log. * @param string $notes * Notes to add to the log. * @param array $categories * An array of categories to add to the log. * * @return \Log * Returns a saved log entity. */ function farm_log_create($type, $name = '', $timestamp = REQUEST_TIME, $done = TRUE, $assets = array(), $notes = '', $categories = array()) { // Create a new log entity. $log = entity_create('log', array('type' => $type)); // If a log name is specified, set it. if (!empty($name)) { $log->name = $name; } // Set the timestamp. $log->timestamp = $timestamp; // Set the log's done status. if (!empty($done)) { $log->done = TRUE; } else { $log->done = FALSE; } // Add asset references. if (!empty($assets)) { foreach ($assets as $asset) { if (!empty($asset->id)) { $log->field_farm_asset[LANGUAGE_NONE][] = array( 'target_id' => $asset->id, ); } } } // Add the notes. if (!empty($notes)) { $log->field_farm_notes[LANGUAGE_NONE][0]['value'] = $notes; $log->field_farm_notes[LANGUAGE_NONE][0]['format'] = 'farm_format'; } // Add the categories. if (!empty($categories)) { foreach ($categories as $category) { // Look up the category taxonomy term. $terms = taxonomy_get_term_by_name($category, 'farm_log_categories'); // If terms were found, use the first one. if (!empty($terms)) { $term = reset($terms); } // If a term wasn't found, create it. else { $vocab = taxonomy_vocabulary_machine_name_load('farm_log_categories'); $term = new stdClass(); $term->name = check_plain($category); $term->vid = $vocab->vid; taxonomy_term_save($term); } // Add the category to the log. $log->field_farm_log_category[LANGUAGE_NONE][] = array( 'tid' => $term->tid, ); } } // Set the log owner to the currently authenticated user. global $user; $log->field_farm_log_owner[LANGUAGE_NONE][] = array( 'target_id' => $user->uid, ); // Save the log. log_save($log); // Set a message. $label = entity_label('log', $log); $uri = entity_uri('log', $log); drupal_set_message('Log created: ' . l($label, $uri['path'])); // Return the log. return $log; } /** * Helper function for generating a summary of entity labels for use in a * log name. * * @param string $entity_type * The entity type. * @param Entity|array $entities * An entity or array of entities. * @param int $cutoff * The number of entity labels to include before summarizing the rest. * If the number of entities exceeds the cutoff, only the first entity's * label will be included, and the rest will be summarized as "(+ X more)". * If the number of entities is less than or equal to the cutoff, or if the * cutoff is 0, all entity labels will be included. * * @return string * Returns a string summarizing the assets. */ function farm_log_entity_label_summary($entity_type, $entities, $cutoff = 3) { $labels = array(); if (!is_array($entities)) { $entities = array($entities); } foreach ($entities as $entity) { $label = entity_label($entity_type, $entity); if (!empty($label)) { $labels[] = $label; } } $count = count($labels); if ($cutoff == 0 || count($labels) <= $cutoff) { $output = implode($labels, ', '); } else { $output = $labels[0] . ' (+' . ($count - 1) . ' ' . t('more') . ')'; } return $output; }