field_weight.module in Field display weights (per node) 7.2
Same filename and directory in other branches
Field display weight module.
Allows site administrators to modify the order that fields are displayed on a per node basis.
File
field_weight.moduleView source
<?php
/**
* @file
* Field display weight module.
*
* Allows site administrators to modify the order that fields
* are displayed on a per node basis.
*/
/**
* Implements hook_permission().
*/
function field_weight_permission() {
return array(
'administer field weight settings' => array(
'title' => t("Administer field weight settings"),
),
'administer field weights' => array(
'title' => t("Administer field weights"),
'description' => t("Administer field weights on nodes using the \\'Manage display weights\\' tab."),
),
);
}
/**
* Implements hook_menu().
*/
function field_weight_menu() {
$items['admin/config/field_weight'] = array(
'title' => "Field display weights",
'description' => "Settings for Field display weights.",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'field_weight_settings_form',
),
'access arguments' => array(
'administer field weight settings',
),
'file' => 'field_weight.admin.inc',
);
$items['node/%node/field_weight'] = array(
'title' => "Field display weights",
'description' => "Manage field display weights for this node.",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'field_weight_display_overview_form',
1,
),
'access callback' => '_field_weight_node_type_check',
'access arguments' => array(
1,
),
'type' => MENU_LOCAL_TASK,
);
$items['node/%node/revisions/%field_weight_node_revision/field_weight'] = array(
'title' => "Field display weights",
'description' => "Manage field display weights for this revision.",
'page callback' => 'drupal_get_form',
'page arguments' => array(
'field_weight_display_overview_form',
1,
3,
),
'access callback' => '_field_weight_node_type_check',
'access arguments' => array(
1,
3,
),
'type' => MENU_LOCAL_TASK,
);
return $items;
}
function field_weight_node_revision_load($vid) {
$nid = arg(1);
if (empty($vid) || empty($nid)) {
return FALSE;
}
return node_load($nid, $vid);
}
/**
* Implements hook_admin_paths().
*/
function field_weight_admin_paths() {
return array(
'node/*/field_weight' => TRUE,
);
}
/**
* Admin form displayed at node/%/display.
*/
function field_weight_display_overview_form($form, &$form_state, $node, $revision = NULL) {
$form = array();
// Save the mode we're in so alterers can see it.
$form_state['#revision'] = FALSE;
if ($revision && !empty($revision->vid)) {
$form_state['#node'] = $node = node_load(NULL, $revision->vid);
$form_state['#revision'] = TRUE;
drupal_set_title(t('Field display weights for revision @revision_date of @title', array(
'@revision_date' => format_date($revision->created),
'@title' => $revision->title,
)));
}
else {
$form_state['#node'] = $node;
drupal_set_title(t('Field display weights for @title', array(
'@title' => $node->title,
)));
}
// Tree set to get weights in nested array under field keys in form state.
$form['#tree'] = TRUE;
// Get all field instances for this bundle type
$instances = _field_weight_field_info_instances('node', $node->type);
$weights = field_weight_get_node_weight($node->vid);
// $weights will only be present if field weights have been saved
// for that node.
if (empty($weights)) {
$weights = array();
drupal_set_message(t("Field weights for this @revision_or_node have not been overridden yet.", array(
'@revision_or_node' => $revision ? t('revision') : t('node'),
)), 'warning');
}
// If this is a revision, we should still show the message even if we fell
// through.
$revision_weights = field_weight_get_node_revision_weight($node->vid);
if (empty($revision_weights)) {
drupal_set_message(t("Field weights for this revision have not been overridden yet."), 'warning');
}
list($weight_instances, $highest_weight) = field_weight_sort_instances($instances, $weights, $revision_weights);
// Allow other modules to change the weight of the instances.
$context = array(
'highest weight' => &$highest_weight,
'original instances' => $instances,
'original weights' => $weights,
'revision weights' => $revision_weights,
'node' => $node,
);
drupal_alter('field_weight_display_overview_weights', $weight_instances, $context);
$form['field_weight'] = array(
'#type' => 'fieldset',
'#title' => t("Manage %node_type field display", array(
'%node_type' => $node->type,
)),
// Put table/tabledrag theming of form into theme template.
'#theme' => 'field_weight_display_overview',
);
$form['field_weight']['markup'] = array(
'#markup' => '<p>' . t("Use the below table to arrange the order that fields will be displayed when this node is viewed. Saving your changes will override the default \n field display order set in admin/structure/types/[node-type]/display. If you change one value, all of the weights will change accordingly. To return to the default display order, use the reset button.") . '</p>',
);
// Create sensible weights based on amount of fields.
// Copied from block.admin.inc.
$weight_delta = $highest_weight;
// Pass hidden value to form submit so we can use instances
// already stored there.
$form['field_weight']['instances'] = array(
'#type' => 'value',
'#value' => $weight_instances,
);
$form['field_weight']['revision_weights'] = array(
'#type' => 'value',
'#value' => $revision_weights,
);
// Include so we can use field_ui_formatter_options().
module_load_include('inc', 'field_ui', 'field_ui.admin');
foreach ($weight_instances as $field => $values) {
$field_info = field_info_field($field);
$formatter_options = field_ui_formatter_options($field_info['type']);
$form['field_weight'][$field]['field'] = array(
'#markup' => (isset($values['new']) && $values['new'] ? t("<strong>(unsaved)</strong>") . ' ' : '') . check_plain($instances[$field]['label']),
);
$form['field_weight'][$field]['weight'] = array(
'#type' => 'weight',
'#delta' => $weight_delta,
'#default_value' => isset($values['weight']) ? $values['weight'] : 0,
'#attributes' => array(
'class' => array(
'field-weight',
),
),
);
$form['field_weight'][$field]['hidden'] = array(
'#type' => 'checkbox',
'#default_value' => isset($values['hidden']) ? $values['hidden'] : 0,
);
}
$form['continue'] = array(
'#type' => 'submit',
'#value' => t("Save and continue"),
'#submit' => array(
'field_weight_display_overview_form_submit',
'_field_weight_redirect_to_form',
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t("Save"),
);
$form['reset'] = array(
'#type' => 'submit',
'#value' => t("Reset"),
'#submit' => array(
'_field_weight_remove_weights',
),
);
$form['field_weight']['#node'] = array(
'#type' => 'value',
'#value' => clone $form_state['#node'],
);
return $form;
}
/**
* @param $instances
* @param $weights
* @param $revision_weights
* @return array
*/
function field_weight_sort_instances($instances, $weights, $revision_weights) {
// Create new array of instances
$weight_instances = array();
$highest_weight = count($instances);
foreach (array_keys($instances) as $key) {
// Default to actual rendered weight if not found. i.e field added
// after weights have last been saved or no weights saved.
if (array_key_exists($key, $weights)) {
$weight_instances[$key] = $weights[$key];
if (!array_key_exists($key, $revision_weights)) {
$weight_instances[$key]['new'] = TRUE;
}
}
else {
// Distinguish between never set (empty $weights) and new field.
// This is so that the form matches the render handling.
$default_weight = empty($weights) ? _field_weight_field_instance_get_weight($instances[$key]) : 0;
$default_hidden = 0;
// Always 0 to start with
$weight_instances[$key] = array(
'weight' => $default_weight,
'hidden' => $default_hidden,
// Set a property so we can show the user this is unsaved later.
'new' => TRUE,
);
}
$highest_weight = max($highest_weight, $weight_instances[$key]['weight']);
}
uasort($weight_instances, 'drupal_sort_weight');
return array(
$weight_instances,
$highest_weight,
);
}
function field_weight_display_overview_form_submit($form, &$form_state) {
$node = $form_state['#node'];
$values = $form_state['values']['field_weight'];
// This is a good enough check. The fact we are here means enough other
// stuff is true.
$revision_mode = arg(2) === 'revisions' ? TRUE : FALSE;
if ($revision_mode) {
$form_state['redirect'] = "node/{$node->nid}/revisions/{$node->vid}/view";
}
else {
$form_state['redirect'] = 'node/' . $node->nid;
}
$instances = _field_weight_field_info_instances('node', $node->type);
$weights = array();
foreach ($instances as $field => $instance) {
$weights[$field] = array(
'weight' => $values[$field]['weight'],
'hidden' => $values[$field]['hidden'],
);
}
// If all weights are 0 (unchanged) will return empty.
$empty_check = array_filter($weights);
if (!empty($empty_check)) {
db_merge('field_weight')
->key(array(
'vid' => $node->vid,
))
->fields(array(
'nid' => $node->nid,
'vid' => $node->vid,
'type' => $node->type,
'field_weights' => serialize($weights),
))
->execute();
}
elseif (empty($empty_check)) {
// Remove entry if user manually sets all weights to 0.
_field_weight_remove_weights($form, $form_state);
}
}
function _field_weight_redirect_to_form(&$form, &$form_state) {
$form_state['redirect'] = current_path();
}
/**
* Implements hook_node_delete().
*
* Clean up field_weight table so we don't get any orphaned entries.
*
*/
function field_weight_node_delete($node) {
db_delete('field_weight')
->condition('nid', $node->nid)
->execute();
}
/**
* Implements hook_node_revision_delete().
*
* Clean up field_weight table so we don't get any orphaned entries.
*/
function field_weight_node_revision_delete($node) {
db_delete('field_weight')
->condition('vid', $node->vid)
->execute();
}
/**
* Implements hook_node_insert().
*
* If cloning a node, we want to copy the field weights as well.
*/
function field_weight_node_insert($node) {
if (isset($node->clone_from_original_nid)) {
$clone_source = node_load($node->clone_from_original_nid);
if ($clone_source) {
$field_weights = field_weight_get_node_weight($clone_source->vid);
}
if ($field_weights) {
// And just save it with the new vid
db_merge('field_weight')
->key(array(
'vid' => $node->vid,
))
->fields(array(
'nid' => $node->nid,
'vid' => $node->vid,
'type' => $node->type,
'field_weights' => serialize($field_weights),
))
->execute();
}
}
}
/**
* Implements hook_node_update().
*
* When we create a new revision, we want to copy the field_weight
* definitions from the old revision if any exist.
*/
function field_weight_node_update($node) {
// Don't need to do anything if no new revision
if ($node->original->vid !== $node->vid) {
$field_weights = field_weight_get_node_weight($node->original->vid);
// ...or if there are no field weights
if ($field_weights) {
// And just save it with the new vid
db_merge('field_weight')
->key(array(
'vid' => $node->vid,
))
->fields(array(
'nid' => $node->nid,
'vid' => $node->vid,
'type' => $node->type,
'field_weights' => serialize($field_weights),
))
->execute();
}
}
}
/**
* Implements hook_entity_view_alter().
*/
function field_weight_entity_view_alter(&$build, $type) {
if ($type == 'node') {
// May be replaced with option variables, if this expands to all entities.
$enabled_node_types = variable_get('field_weight_node_types', array());
// Check if the bundle type is enabled.
if (in_array($build['#bundle'], $enabled_node_types, TRUE)) {
// See if any field weights have been set.
$node_weights = field_weight_get_node_weight($build['#node']->vid);
if ($node_weights) {
// Amend weights for entity fields,
// if there is a db entry the weights will be changed for all fields stored.
foreach ($node_weights as $key => $values) {
// Only do anything if the field has a value, or at least exists
if (isset($build[$key])) {
$build[$key]['#weight'] = $values['weight'];
if ($values['hidden'] == TRUE) {
// If field has been hidden set this to FALSE, therefore won't be displayed.
$build[$key]['#access'] = FALSE;
}
}
}
}
}
}
}
/**
*
* Wrap field_info_instances() and only return fields not marked as hidden
*
* NOTE: This function would need to be rewritten if ever allow conditionally
* *showing* fields hidden on the Manage Display page.
*/
function _field_weight_field_info_instances($bundle, $type) {
static $default_is_full = array();
if (!isset($default_is_full[$type])) {
$settings = field_bundle_settings($bundle, $type);
}
// Get all field instances for this bundle type
$instances = field_info_instances($bundle, $type);
// Remove field instances marked as hidden
foreach ($instances as $instance_key => $instance) {
foreach ($instance['display'] as $display_key => $view_mode) {
if (in_array($display_key, array(
'default',
'full',
))) {
if ($view_mode['type'] == 'hidden') {
unset($instances[$instance_key]);
}
}
}
}
return $instances;
}
/**
* Determine the default weight set for the field. This only checks the
* 'default' and 'full' keys.
*
* @param $instance The field instance as returned by field_info_instances()
* (or, in our case, _field_weight_field_info_instances()).
*/
function _field_weight_field_instance_get_weight(array $instance) {
if (isset($instance['display'])) {
foreach ($instance['display'] as $display_key => $view_mode) {
if (in_array($display_key, array(
'default',
'full',
))) {
if (isset($view_mode['weight'])) {
return $view_mode['weight'];
}
}
}
}
return FALSE;
}
/**
* Helper function to check if current node type is enabled in settings.
*/
function _field_weight_node_type_check($node) {
$enabled_node_types = variable_get('field_weight_node_types', array());
if (in_array($node->type, $enabled_node_types, TRUE) && _field_weight_access()) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* Access check for both field_weight permissions.
*/
function _field_weight_access() {
if (user_access('administer field weights') || user_access('administer field weight settings')) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* Helper function to get weights from field_weight table for nids passed in.
*/
function field_weight_get_node_weight($vid) {
$weights = field_weight_get_node_revision_weight($vid);
if (!$weights) {
// See if the published node revision has weights, and fall back to these.
// This should not normally happen, but it can if, for example, the user
// has clicked Reset on a specific revision.
$node = node_load(NULL, $vid);
$node_result = db_select('field_weight', 'fw')
->fields('fw', array(
'field_weights',
))
->condition('nid', $node->nid)
->execute()
->fetchField();
$weights = unserialize($node_result);
}
return $weights;
}
/**
* Helper function; separated this out because the fallback in
* _field_weight_get_node_weight() doesn't reveal whether the current
* *revision* has weights set or not.
*/
function field_weight_get_node_revision_weight($vid) {
$result = db_select('field_weight', 'fw')
->fields('fw', array(
'field_weights',
))
->condition('vid', $vid)
->execute()
->fetchField();
$weights = unserialize($result);
return $weights;
}
/**
* Helper function to remove entry from field_weight table for current node.
*/
function _field_weight_remove_weights($form, $form_state) {
$node = $form_state['#node'];
db_delete('field_weight')
->condition('nid', $node->nid)
->execute();
}
/**
* Implements hook_theme().
*/
function field_weight_theme() {
return array(
'field_weight_display_overview' => array(
'render element' => 'form',
'template' => 'field_weight_display_overview',
),
);
}
/**
* Implements hook_preprocess_hook().
*/
function field_weight_preprocess_field_weight_display_overview(&$variables) {
$variables['instances'] = $variables['form']['instances']['#value'];
}
/**
* Implements hook_process_node().
*/
function field_weight_process_field_weight_display_overview(&$variables) {
// @todo: Expose setting in UI for this.
if (module_exists('beautytips') && variable_get('field_weight_show_field_previews', TRUE)) {
$node = $variables['form']['#node']['#value'];
$build = node_view($node, 'full', $node->language);
if ($build) {
// Alright! Go through the instances and put the rendered value of stuff
// into BeautyTips on the relevant id element from the template.
$beautytips_popups = array();
foreach (array_keys($variables['instances']) as $instance_name) {
if (isset($build[$instance_name])) {
$rendered_instance = drupal_render($build[$instance_name]);
$beautytips_popups["field_weight_{$instance_name}"] = array(
'cssSelect' => "#row-{$instance_name} .field-element",
'text' => $rendered_instance,
'shrinkToFit' => TRUE,
'fill' => 'white',
);
}
// Let other modules alter how our BeautyTips look.
// They can also just implement hook_define_beautytips_styles(),
// but if they want to change the text (for example) then they
// need this.
$hook = __FUNCTION__;
drupal_alter('field_weight_preview_popups', $beautytips_popups, $variables, $hook);
beautytips_add_beautytips($beautytips_popups);
}
}
}
}