array( 'title' => t('Administer farm plan module'), 'description' => t('Gives full access to everything in the farm plan module.'), 'restrict access' => TRUE, ), 'administer farm_plan types' => array( 'title' => t('Administer farm plan types'), 'restrict access' => TRUE, ), 'view farm plans' => array( 'title' => t('View farm plans'), 'description' => t('Allows users to view the full list of farm plans.'), ), ); // Add permissions for each farm_plan type. foreach (farm_plan_types() as $farm_plan_type) { $type = $farm_plan_type->type; $ops = array('view', 'edit', 'delete'); $scopes = array('any', 'own'); $perms += array( "create $type farm plans" => array( 'title' => t('Create new %type_name farm plans', array('%type_name' => $farm_plan_type->label)), ), ); foreach ($ops as $op) { foreach ($scopes as $scope) { $perms += array( "$op $scope $type farm plans" => array( 'title' => drupal_ucfirst($op) . ' ' . $scope . ' ' . t('%type_name farm plans', array('%type_name' => $farm_plan_type->label)), ), ); } } } return $perms; } /** * Implements hook_menu(). */ function farm_plan_menu() { $items = array(); $items['farm/plan/add'] = array( 'title' => 'Add plan', 'page callback' => 'farm_plan_add_types_page', 'access callback' => 'farm_plan_add_access', 'file' => 'farm_plan.pages.inc', ); foreach (farm_plan_types() as $type => $info) { $items['farm/plan/add/' . $type] = array( 'title' => 'Add ' . $info->label, 'page callback' => 'farm_plan_add', 'page arguments' => array(3), 'access callback' => 'farm_plan_access', 'access arguments' => array('create', 3), 'file' => 'farm_plan.pages.inc', ); } $farm_plan_uri = 'farm/plan/%farm_plan'; $farm_plan_uri_argument_position = 2; $items[$farm_plan_uri] = array( 'title callback' => 'entity_label', 'title arguments' => array('farm_plan', $farm_plan_uri_argument_position), 'page callback' => 'farm_plan_view', 'page arguments' => array($farm_plan_uri_argument_position), 'access callback' => 'farm_plan_access', 'access arguments' => array('view', $farm_plan_uri_argument_position), 'file' => 'farm_plan.pages.inc', ); $items[$farm_plan_uri . '/view'] = array( 'title' => 'Plan', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items[$farm_plan_uri . '/delete'] = array( 'title' => 'Delete plan', 'title callback' => 'farm_plan_label', 'title arguments' => array($farm_plan_uri_argument_position), 'page callback' => 'drupal_get_form', 'page arguments' => array('farm_plan_delete_form', $farm_plan_uri_argument_position), 'access callback' => 'farm_plan_access', 'access arguments' => array('update', $farm_plan_uri_argument_position), 'file' => 'farm_plan.pages.inc', ); $items[$farm_plan_uri . '/edit'] = array( 'title' => 'Settings', 'page callback' => 'drupal_get_form', 'page arguments' => array('farm_plan_form', $farm_plan_uri_argument_position), 'access callback' => 'farm_plan_access', 'access arguments' => array('update', $farm_plan_uri_argument_position), 'file' => 'farm_plan.pages.inc', 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, 'weight' => 100, ); // Form for removing a record from a plan. // This is the same as the form for deleting records (below), but it will not // delete records. It will only unlink them from the plan. $items[$farm_plan_uri . '/%/%/remove'] = array( 'title' => 'Remove record', 'page callback' => 'drupal_get_form', 'page arguments' => array('farm_plan_record_remove_form', $farm_plan_uri_argument_position, 3, 4), 'access callback' => 'farm_plan_access', 'access arguments' => array('update', $farm_plan_uri_argument_position), 'file' => 'farm_plan.pages.inc', 'type' => MENU_CALLBACK, ); // Form for deleting a record from a plan. // This is the same as the form for removing records (above), but it also // gives the option to delete the record. $items[$farm_plan_uri . '/%/%/delete'] = array( 'title' => 'Remove record', 'page callback' => 'drupal_get_form', 'page arguments' => array('farm_plan_record_delete_form', $farm_plan_uri_argument_position, 3, 4), 'access callback' => 'farm_plan_access', 'access arguments' => array('update', $farm_plan_uri_argument_position), 'file' => 'farm_plan.pages.inc', 'type' => MENU_CALLBACK, ); // Plan type delete form. $items['admin/config/farm/plan-types/%farm_plan_type/delete'] = array( 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array('farm_plan_type_form_delete_confirm', 4), 'access arguments' => array('administer farm_plan types'), 'weight' => 1, 'type' => MENU_NORMAL_ITEM, 'file' => 'farm_plan.admin.inc', ); return $items; } /** * Implements hook_entity_info(). */ function farm_plan_entity_info() { $return = array( 'farm_plan' => array( 'label' => t('Farm plan'), 'entity class' => 'FarmPlan', 'controller class' => 'FarmPlanController', 'base table' => 'farm_plan', 'fieldable' => TRUE, 'entity keys' => array( 'id' => 'id', 'bundle' => 'type', 'label' => 'name', ), 'bundle keys' => array( 'bundle' => 'type', ), 'bundles' => array(), 'load hook' => 'farm_plan_load', 'view modes' => array( 'full' => array( 'label' => t('Default'), 'custom settings' => FALSE, ), ), 'label callback' => 'entity_class_label', 'uri callback' => 'entity_class_uri', 'module' => 'farm_plan', 'access callback' => 'farm_plan_access', ), ); $return['farm_plan_type'] = array( 'label' => t('Farm plan type'), 'entity class' => 'FarmPlanType', 'controller class' => 'FarmPlanTypeController', 'base table' => 'farm_plan_type', 'fieldable' => FALSE, 'bundle of' => 'farm_plan', 'exportable' => TRUE, 'entity keys' => array( 'id' => 'id', 'name' => 'type', 'label' => 'label', ), 'module' => 'farm_plan', // Enable the entity API admin UI. 'admin ui' => array( 'path' => 'admin/config/farm/plan-types', 'file' => 'farm_plan.admin.inc', 'controller class' => 'FarmPlanTypeUIController', ), 'access callback' => 'farm_plan_type_access', ); return $return; } /** * Implements hook_entity_info_alter(). */ function farm_plan_entity_info_alter(&$entity_info) { foreach (farm_plan_types() as $type => $info) { $entity_info['farm_plan']['bundles'][$type] = array( 'label' => $info->label, 'admin' => array( 'path' => 'admin/config/farm/plan-types/manage/%farm_plan_type', 'real path' => 'admin/config/farm/plan-types/manage/' . $type, 'bundle argument' => 5, ), ); } } /** * Implements hook_entity_property_info_alter(). */ function farm_plan_entity_property_info_alter(&$info) { $properties = &$info['farm_plan']['properties']; $properties['name'] = array( 'label' => t('Name'), 'description' => t('The name of the plan.'), 'setter callback' => 'entity_property_verbatim_set', 'schema field' => 'name', ); $properties['type'] = array( 'label' => t('Farm plan type'), 'type' => 'token', 'description' => t('The farm plan type.'), 'setter callback' => 'entity_property_verbatim_set', 'access callback' => 'farm_plan_properties_access', 'options list' => 'farm_plan_type_get_names', 'required' => TRUE, 'schema field' => 'type', ); $properties['uid'] = array( 'label' => t('Owner'), 'type' => 'user', 'description' => t('The owner of the plan.'), 'setter callback' => 'entity_property_verbatim_set', 'access callback' => 'farm_plan_properties_access', 'required' => TRUE, 'schema field' => 'uid', ); $properties['created'] = array( 'label' => t('Created'), 'type' => 'date', 'description' => t('The timestamp when the plan was created.'), 'setter callback' => 'entity_property_verbatim_set', 'access callback' => 'farm_plan_properties_access', 'required' => TRUE, 'schema field' => 'created', ); $properties['changed'] = array( 'label' => t('Changed'), 'type' => 'date', 'description' => t('The timestamp when the plan was last modified.'), 'setter callback' => 'entity_property_verbatim_set', 'access callback' => 'farm_plan_properties_access', 'required' => TRUE, 'schema field' => 'changed', ); $properties['active'] = array( 'label' => t('Active'), 'description' => t('Whether the plan is active.'), 'setter callback' => 'entity_property_verbatim_set', 'access callback' => 'farm_plan_properties_access', 'schema field' => 'active', 'type' => 'boolean', ); } /** * Implements hook_field_extra_fields(). */ function farm_plan_field_extra_fields() { $farm_plan_types = farm_plan_types(); $extra_fields = array( 'farm_plan' => array(), ); foreach ($farm_plan_types as $type) { $extra_fields['farm_plan'][$type->type] = array( 'form' => array( // Add plan name field to field UI. 'name' => array( 'label' => t('Name'), 'description' => t('The name of the plan.'), 'weight' => -10, ), ), ); } return $extra_fields; } /** * Implements hook_entity_view(). */ function farm_plan_entity_view($entity, $type, $view_mode, $langcode) { // If the entity is not a farm_plan, bail. if ($type != 'farm_plan') { return; } // Add the plan type. $plan_types = farm_plan_type_get_names(); if (!empty($plan_types[$entity->type])) { $entity->content['type'] = array( '#markup' => '
Plan type: ' . $plan_types[$entity->type] . '
', '#weight' => -101, ); } // Add the plan's "active" status. if ($entity->active) { $status = 'Yes'; } else { $status = 'No'; drupal_set_message(t('This plan is no longer active. Inactive plans should be considered "archived" and should not be edited or deleted unless they contain information that is incorrect.'), 'warning'); } $entity->content['active'] = array( '#markup' => '
Active plan: ' . $status . '
', '#weight' => -100, ); // Summarize areas and assets associated with the plan (logs will be added via // Views in hook_farm_ui_entity_views() below. $plan_areas = farm_plan_linked_records('area', $entity->id); if (!empty($plan_areas)) { $plan_area_links = array(); foreach ($plan_areas as $area_id) { $area = taxonomy_term_load($area_id); $entity_label = entity_label('taxonomy_term', $area); $entity_uri = entity_uri('taxonomy_term', $area); $plan_area_links[] = l($entity_label, $entity_uri['path']); } $entity->content['farm_plan_areas'] = array( '#markup' => '
Areas: ' . implode(', ', $plan_area_links) . '
', ); } $plan_assets = farm_plan_linked_records('asset', $entity->id); if (!empty($plan_assets)) { $plan_asset_links = array(); foreach ($plan_assets as $asset_id) { $asset = farm_asset_load($asset_id); $entity_label = entity_label('farm_asset', $asset); $entity_uri = entity_uri('farm_asset', $asset); $plan_asset_links[] = l($entity_label, $entity_uri['path']); } $entity->content['farm_plan_assets'] = array( '#markup' => '
Assets: ' . implode(', ', $plan_asset_links) . '
', ); } } /** * Implements hook_farm_ui_entity_views(). */ function farm_plan_farm_ui_entity_views($entity_type, $bundle, $entity) { // If the entity is not a plantation plan, bail. if (!($entity_type == 'farm_plan' && $bundle == 'forest_plantation')) { return array(); } // Display a View of logs associated with the plan. return array( 'farm_plan_logs', ); } /*************************************************************** * Access functions * *************************************************************/ /** * Access callback for plan entities. * * @param string $op * The operation being performed. One of 'view', 'update', 'create', 'delete'. * @param FarmPlan|string $farm_plan * Optionally a specific plan entity to check. * @param object $account * The user to check for. Leave it to NULL to check for the global user. * * @return bool * Whether access is allowed or not. */ function farm_plan_access($op, $farm_plan = NULL, $account = NULL) { $rights = &drupal_static(__FUNCTION__, array()); // If $op is not one of the supported ones, deny access. if (!in_array($op, array('create', 'view', 'update', 'delete'), TRUE)) { return FALSE; } // If no user object is supplied, the access check is for the current user. if (empty($account)) { global $user; $account = $user; } // If no plan is provided, check for access to all plans. if (empty($farm_plan)) { return user_access('view farm plans', $account); } // $farm_plan may be either an object or a plan type. Since plan types // cannot be an integer, use either id or type as the static cache id. $cid = is_object($farm_plan) ? $farm_plan->id : $farm_plan; // If we've already checked access for this plan, user and op, return from // cache. if (isset($rights[$account->uid][$cid][$op])) { return $rights[$account->uid][$cid][$op]; } // If the user has 'administer farm_plan module' permission, grant them // access. if (user_access('administer farm_plan module', $account)) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } // Check access to the plan based on it's type. $type = is_string($farm_plan) ? $farm_plan : $farm_plan->type; $farm_plan_types = farm_plan_types(); $type_names = array(); foreach ($farm_plan_types as $name => $farm_plan_type) { $type_names[] = $name; } if (in_array($type, $type_names)) { if ($op == 'create' && user_access('create ' . $type . ' farm plans', $account)) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } if ($op == 'view') { if (user_access('view any ' . $type . ' farm plans', $account) || (user_access('view own ' . $type . ' farm plans', $account) && ($account->uid == $farm_plan->uid))) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } } if ($op == 'update') { if (user_access('edit any ' . $type . ' farm plans', $account) || (user_access('edit own ' . $type . ' farm plans', $account) && ($account->uid == $farm_plan->uid))) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } } if ($op == 'delete') { if (user_access('delete any ' . $type . ' farm plans', $account) || (user_access('delete own ' . $type . ' farm plans', $account) && ($account->uid == $farm_plan->uid))) { $rights[$account->uid][$cid][$op] = TRUE; return TRUE; } } } // If all else fails, deny access. return FALSE; } /** * Access callback: Checks whether the user has permission to add a plan. * * @param object|null $account * The user account. * * @return bool * TRUE if the user has add permission, otherwise FALSE. */ function farm_plan_add_access($account = NULL) { // If no user object is supplied, the access check is for the current user. if (empty($account)) { global $user; $account = $user; } // Check each of the plan types to see if the user has access. $types = farm_plan_types(); foreach ($types as $type) { if (farm_plan_access('create', $type->type, $account)) { return TRUE; } } // If all else fails, deny access. return FALSE; } /** * Access callback for plan types. * * @param string $op * The operation being performed. * @param FarmPlanType $farm_plan_type * The farm plan entity. * * @return bool * Returns true if the user has access. */ function farm_plan_type_access($op, FarmPlanType $farm_plan_type = NULL) { return user_access('administer farm_plan types'); } /** * Access callback for farm_plan properties. */ function farm_plan_properties_access($op, $property, $entity = NULL, $account = NULL) { // Delegate to the general farm_plan access callback, based on the $op. switch ($op) { case 'view': return farm_plan_access('view', $entity, $account); case 'edit': return farm_plan_access('update', $entity, $account); } // Otherwise, deny access. return FALSE; } /*************************************************************** * Farm plan API functions * *************************************************************/ /** * Load a plan. * * @param int $id * The plan id. * @param bool $reset * Whether or not to reset the entity cache. * * @return FarmPlan * Returns a farm plan object. */ function farm_plan_load($id, $reset = FALSE) { $farm_plans = farm_plan_load_multiple(array($id), array(), $reset); return reset($farm_plans); } /** * Load multiple plans based on certain conditions. * * @param array $ids * An array of farm plan ids. * @param array $conditions * An array of entity load conditions. * @param bool $reset * Whether or not to reset the entity cache. * * @return array * Returns an array of farm plans. */ function farm_plan_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) { return entity_load('farm_plan', $ids, $conditions, $reset); } /** * Save plan. * * @param FarmPlan $farm_plan * The farm plan entity. */ function farm_plan_save(FarmPlan $farm_plan) { entity_save('farm_plan', $farm_plan); } /** * Delete single plan. * * @param FarmPlan $farm_plan * The farm plan entity. */ function farm_plan_delete(FarmPlan $farm_plan) { entity_delete('farm_plan', entity_id('farm_plan', $farm_plan)); } /** * Delete multiple plans. * * @param array $farm_plan_ids * An array of farm plan ids. */ function farm_plan_delete_multiple(array $farm_plan_ids) { entity_delete_multiple('farm_plan', $farm_plan_ids); } /*************************************************************** * Farm plan type API functions * *************************************************************/ /** * Load plan type. * * @param string $farm_plan_type * The farm plan type. * * @return FarmPlanType * Returns a farm plan type entity. */ function farm_plan_type_load($farm_plan_type) { return farm_plan_types($farm_plan_type); } /** * List of plan types. * * @param string $type_name * The farm plan type name. * * @return FarmPlanType|array * Returns either a single type, or an array of types. */ function farm_plan_types($type_name = NULL) { $types = entity_load_multiple_by_name('farm_plan_type', isset($type_name) ? array($type_name) : FALSE); return isset($type_name) ? reset($types) : $types; } /** * Save plan type entity. * * @param FarmPlanType $farm_plan_type * The farm plan type entity. */ function farm_plan_type_save(FarmPlanType $farm_plan_type) { entity_save('farm_plan_type', $farm_plan_type); } /** * Delete single plan type. * * @param FarmPlanType $farm_plan_type * The farm plan type entity. */ function farm_plan_type_delete(FarmPlanType $farm_plan_type) { entity_delete('farm_plan_type', entity_id('farm_plan_type', $farm_plan_type)); } /** * Delete multiple plan types. * * @param array $farm_plan_type_ids * An array of farm plan type ids. */ function farm_plan_type_delete_multiple(array $farm_plan_type_ids) { entity_delete_multiple('farm_plan_type', $farm_plan_type_ids); } /** * Get the names of all plan types. * * @return array * Returns an array of plan type names, keyed by machine name. */ function farm_plan_type_get_names() { $names = array(); $types = farm_plan_types(); foreach ($types as $type) { $names[$type->type] = $type->label; } return $names; } /*************************************************************** * Functions for linking/unlinking plans with other records. * *************************************************************/ /** * Load a list of records associated with a plan. * * @param string $record_type * The record type (see farm_plan_record_relationships()). * @param int $plan_id * The plan ID. * * @return array * Returns an array of record IDs associated with the plan ID. */ function farm_plan_linked_records($record_type, $plan_id) { // Start an empty records array. $records = array(); // If the plan ID is empty, bail. if (empty($plan_id)) { return; } // Get available relationships between plans and other record types. $relationships = farm_plan_record_relationships(); // If a database table and field are not available, bail. if (empty($relationships[$record_type]['table']) || empty($relationships[$record_type]['field'])) { return; } // Get the table and field. $table = $relationships[$record_type]['table']; $field = $relationships[$record_type]['field']; // Query for record IDs. $query = db_select($table, 'r'); $query->addField('r', $field); $query->condition('plan_id', $plan_id); $result = $query->execute(); foreach ($result as $row) { if (!empty($row->{$field})) { $records[] = $row->{$field}; } } // Return the record IDs. return $records; } /** * Link a plan to a record. * * @param string $record_type * The record type (see farm_plan_record_relationships()). * @param int $plan_id * The plan ID. * @param int $record_id * The record ID. */ function farm_plan_link_record($record_type, $plan_id, $record_id) { // First, delete any existing link. farm_plan_unlink_record($record_type, $plan_id, $record_id); // Get available relationships between plans and other record types. $relationships = farm_plan_record_relationships(); // If a database table and field are not available, bail. if (empty($relationships[$record_type]['table']) || empty($relationships[$record_type]['field'])) { return; } // Get the table and field. $table = $relationships[$record_type]['table']; $field = $relationships[$record_type]['field']; // Create a new relationship record. $record = array( 'plan_id' => $plan_id, $field => $record_id, ); drupal_write_record($table, $record); } /** * Unlink a plan from an area. * * @param string $record_type * The record type (see farm_plan_record_relationships()). * @param int $plan_id * The plan ID. * @param int $record_id * The record ID. */ function farm_plan_unlink_record($record_type, $plan_id, $record_id) { // Get available relationships between plans and other record types. $relationships = farm_plan_record_relationships(); // If a database table and field are not available, bail. if (empty($relationships[$record_type]['table']) || empty($relationships[$record_type]['field'])) { return; } // Get the table and field. $table = $relationships[$record_type]['table']; $field = $relationships[$record_type]['field']; // Delete the relationship. $query = db_delete($table); $query->condition('plan_id', $plan_id); $query->condition($field, $record_id); $query->execute(); } /** * Defines available relationships between plans and other record types. * * @return array * Returns an array of record types with the database table and field names * used to store relationships. */ function farm_plan_record_relationships() { return array( 'area' => array( 'entity_type' => 'taxonomy_term', 'entity_type_table' => 'taxonomy_term_data', 'entity_pk' => 'tid', 'table' => 'farm_plan_area', 'field' => 'area_id', ), 'asset' => array( 'entity_type' => 'farm_asset', 'entity_pk' => 'id', 'table' => 'farm_plan_asset', 'field' => 'asset_id', ), 'log' => array( 'entity_type' => 'log', 'entity_pk' => 'id', 'table' => 'farm_plan_log', 'field' => 'log_id', ), 'user' => array( 'entity_type' => 'user', 'entity_pk' => 'uid', 'table' => 'farm_plan_user', 'field' => 'user_id', ), ); } /** * Implements hook_entity_view_alter(). */ function farm_plan_entity_view_alter(&$build, $type) { // Get the entity ID. Bail if not found. $entity_id = NULL; if (!empty($build['#entity'])) { $entity_id = entity_id($type, $build['#entity']); } elseif (!empty($build['#term'])) { $entity_id = entity_id($type, $build['#term']); } if (empty($entity_id)) { return; } // Get available relationships between plans and other record types. $relationships = farm_plan_record_relationships(); // Iterate through the relationships to find a matching record type. $record_type = ''; foreach ($relationships as $relationship => $info) { if ($type == $info['entity_type']) { $record_type = $relationship; break; } } // If a record type wasn't found, bail. if (empty($record_type)) { return; } // Find plan(s) that this entity is associated with. $query = 'SELECT plan_id FROM {' . $relationships[$record_type]['table'] . '} WHERE ' . $relationships[$record_type]['field'] . ' = :entity_id'; $args = array(':entity_id' => $entity_id); $result = db_query($query, $args); $plan_ids = array(); foreach ($result as $row) { if (!empty($row->plan_id)) { $plan_ids[] = $row->plan_id; } } // Iterate through the plans. foreach ($plan_ids as $plan_id) { // Load the plan. $plan = farm_plan_load($plan_id); // Get the plan URL and name. $plan_uri = entity_uri('farm_plan', $plan); $plan_path = $plan_uri['path']; $plan_name = entity_label('farm_plan', $plan); // Set a message pointing to the plan. $args = array( '@record_type' => $record_type, '!plan_path' => $plan_path, '%plan_name' => $plan_name, ); $message = t('This @record_type is part of the plan: %plan_name', $args); drupal_set_message($message); } } /** * Implements hook_entity_delete(). */ function farm_plan_entity_delete($entity, $type) { // Get the entity ID, and skip if it couldn't be found. $entity_id = entity_id($type, $entity); if (empty($entity_id)) { return; } // Get available relationships between plans and other record types. $relationships = farm_plan_record_relationships(); // Make a list of relationships indexed by entity type. $entity_types = array(); foreach ($relationships as $relationship => $info) { if (!empty($info['entity_type'])) { $entity_types[$info['entity_type']][] = $relationship; } } // If a related entity type is being deleted, delete any references to that // entity in the relationship table. // In theory, this shouldn't happen because farm_constraint prevents entities // from being deleted that are linked to plans. But, it could still happen via // an uninformed call to entity_delete(), so we have this logic here to // handle cleanup regardless. if (array_key_exists($type, $entity_types)) { foreach ($entity_types[$type] as $relationship) { // Get information about the relationship. $info = $relationships[$relationship]; // If it doesn't have a table, or a field, skip it. if (empty($info['table']) || empty($info['field'])) { continue; } // Delete all references to this plan in the table. db_query('DELETE FROM {' . $info['table'] . '} WHERE ' . $info['field'] . ' = :entity_id', array(':entity_id' => $entity_id)); } } // If a plan is being deleted, remove references from all relationship tables. elseif ($type == 'farm_plan') { foreach ($relationships as $relationship => $info) { // If it doesn't have a table, skip it. if (empty($info['table'])) { continue; } // Delete all references to this plan in the table. db_query('DELETE FROM {' . $info['table'] . '} WHERE plan_id = :plan_id', array(':plan_id' => $entity_id)); } } }