2014-02-04 09:41:17 +01:00
<?php
/**
* @file
* Code for the Farm Log feature.
*/
2015-11-28 19:02:44 +01:00
// Include Features code.
2014-02-04 09:41:17 +01:00
include_once 'farm_log.features.inc';
2014-11-13 21:53:13 +01:00
/**
* Implements hook_form_alter().
*/
function farm_log_form_alter(&$form, &$form_state, $form_id) {
2017-07-14 21:13:25 +02:00
// If this is a log form, attempt to prepopulate name and reference fields.
2015-04-09 04:49:34 +02:00
if ($form_id == 'log_form') {
2017-07-14 21:13:25 +02:00
farm_log_prepopulate_log_form_name($form);
2017-07-10 17:35:12 +02:00
farm_log_prepopulate_log_form_references($form);
2014-11-19 19:25:22 +01:00
}
2015-05-27 17:53:19 +02:00
}
2016-06-08 21:01:48 +02:00
/**
* Implements hook_entity_presave().
*/
function farm_log_entity_presave($entity, $type) {
2017-05-10 15:43:01 +02:00
// When a log entity is being saved, populate the geometry field from areas.
2016-06-08 21:01:48 +02:00
if ($type == 'log') {
2017-05-10 15:43:01 +02:00
farm_log_populate_geometry($entity);
2016-06-08 21:01:48 +02:00
}
}
2017-08-29 23:51:01 +02:00
/**
* Implements hook_action_info().
*/
function farm_log_action_info() {
return array(
'farm_log_assign_action' => array(
'type' => 'log',
'label' => t('Assign'),
'configurable' => TRUE,
'triggers' => array('any'),
),
);
}
/**
* 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,
2017-08-30 19:13:46 +02:00
);
// 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',
2017-08-29 23:51:01 +02:00
);
// 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'],
2017-08-30 19:13:46 +02:00
'operation' => $form_state['values']['operation'],
2017-08-29 23:51:01 +02:00
);
}
/**
* 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()) {
2017-08-30 19:13:46 +02:00
// 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'])) {
2017-08-29 23:51:01 +02:00
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;
}
2017-08-30 19:13:46 +02:00
// Keep track of users that are already assigned.
2017-08-29 23:51:01 +02:00
$existing_users = array();
2017-08-30 19:13:46 +02:00
// If we are appending, load existing owner IDs.
if ($context['operation'] == 'append' && !empty($log_wrapper->field_farm_log_owner)) {
2017-08-29 23:51:01 +02:00
foreach ($log_wrapper->field_farm_log_owner->getIterator() as $delta => $user_wrapper) {
$existing_users[] = $user_wrapper->uid->value();
}
}
2017-08-30 19:13:46 +02:00
// Or, if we are replacing, clear out the existing owner IDs.
elseif ($context['operation'] == 'replace') {
$log_wrapper->field_farm_log_owner = array();
}
2017-08-29 23:51:01 +02:00
// Assume that we are not going to save the log.
$save = FALSE;
// Iterate through the users.
foreach ($context['users'] as $uid) {
2017-08-30 19:13:46 +02:00
// If the ID is empty, skip it.
if (empty($uid)) {
continue;
}
2017-08-29 23:51:01 +02:00
// 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();
}
}
2017-05-30 20:29:14 +02:00
/**
* 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;
}
2017-05-30 20:45:38 +02:00
// Translate the category name.
$term_name = t($category);
2017-05-30 20:29:14 +02:00
// Create the new term.
$term = new stdClass();
2017-05-30 20:45:38 +02:00
$term->name = $term_name;
2017-05-30 20:29:14 +02:00
$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);
}
}
2016-06-08 21:01:48 +02:00
/**
2017-05-10 15:43:01 +02:00
* Helper function for populating a log's geometry from an area reference field.
2016-06-08 21:01:48 +02:00
*
2017-05-10 15:43:01 +02:00
* @param Entity $entity
* The entity to act upon.
2017-04-27 16:24:02 +02:00
*
* @see farm_log_entity_movement_presave().
2016-06-08 21:01:48 +02:00
*/
2017-05-10 15:43:01 +02:00
function farm_log_populate_geometry($entity) {
// Define the area field name.
$area_field = 'field_farm_area';
2016-06-08 21:01:48 +02:00
2016-10-22 18:15:37 +02:00
// If the log doesn't have an area reference field, bail.
2017-05-10 15:43:01 +02:00
if (!isset($entity->{$area_field})) {
2016-06-08 21:01:48 +02:00
return;
}
// If a geometry is already defined, bail.
2017-05-10 15:43:01 +02:00
if (!empty($entity->field_farm_geofield[LANGUAGE_NONE][0]['geom'])) {
2016-06-08 21:01:48 +02:00
return;
}
2016-06-10 16:26:16 +02:00
// Load the area(s) referenced by the area reference field.
2017-05-10 15:34:09 +02:00
$area_ids = array();
2017-05-10 15:43:01 +02:00
if (!empty($entity->{$area_field}[LANGUAGE_NONE])) {
foreach ($entity->{$area_field}[LANGUAGE_NONE] as $area_reference) {
2016-06-08 21:01:48 +02:00
if (!empty($area_reference['tid'])) {
2017-05-10 15:34:09 +02:00
$area_ids[] = $area_reference['tid'];
2016-06-08 21:01:48 +02:00
}
}
}
2017-05-10 15:34:09 +02:00
// Extract geometries from the areas.
$geoms = farm_area_extract_geoms($area_ids);
2016-06-08 21:01:48 +02:00
2017-05-10 15:03:11 +02:00
// Populate the geofield.
2017-05-10 15:43:01 +02:00
farm_map_geofield_populate($entity, $geoms);
2017-07-14 21:13:25 +02:00
}
/**
* 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'];
2016-06-08 21:01:48 +02:00
}
2014-11-19 19:25:22 +01:00
/**
2017-07-10 17:35:12 +02:00
* Helper function for populating entity reference fields in log forms.
2014-11-19 19:25:22 +01:00
*
2015-04-13 22:49:57 +02:00
* @param array $form
2014-11-19 19:25:22 +01:00
* The form array to modify, passed by reference.
*/
2017-07-10 17:35:12 +02:00
function farm_log_prepopulate_log_form_references(&$form) {
// Define the fields we will be populating.
$fields = 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',
),
2017-07-14 21:51:23 +02:00
'field_farm_log_category' => array(
'entity_type' => 'taxonomy_term',
'lookup' => TRUE,
'vocabulary' => 'farm_log_categories',
'url_param' => 'category',
),
2017-07-10 17:35:12 +02:00
'field_farm_log_owner' => array(
'entity_type' => 'user',
),
);
// 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;
}
2015-09-11 21:01:25 +02:00
2017-07-10 17:35:12 +02:00
// If a URL param is available, get a list of entity IDs from it.
if (!empty($info['url_param'])) {
2017-07-14 21:40:53 +02:00
// 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']];
}
2017-07-10 17:35:12 +02:00
}
2015-10-02 02:51:40 +02:00
2017-07-10 17:35:12 +02:00
// 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;
}
}
2017-07-14 21:40:53 +02:00
// Ensure that the IDs are an array.
if (!is_array($ids)) {
$ids = array($ids);
}
2017-07-18 15:08:39 +02:00
// 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);
}
}
}
}
2017-07-10 17:35:12 +02:00
// If there are no IDs, skip.
if (empty($ids)) {
continue;
}
2017-07-14 21:51:23 +02:00
// 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;
}
}
}
2017-07-10 18:17:31 +02:00
// Prepopulate with the farm_fields helper function.
farm_fields_prepopulate_entityreference($form, $info['entity_type'], $field, $ids);
2014-11-13 21:53:13 +01:00
}
2014-12-01 03:49:46 +01:00
}
2017-10-12 16:08:27 +02:00
/**
* Helper function for building a select query of logs related to assets.
*
2017-10-17 19:04:20 +02:00
* This builds on top of the more general farm_log_query() function.
2017-10-12 16:08:27 +02:00
*
2017-10-17 19:04:20 +02:00
* @see farm_log_query()
2017-10-12 16:08:27 +02:00
*
* @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().
* @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.
2017-10-17 19:53:44 +02:00
* @param bool $single
* Whether or not to limit the query to a single result. Defaults to TRUE.
2017-10-12 16:08:27 +02:00
*
* @return \SelectQuery
* Returns a SelectQuery object.
*/
2017-10-17 19:53:44 +02:00
function farm_log_asset_query($asset_id, $time = REQUEST_TIME, $done = TRUE, $single = TRUE) {
2017-10-12 16:08:27 +02:00
2017-10-17 19:04:20 +02:00
/**
* 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.
2017-10-17 19:53:44 +02:00
$query = farm_log_query($time, $done, $single);
2017-10-17 19:04:20 +02:00
2017-10-17 19:08:13 +02:00
// Ensure $asset_id is valid, because it will be used directly in the query
2017-10-17 19:27:26 +02:00
// string. This is defensive code. See note about farm_log_query() above.
2017-11-06 18:49:19 +01:00
if (!is_numeric($asset_id) || $asset_id < 0) {
2017-10-17 19:04:20 +02:00
$asset_id = db_escape_field($asset_id);
}
// Join in asset references and filter to only include logs that reference the
2017-12-07 23:11:30 +01:00
// specified asset. 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");
2017-10-17 19:08:13 +02:00
$query->where('ss_fdffa.field_farm_asset_target_id = ' . $asset_id);
2017-10-17 19:04:20 +02:00
// 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.
2017-10-17 19:53:44 +02:00
* @param bool $single
* Whether or not to limit the query to a single result. Defaults to TRUE.
2017-10-17 19:04:20 +02:00
*
* @return \SelectQuery
* Returns a SelectQuery object.
*/
2017-10-17 19:53:44 +02:00
function farm_log_query($time = REQUEST_TIME, $done = TRUE, $single = TRUE) {
2017-10-17 19:04:20 +02:00
2017-10-12 16:08:27 +02:00
/**
* 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.
*/
2017-10-17 19:04:20 +02:00
// 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.
2017-11-06 18:49:19 +01:00
if (!is_numeric($time) || $time < 0) {
2017-10-12 16:08:27 +02:00
$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');
2017-10-17 19:53:44 +02:00
// Limit the query to a single result (the first one), if desired.
if (!empty($single)) {
$query->range(0, 1);
}
2017-10-12 16:08:27 +02:00
// Return the query object.
return $query;
}