mirror of
https://github.com/farmOS/farmOS.git
synced 2024-02-23 11:37:38 +01:00
368 lines
10 KiB
Text
368 lines
10 KiB
Text
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Farm constraint module.
|
|
*/
|
|
|
|
/**
|
|
* Implements hook_hook_info().
|
|
*/
|
|
function farm_constraint_hook_info() {
|
|
$hooks['farm_constraint'] = array(
|
|
'group' => 'farm_constraint',
|
|
);
|
|
return $hooks;
|
|
}
|
|
|
|
/**
|
|
* Function for checking if a constraint exists (if a record is referenced by
|
|
* other records).
|
|
*
|
|
* @param $type
|
|
* The entity type.
|
|
* @param $bundle
|
|
* The entity bundle.
|
|
* @param $id
|
|
* The entity id.
|
|
*
|
|
* @return bool
|
|
* Returns TRUE if the record is referenced elsewhere, FALSE otherwise.
|
|
*/
|
|
function farm_constraint_exists($type, $bundle, $id) {
|
|
|
|
// Get a list of constraints.
|
|
$constraints = farm_constraint_list($type, $bundle, $id);
|
|
|
|
// Iterate through the constraints and return TRUE if any were detected.
|
|
foreach ($constraints as $constraint) {
|
|
if (!empty($constraint)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// If we haven't already returned, no constraints were detected.
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Function for generating a list of constraints for a given entity.
|
|
*
|
|
* @param $type
|
|
* The entity type.
|
|
* @param $bundle
|
|
* The entity bundle.
|
|
* @param $id
|
|
* The entity id.
|
|
*
|
|
* @return array
|
|
* Returns an array of constraints preventing an entity from being deleted.
|
|
*/
|
|
function farm_constraint_list($type, $bundle, $id) {
|
|
|
|
// Ask other modules if they are aware of any constraints.
|
|
$constraints = module_invoke_all('farm_constraint', $type, $bundle, $id);
|
|
|
|
// Return the constraints.
|
|
return $constraints;
|
|
}
|
|
|
|
/**
|
|
* Helper function for checking to see if an entity reference exists in a
|
|
* database table.
|
|
*
|
|
* @param $references
|
|
* See farm_constraint_table_references() below.
|
|
* @param $type
|
|
* The entity type.
|
|
* @param $bundle
|
|
* The entity bundle.
|
|
* @param $id
|
|
* The entity ID.
|
|
*
|
|
* @return bool
|
|
* Returns TRUE if a constraint was found, FALSE otherwise.
|
|
*/
|
|
function farm_constraint_table_references_exist($references, $type, $bundle, $id) {
|
|
|
|
// Get a list of matching records.
|
|
$records = farm_constraint_table_references($references, $type, $bundle, $id);
|
|
|
|
// If records were found, return TRUE. Otherwise, return FALSE.
|
|
if (!empty($records)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Helper function for finding references to entities in tables.
|
|
*
|
|
* @param $references
|
|
* An array of information about entity references. For example:
|
|
* $references = array(
|
|
* 'herd' => array(
|
|
* 'type' => 'farm_asset',
|
|
* 'bundle' => 'group',
|
|
* 'tables' => array(
|
|
* 'farm_grazing_rotations' => 'herd_id',
|
|
* 'farm_grazing_herds' => 'herd_id',
|
|
* ),
|
|
* ),
|
|
* );
|
|
* @param $type
|
|
* The entity type.
|
|
* @param $bundle
|
|
* The entity bundle.
|
|
* @param $id
|
|
* The entity ID.
|
|
*
|
|
* @return array
|
|
* Returns an array of matching records.
|
|
*/
|
|
function farm_constraint_table_references($references, $type, $bundle, $id) {
|
|
|
|
// Start an empty records array.
|
|
$records = array();
|
|
|
|
// Iterate through the references.
|
|
foreach ($references as $info) {
|
|
|
|
// If the entity type doesn't match, skip it.
|
|
if ($type != $info['type']) {
|
|
continue;
|
|
}
|
|
|
|
// If a the referenced bundle matters, check it.
|
|
if (!empty($info['bundle']) && $bundle != $info['bundle']) {
|
|
continue;
|
|
}
|
|
|
|
// If there is no table information defined, skip it.
|
|
if (empty($info['tables'])) {
|
|
continue;
|
|
}
|
|
|
|
// Iterate through the tables.
|
|
foreach ($info['tables'] as $table => $column) {
|
|
|
|
// Query for a matching entity reference in the table.
|
|
$exists = db_query('SELECT COUNT(*) FROM {' . $table . '} WHERE ' . $column . ' = :id', array(':id' => $id))->fetchField();
|
|
|
|
// If one exists, a constraint exists. Return TRUE.
|
|
if (!empty($exists)) {
|
|
$records[] = array(
|
|
'constraint' => 'table_reference',
|
|
'table' => $table,
|
|
'column' => $column,
|
|
'entity_type' => $type,
|
|
'entity_id' => $id,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return matching records.
|
|
return $records;
|
|
}
|
|
|
|
/**
|
|
* Implements hook_form_alter().
|
|
*/
|
|
function farm_constraint_form_alter(&$form, &$form_state, $form_id) {
|
|
|
|
// This alters forms for deleting individual entities.
|
|
|
|
// Extract the entity type, bundle, and ID from the form.
|
|
$type = NULL;
|
|
$bundle = NULL;
|
|
$id = NULL;
|
|
switch ($form_id) {
|
|
|
|
// Farm asset delete form.
|
|
case 'farm_asset_delete_form':
|
|
$type = 'farm_asset';
|
|
if (!empty($form['farm_asset']['#value'])) {
|
|
$bundle = $form['farm_asset']['#value']->type;
|
|
$id = $form['farm_asset']['#value']->id;
|
|
}
|
|
break;
|
|
|
|
// Log delete form.
|
|
case 'log_delete_form':
|
|
$type = 'log';
|
|
if (!empty($form['log']['#value'])) {
|
|
$bundle = $form['log']['#value']->type;
|
|
$id = $form['log']['#value']->id;
|
|
}
|
|
break;
|
|
|
|
// User cancel form.
|
|
case 'user_cancel_confirm_form':
|
|
$type = 'user';
|
|
if (!empty($form['_account']['#value'])) {
|
|
$bundle = '';
|
|
$id = $form['_account']['#value']->uid;
|
|
}
|
|
break;
|
|
|
|
// Taxonomy term edit form.
|
|
// The taxonomy module uses the same form for editing and deleting,
|
|
// differentiated by the presence of $form_state['confirm_delete'].
|
|
case 'taxonomy_form_term':
|
|
$type = 'taxonomy_term';
|
|
if (!empty($form_state['confirm_delete']) && !empty($form['#term'])) {
|
|
$bundle = $form['#term']->vocabulary_machine_name;
|
|
$id = $form['#term']->tid;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// If the entity type and ID were not found, bail (bundle will be blank on
|
|
// user entities).
|
|
if (empty($type) || empty($id)) {
|
|
return;
|
|
}
|
|
|
|
// Check to see if any constraints exist for this entity. If not, bail.
|
|
if (!farm_constraint_exists($type, $bundle, $id)) {
|
|
return;
|
|
}
|
|
|
|
// From here on, we know that constraints exist, so we want to prevent
|
|
// deletion of the entity.
|
|
|
|
// If this is a user, remove additional options.
|
|
if ($type == 'user') {
|
|
unset($form['user_cancel_method']);
|
|
unset($form['user_cancel_confirm']);
|
|
unset($form['user_cancel_notify']);
|
|
}
|
|
|
|
// Override the page title.
|
|
drupal_set_title('Unable to delete this record');
|
|
|
|
// Modify the form description.
|
|
$form['description']['#markup'] = t('This record cannot be deleted because it is referenced by other records.') . ' ' . t('You must remove all references to this record before you can delete it.');
|
|
|
|
// Remove submit handlers and buttons.
|
|
unset($form['#submit']);
|
|
unset($form['actions']);
|
|
}
|
|
|
|
/**
|
|
* Implements hook_views_bulk_operations_form_alter().
|
|
*/
|
|
function farm_constraint_views_bulk_operations_form_alter(&$form, &$form_state, $vbo) {
|
|
|
|
// Add the entity type as a form value, for use in validation.
|
|
if (!empty($vbo->entity_type)) {
|
|
$form['farm_constraint_entity_type'] = array(
|
|
'#type' => 'value',
|
|
'#value' => $vbo->entity_type,
|
|
);
|
|
}
|
|
|
|
// Add a validation function to the submit button which checks for
|
|
// constraints before deleting entities.
|
|
if ($vbo->get_vbo_option('display_type') == 0) {
|
|
$form['select']['submit']['#validate'][] = 'farm_constraint_views_bulk_operations_form_validate';
|
|
}
|
|
else {
|
|
if (!empty($form['select']['action::views_bulk_operations_delete_item'])) {
|
|
$form['select']['action::views_bulk_operations_delete_item']['#validate'][] = 'farm_constraint_views_bulk_operations_form_validate';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validation function for Views Bulk Operations form.
|
|
*/
|
|
function farm_constraint_views_bulk_operations_form_validate(&$form, &$form_state) {
|
|
|
|
// Only validate if entities are being deleted.
|
|
if (empty($form_state['values']['operation']) || $form_state['values']['operation'] != 'action::views_bulk_operations_delete_item') {
|
|
return;
|
|
}
|
|
|
|
// If the entity type was not saved, bail.
|
|
if (empty($form_state['values']['farm_constraint_entity_type'])) {
|
|
return;
|
|
}
|
|
|
|
// Get the entity type.
|
|
$type = $form_state['values']['farm_constraint_entity_type'];
|
|
|
|
// Iterate through the selected entities.
|
|
foreach ($form_state['values']['views_bulk_operations'] as $key => $id) {
|
|
if (!empty($id)) {
|
|
|
|
// Load the entity.
|
|
$entities = entity_load($type, array($id));
|
|
$entity = reset($entities);
|
|
|
|
// Get the entity bundle.
|
|
list(, , $bundle) = entity_extract_ids($type, $entity);
|
|
|
|
// If a constraint exists on the entity, set a form error.
|
|
if (farm_constraint_exists($type, $bundle, $id)) {
|
|
$entity_label = entity_label($type, $entity);
|
|
$entity_uri = entity_uri($type, $entity);
|
|
$message = t('The record <a href="@entity_path">@entity_label</a> cannot be deleted because it is referenced by other records.', array('@entity_path' => url($entity_uri['path']), '@entity_label' => $entity_label)) . ' ' . t('You must remove all references to this record before you can delete it.');
|
|
form_set_error('views_bulk_operations][' . $key, $message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements hook_restws_request_alter().
|
|
*/
|
|
function farm_constraint_restws_request_alter(array &$request) {
|
|
|
|
// Prevent deletion of entities that are referenced by other entities.
|
|
// This works by altering the "operation" in the request object that is built
|
|
// in restws_handle_request() before the access check is called. We replace
|
|
// the "delete" operation with "noop" so that the access check fails.
|
|
// This is a heavy-handed approach, and doesn't provide more nuanced options
|
|
// for dealing with referenced data, but it works to stop the deletion from
|
|
// occurring.
|
|
/**
|
|
* @todo
|
|
* This is a very hacky way to do this, and doesn't provide any feedback to
|
|
* the API user as to why their request fails.
|
|
*/
|
|
|
|
// If an operation, resource, and ID are not available, bail.
|
|
if (empty($request['op']) || empty($request['resource']) || empty($request['id'])) {
|
|
return;
|
|
}
|
|
|
|
// We only care about delete operations.
|
|
if ($request['op'] != 'delete') {
|
|
return;
|
|
}
|
|
|
|
// Get the entity type.
|
|
$type = $request['resource']->resource();
|
|
|
|
// Get the entity ID.
|
|
$id = $request['id'];
|
|
|
|
// Get the entity.
|
|
$entity = $request['resource']->read($id);
|
|
|
|
// If the entity doesn't exist it cannot have constraints.
|
|
if (empty($entity)) {
|
|
return;
|
|
}
|
|
|
|
// Get the entity bundle.
|
|
list($entity_id, $revision_id, $bundle) = entity_extract_ids($type, $entity);
|
|
|
|
// If constraints exist on the entity, change the operation to 'noop' so the
|
|
// access check in restws_handle_request() fails.
|
|
if (farm_constraint_exists($type, $bundle, $id)) {
|
|
$request['op'] = 'noop';
|
|
}
|
|
}
|