farmOS/modules/farm/farm_log/farm_log.module

561 lines
16 KiB
Plaintext
Raw Normal View History

<?php
/**
* @file
* Code for the Farm Log feature.
*/
// Include Features code.
include_once 'farm_log.features.inc';
/**
* 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_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,
);
// 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;
}
2016-06-10 16:26:16 +02:00
// 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'];
}
/**
* Helper function for populating entity reference fields in log forms.
*
2015-04-13 22:49:57 +02:00
* @param array $form
* The form array to modify, passed by reference.
*/
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',
),
'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',
),
);
// 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);
}
2014-12-01 03:49:46 +01:00
}
/**
* 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().
* @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 $log_id_field
* Whether or not to add the log ID field to the query. Defaults to TRUE. You
* may want to set this to FALSE if you want to specify a different field or
* expression to add to the query instead of the log ID.
* @param bool $asset_filter
* Whether or not to join in the log's asset reference field and filter to
* only include logs that reference the $asset_id. Defaults to TRUE. You may
* want to set this to FALSE if you want to filter using a different asset
* reference field, in which case you must handle the join() and where()
* yourself.
*
* @return \SelectQuery
* Returns a SelectQuery object.
*/
function farm_log_asset_query($asset_id, $time = REQUEST_TIME, $done = TRUE, $log_id_field = TRUE, $asset_filter = 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, $log_id_field);
// Ensure $asset_id 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_int($asset_id) || $asset_id < 0) {
$asset_id = db_escape_field($asset_id);
}
// Join in asset references and filter to only include logs that reference the
// specified asset (only if $asset_filter is TRUE).
if ($asset_filter === TRUE) {
$query->join('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");
$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 $log_id_field
* Whether or not to add the log ID field to the query. Defaults to TRUE. You
* may want to set this to FALSE if you want to specify a different field or
* expression to add to the query instead of the log ID.
*
* @return \SelectQuery
* Returns a SelectQuery object.
*/
function farm_log_query($time = REQUEST_TIME, $done = TRUE, $log_id_field = 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_int($time) || $time < 0) {
$time = REQUEST_TIME;
}
// Build a query to find a log that references an asset.
$query = db_select('log', 'ss_log');
// Add the log ID field (only if $log_id_field is TRUE.
if ($log_id_field === TRUE) {
$query->addField('ss_log', 'id');
}
// 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');
// We only want the first result.
$query->range(0, 1);
// Return the query object.
return $query;
}