fape.module in Field API Pane Editor (FAPE) 7
Adds direct field editing via contextual links.
File
fape.moduleView source
<?php
/**
* @file
* Adds direct field editing via contextual links.
*/
/**
* Implements hook_menu()
*/
function fape_menu() {
$items = array();
$items['admin/field/edit/%/%/%'] = array(
// Access is controlled after we have inspected the entity which
// can't easily happen until the callback.
'access arguments' => array(
TRUE,
),
'access callback' => TRUE,
'page callback' => 'fape_field_edit_page',
'page arguments' => array(
3,
4,
5,
),
);
return $items;
}
/**
* Page callback to edit an entity field.
*/
function fape_field_edit_page($entity_type, $entity_id, $field_name, $langcode = NULL) {
// Ensure the entity type is valid:
if (empty($entity_type)) {
return MENU_NOT_FOUND;
}
$entity_info = entity_get_info($entity_type);
if (!$entity_info) {
return MENU_NOT_FOUND;
}
$entities = entity_load($entity_type, array(
$entity_id,
));
if (!$entities) {
return MENU_NOT_FOUND;
}
$entity = reset($entities);
if (!$entity) {
return MENU_NOT_FOUND;
}
if (!isset($langcode)) {
$langcode = entity_language($entity_type, $entity);
}
// Ensure access to update the entity is granted.
if (!entity_access('update', $entity_type, $entity)) {
return MENU_ACCESS_DENIED;
}
// Ensure access to actually update this particular field is granted.
$field = field_info_field($field_name);
if (!field_access('edit', $field, $entity_type, $entity)) {
return MENU_ACCESS_DENIED;
}
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
// This allows us to have limited support for non-field API fields.
// Currently, we have support for node:title, node:author, and node:created.
if ($entity_type == 'node' && in_array($field_name, array(
'title',
'author',
'created',
))) {
$field_instance = TRUE;
switch ($field_name) {
case 'title':
$subform_id = 'fape_field_edit_node_title_form';
break;
case 'author':
$subform_id = 'fape_field_edit_node_author_form';
break;
case 'created':
$subform_id = 'fape_field_edit_node_created_form';
break;
}
if (!node_access('update', $entity)) {
return MENU_ACCESS_DENIED;
}
}
else {
$field_instance = field_info_instance($entity_type, $field_name, $bundle);
$subform_id = 'fape_field_edit_field_form';
}
if (empty($field_instance)) {
return MENU_NOT_FOUND;
}
$form_state = array(
'entity_type' => $entity_type,
'entity' => $entity,
'field_name' => $field_name,
'langcode' => $langcode,
'no_redirect' => TRUE,
'redirect' => $_GET['q'],
'field_instance' => $field_instance,
'bundle' => $bundle,
'subform_id' => $subform_id,
);
// Try to figure out bundle's label for nicer field editing title.
$entity_bundles = $entity_info['bundles'];
$bundle_label = $bundle;
foreach ($entity_bundles as $key => $entity_bundle) {
if ($key == $bundle) {
if (isset($entity_bundle['label'])) {
$bundle_label = $entity_bundle['label'];
}
break;
}
}
drupal_set_title(t('<em>Edit @type</em> @title', array(
'@type' => $bundle_label,
'@title' => $field_instance['label'],
)), PASS_THROUGH);
$output = drupal_build_form('fape_field_edit_form', $form_state);
if (!empty($form_state['executed'])) {
entity_save($entity_type, $form_state['entity']);
drupal_goto($form_state['redirect']);
}
return $output;
}
/**
* Field editing form.
*/
function fape_field_edit_form($form, &$form_state) {
// Since we could edit a number of different things here, immediately
// add whatever else is needed.
$form_state['subform_id']($form, $form_state);
$form['#parents'] = array();
$entity_type = $form_state['entity_type'];
$entity = $form_state['entity'];
$bundle = $form_state['bundle'];
list($use_revisions, $control_revisions) = _fape_entity_allows_revisions($entity_type, $bundle, $entity);
if ($use_revisions) {
$form_state['use revisions'] = TRUE;
$form['revision_information'] = array(
'#weight' => 11,
);
$form['revision_information']['revision'] = array(
'#type' => 'checkbox',
'#title' => t('Create new revision'),
'#default_value' => $entity->revision,
'#id' => 'edit-revision',
'#access' => $control_revisions,
);
if ($control_revisions || $entity->revision) {
$form['revision_information']['log'] = array(
'#type' => 'textarea',
'#title' => t('Log message'),
'#description' => t('Provide an explanation of the changes you are making. This will help other authors understand your motivations.'),
'#default_value' => $entity->log,
);
if ($control_revisions) {
$form['revision_information']['log']['#dependency'] = array(
'edit-revision' => array(
1,
),
);
}
}
}
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
);
$form['actions']['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel'),
'#validate' => array(
'fape_field_edit_form_cancel',
),
'#executes_submit_callback' => FALSE,
);
// Ensure this actually gets on there.
$form['#submit'][] = 'fape_field_edit_form_submit';
return $form;
}
/**
* Subform to edit a field instance.
*
* This isn't a true form. As such it modifies the $form by reference.
*/
function fape_field_edit_field_form(&$form, &$form_state) {
$form['#parents'] = array();
$entity_type = $form_state['entity_type'];
$entity = $form_state['entity'];
$field_name = $form_state['field_name'];
$field_instance = $form_state['field_instance'];
$langcode = $form_state['langcode'];
$bundle = $form_state['bundle'];
ctools_include('fields');
// If no language is provided use the default site language.
$options = array(
'language' => field_valid_language($langcode),
'default' => TRUE,
);
$form += (array) ctools_field_invoke_field($field_instance, 'form', $entity_type, $entity, $form, $form_state, $options);
$form['#pre_render'][] = '_field_extra_fields_pre_render';
$form['#entity_type'] = $entity_type;
$form['#bundle'] = $bundle;
// Let other modules make changes to the form.
// Exclude some modules.
$excluded_modules = array(
'field_group',
'metatag',
'panelizer',
'redirect',
);
// Avoid module_invoke_all() to let parameters be taken by reference.
foreach (module_implements('field_attach_form') as $module) {
if (in_array($module, $excluded_modules)) {
continue;
}
$function = $module . '_field_attach_form';
$function($entity_type, $entity, $form, $form_state, $langcode);
}
$form['#validate'][] = 'fape_field_edit_field_form_validate';
$form['#submit'][] = 'fape_field_edit_field_form_submit';
}
/**
* Validate field editing form
*/
function fape_field_edit_field_form_validate($form, &$form_state) {
ctools_include('fields');
$entity_type = $form_state['entity_type'];
$entity = $form_state['entity'];
$field_name = $form_state['field_name'];
$field_instance = $form_state['field_instance'];
$langcode = $form_state['langcode'];
// Extract field values from submitted values.
ctools_field_invoke_field_default($field_instance, 'extract_form_values', $entity_type, $entity, $form, $form_state);
$errors = array();
// Check generic, field-type-agnostic errors first.
ctools_field_invoke_field_default($field_instance, 'validate', $entity_type, $entity, $errors);
// Check field-type specific errors.
ctools_field_invoke_field($field_instance, 'validate', $entity_type, $entity, $errors);
// Let other modules validate the entity.
// Avoid module_invoke_all() to let $errors be taken by reference.
foreach (module_implements('field_attach_validate') as $module) {
$function = $module . '_field_attach_validate';
$function($entity_type, $entity, $errors);
}
if ($errors) {
// Pass field-level validation errors back to widgets for accurate error
// flagging.
foreach ($errors as $field_errors) {
foreach ($field_errors as $langcode => $errors) {
$field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
$field_state['errors'] = $errors;
field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
}
}
ctools_field_invoke_field_default($field_instance, 'form_errors', $entity_type, $entity, $form, $form_state);
}
}
/**
* Submit field editing form
*/
function fape_field_edit_field_form_submit($form, &$form_state) {
$entity_type = $form_state['entity_type'];
$entity = $form_state['entity'];
$field_name = $form_state['field_name'];
$field_instance = $form_state['field_instance'];
$langcode = $form_state['langcode'];
// Extract field values from submitted values.
ctools_field_invoke_field_default($field_instance, 'extract_form_values', $entity_type, $entity, $form, $form_state);
ctools_field_invoke_field_default($field_instance, 'submit', $entity_type, $entity, $form, $form_state);
// Let other modules act on submitting the entity.
// Avoid module_invoke_all() to let $form_state be taken by reference.
foreach (module_implements('field_attach_submit') as $module) {
$function = $module . '_field_attach_submit';
$function($entity_type, $entity, $form, $form_state);
}
}
/**
* Subform to edit the entity 'title' field.
*
* This isn't a true form. As such it modifies the $form by reference.
*/
function fape_field_edit_node_title_form(&$form, &$form_state) {
$node = $form_state['entity'];
$type = node_type_get_type($node);
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($type->title_label),
'#default_value' => !empty($node->title) ? $node->title : '',
'#required' => TRUE,
'#weight' => -5,
);
$form['#submit'][] = 'fape_field_edit_node_title_form_submit';
}
function fape_field_edit_node_title_form_submit($form, &$form_state) {
$form_state['entity']->title = $form_state['values']['title'];
}
/**
* Subform to edit the entity 'author' field.
*
* This isn't a true form. As such it modifies the $form by reference.
*/
function fape_field_edit_node_author_form(&$form, &$form_state) {
$node = $form_state['entity'];
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Authored by'),
'#maxlength' => 60,
'#autocomplete_path' => 'user/autocomplete',
'#default_value' => !empty($node->name) ? $node->name : '',
'#weight' => -1,
'#description' => t('Leave blank for %anonymous.', array(
'%anonymous' => variable_get('anonymous', t('Anonymous')),
)),
);
$form['#validate'][] = 'fape_field_edit_node_author_form_validate';
$form['#submit'][] = 'fape_field_edit_node_author_form_submit';
}
/**
* Validates that the submitted author user actually exists.
*
* @see node_validate()
*/
function fape_field_edit_node_author_form_validate($form, &$form_state) {
$name = $form_state['values']['name'];
if (!empty($name) && !($account = user_load_by_name($name))) {
// The use of empty() is mandatory in the context of usernames
// as the empty string denotes the anonymous user. In case we
// are dealing with an anonymous user we set the user ID to 0.
form_set_error('name', t('The username %name does not exist.', array(
'%name' => $name,
)));
}
}
/**
* Sets the node's user ID to the submitted author user.
*
* @see node_submit()
*/
function fape_field_edit_node_author_form_submit($form, &$form_state) {
if ($account = user_load_by_name($form_state['values']['name'])) {
$form_state['entity']->uid = $account->uid;
}
else {
$form_state['entity']->uid = 0;
}
}
/**
* Subform to edit the entity 'created' field.
*
* This isn't a true form. As such it modifies the $form by reference.
*/
function fape_field_edit_node_created_form(&$form, &$form_state) {
$node = $form_state['entity'];
// It is necessary to call node_object_prepare() on the node to calculate
// the node's creation date.
node_object_prepare($node);
$time = !empty($node->date) ? date_format(date_create($node->date), 'Y-m-d H:i:s O') : format_date($node->created, 'custom', 'Y-m-d H:i:s O');
$timezone = !empty($node->date) ? date_format(date_create($node->date), 'O') : format_date($node->created, 'custom', 'O');
$form['date'] = array(
'#type' => 'textfield',
'#title' => t('Authored on'),
'#maxlength' => 25,
'#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array(
'%time' => $time,
'%timezone' => $timezone,
)),
'#default_value' => !empty($node->date) ? $node->date : '',
);
$form['#validate'][] = 'fape_field_edit_node_created_form_validate';
$form['#submit'][] = 'fape_field_edit_node_created_form_submit';
}
/**
* Validates that the submitted date exists and is a valid Unix timestamp.
*
* @see node_validate()
*/
function fape_field_edit_node_created_form_validate($form, &$form_state) {
$date = $form_state['values']['date'];
if (!empty($date) && strtotime($date) === FALSE) {
form_set_error('date', t('You have to specify a valid date.'));
}
}
/**
* Sets the node's creation date based on the submitted date value.
*
* @see node_submit()
*/
function fape_field_edit_node_created_form_submit($form, &$form_state) {
$date = $form_state['values']['date'];
$form_state['entity']->created = !empty($date) ? strtotime($date) : REQUEST_TIME;
}
/**
* General submit callback. Simply handles the revision.
*/
function fape_field_edit_form_submit($form, &$form_state) {
$entity = $form_state['entity'];
if (!empty($form_state['use revisions'])) {
$entity->revision = $form_state['values']['revision'];
$entity->log = $form_state['values']['log'];
}
}
/**
* Redirect back to original page when user hits the cancel button.
*
* Note:
* This is registered as a form validate handler on the cancel submit element
* so that we can bypass the submit logic which the ers module ties into.
*/
function fape_field_edit_form_cancel($form, &$form_state) {
drupal_goto($form_state['redirect']);
}
/**
* Determine if an entity uses revisions.
*
* @return
* An array where the first item is whether or not revisions are
* supported, and the second item is whether or not the current
* user has a choice.
*
* Much of this is specific to entity types, so entities that are not
* nodes that support revisions may not handle this quite correctly
* without help. We'll put in an alter hook so that support can
* be added, but we have no confidence anyone will ever use it.
*/
function _fape_entity_allows_revisions($entity_type, $bundle, $entity) {
$retval = array(
FALSE,
FALSE,
);
switch ($entity_type) {
case 'node':
$node_options = variable_get('node_options_' . $bundle, array(
'status',
'promote',
));
$retval[0] = in_array('revision', $node_options);
$retval[1] = user_access('administer nodes');
break;
default:
$entity_info = entity_get_info($entity_type);
$retval[0] = !empty($entity_info['revision table']);
break;
}
drupal_alter('fape_entity_allow_revisions', $retval);
$entity->revision = $retval[0];
$entity->log = '';
return $retval;
}
/**
* Implement hook_panels_pane_content_alter().
*
* This adds the contextual link to the entity field pane.
*/
function fape_panels_pane_content_alter($content, $pane, $args, $context) {
// Don't bother with empty panes.
if (empty($content->content)) {
return;
}
// Verify access to the field.
if ($pane->type == 'entity_field') {
list($entity_type, $field_name) = explode(':', $pane->subtype);
$plugin = ctools_get_content_type($pane->type);
$entity = ctools_content_select_context($plugin, $pane->subtype, $pane->configuration, $context)->data;
if (entity_access('update', $entity_type, $entity)) {
$field = field_info_field($field_name);
if (field_access('edit', $field, $entity_type, $entity)) {
list($entity_id, , ) = entity_extract_ids($entity_type, $entity);
$content->admin_links[] = array(
'title' => t('Edit field'),
'alt' => t('Edit the data in this field.'),
'href' => "admin/field/edit/{$entity_type}/{$entity_id}/{$field_name}",
'query' => drupal_get_destination(),
);
}
}
}
elseif (in_array($pane->type, array(
'node_title',
'node_author',
'node_created',
))) {
$plugin = ctools_get_content_type($pane->type);
$entity = ctools_content_select_context($plugin, $pane->subtype, $pane->configuration, $context)->data;
if (node_access('update', $entity)) {
list($ignore, $field_name) = explode('_', $pane->type);
$content->admin_links[] = array(
'title' => t('Edit field'),
'alt' => t('Edit the data in this field.'),
'href' => "admin/field/edit/node/{$entity->nid}/{$field_name}",
'query' => drupal_get_destination(),
);
}
}
}
Functions
Name | Description |
---|---|
fape_field_edit_field_form | Subform to edit a field instance. |
fape_field_edit_field_form_submit | Submit field editing form |
fape_field_edit_field_form_validate | Validate field editing form |
fape_field_edit_form | Field editing form. |
fape_field_edit_form_cancel | Redirect back to original page when user hits the cancel button. |
fape_field_edit_form_submit | General submit callback. Simply handles the revision. |
fape_field_edit_node_author_form | Subform to edit the entity 'author' field. |
fape_field_edit_node_author_form_submit | Sets the node's user ID to the submitted author user. |
fape_field_edit_node_author_form_validate | Validates that the submitted author user actually exists. |
fape_field_edit_node_created_form | Subform to edit the entity 'created' field. |
fape_field_edit_node_created_form_submit | Sets the node's creation date based on the submitted date value. |
fape_field_edit_node_created_form_validate | Validates that the submitted date exists and is a valid Unix timestamp. |
fape_field_edit_node_title_form | Subform to edit the entity 'title' field. |
fape_field_edit_node_title_form_submit | |
fape_field_edit_page | Page callback to edit an entity field. |
fape_menu | Implements hook_menu() |
fape_panels_pane_content_alter | Implement hook_panels_pane_content_alter(). |
_fape_entity_allows_revisions | Determine if an entity uses revisions. |