farmOS/modules/farm/farm_movement/farm_movement.location.inc

972 lines
33 KiB
PHP

<?php
/**
* @file
* Code for managing the location of assets with movement logs.
*/
/**
* Generate markup that describes an asset's current location.
*
* @param FarmAsset $asset
* The farm asset.
*
* @return string
* Returns rendered HTML.
*/
function farm_movement_asset_location_markup($asset) {
// Start an output string.
$output = '<strong>' . t('Location') . ':</strong> ';
// Get the asset's location.
$areas = farm_movement_asset_location($asset);
// If locations were found, add links to them.
if (!empty($areas)) {
$area_links = array();
foreach ($areas as $area) {
if (!empty($area->tid)) {
$area_links[] = l($area->name, 'taxonomy/term/' . $area->tid);
}
}
$output .= implode(', ', $area_links);
}
// Otherwise, none.
else {
$output .= 'N/A';
}
// Get the asset's most recent movement.
$log = farm_movement_asset_latest_movement($asset);
// Load the log's movement field, if it exists.
if (!empty($log->field_farm_movement[LANGUAGE_NONE][0]['value'])) {
$movement = field_collection_item_load($log->field_farm_movement[LANGUAGE_NONE][0]['value']);
}
// If a geofield exists on the movement, display it.
if (!empty($movement->field_farm_geofield[LANGUAGE_NONE][0]['geom'])) {
// Build the geofield map and add it to the page content.
$field_instance = field_info_instance('field_collection_item', 'field_farm_geofield', 'field_farm_movement');
$geofield = field_view_field('field_collection_item', $movement, 'field_farm_geofield', $field_instance['display']['default']);
$geofield['#title'] = t('Geometry');
$output .= drupal_render($geofield);
}
// Return the output markup.
return $output;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function farm_movement_form_farm_asset_form_alter(&$form, &$form_state, $form_id) {
// Get the farm asset entity from the form.
$asset = $form['farm_asset']['#value'];
// Get the asset's current location.
$areas = farm_movement_asset_location($asset);
$area_names = array();
if (!empty($areas)) {
foreach ($areas as $area) {
if (!empty($area->name)) {
// Get the area name.
$name = $area->name;
// If the area name contains commas, wrap it in quotes.
if (strpos($area->name, ',') !== FALSE) {
$name = '"' . $area->name . '"';
}
// Add the name to the list.
$area_names[] = $name;
}
}
}
// Assemble the list of areas into a string.
$location = implode(', ', $area_names);
// Add a field for setting the asset's current location.
$form['location'] = array(
'#type' => 'fieldset',
'#title' => t('Location'),
'#description' => t('Set the current areas(s) that this asset is in. Separate multiple areas with commas. A movement observation log will be created automatically if you change this field.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => 100,
'#tree' => TRUE,
);
$form['location']['areas'] = array(
'#type' => 'textfield',
'#title' => t('Current location'),
'#autocomplete_path' => 'taxonomy/autocomplete/field_farm_area',
'#default_value' => $location,
'#maxlength' => NULL,
);
// Add validation function to validate location input.
$form['actions']['submit']['#validate'][] = 'farm_movement_asset_location_validate';
// Add submit function to process the location.
$form['actions']['submit']['#submit'][] = 'farm_movement_asset_location_submit';
// Put the location fieldset into the "General" field group.
$form['#group_children']['location'] = 'group_farm_general';
}
/**
* Validation handler for processing the asset location field.
*
* @param array $form
* The form array.
* @param array $form_state
* The form state array.
*/
function farm_movement_asset_location_validate(array $form, array &$form_state) {
// Only proceed if current location field has a value.
if (empty($form_state['values']['location']['areas'])) {
return;
}
// Explode the value into an array and only take the first value.
// (Same behavior as taxonomy autocomplete widget.)
$values = drupal_explode_tags($form_state['values']['location']['areas']);
// Iterate over the values.
foreach ($values as $value) {
// If the area name is over 255 characters long, throw a form validation
// error.
if (strlen($value) > 255) {
$message = t('The area name "%name" is too long. It must be under 255 characters.', array('%name' => $value));
form_set_error('location][areas', $message);
}
}
}
/**
* Submit handler for processing the asset location field.
*
* @param array $form
* The form array.
* @param array $form_state
* The form state array.
*/
function farm_movement_asset_location_submit(array $form, array &$form_state) {
// Only proceed if current location field has a value.
if (empty($form_state['values']['location']['areas'])) {
return;
}
// Only proceed if the value is not the default value.
if ($form_state['values']['location']['areas'] == $form['location']['areas']['#default_value']) {
return;
}
// If an asset doesn't exist, bail.
if (empty($form_state['values']['farm_asset'])) {
return;
}
// Grab the asset.
$asset = $form_state['values']['farm_asset'];
// Load the areas.
$areas = farm_term_parse_names($form_state['values']['location']['areas'], 'farm_areas', TRUE);
// Create an observation log to record the movement.
farm_movement_create($asset, $areas, REQUEST_TIME);
}
/**
* Find the location of an asset, based on movement logs.
*
* @param FarmAsset $asset
* The farm_asset object to look for.
* @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|null $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* this is set to NULL, no filtering will be applied. Defaults to TRUE.
*
* @return array
* Returns an array of areas that the asset is in.
*/
function farm_movement_asset_location(FarmAsset $asset, $time = REQUEST_TIME, $done = TRUE) {
$areas = array();
// Load the log using our helper function.
$log = farm_movement_asset_latest_movement($asset, $time, $done);
// If a movement field doesn't exist, bail.
if (empty($log->field_farm_movement[LANGUAGE_NONE][0]['value'])) {
return $areas;
}
// Load the log's movement field
$movement = field_collection_item_load($log->field_farm_movement[LANGUAGE_NONE][0]['value']);
// Load the areas referenced in the "Move to" field.
if (!empty($movement->field_farm_move_to[LANGUAGE_NONE])) {
foreach ($movement->field_farm_move_to[LANGUAGE_NONE] as $area_reference) {
if (!empty($area_reference['tid'])) {
$term = taxonomy_term_load($area_reference['tid']);
if (!empty($term)) {
$areas[] = $term;
}
}
}
}
return $areas;
}
/**
* Find the geometry of an asset, based on movement logs.
*
* @param FarmAsset $asset
* The farm_asset object to look for.
* @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|null $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* this is set to NULL, no filtering will be applied. Defaults to TRUE.
*
* @return string
* Returns the asset's current geometry, in WKT (well-known text).
*/
function farm_movement_asset_geometry(FarmAsset $asset, $time = REQUEST_TIME, $done = TRUE) {
$geometry = '';
// Load the log using our helper function.
$log = farm_movement_asset_latest_movement($asset, $time, $done);
// If a movement field doesn't exist, bail.
if (empty($log->field_farm_movement[LANGUAGE_NONE][0]['value'])) {
return $geometry;
}
// Load the log's movement field
$movement = field_collection_item_load($log->field_farm_movement[LANGUAGE_NONE][0]['value']);
// Load the movement geometry.
if (!empty($movement->field_farm_geofield[LANGUAGE_NONE][0]['geom'])) {
$geometry = $movement->field_farm_geofield[LANGUAGE_NONE][0]['geom'];
}
return $geometry;
}
/**
* Retrieve an area's movement history. This will provide an array of arrival
* and departure logs for each asset that has been moved to the area. Only
* movement logs that have been marked "done" will be included in the history.
*
* @param $area
* The farm area (taxonomy term object).
* @param string|array $asset_types
* Limit to only include certain asset types. This can be a single asset type
* as a string, or an array of asset types. Defaults to empty array, which
* will include all asset types.
* @param int $start_time
* How far back to look? This should be a UNIX timestamp. Defaults to NULL,
* which looks back through all movement logs in the system.
* @param int $end_time
* How far forward to look? This should be a UNIX timestamp. Defaults to the
* current time, which causes future arrival movements to be excluded.
*
* @return array
* Returns an array of movement history for each asset in the area. Array
* keys are asset IDs, and each asset will contain an array of arrays that
* contain arrival and departure logs for each movement through the area.
* If an asset moved through the area more than once, it will have multiple
* sub-arrays for each arrival+departure. If a departure log is not found
* (eg: if the asset has not left the area), the 'depart' key will be NULL.
*
* Example:
* array(
* '50' => array(
* array(
* 'arrive' => [$log],
* 'depart' => [$log],
* ),
* array(
* 'arrive' => [$log],
* 'depart' => NULL,
* ),
* ),
* '51' => array(
* array(
* 'arrive' => [$log],
* 'depart' => [$log],
* ),
* ),
* );
*/
function farm_movement_area_history($area, $asset_types = array(), $start_time = NULL, $end_time = REQUEST_TIME) {
// Start an empty history array.
$history = array();
// If the area doesn't have an id, bail.
if (empty($area->tid)) {
return $history;
}
// If $asset_types is not an array, wrap it in one.
if (!is_array($asset_types)) {
$asset_types = array($asset_types);
}
// Build a query to retrieve movement logs to this area.
$query = farm_movement_area_movement_query($area->tid, $end_time);
// Add the log ID field.
$query->addField('ss_log', 'id');
// Filter to only include logs that happened AFTER the start time.
if (!empty($start_time)) {
$query->condition('ss_log.timestamp', $start_time, '>');
}
// Join in asset references, and then the farm_asset table record for each.
$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->join('farm_asset', 'ss_fa', 'ss_fa.id = ss_fdffa.field_farm_asset_target_id');
// Filter to only include certain asset types.
if (!empty($asset_types)) {
$query->condition('ss_fa.type', $asset_types, 'IN');
}
// Group by log ID so that we don't get duplicate rows from logs that
// reference multiple assets.
$query->groupBy('ss_log.id');
// Execute the query to get a list of log IDs.
$result = $query->execute();
// Iterate through the log IDs.
foreach ($result as $row) {
// If the log ID is empty, skip it.
if (empty($row->id)) {
continue;
}
// Load the asset's arrival log.
$log_arrive = log_load($row->id);
// Create an entity metadata wrapper for the log.
$log_wrapper = entity_metadata_wrapper('log', $log_arrive);
// Iterate through the assets.
foreach ($log_wrapper->field_farm_asset as $asset_wrapper) {
// Get the asset object.
$asset = $asset_wrapper->value();
// The the asset doesn't have an ID, skip it.
if (empty($asset->id)) {
continue;
}
// If the asset is not one of the desired types, skip it.
if (!empty($asset_types) && !in_array($asset->type, $asset_types)) {
continue;
}
// Look up the asset's next movement log (departure from the area). Only
// include logs that have been marked "done".
$log_depart = farm_movement_asset_next_movement($asset, $log_arrive->timestamp, TRUE);
// Record the asset's time spent in this area.
$history[$asset->id][] = array(
'arrive' => $log_arrive,
'depart' => !empty($log_depart) ? $log_depart : NULL,
);
}
}
// Return the history.
return $history;
}
/**
* Load an asset's latest log that defines a movement.
*
* @param FarmAsset $asset
* The farm_asset object to look for.
* @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 $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* any other value is used, no filtering will be applied. Defaults to TRUE.
*
* @return Log|bool
* Returns a log entity. FALSE if something goes wrong.
*/
function farm_movement_asset_latest_movement(FarmAsset $asset, $time = REQUEST_TIME, $done = TRUE) {
/**
* Please read the comments in farm_movement_asset_movement_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.
*/
// If the asset doesn't have an ID (for instance if it is new and hasn't been
// saved yet), bail.
if (empty($asset->id)) {
return FALSE;
}
// Make a query for loading the latest movement log.
$query = farm_movement_asset_movement_query($asset->id, $time, $done);
// Execute the query and gather the log id.
$result = $query->execute();
$log_id = $result->fetchField();
// If a log id exists, load and return it.
if (!empty($log_id)) {
return log_load($log_id);
}
return FALSE;
}
/**
* Load an asset's next log that defines a movement.
*
* @param FarmAsset $asset
* The farm_asset object to look for.
* @param int $time
* Unix timestamp limiter. Only logs after this time will be included.
* Defaults to the current time. Set to 0 to load the absolute first.
* @param bool|null $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* this is set to NULL, no filtering will be applied. Defaults to FALSE
* because the default $time is now, and future logs are generally not done
* yet.
*
* @return Log|bool
* Returns a log entity. FALSE if something goes wrong.
*/
function farm_movement_asset_next_movement(FarmAsset $asset, $time = REQUEST_TIME, $done = FALSE) {
/**
* Please read the comments in farm_movement_asset_movement_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.
*/
// Ensure $time is valid, because it may be used directly in the query
// string. This is defensive code. See note about
// farm_movement_asset_movement_query() above.
if (!is_numeric($time) || $time < 0) {
$time = REQUEST_TIME;
}
// If the asset doesn't have an ID (for instance if it is new and hasn't been
// saved yet), bail.
if (empty($asset->id)) {
return FALSE;
}
// Make a query to load all movement logs for the asset. Use a timestamp of 0
// to include future logs.
$query = farm_movement_asset_movement_query($asset->id, 0, $done, FALSE);
// Filter to only include movements after the specified timestamp.
$query->where('ss_log.timestamp > ' . $time);
// Order by timestamp and log ID ascending so we can get the first one (this
// overrides the default sort added by farm_log_query())
$query->orderBy('ss_log.timestamp', 'ASC');
$query->orderBy('ss_log.id', 'ASC');
// Limit to 1 record.
$query->range(0, 1);
// Execute the query and gather the log id.
$result = $query->execute();
$log_id = $result->fetchField();
// If a log id exists, load and return it.
if (!empty($log_id)) {
return log_load($log_id);
}
return FALSE;
}
/**
* Build a query to find movement logs of a specific asset.
*
* @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 relationship 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 $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* any other value is used, no filtering will be applied. Defaults to TRUE.
* @param bool $single
* Whether or not to limit the query to a single result. Defaults to TRUE.
* @param string $field
* If the log id is desired, use "log_id. If the movement field_collection id
* is desired, use "movement_id".
*
* @return \SelectQuery
* Returns a SelectQuery object.
*/
function farm_movement_asset_movement_query($asset_id, $time = REQUEST_TIME, $done = TRUE, $single = TRUE, $field = 'log_id') {
/**
* Please read the comments in farm_log_asset_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.
*/
// Use the farm_log_asset_query() helper function to start a query object.
$query = farm_log_asset_query($asset_id, $time, $done, NULL, $single);
// Add a query tag to identify where this came from.
$query->addTag('farm_movement_asset_movement_query');
// Join in the Movement field collection and filter to only include logs with
// movements. Use an inner join to exclude logs that do not have any
// movement field collections attached.
$query->innerJoin('field_data_field_farm_movement', 'ss_fdffm', "ss_fdffm.entity_type = 'log' AND ss_fdffm.entity_id = ss_log.id AND ss_fdffm.deleted = 0");
// Join in the movement's "move to" field, and filter to only include logs
// that have a movement with a "move to" value. Use an inner join to exclude
// logs that do not have a "move to" area reference.
$query->innerJoin('field_data_field_farm_move_to', 'ss_fdffmt', "ss_fdffmt.entity_type = 'field_collection_item' AND ss_fdffmt.bundle = 'field_farm_movement' AND ss_fdffmt.entity_id = ss_fdffm.field_farm_movement_value AND ss_fdffmt.deleted = 0");
// If $field is 'log_id', then add the log ID field.
if ($field == 'log_id') {
$query->addField('ss_log', 'id');
}
// Or, if $field is 'movement_id', then add the movement ID field.
elseif ($field == 'movement_id') {
$query->addField('ss_fdffm', 'field_farm_movement_value');
}
// Return the query object.
return $query;
}
/**
* Build a query to find movement logs to a specific area.
*
* @param int $area_id
* The area id to search for.
* @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 $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* any other value is used, no filtering will be applied. Defaults to TRUE.
*
* @return \SelectQuery
* Returns a SelectQuery object.
*/
function farm_movement_area_movement_query($area_id, $time = REQUEST_TIME, $done = 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.
*/
// Ensure $area_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($area_id) || $area_id < 0) {
$area_id = db_escape_field($area_id);
}
// Use the farm_log_query() helper function to start a query object. Do not
// limit the results to a single row because by the very nature of this we
// want to find all assets in the area, which may come from multiple logs.
$query = farm_log_query($time, $done, NULL, FALSE);
// Add a query tag to identify where this came from.
$query->addTag('farm_movement_area_movement_query');
// Join in the Movement field collection and filter to only include logs with
// movements. Use an inner join to exclude logs that do not have a movement
// field collection attached.
$query->innerJoin('field_data_field_farm_movement', 'ss_fdffm', "ss_fdffm.entity_type = 'log' AND ss_fdffm.entity_id = ss_log.id AND ss_fdffm.deleted = 0");
// Join in the movement's "move to" field, and filter to only include logs
// that have a movement with a "move to" the specified area. Use an inner
// join to exclude logs that do not have a "move to" area reference.
$query->innerJoin('field_data_field_farm_move_to', 'ss_fdffmt', "ss_fdffmt.entity_type = 'field_collection_item' AND ss_fdffmt.bundle = 'field_farm_movement' AND ss_fdffmt.entity_id = ss_fdffm.field_farm_movement_value AND ss_fdffmt.deleted = 0");
$query->where('ss_fdffmt.field_farm_move_to_tid = ' . $area_id);
// Return the query object.
return $query;
}
/**
* Load all assets in an area.
*
* @param $area
* The area to load assets from.
* @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|null $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* this is set to NULL, no filtering will be applied. Defaults to TRUE.
* @param bool $archived
* Whether or not to include archived assets. Defaults to FALSE.
*
* @return array
* Returns an array of the area's assets, keyed by asset ID.
*/
function farm_movement_area_assets($area, $time = REQUEST_TIME, $done = TRUE, $archived = FALSE) {
/**
* @todo
* Merge/abstract with farm_group_members().
*/
// Start an empty array of assets.
$assets = array();
// If the area doesn't have an id, bail.
if (empty($area->tid)) {
return $assets;
}
// Build a query to find all assets in the area.
$query = farm_movement_area_assets_query($area->tid, $time, $done, $archived);
// Execute the query to get a list of asset IDs.
$result = $query->execute();
// Iterate through the results.
foreach ($result as $row) {
// If the asset ID is empty, skip it.
if (empty($row->asset_id)) {
continue;
}
// If the asset has already been loaded, skip it.
if (array_key_exists($row->asset_id, $assets)) {
continue;
}
// Load the asset.
$assets[$row->asset_id] = farm_asset_load($row->asset_id);
}
// Return the array of assets.
return $assets;
}
/**
* Build a query to find assets in a given area.
*
* @param int $area_id
* The area's taxonomy term id to search for.
* @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 $done
* Whether or not to only show logs that are marked as "done". TRUE will limit
* to logs that are done, and FALSE will limit to logs that are not done. If
* any other value is used, no filtering will be applied. Defaults to TRUE.
* @param bool $archived
* Whether or not to include archived assets. Defaults to FALSE.
*
* @return \SelectQuery
* Returns a SelectQuery object.
*/
function farm_movement_area_assets_query($area_id, $time = REQUEST_TIME, $done = TRUE, $archived = FALSE) {
/**
* @todo
* Merge/abstract with farm_group_members_query().
*/
/**
* Please read the comments in farm_log_asset_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.
*/
// Ensure $area_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($area_id) || $area_id < 0) {
$area_id = db_escape_field($area_id);
}
// Use the farm_log_asset_query() helper function to start a subquery object.
// Do not limit the results to a single row because by the very nature of
// this we want to find all assets in the area, which may come from multiple
// logs.
$subquery = farm_log_asset_query(NULL, $time, $done, NULL, FALSE);
// Add a query tag to identify where this came from.
$subquery->addTag('farm_movement_area_assets_query');
// Join in the Movement field collection. Use an inner join to exclude logs
// that do not have a movement field collection attached.
$subquery->innerJoin('field_data_field_farm_movement', 'ss_fdffm', "ss_fdffm.entity_type = 'log' AND ss_fdffm.entity_id = ss_log.id AND ss_fdffm.deleted = 0");
// Add the asset ID field.
$subquery->addField('ss_fdffa', 'field_farm_asset_target_id');
// Add an expression to extract the assets most recent movement log ID.
$subquery->addExpression("SUBSTRING_INDEX(GROUP_CONCAT(ss_log.id ORDER BY ss_log.timestamp DESC, ss_log.id DESC SEPARATOR ','), ',', 1)", 'ss_current_log_id');
// Group by asset ID.
$subquery->groupBy('ss_fdffa.field_farm_asset_target_id');
// Create a query that selects from the subquery.
$query = db_select($subquery, 'ss_asset_current_log');
// Join in the asset's current log.
$query->join('log', 'ss_current_log', 'ss_current_log.id = ss_asset_current_log.ss_current_log_id');
// Join in the Movement field collection. Use an inner join to exclude logs
// that do not have a movement field collection attached.
$query->innerJoin('field_data_field_farm_movement', 'ss_current_log_fdffm', "ss_current_log_fdffm.entity_type = 'log' AND ss_current_log_fdffm.entity_id = ss_current_log.id AND ss_current_log_fdffm.deleted = 0");
// Join in the movement's "move to" field, and filter to only include logs
// that have a movement that references the specified area. Use an inner
// join to exclude logs that do not have an area reference.
$query->innerJoin('field_data_field_farm_move_to', 'ss_current_log_fdffmt', "ss_current_log_fdffmt.entity_type = 'field_collection_item' AND ss_current_log_fdffmt.bundle = 'field_farm_movement' AND ss_current_log_fdffmt.entity_id = ss_current_log_fdffm.field_farm_movement_value AND ss_current_log_fdffmt.deleted = 0");
$query->where('ss_current_log_fdffmt.field_farm_move_to_tid = ' . $area_id);
// Exclude archived assets, if requested.
if (empty($archived)) {
$query->join('farm_asset', 'ss_current_log_fa', "ss_asset_current_log.field_farm_asset_target_id = ss_current_log_fa.id");
$query->where('ss_current_log_fa.archived = 0');
}
// Add the asset ID field.
$query->addField('ss_asset_current_log', 'field_farm_asset_target_id', 'asset_id');
// Return the query object.
return $query;
}
/**
* Implements hook_action_info().
*/
function farm_movement_action_info() {
return array(
'farm_movement_asset_move_action' => array(
'type' => 'farm_asset',
'label' => t('Move'),
'configurable' => TRUE,
'triggers' => array('any'),
'aggregate' => TRUE,
),
);
}
/**
* Configuration form for farm_movement_asset_move 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_movement_asset_move_action_form(array $context, array $form_state) {
// Date field.
$form['date'] = array(
'#type' => 'date_select',
'#title' => t('Date'),
'#date_format' => 'M j Y',
'#date_type' => DATE_FORMAT_UNIX,
'#date_year_range' => '-10:+3',
'#default_value' => date('Y-m-d H:i', REQUEST_TIME),
'#required' => TRUE,
);
// Area reference field.
$form['areas'] = array(
'#type' => 'textfield',
'#title' => t('Location'),
'#autocomplete_path' => 'taxonomy/autocomplete/field_farm_area',
'#required' => TRUE,
);
// Done field.
$form['done'] = array(
'#type' => 'checkbox',
'#title' => t('This movement has taken place (mark the log as done)'),
'#default_value' => TRUE,
);
// Return the form.
return $form;
}
/**
* Submit handler for farm_movement_asset_move 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_movement_asset_move_action_submit(array $form, array $form_state) {
// Start to build the context array.
$context = array();
// Load the areas.
$context['areas'] = farm_term_parse_names($form_state['values']['areas'], 'farm_areas', TRUE);
// Convert the date to a timestamp.
$timestamp = strtotime($form_state['values']['date']);
// The action form only includes month, day, and year. If the movement is
// today, then we assume that the current time should also be included.
if (date('Ymd', $timestamp) == date('Ymd', REQUEST_TIME)) {
$context['timestamp'] = REQUEST_TIME;
}
// Otherwise, the movement is in the past/future, so don't include a time.
else {
$context['timestamp'] = $timestamp;
}
// Copy the "done" value as a boolean.
$context['done'] = !empty($form_state['values']['done']) ? TRUE : FALSE;
// Return the context array.
return $context;
}
/**
* Action function for farm_movement_asset_move.
*
* Creates a new movement activity log for the specified assets.
*
* @param array $assets
* An array of asset entities to move.
* @param array $context
* Array with parameters for this action.
*/
function farm_movement_asset_move_action(array $assets, $context = array()) {
// If we're missing assets, areas, or a timestamp, bail.
if (empty($assets) || empty($context['areas']) || empty($context['timestamp'])) {
drupal_set_message(t('Could not perform movement because required information was missing.'), 'error');
return;
}
// Create a movement activity log.
farm_movement_create($assets, $context['areas'], $context['timestamp'], 'farm_activity', $context['done']);
}
/**
* Create a log for moving assets to areas.
*
* @param array|FarmAsset $assets
* Array of assets to include in the move.
* @param array $areas
* An array of areas to move to.
* @param int $timestamp
* The timestamp of the move. Defaults to the current time.
* @param string $log_type
* The type of log to create. Defaults to "farm_observation".
* @param bool $done
* Boolean indicating whether or not the log should be marked "done". Defaults
* to TRUE.
* @param string $geom
* Optionally provide a movement geometry in WKT format. If this is empty, the
* geometry will be copied from the referenced area(s).
*
* @return \Log
* Returns the log that was created.
*/
function farm_movement_create($assets, $areas = array(), $timestamp = REQUEST_TIME, $log_type = 'farm_observation', $done = TRUE, $geom = '') {
// If no areas are defined, bail.
if (empty($areas)) {
return;
}
// If $assets isn't an array, wrap it.
if (!is_array($assets)) {
$assets = array($assets);
}
// If the log is an observation, set the name to:
// "[assets] located in [areas]".
// If the log is an activity, set the name to:
// "Move [assets] to [areas]".
$log_name = '';
$assets_summary = farm_log_entity_label_summary('farm_asset', $assets);
$areas_summary = farm_log_entity_label_summary('taxonomy_term', $areas);
$arguments = array('!assets' => $assets_summary, '!areas' => $areas_summary);
if ($log_type == 'farm_observation') {
$log_name = t('!assets located in !areas', $arguments);
}
elseif ($log_type == 'farm_activity') {
$log_name = t('Move !assets to !areas', $arguments);
}
// Create a new farm log entity.
$log = farm_log_create($log_type, $log_name, $timestamp, $done, $assets);
// Create a new movement field_collection entity attached to the log.
$movement = entity_create('field_collection_item', array('field_name' => 'field_farm_movement'));
$movement->setHostEntity('log', $log);
// Create an entity wrapper for the adjustment.
$movement_wrapper = entity_metadata_wrapper('field_collection_item', $movement);
// Iterate through the areas and add each to the "Move to" field.
foreach ($areas as $area) {
$movement_wrapper->field_farm_move_to[] = $area;
}
// Add geometry, if provided.
if (!empty($geom)) {
$movement_wrapper->field_farm_geofield->set(array(array('geom' => $geom)));
}
// Save the movement.
$movement_wrapper->save();
// Return the log.
return $log;
}