auto_entitylabel.module in Automatic Entity Label 7
Same filename and directory in other branches
Allows hiding of entity label fields and automatic label creation.
File
auto_entitylabel.moduleView source
<?php
/**
* @file
* Allows hiding of entity label fields and automatic label creation.
*/
define('AUTO_ENTITYLABEL_DISABLED', 0);
define('AUTO_ENTITYLABEL_ENABLED', 1);
define('AUTO_ENTITYLABEL_OPTIONAL', 2);
define('AUTO_ENTITYLABEL_PLACEHOLDER', '%AutoEntityLabel%');
/**
* Implements hook_help().
*/
function auto_entitylabel_help($path) {
switch ($path) {
case 'admin/help#auto_entitylabel':
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('This is a small and efficient module that allows hiding of entity label fields. To prevent empty labels it can be configured to generate the label automatically by a given pattern. The module can be used for any entity type that has a label, including e.g. for node titles, comment subjects, taxonomy term names and profile2 labels.') . '</p>';
$output .= '<p>' . t('Patterns for automatic labels are constructed with the help of tokens. Drupal core provides a basic set of <a href="@url_tokens">tokens</a>. For a token selection widget install the token. Some entity types (e.g. profile2) provide tokens via the entity_token, which is part of the entity module.', array(
'@url_tokens' => 'https://www.drupal.org/project/token',
)) . '</p>';
$output .= '<p>' . t('Watch the <a href="@url_daily_dose_of_drupal" target="blank">Daily Dose of Drupal</a> screencast by <a href="@url_shane_thomas" target="blank">Shane Thomas</a> for a short introduction and demonstration of the module and some of its features.', array(
'@url_daily_dose_of_drupal' => 'http://codekarate.com/daily-dose-of-drupal/drupal-7-automatic-entity-label-module',
'@url_shane_thomas' => 'https://www.drupal.org/user/506260',
)) . '</p>';
$output .= '<h3>' . t('Usage') . '</h3>';
$output .= '<p>' . t('The configuration can be accessed in <i>Structure</i> » <i>Automatic label</i> screen.') . '</p>';
$output .= '<p>' . t('<a href="@automatic_entitylabel_configuration">Click here</a> to redirect to Automatic Label configuration.', array(
'@automatic_entitylabel_configuration' => url('admin/structure/auto-label'),
)) . '</p>';
$output .= '<p>' . t('Advanced users can use PHP code for automatically generating labels. See below for more information.') . '</p>';
return $output;
}
}
/**
* Implements hook_permission().
*/
function auto_entitylabel_permission() {
return array(
'use PHP for label patterns' => array(
'title' => t('Use PHP for label patterns'),
'description' => t('Use PHP evaluation for automatic entity label patterns.'),
'restrict access' => TRUE,
),
);
}
/**
* Implements hook_menu().
*/
function auto_entitylabel_menu() {
$items = array();
$items['admin/structure/auto-label'] = array(
'title' => 'Automatic label',
'page callback' => 'auto_entitylabel_settings_default_page',
'access callback' => 'user_access',
'file' => 'auto_entitylabel.admin.inc',
'access arguments' => array(
'administer site configuration',
),
);
// This logic is taken from the field_ui module.
// Create tabs for all possible bundles.
foreach (entity_get_info() as $entity_type => $entity_info) {
if (isset($entity_info['entity keys']['label'])) {
foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
if (isset($bundle_info['admin'])) {
// Extract path information from the bundle.
$path = $bundle_info['admin']['path'];
// Different bundles can appear on the same path (e.g. %node_type and
// %comment_node_type). To allow field_ui_menu_load() to extract the
// actual bundle object from the translated menu router path
// arguments, we need to identify the argument position of the bundle
// name string ('bundle argument') and pass that position to the menu
// loader. The position needs to be casted into a string; otherwise it
// would be replaced with the bundle name string.
if (isset($bundle_info['admin']['bundle argument'])) {
$bundle_arg = $bundle_info['admin']['bundle argument'];
}
else {
$bundle_arg = $bundle_name;
}
// Extract access information, providing defaults.
$access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array(
'access callback',
'access arguments',
)));
$access += array(
'access callback' => 'user_access',
'access arguments' => array(
'administer site configuration',
),
);
$items["{$path}/auto_label"] = array(
'title' => 'Auto label',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'auto_entitylabel_settings_form',
$entity_type,
$bundle_arg,
),
'type' => MENU_LOCAL_TASK,
'weight' => 2,
'file' => 'auto_entitylabel.admin.inc',
) + $access;
// Prefix comment entities, because otherwise there would be multiple
// menu items that just said "Auto label".
if ($entity_type == 'comment') {
$items["{$path}/auto_label"]['title'] = '@type Auto label';
$items["{$path}/auto_label"]['title arguments'] = array(
'@type' => $entity_info['label'],
);
$items["{$path}/auto_label"]['weight'] = 4;
}
}
}
}
}
return $items;
}
/**
* Implements hook_form_alter().
*/
function auto_entitylabel_form_alter(&$form, &$form_state, $form_id) {
if (isset($form['#entity_type']) && isset($form['#bundle']) && empty($form['#auto_entitylabel_processed'])) {
$info = entity_get_info($form['#entity_type']);
// Exit if there is no label field, e.g. users only have a "label callback".
if (!isset($info['entity keys']['label'])) {
return;
}
$key = $form['#entity_type'] . '_' . $form['#bundle'];
$title = $info['entity keys']['label'];
$form['#auto_entitylabel_processed'] = TRUE;
// Integration with the title module.
$replacement = FALSE;
if (module_exists('title') && title_field_replacement_enabled($form['#entity_type'], $form['#bundle'], $title)) {
$title = $info['field replacement'][$title]['instance']['field_name'];
$replacement = TRUE;
}
if (auto_entitylabel_get_setting($key) == AUTO_ENTITYLABEL_ENABLED) {
// We will autogenerate the title later, just hide the title field in the
// meanwhile.
if ($replacement && isset($form[$title])) {
$form[$title][$form[$title]['#language']][0]['value']['#value'] = AUTO_ENTITYLABEL_PLACEHOLDER;
$form[$title][$form[$title]['#language']][0]['value']['#type'] = 'value';
$form[$title][$form[$title]['#language']][0]['value']['#required'] = FALSE;
}
else {
$form[$title]['#value'] = AUTO_ENTITYLABEL_PLACEHOLDER;
$form[$title]['#type'] = 'value';
$form[$title]['#required'] = FALSE;
}
}
elseif (auto_entitylabel_get_setting($key) == AUTO_ENTITYLABEL_OPTIONAL) {
if ($replacement && isset($form[$title])) {
$form[$title][$form[$title]['#language']][0]['value']['#required'] = FALSE;
}
else {
$form[$title]['#required'] = FALSE;
}
}
}
}
/**
* Implements hook_entity_insert().
*/
function auto_entitylabel_entity_insert($entity, $type) {
if (auto_entitylabel_is_needed($entity, $type, TRUE)) {
list($id, , ) = entity_extract_ids($type, $entity);
$entity_list =& drupal_static(__FUNCTION__, array());
$entity_list[$type][] = $id;
}
}
/**
* Implements hook_exit().
*
* @see auto_entitylabel_entity_insert()
*/
function auto_entitylabel_exit($destination = NULL) {
$entity_list =& drupal_static('auto_entitylabel_entity_insert', array());
// Loop through the entity types and then individual entity IDs.
foreach ($entity_list as $entity_type => $entity_ids) {
foreach ($entity_ids as $entity_id) {
$entity = entity_load_single($entity_type, $entity_id);
$settings = _auto_entitylabel_get_settings($entity, $entity_type);
if ($entity) {
// Store the old label.
$old_label = $entity->{$settings['title']};
// Update the entity label.
auto_entitylabel_set_title($entity, $entity_type);
// Save it only if the title has changed.
if ($entity->{$settings['title']} != $old_label) {
entity_save($entity_type, $entity);
}
}
}
}
}
/**
* Implements hook_field_attach_form().
*/
function auto_entitylabel_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
auto_entitylabel_form_alter($form, $form_state, NULL);
}
/**
* Implements hook_entity_presave().
*/
function auto_entitylabel_entity_presave($entity, $type) {
if (auto_entitylabel_is_needed($entity, $type)) {
auto_entitylabel_set_title($entity, $type);
}
}
/**
* Implements hook_field_attach_presave().
*
* Required for compatibility with entity_translation module, which does not
* invoke hook_entity_presave() when saving new translations.
*/
function auto_entitylabel_field_attach_presave($entity_type, $entity) {
auto_entitylabel_entity_presave($entity, $entity_type);
}
/**
* Returns whether the auto entitylabel has to be set on the provided entity.
*
* @param object $entity
* A Drupal entity object.
* @param string $entity_type
* The entity type.
* @param bool $reset
* Force set the label, even if a title already exists.
*
* @return bool
* Return TRUE if needed.
*/
function auto_entitylabel_is_needed($entity, $entity_type, $reset = FALSE) {
// First, we'll attempt to get the entity type specific settings.
$settings = _auto_entitylabel_get_settings($entity, $entity_type);
// Stub out a few rules we'll need to verify in addition to the $reset flag.
// Do we have configs for this entity type?
$has_settings = $settings && !empty($settings['key']) && ($setting = auto_entitylabel_get_setting($settings['key']));
// We can't do much if there are no settings.
if ($has_settings) {
// Is the label empty?
$empty_label = empty($entity->{$settings['title']}) || $entity->{$settings['title']} == AUTO_ENTITYLABEL_PLACEHOLDER || empty($entity->auto_entitylabel_applied);
// Is the auto label optional?
$label_optional = $setting == AUTO_ENTITYLABEL_OPTIONAL;
// Check if the label is empty or forced.
// Then makes sure an existing label isn't overwritten if the autolabel is
// optional.
return ($empty_label || $reset) && !($label_optional && !$empty_label);
}
// The above conditions where not met so we'll return "false".
return FALSE;
}
/**
* Sets the automatically generated entitylabel for the entity.
*/
function auto_entitylabel_set_title(&$entity, $type) {
$settings = _auto_entitylabel_get_settings($entity, $type);
// Generate title in different languages?
$multilingual = FALSE;
// Support for title module.
$entity_info = entity_get_info($type);
list(, , $bundle) = entity_extract_ids($type, $entity);
$title_field_name = FALSE;
$title_languages = array();
if (module_exists('title') && title_field_replacement_enabled($type, $bundle, $settings['title'])) {
$title_field_name = $entity_info['field replacement'][$settings['title']]['instance']['field_name'];
$field_info = field_info_field($title_field_name);
$title_languages = field_available_languages($type, $field_info);
$multilingual = count($title_languages) > 1;
}
// Remove LANGUAGE_NONE from array of languages.
if (($key = array_search(LANGUAGE_NONE, $title_languages, TRUE)) && auto_entitylabel_entity_language($type, $entity) !== LANGUAGE_NONE) {
unset($title_languages[$key]);
}
// Generate titles.
$titles = array();
$pattern = variable_get('auto_entitylabel_pattern_' . $settings['key'], '');
if (trim($pattern)) {
$entity->changed = REQUEST_TIME;
if ($multilingual) {
foreach ($title_languages as $language) {
$titles[$language] = _auto_entitylabel_patternprocessor($pattern, $entity, $type, $language);
}
}
else {
$titles[LANGUAGE_NONE] = _auto_entitylabel_patternprocessor($pattern, $entity, $type);
}
}
elseif ($type == 'node' && !empty($entity->nid)) {
$titles[LANGUAGE_NONE] = t('@bundle @node-id', array(
'@bundle' => $bundle,
'@node-id' => $entity->nid,
));
}
else {
$titles[LANGUAGE_NONE] = t('@bundle', array(
'@bundle' => $bundle,
));
}
$clone = clone $entity;
drupal_alter('auto_entitylabel_title', $titles, $clone);
// Ensure the generated title isn't too long.
$max_length = auto_entitylabel_max_length($type, $settings['title']);
foreach ($titles as $k => $v) {
$titles[$k] = drupal_substr($v, 0, $max_length);
}
// Save titles on entity (field)
if (module_exists('title') && title_field_replacement_enabled($type, $bundle, $settings['title'])) {
foreach ($titles as $lang => $title) {
if (!isset($entity->{$title_field_name}[$lang][0]['value']) || $entity->{$title_field_name}[$lang][0]['value'] != $title) {
$entity->{$title_field_name}[$lang][0]['format'] = NULL;
$entity->{$title_field_name}[$lang][0]['safe_value'] = check_plain($title);
$entity->{$title_field_name}[$lang][0]['value'] = $title;
$entity->auto_entitylabel_changed = TRUE;
}
}
}
// Save title on entity (non-field). This needs be done even if field_title
// above is updated, because the title module automatically syncs changes
// from the "non field title" to the "title field". Without this line we end
// up getting AUTO_ENTITYLABEL_PLACEHOLDER as the title.
$entity_language = auto_entitylabel_entity_language($type, $entity);
$title = isset($titles[$entity_language]) ? $titles[$entity_language] : $titles[LANGUAGE_NONE];
if (!isset($entity->{$settings['title']}) || $entity->{$settings['title']} != $title) {
$entity->{$settings['title']} = $title;
$entity->auto_entitylabel_changed = TRUE;
}
// With that flag we ensure we don't apply the title two times to the same
// node. See auto_entitylabel_is_needed().
$entity->auto_entitylabel_applied = TRUE;
}
/**
* Implements hook_action_info().
*/
function auto_entitylabel_action_info() {
$info['auto_entitylabel_entity_update_action'] = array(
'type' => 'entity',
'label' => t('Update automatic entity labels'),
'configurable' => FALSE,
);
return $info;
}
/**
* Update action wrapper.
*
* @see auto_entitylabel_action_info()
*/
function auto_entitylabel_entity_update_action(&$entity, &$context = array()) {
if (auto_entitylabel_is_needed($entity, $context['entity_type'], TRUE)) {
auto_entitylabel_set_title($entity, $context['entity_type']);
// Only save if the title has actually changed.
if (!empty($entity->auto_entitylabel_changed)) {
entity_save($context['entity_type'], $entity);
}
}
}
/**
* Implements hook_node_operations().
*/
function auto_entitylabel_node_operations() {
$operations = array(
'entitylabel_update' => array(
'label' => t('Update automatic node titles'),
'callback' => 'auto_entitylabel_operations_update',
),
);
return $operations;
}
/**
* Callback function for updating node titles.
*/
function auto_entitylabel_operations_update($nodes) {
foreach (entity_load('node', $nodes) as $node) {
$context = array(
'entity_type' => 'node',
);
auto_entitylabel_entity_update_action($node, $context);
}
}
/**
* Callback function for decoding HTML entities.
*/
function _auto_entitylabel_nohtmlentities(&$replacements, $data, $options) {
foreach ($replacements as $key => $value) {
$replacements[$key] = decode_entities($value);
}
}
/**
* Helper function to generate the title according to the settings.
*
* @return output
* A title string
*/
function _auto_entitylabel_patternprocessor($pattern, $entity, $entity_type, $language = LANGUAGE_NONE) {
// Replace tokens.
$info = entity_get_info($entity_type);
$languages = language_list();
$language_obj = $language == LANGUAGE_NONE ? NULL : $languages[$language];
$token_data = isset($info['token type']) ? array(
$info['token type'] => $entity,
) : array();
$output = token_replace($pattern, $token_data, array(
'callback' => '_auto_entitylabel_nohtmlentities',
'sanitize' => FALSE,
'clear' => TRUE,
'language' => $language_obj,
));
// Evalute PHP.
$settings = _auto_entitylabel_get_settings($entity, $entity_type);
if (variable_get('auto_entitylabel_php_' . $settings['key'], 0)) {
$output = auto_entitylabel_eval($output, $entity, $language);
}
// Strip tags.
if ($entity_type && $entity) {
list(, , $bundle_name) = entity_extract_ids($entity_type, $entity);
$strip_tags = variable_get('auto_entitylabel_strip_' . $entity_type . '_' . $bundle_name, 1);
if ($strip_tags) {
$output = preg_replace('/[\\t\\n\\r\\0\\x0B]/', '', strip_tags($output));
}
}
return $output;
}
/**
* Gets the auto node title setting associated with the given content type.
*/
function auto_entitylabel_get_setting($type) {
return variable_get('auto_entitylabel_' . $type, AUTO_ENTITYLABEL_DISABLED);
}
/**
* Evaluates php code and passes $entity and $language to it.
*/
function auto_entitylabel_eval($code, $entity, $language = LANGUAGE_NONE) {
ob_start();
// @codingStandardsIgnoreLine
print eval('?>' . $code);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* Implements hook_field_attach_delete_bundle().
*/
function auto_entitylabel_field_attach_delete_bundle($entity_type, $bundle, $instances) {
static $variables = array(
'auto_entitylabel',
'auto_entitylabel_pattern',
'auto_entitylabel_php',
);
foreach ($variables as $variable) {
variable_del($variable . '_' . $entity_type . '_' . $bundle);
}
}
/**
* Implements hook_field_attach_rename_bundle().
*/
function auto_entitylabel_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
static $variables = array(
'auto_entitylabel',
'auto_entitylabel_pattern',
'auto_entitylabel_php',
);
if ($bundle_old !== $bundle_new) {
foreach ($variables as $variable) {
$key_old = $variable . '_' . $entity_type . '_' . $bundle_old;
$key_new = $variable . '_' . $entity_type . '_' . $bundle_new;
variable_set($key_new, variable_get($key_old, ''));
variable_del($key_old);
}
}
}
/**
* Function to get settings.
*/
function _auto_entitylabel_get_settings($entity, $entity_type) {
$entity_info = entity_get_info($entity_type);
if ($entity_info['entity keys']['bundle'] && !empty($entity)) {
$result['key'] = $entity_type . '_' . $entity->{$entity_info['entity keys']['bundle']};
$result['title'] = isset($entity_info['entity keys']['label']) ? $entity_info['entity keys']['label'] : 'none';
return $result;
}
return FALSE;
}
/**
* Implements hook_features_pipe_node_alter().
*
* Adds auto_entitylabel_* variables when exporting node types to features.
*
* @todo
* Generalize for other entity types.
*/
function auto_entitylabel_features_pipe_node_alter(&$pipe, $data, $export) {
if (!empty($data)) {
$variables = array(
'auto_entitylabel_node',
'auto_entitylabel_pattern_node',
'auto_entitylabel_php_node',
);
foreach ($data as $node_type) {
foreach ($variables as $variable_name) {
$pipe['variable'][] = "{$variable_name}_{$node_type}";
}
}
}
}
/**
* Function to extract the maximum length.
*
* Tries to extract the maximum length for the given property from the
* respective database schema field.
*
* Assumes that the schema field is named identical to the property.
*/
function auto_entitylabel_max_length($entity_type, $property_name) {
// Load entity and schema info.
$entity_info = entity_get_info($entity_type);
$schema = drupal_get_schema($entity_info['base table']);
// Return 'length' from schema field as maximum length, fall back to 255.
return isset($schema['fields'][$property_name]['length']) ? $schema['fields'][$property_name]['length'] : 255;
}
/**
* Returns the language code of the given entity.
*
* Backward compatibility layer to ensure that installations running an older
* version of core where entity_language() is not available do not break.
*
* @param string $entity_type
* An entity type.
* @param object $entity
* An entity object.
* @param bool $check_language_property
* A boolean if TRUE, will attempt to fetch the language code from
* $entity->language if the entity_language() function failed or does not
* exist. Default is TRUE.
*/
function auto_entitylabel_entity_language($entity_type, $entity, $check_language_property = TRUE) {
$langcode = NULL;
// When entity_translation is installed, if overrides the behavior of the
// entity_language() function by returning the "active form language" instead
// of the "original entity language". In this case we need to retrieve the
// original language directly from the translation handler.
if (module_exists('entity_translation')) {
$handler = entity_translation_get_handler($entity_type, $entity);
$langcode = $handler
->getLanguage();
}
elseif (function_exists('entity_language')) {
$langcode = entity_language($entity_type, $entity);
}
elseif ($check_language_property && !empty($entity->language)) {
$langcode = $entity->language;
}
return !empty($langcode) ? $langcode : LANGUAGE_NONE;
}
Functions
Constants
Name | Description |
---|---|
AUTO_ENTITYLABEL_DISABLED | @file Allows hiding of entity label fields and automatic label creation. |
AUTO_ENTITYLABEL_ENABLED | |
AUTO_ENTITYLABEL_OPTIONAL | |
AUTO_ENTITYLABEL_PLACEHOLDER |