the term,
// so that the depth attribute remains correct.
$term = clone $term;
}
$term->depth = $depth;
unset($term->parent);
$term->parents = $parents[$vid][$term->tid];
$tree[] = $term;
if (!empty($children[$vid][$term->tid])) {
$has_children = TRUE;
// We have to continue with this parent later.
$process_parents[] = $parent;
// Use the current term as parent for the next iteration.
$process_parents[] = $term->tid;
// Reset pointers for child lists because we step in there more often
// with multi parents.
reset($children[$vid][$term->tid]);
// Move pointer so that we get the correct term the next time.
next($children[$vid][$parent]);
break;
}
} while ($child = next($children[$vid][$parent]));
if (!$has_children) {
// We processed all terms in this hierarchy-level, reset pointer
// so that this function works the next time it gets called.
reset($children[$vid][$parent]);
}
}
}
return $tree;
}
/**
* Try to map a string to an existing term, as for glossary use.
*
* Provides a case-insensitive and trimmed mapping, to maximize the
* likelihood of a successful match.
*
* @param name
* Name of the term to search for.
*
* @return
* An array of matching term objects.
*/
function taxonomy_get_term_by_name($name) {
return taxonomy_term_load_multiple(array(), array('name' => trim($name)));
}
/**
* Controller class for taxonomy terms.
*
* This extends the DrupalDefaultEntityController class. Only alteration is
* that we match the condition on term name case-independently.
*/
class TaxonomyTermController extends DrupalDefaultEntityController {
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
$query = parent::buildQuery($ids, $conditions, $revision_id);
$query->addTag('translatable');
$query->addTag('term_access');
// When name is passed as a condition use LIKE.
if (isset($conditions['name'])) {
$query_conditions = &$query->conditions();
foreach ($query_conditions as $key => $condition) {
if ($condition['field'] == 'base.name') {
$query_conditions[$key]['operator'] = 'LIKE';
$query_conditions[$key]['value'] = db_like($query_conditions[$key]['value']);
}
}
}
// Add the machine name field from the {taxonomy_vocabulary} table.
$query->innerJoin('taxonomy_vocabulary', 'v', 'base.vid = v.vid');
$query->addField('v', 'machine_name', 'vocabulary_machine_name');
return $query;
}
protected function cacheGet($ids, $conditions = array()) {
$terms = parent::cacheGet($ids, $conditions);
// Name matching is case insensitive, note that with some collations
// LOWER() and drupal_strtolower() may return different results.
foreach ($terms as $term) {
$term_values = (array) $term;
if (isset($conditions['name']) && drupal_strtolower($conditions['name'] != drupal_strtolower($term_values['name']))) {
unset($terms[$term->tid]);
}
}
return $terms;
}
}
/**
* Controller class for taxonomy vocabularies.
*
* This extends the DrupalDefaultEntityController class, adding required
* special handling for taxonomy vocabulary objects.
*/
class TaxonomyVocabularyController extends DrupalDefaultEntityController {
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
$query = parent::buildQuery($ids, $conditions, $revision_id);
$query->addTag('translatable');
$query->orderBy('base.weight');
$query->orderBy('base.name');
return $query;
}
}
/**
* Load multiple taxonomy terms based on certain conditions.
*
* This function should be used whenever you need to load more than one term
* from the database. Terms are loaded into memory and will not require
* database access if loaded again during the same page request.
*
* @see entity_load()
* @see EntityFieldQuery
*
* @param $tids
* An array of taxonomy term IDs.
* @param $conditions
* (deprecated) An associative array of conditions on the {taxonomy_term}
* table, where the keys are the database fields and the values are the
* values those fields must have. Instead, it is preferable to use
* EntityFieldQuery to retrieve a list of entity IDs loadable by
* this function.
*
* @return
* An array of term objects, indexed by tid. When no results are found, an
* empty array is returned.
*
* @todo Remove $conditions in Drupal 8.
*/
function taxonomy_term_load_multiple($tids = array(), $conditions = array()) {
return entity_load('taxonomy_term', $tids, $conditions);
}
/**
* Load multiple taxonomy vocabularies based on certain conditions.
*
* This function should be used whenever you need to load more than one
* vocabulary from the database. Terms are loaded into memory and will not
* require database access if loaded again during the same page request.
*
* @see entity_load()
*
* @param $vids
* An array of taxonomy vocabulary IDs, or FALSE to load all vocabularies.
* @param $conditions
* An array of conditions to add to the query.
*
* @return
* An array of vocabulary objects, indexed by vid.
*/
function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array()) {
return entity_load('taxonomy_vocabulary', $vids, $conditions);
}
/**
* Return the vocabulary object matching a vocabulary ID.
*
* @param $vid
* The vocabulary's ID.
*
* @return
* The vocabulary object with all of its metadata, if exists, FALSE otherwise.
* Results are statically cached.
*/
function taxonomy_vocabulary_load($vid) {
$vocabularies = taxonomy_vocabulary_load_multiple(array($vid));
return reset($vocabularies);
}
/**
* Return the vocabulary object matching a vocabulary machine name.
*
* @param $name
* The vocabulary's machine name.
*
* @return
* The vocabulary object with all of its metadata, if exists, FALSE otherwise.
* Results are statically cached.
*/
function taxonomy_vocabulary_machine_name_load($name) {
$vocabularies = taxonomy_vocabulary_load_multiple(NULL, array('machine_name' => $name));
return reset($vocabularies);
}
/**
* Return the term object matching a term ID.
*
* @param $tid
* A term's ID
*
* @return
* A term object. Results are statically cached.
*/
function taxonomy_term_load($tid) {
if (!is_numeric($tid)) {
return FALSE;
}
$term = taxonomy_term_load_multiple(array($tid), array());
return $term ? $term[$tid] : FALSE;
}
/**
* Helper function for array_map purposes.
*/
function _taxonomy_get_tid_from_term($term) {
return $term->tid;
}
/**
* Implodes a list of tags of a certain vocabulary into a string.
*
* @see drupal_explode_tags()
*/
function taxonomy_implode_tags($tags, $vid = NULL) {
$typed_tags = array();
foreach ($tags as $tag) {
// Extract terms belonging to the vocabulary in question.
if (!isset($vid) || $tag->vid == $vid) {
// Make sure we have a completed loaded taxonomy term.
if (isset($tag->name)) {
// Commas and quotes in tag names are special cases, so encode 'em.
if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
$typed_tags[] = '"' . str_replace('"', '""', $tag->name) . '"';
}
else {
$typed_tags[] = $tag->name;
}
}
}
}
return implode(', ', $typed_tags);
}
/**
* Implements hook_field_info().
*
* Field settings:
* - allowed_values: a list array of one or more vocabulary trees:
* - vocabulary: a vocabulary machine name.
* - parent: a term ID of a term whose children are allowed. This should be
* '0' if all terms in a vocabulary are allowed. The allowed values do not
* include the parent term.
*
*/
function taxonomy_field_info() {
return array(
'taxonomy_term_reference' => array(
'label' => t('Term reference'),
'description' => t('This field stores a reference to a taxonomy term.'),
'default_widget' => 'options_select',
'default_formatter' => 'taxonomy_term_reference_link',
'settings' => array(
'allowed_values' => array(
array(
'vocabulary' => '',
'parent' => '0',
),
),
),
),
);
}
/**
* Implements hook_field_widget_info().
*/
function taxonomy_field_widget_info() {
return array(
'taxonomy_autocomplete' => array(
'label' => t('Autocomplete term widget (tagging)'),
'field types' => array('taxonomy_term_reference'),
'settings' => array(
'size' => 60,
'autocomplete_path' => 'taxonomy/autocomplete',
),
'behaviors' => array(
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
),
),
);
}
/**
* Implements hook_field_widget_info_alter().
*/
function taxonomy_field_widget_info_alter(&$info) {
$info['options_select']['field types'][] = 'taxonomy_term_reference';
$info['options_buttons']['field types'][] = 'taxonomy_term_reference';
}
/**
* Implements hook_options_list().
*/
function taxonomy_options_list($field) {
$function = !empty($field['settings']['options_list_callback']) ? $field['settings']['options_list_callback'] : 'taxonomy_allowed_values';
return $function($field);
}
/**
* Implements hook_field_validate().
*
* Taxonomy field settings allow for either a single vocabulary ID, multiple
* vocabulary IDs, or sub-trees of a vocabulary to be specified as allowed
* values, although only the first of these is supported via the field UI.
* Confirm that terms entered as values meet at least one of these conditions.
*
* Possible error codes:
* - 'taxonomy_term_illegal_value': The value is not part of the list of allowed values.
*/
function taxonomy_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
// Build an array of existing term IDs so they can be loaded with
// taxonomy_term_load_multiple();
foreach ($items as $delta => $item) {
if (!empty($item['tid']) && $item['tid'] != 'autocreate') {
$tids[] = $item['tid'];
}
}
if (!empty($tids)) {
$terms = taxonomy_term_load_multiple($tids);
// Check each existing item to ensure it can be found in the
// allowed values for this field.
foreach ($items as $delta => $item) {
$validate = TRUE;
if (!empty($item['tid']) && $item['tid'] != 'autocreate') {
$validate = FALSE;
foreach ($field['settings']['allowed_values'] as $settings) {
// If no parent is specified, check if the term is in the vocabulary.
if (isset($settings['vocabulary']) && empty($settings['parent'])) {
if ($settings['vocabulary'] == $terms[$item['tid']]->vocabulary_machine_name) {
$validate = TRUE;
break;
}
}
// If a parent is specified, then to validate it must appear in the
// array returned by taxonomy_get_parents_all().
elseif (!empty($settings['parent'])) {
$ancestors = taxonomy_get_parents_all($item['tid']);
foreach ($ancestors as $ancestor) {
if ($ancestor->tid == $settings['parent']) {
$validate = TRUE;
break 2;
}
}
}
}
}
if (!$validate) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'taxonomy_term_reference_illegal_value',
'message' => t('%name: illegal value.', array('%name' => $instance['label'])),
);
}
}
}
}
/**
* Implements hook_field_is_empty().
*/
function taxonomy_field_is_empty($item, $field) {
if (!is_array($item) || (empty($item['tid']) && (string) $item['tid'] !== '0')) {
return TRUE;
}
return FALSE;
}
/**
* Implements hook_field_formatter_info().
*/
function taxonomy_field_formatter_info() {
return array(
'taxonomy_term_reference_link' => array(
'label' => t('Link'),
'field types' => array('taxonomy_term_reference'),
),
'taxonomy_term_reference_plain' => array(
'label' => t('Plain text'),
'field types' => array('taxonomy_term_reference'),
),
);
}
/**
* Implements hook_field_formatter_view().
*/
function taxonomy_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
// Terms whose tid is 'autocreate' do not exist
// yet and $item['taxonomy_term'] is not set. Theme such terms as
// just their name.
switch ($display['type']) {
case 'taxonomy_term_reference_link':
foreach ($items as $delta => $item) {
if ($item['tid'] == 'autocreate') {
$element[$delta] = array(
'#markup' => check_plain($item['name']),
);
}
else {
$term = $item['taxonomy_term'];
$uri = entity_uri('taxonomy_term', $term);
$element[$delta] = array(
'#type' => 'link',
'#title' => $term->name,
'#href' => $uri['path'],
'#options' => $uri['options'],
);
}
}
break;
case 'taxonomy_term_reference_plain':
foreach ($items as $delta => $item) {
$name = ($item['tid'] != 'autocreate' ? $item['taxonomy_term']->name : $item['name']);
$element[$delta] = array(
'#markup' => check_plain($name),
);
}
break;
}
return $element;
}
/**
* Returns the set of valid terms for a taxonomy field.
*
* @param $field
* The field definition.
* @return
* The array of valid terms for this field, keyed by term id.
*/
function taxonomy_allowed_values($field) {
$options = array();
foreach ($field['settings']['allowed_values'] as $tree) {
if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
if ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'])) {
foreach ($terms as $term) {
$options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
}
}
}
}
return $options;
}
/**
* Implements hook_field_formatter_prepare_view().
*
* This preloads all taxonomy terms for multiple loaded objects at once and
* unsets values for invalid terms that do not exist.
*/
function taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
$tids = array();
// Collect every possible term attached to any of the fieldable entities.
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
// Force the array key to prevent duplicates.
if ($item['tid'] != 'autocreate') {
$tids[$item['tid']] = $item['tid'];
}
}
}
if ($tids) {
$terms = taxonomy_term_load_multiple($tids);
// Iterate through the fieldable entities again to attach the loaded term data.
foreach ($entities as $id => $entity) {
$rekey = FALSE;
foreach ($items[$id] as $delta => $item) {
// Check whether the taxonomy term field instance value could be loaded.
if (isset($terms[$item['tid']])) {
// Replace the instance value with the term data.
$items[$id][$delta]['taxonomy_term'] = $terms[$item['tid']];
}
// Terms to be created are not in $terms, but are still legitimate.
else if ($item['tid'] == 'autocreate') {
// Leave the item in place.
}
// Otherwise, unset the instance value, since the term does not exist.
else {
unset($items[$id][$delta]);
$rekey = TRUE;
}
}
if ($rekey) {
// Rekey the items array.
$items[$id] = array_values($items[$id]);
}
}
}
}
/**
* Title callback for term pages.
*
* @param $term
* A term object.
*
* @return
* The term name to be used as the page title.
*/
function taxonomy_term_title($term) {
return $term->name;
}
/**
* Implements hook_field_widget_form().
*/
function taxonomy_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$tags = array();
foreach ($items as $item) {
$tags[$item['tid']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
}
$element += array(
'#type' => 'textfield',
'#default_value' => taxonomy_implode_tags($tags),
'#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $field['field_name'],
'#size' => $instance['widget']['settings']['size'],
'#maxlength' => 1024,
'#element_validate' => array('taxonomy_autocomplete_validate'),
);
return $element;
}
/**
* Form element validate handler for taxonomy term autocomplete element.
*/
function taxonomy_autocomplete_validate($element, &$form_state) {
// Autocomplete widgets do not send their tids in the form, so we must detect
// them here and process them independently.
$value = array();
if ($tags = $element['#value']) {
// Collect candidate vocabularies.
$field = field_widget_field($element, $form_state);
$vocabularies = array();
foreach ($field['settings']['allowed_values'] as $tree) {
if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
$vocabularies[$vocabulary->vid] = $vocabulary;
}
}
// Translate term names into actual terms.
$typed_terms = drupal_explode_tags($tags);
foreach ($typed_terms as $typed_term) {
// See if the term exists in the chosen vocabulary and return the tid;
// otherwise, create a new 'autocreate' term for insert/update.
if ($possibilities = taxonomy_term_load_multiple(array(), array('name' => trim($typed_term), 'vid' => array_keys($vocabularies)))) {
$term = array_pop($possibilities);
}
else {
$vocabulary = reset($vocabularies);
$term = array(
'tid' => 'autocreate',
'vid' => $vocabulary->vid,
'name' => $typed_term,
'vocabulary_machine_name' => $vocabulary->machine_name,
);
}
$value[] = (array)$term;
}
}
form_set_value($element, $value, $form_state);
}
/**
* Implements hook_field_widget_error().
*/
function taxonomy_field_widget_error($element, $error, $form, &$form_state) {
form_error($element, $error['message']);
}
/**
* Implements hook_field_settings_form().
*/
function taxonomy_field_settings_form($field, $instance, $has_data) {
// Get proper values for 'allowed_values_function', which is a core setting.
$vocabularies = taxonomy_get_vocabularies();
$options = array();
foreach ($vocabularies as $vocabulary) {
$options[$vocabulary->machine_name] = $vocabulary->name;
}
$form['allowed_values'] = array(
'#tree' => TRUE,
);
foreach ($field['settings']['allowed_values'] as $delta => $tree) {
$form['allowed_values'][$delta]['vocabulary'] = array(
'#type' => 'select',
'#title' => t('Vocabulary'),
'#default_value' => $tree['vocabulary'],
'#options' => $options,
'#required' => TRUE,
'#description' => t('The vocabulary which supplies the options for this field.'),
'#disabled' => $has_data,
);
$form['allowed_values'][$delta]['parent'] = array(
'#type' => 'value',
'#value' => $tree['parent'],
);
}
return $form;
}
/**
* Implements hook_rdf_mapping().
*
* @return array
* The rdf mapping for vocabularies and terms.
*/
function taxonomy_rdf_mapping() {
return array(
array(
'type' => 'taxonomy_term',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('skos:Concept'),
'name' => array(
'predicates' => array('rdfs:label', 'skos:prefLabel'),
),
'description' => array(
'predicates' => array('skos:definition'),
),
'vid' => array(
'predicates' => array('skos:inScheme'),
'type' => 'rel',
),
'parent' => array(
'predicates' => array('skos:broader'),
'type' => 'rel',
),
),
),
array(
'type' => 'taxonomy_vocabulary',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('skos:ConceptScheme'),
'name' => array(
'predicates' => array('dc:title'),
),
'description' => array(
'predicates' => array('rdfs:comment'),
),
),
),
);
}
/**
* @defgroup taxonomy_index Taxonomy indexing
* @{
* Functions to maintain taxonomy indexing.
*
* Taxonomy uses default field storage to store canonical relationships
* between terms and fieldable entities. However h_update('taxonomy_term', $term);
if (isset($term->parent)) {
db_delete('taxonomy_term_hierarchy')
->condition('tid', $term->tid)
->execute();
}
}
if (isset($term->parent)) {
if (!is_array($term->parent)) {
$term->parent = array($term->parent);
}
$query = db_insert('taxonomy_term_hierarchy')
->fields(array('tid', 'parent'));
foreach ($term->parent as $parent) {
if (is_array($parent)) {
foreach ($parent as $tid) {
$query->values(array(
'tid' => $term->tid,
'parent' => $tid
));
}
}
else {
$query->values(array(
'tid' => $term->tid,
'parent' => $parent
));
}
}
$query->execute();
}
// Reset the taxonomy term static variables.
taxonomy_terms_static_reset();
// Invoke the taxonomy hooks.
module_invoke_all("taxonomy_term_$op", $term);
module_invoke_all("entity_$op", $term, 'taxonomy_term');
unset($term->original);
return $status;
}
/**
* Delete a term.
*
* @param $tid
* The term ID.
* @return
* Status constant indicating deletion.
*/
function taxonomy_term_delete($tid) {
$transaction = db_transaction();
try {
$tids = array($tid);
while ($tids) {
$children_tids = $orphans = array();
foreach ($tids as $tid) {
// See if any of the term's children are about to be become orphans:
if ($children = taxonomy_get_children($tid)) {
foreach ($children as $child) {
// If the term has multiple parents, we don't delete it.
$parents = taxonomy_get_parents($child->tid);
if (count($parents) == 1) {
$orphans[] = $child->tid;
}
}
}
if ($term = taxonomy_term_load($tid)) {
db_delete('taxonomy_term_data')
->condition('tid', $tid)
->execute();
db_delete('taxonomy_term_hierarchy')
->condition('tid', $tid)
->execute();
field_attach_delete('taxonomy_term', $term);
module_invoke_all('taxonomy_term_delete', $term);
module_invoke_all('entity_delete', $term, 'taxonomy_term');
taxonomy_terms_static_reset();
}
}
$tids = $orphans;
}
return SAVED_DELETED;
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('taxonomy', $e);
throw $e;
}
}
/**
* Generate an array for rendering the given term.
*
* @param $term
* A term object.
* @param $view_mode
* View mode, e.g. 'full', 'teaser'...
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*
* @return
* An array as expected by drupal_render().
*/
function taxonomy_term_view($term, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = $GLOBALS['language_content']->language;
}
field_attach_prepare_view('taxonomy_term', array($term->tid => $term), $view_mode, $langcode);
entity_prepare_view('taxonomy_term', array($term->tid => $term), $langcode);
$build = array(
'#theme' => 'taxonomy_term',
'#term' => $term,
'#view_mode' => $view_mode,
'#language' => $langcode,
);
$build += field_attach_view('taxonomy_term', $term, $view_mode, $langcode);
// Add term description if the term has one.
if (!empty($term->description)) {
$build['description'] = array(
'#markup' => check_markup($term->description, $term->format, '', TRUE),
'#weight' => 0,
'#prefix' => '
',
'#suffix' => '
',
);
}
$build['#attached']['css'][] = drupal_get_path('module', 'taxonomy') . '/taxonomy.css';
// Allow modules to modify the structured term.
$type = 'taxonomy_term';
drupal_alter(array('taxonomy_term_view', 'entity_view'), $build, $type);
return $build;
}
/**
* Process variables for taxonomy-term.tpl.php.
*/
function template_preprocess_taxonomy_term(&$variables) {
$variaboken/flush-cache')),
),
);
}
function token_type_load($token_type) {
$info = token_get_info();
return isset($info['types'][$token_type]) ? $info['types'][$token_type] : FALSE;
}
/**
* Implements hook_theme().
*/
function token_theme() {
return array(
'tree_table' => array(
'variables' => array('header' => array(), 'rows' => array(), 'attributes' => array(), 'empty' => '', 'caption' => ''),
'file' => 'token.pages.inc',
),
'token_tree' => array(
'variables' => array('token_types' => array(), 'global_types' => TRUE, 'click_insert' => TRUE, 'show_restricted' => FALSE, 'recursion_limit' => 4),
'file' => 'token.pages.inc',
),
);
}
/**
* Implements hook_library().
*/
function token_library() {
// jQuery treeTable plugin.
$libraries['treeTable'] = array(
'title' => 'jQuery treeTable',
'website' => 'http://plugins.jquery.com/project/treetable',
'version' => '2.3.0',
'js' => array(
drupal_get_path('module', 'token') . '/jquery.treeTable.js' => array(),
),
'css' => array(
drupal_get_path('module', 'token') . '/jquery.treeTable.css' => array(),
),
);
return $libraries;
}
/**
* Implements hook_form_alter().
*
* Adds a submit handler to forms which could affect the tokens available on
* the site.
*/
function token_form_alter(&$form, $form_state, $form_id) {
switch ($form_id) {
// Profile field forms.
case 'profile_field_form':
case 'profile_field_delete':
// User picture form.
case 'user_admin_settings':
// System date type form.
// @todo Remove when http://drupal.org/node/1173706 is fixed.
case 'system_add_date_format_type_form':
case 'system_delete_date_format_type_form':
$form += array('#submit' => array());
array_unshift($form['#submit'], 'token_clear_cache');
break;
}
}
/**
* Implements hook_field_info_alter().
*/
function token_field_info_alter(&$info) {
$defaults = array(
'taxonomy_term_reference' => 'taxonomy_term_reference_plain',
'number_integer' => 'number_unformatted',
'number_decimal' => 'number_unformatted',
'number_float' => 'number_unformatted',
'file' => 'file_url_plain',
'image' => 'file_url_plain',
);
foreach ($defaults as $field_type => $default_token_formatter) {
if (isset($info[$field_type])) {
$info[$field_type] += array('default_token_formatter' => $default_token_formatter);
}
}
}
/**
* Implements hook_field_display_alter().
*/
function token_field_display_alter(&$display, $context) {
if ($context['view_mode'] == 'token') {
$view_mode_settings = field_view_mode_settings($context['instance']['entity_type'], $context['instance']['bundle']);
// If the token view mode fell back to the 'default' view mode, then
// use the default token formatter.
if (empty($view_mode_settings[$context['view_mode']]['custom_settings'])) {
$field_type_info = field_info_field_types($context['field']['type']);
if (!empty($field_type_info['default_token_formatter'])) {
$display['type'] = $field_type_info['default_token_formatter'];
$formatter_info = field_info_formatter_types($display['type']);
$display['settings'] = isset($formatter_info['settings']) ? $formatter_info['settings'] : array();
$display['settings']['label'] = 'hidden';
$display['module'] = $formatter_info['module'];
}
}
}
}
/**
* Implements hook_field_create_instance().
*/
function token_field_create_instance($instance) {
token_clear_cache();
}
/**
* Implements hook_field_update_instance().
*/
function token_field_update_instance($instance) {
token_clear_cache();
}
/**
* Implements hook_field_delete_instance().
*/
function token_field_delete_instance($instance) {
token_clear_cache();
}
/**
* Clear token caches and static variables.
*/
function token_clear_cache() {
cache_clear_all('*', 'cache_token', TRUE);
drupal_static_reset('token_get_info');
drupal_static_reset('token_get_global_token_types');
drupal_static_reset('token_get_entity_mapping'); * A taxonomy term ID.
* @param $vid
* An optional vocabulary ID to restrict the child search.
*
* @return
* An array of term objects that are the children of the term $tid, or an
* empty array when no children exist.
*/
function taxonomy_get_children($tid, $vid = 0) {
$children = &drupal_static(__FUNCTION__, array());
if ($tid && !isset($children[$tid])) {
$query = db_select('taxonomy_term_data', 't');
$query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
$query->addField('t', 'tid');
$query->condition('h.parent', $tid);
if ($vid) {
$query->condition('t.vid', $vid);
}
$query->addTag('term_access');
$query->orderBy('t.weight');
$query->orderBy('t.name');
$tids = $query->execute()->fetchCol();
$children[$tid] = taxonomy_term_load_multiple($tids);
}
return isset($children[$tid]) ? $children[$tid] : array();
}
/**
* Create a hierarchical representation of a vocabulary.
*
* @param $vid
* Which vocabulary to generate the tree for.
* @param $parent
* The term ID under which to generate the tree. If 0, generate the tree
* for the entire vocabulary.
* @param $max_depth
* The number of levels of the tree to return. Leave NULL to return all levels.
* @param $load_entities
* If TRUE, a full entity load will occur on the term objects. Otherwise they
* are partial objects queried directly from the {taxonomy_term_data} table to
* save execution time and memory consumption when listing large numbers of
* terms. Defaults to FALSE.
*
* @return
* An array of all term objects in the tree. Each term object is extended
* to have "depth" and "parents" attributes in addition to its normal ones.
* Results are statically cached. Term objects will be partial or complete
* depending on the $load_entities parameter.
*/
function taxonomy_get_tree($vid, $parent = 0, $max_depth = NULL, $load_entities = FALSE) {
$children = &drupal_static(__FUNCTION__, array());
$parents = &drupal_static(__FUNCTION__ . ':parents', array());
$terms = &drupal_static(__FUNCTION__ . ':terms', array());
// We cache trees, so it's not CPU-intensive to call get_tree() on a term
// and its children, too.
if (!isset($children[$vid])) {
$children[$vid] = array();
$parents[$vid] = array();
$terms[$vid] = array();
$query = db_select('taxonomy_term_data', 't');
$query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
$result = $query
->addTag('translatable')
->addTag('term_access')
->fields('t')
->fields('h', array('parent'))
->condition('t.vid', $vid)
->orderBy('t.weight')
->orderBy('t.name')
->execute();
foreach ($result as $term) {
$children[$vid][$term->parent][] = $term->tid;
$parents[$vid][$term->tid][] = $term->parent;
$terms[$vid][$term->tid] = $term;
}
}
// Load full entities, if necessary. The entity controller statically
// caches the results.
if ($load_entities) {
$term_entities = taxonomy_term_load_multiple(array_keys($terms[$vid]));
}
$max_depth = (!isset($max_depth)) ? count($children[$vid]) : $max_depth;
$tree = array();
// Keeps track of the parents we have to process, the last entry is used
// for the next processing step.
$process_parents = array();
$process_parents[] = $parent;
// Loops over the parent terms and adds its children to the tree array.
// Uses a loop instead of a recursion, because it's more efficient.
while (count($process_parents)) {
$parent = array_pop($process_parents);
// The number of parents determines the current depth.
$depth = count($process_parents);
if ($max_depth > $depth && !empty($children[$vid][$parent])) {
$has_children = FALSE;
$child = current($children[$vid][$parent]);
do {
if (empty($child)) {
break;
}
$term = $load_entities ? $term_entities[$child] : $terms[$vid][$child];
if (count($parents[$vid][$term->tid]) > 1) {
// We have a term with multi parents here. Clone the term,
// so that the depth attribute remains correct.
$term = clone $term;
}
$term->depth = $depth;
unset($term->parent);
$term->parents = $parents[$vid][$term->tid];
$tree[] = $term;
if (!empty($children[$vid][$term->tid])) {
$has_children = TRUE;
// We have to continue with this parent later.
$process_parents[] = $parent;
// Use the current term as parent for the next iteration.
$process_parents[] = $term->tid;
// Reset pointers for child lists because we step in there more often
// with multi parents.
reset($children[$vid][$term->tid]);
// Move pointer so that we get the correct term the next time.
next($children[$vid][$parent]);
break;
}
} while ($child = next($children[$vid][$parent]));
if (!$has_children) {
// We processed all terms in this hierarchy-level, reset pointer
// so that this function works the next time it gets called.
reset($children[$vid][$parent]);
}
}
}
return $tree;
}
/**
* Try to map a string to an existing term, as for glossary use.
*
* Provides a case-insensitive and trimmed mapping, to maximize the
* likelihood of a successful match.
*
* @param name
* Name of the term to search for.
*
* @return
* An array of matching term objects.
*/
function taxonomy_get_term_by_name($name) {
return taxonomy_term_load_multiple(array(), array('name' => trim($name)));
}
/**
* Controller class for taxonomy terms.
*
* This extends the DrupalDefaultEntityController class. Only alteration is
* that we match the condition on term name case-independently.
*/
class TaxonomyTermController extends DrupalDefaultEntityController {
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
$query = parent::buildQuery($ids, $conditions, $revision_id);
$query->addTag('translatable');
$query->addTag('term_access');
// When name is passed as a condition use LIKE.
if (isset($conditions['name'])) {
$query_conditions = &$query->conditions();
foreach ($query_conditions as $key => $condition) {
if ($condition['field'] == 'base.name') {
$query_conditions[$key]['operator'] = 'LIKE';
$query_conditions[$key]['value'] = db_like($query_conditions[$key]['value']);
}
}
}
// Add the machine name field from the {taxonomy_vocabulary} table.
$query->innerJoin('taxonomy_vocabulary', 'v', 'base.vid = v.vid');
$query->addField('v', 'machine_name', 'vocabulary_machine_name');
return $query;
}
protected function cacheGet($ids, $conditions = array()) {
$terms = parent::cacheGet($ids, $conditions);
// Name matching is case insensitive, note that with some collations
// LOWER() and drupal_strtolower() may return different results.
foreach ($terms as $term) {
$term_values = (array) $term;
if (isset($conditions['name']) && drupal_strtolower($conditions['name'] != drupal_strtolower($term_values['name']))) {
unset($terms[$term->tid]);
}
}
return $terms;
}
}
/**
* Controller class for taxonomy vocabularies.
*
* This extends the DrupalDefaultEntityController class, adding required
* special handling for taxonomy vocabulary objects.
*/
class TaxonomyVocabularyController extends DrupalDefaultEntityController {
protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
$query = parent::buildQuery($ids, $conditions, $revision_id);
$query->addTag('translatable');
$query->orderBy('base.weight');
$query->orderBy('base.name');
return $query;
}
}
/**
* Load multiple taxonomy terms based on certain conditions.
*
* This function should be used whenever you need to load more than one term
* from the database. Terms are loaded into memory and will not require
* database access if loaded again during the same page request.
*
* @see entity_load()
* @see EntityFieldQuery
*
* @param $tids
* An array of taxonomy term IDs.
* @param $conditions
* (deprecated) An associative array of conditions on the {taxonomy_term}
* table, where the keys are the database fields and the values are the
* values those fields must have. Instead, it is preferable to use
* EntityFieldQuery to retrieve a list of entity IDs loadable by
* this function.
*
* @return
* An array of term objects, indexed by tid. When no results are found, an
* empty array is returned.
*
* @todo Remove $conditions in Drupal 8.
*/
function taxonomy_term_load_multiple($tids = array(), $conditions = array()) {
return entity_load('taxonomy_term', $tids, $conditions);
}
/**
* Load multiple taxonomy vocabularies based on certain conditions.
*
* This function should be used whenever you need to load more than one
* vocabulary from the database. Terms are loaded into memory and will not
* require database access if loaded again during the same page request.
*
* @see entity_load()
*
* @param $vids
* An array of taxonomy vocabulary IDs, or FALSE to load all vocabularies.
* @param $conditions
* An array of conditions to add to the query.
*
* @return
* An array of vocabulary objects, indexed by vid.
*/
function taxonomy_vocabulary_load_multiple($vids = array(), $conditions = array()) {
return entity_load('taxonomy_vocabulary', $vids, $conditions);
}
/**
* Return the vocabulary object matching a vocabulary ID.
*
* @param $vid
* The vocabulary's ID.
*
* @return
* The vocabulary object with all of its metadata, if exists, FALSE otherwise.
* Results are statically cached.
*/
function taxonomy_vocabulary_load($vid) {
$vocabularies = taxonomy_vocabulary_load_multiple(array($vid));
return reset($vocabularies);
}
/**
* Return the vocabulary object matching a vocabulary machine name.
*
* @param $name
* The vocabulary's machine name.
*
* @return
* The vocabulary object with all of its metadata, if exists, FALSE otherwise.
* Results are statically cached.
*/
function taxonomy_vocabulary_machine_name_load($name) {
$vocabularies = taxonomy_vocabulary_load_multiple(NULL, array('machine_name' => $name));
return reset($vocabularies);
}
/**
* Return the term object matching a term ID.
*
* @param $tid
* A term's ID
*
* @return
* A term object. Results are statically cached.
*/
function taxonomy_term_load($tid) {
if (!is_numeric($tid)) {
return FALSE;
}
$term = taxonomy_term_load_multiple(array($tid), array());
return $term ? $term[$tid] : FALSE;
}
/**
* Helper function for array_map purposes.
*/
function _taxonomy_get_tid_from_term($term) {
return $term->tid;
}
/**
* Implodes a list of tags of a certain vocabulary into a string.
*
* @see drupal_explode_tags()
*/
function taxonomy_implode_tags($tags, $vid = NULL) {
$typed_tags = array();
foreach ($tags as $tag) {
// Extract terms belonging to the vocabulary in question.
if (!isset($vid) || $tag->vid == $vid) {
// Make sure we have a completed loaded taxonomy term.
if (isset($tag->name)) {
// Commas and quotes in tag names are special cases, so encode 'em.
if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
$typed_tags[] = '"' . str_replace('"', '""', $tag->name) . '"';
}
else {
$typed_tags[] = $tag->name;
}
}
}
}
return implode(', ', $typed_tags);
}
/**
* Implements hook_field_info().
*
* Field settings:
* - allowed_values: a list array of one or more vocabulary trees:
* - vocabulary: a vocabulary machine name.
* - parent: a term ID of a term whose children are allowed. This should be
* '0' if all terms in a vocabulary are allowed. The allowed values do not
* include the parent term.
*
*/
function taxonomy_field_info() {
return array(
'taxonomy_term_reference' => array(
'label' => t('Term reference'),
'description' => t('This field stores a reference to a taxonomy term.'),
'default_widget' => 'options_select',
'default_formatter' => 'taxonomy_term_reference_link',
'settings' => array(
'allowed_values' => array(
array(
'vocabulary' => '',
'parent' => '0',
),
),
),
),
);
}
/**
* Implements hook_field_widget_info().
*/
function taxonomy_field_widget_info() {
return array(
'taxonomy_autocomplete' => array(
'label' => t('Autocomplete term widget (tagging)'),
'field types' => array('taxonomy_term_reference'),
'settings' => array(
'size' => 60,
'autocomplete_path' => 'taxonomy/autocomplete',
),
'behaviors' => array(
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
),
),
);
}
/**
* Implements hook_field_widget_info_alter().
*/
function taxonomy_field_widget_info_alter(&$info) {
$info['options_select']['field types'][] = 'taxonomy_term_reference';
$info['options_buttons']['field types'][] = 'taxonomy_term_reference';
}
/**
* Implements hook_options_list().
*/
function taxonomy_options_list($field) {
$function = !empty($field['settings']['options_list_callback']) ? $field['settings']['options_list_callback'] : 'taxonomy_allowed_values';
return $function($field);
}
/**
* Implements hook_field_validate().
*
* Taxonomy field settings allow for either a single vocabulary ID, multiple
* vocabulary IDs, or sub-trees of a vocabulary to be specified as allowed
* values, although only the first of these is supported via the field UI.
* Confirm that terms entered as values meet at least one of these conditions.
*
* Possible error codes:
* - 'taxonomy_term_illegal_value': The value is not part of the list of allowed values.
*/
function taxonomy_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
// Build an array of existing term IDs so they can be loaded with
// taxonomy_term_load_multiple();
foreach ($items as $delta => $item) {
if (!empty($item['tid']) && $item['tid'] != 'autocreate') {
$tids[] = $item['tid'];
}
}
if (!empty($tids)) {
$terms = taxonomy_term_load_multiple($tids);
// Check each existing item to ensure it can be found in the
// allowed values for this field.
foreach ($items as $delta => $item) {
$validate = TRUE;
if (!empty($item['tid']) && $item['tid'] != 'autocreate') {
$validate = FALSE;
foreach ($field['settings']['allowed_values'] as $settings) {
// If no parent is specified, check if the term is in the vocabulary.
if (isset($settings['vocabulary']) && empty($settings['parent'])) {
if ($settings['vocabulary'] == $terms[$item['tid']]->vocabulary_machine_name) {
$validate = TRUE;
break;
}
}
// If a parent is specified, then to validate it must appear in the
// array returned by taxonomy_get_parents_all().
elseif (!empty($settings['parent'])) {
$ancestors = taxonomy_get_parents_all($item['tid']);
foreach ($ancestors as $ancestor) {
if ($ancestor->tid == $settings['parent']) {
$validate = TRUE;
break 2;
}
}
}
}
}
if (!$validate) {
$errors[$field['field_name']][$langcode][$delta][] = array(
'error' => 'taxonomy_term_reference_illegal_value',
'message' => t('%name: illegal value.', array('%name' => $instance['label'])),
);
}
}
}
}
/**
* Implements hook_field_is_empty().
*/
function taxonomy_field_is_empty($item, $field) {
if (!is_array($item) || (empty($item['tid']) && (string) $item['tid'] !== '0')) {
return TRUE;
}
return FALSE;
}
/**
* Implements hook_field_formatter_info().
*/
function taxonomy_field_formatter_info() {
return array(
'taxonomy_term_reference_link' => array(
'label' => t('Link'),
'field types' => array('taxonomy_term_reference'),
),
'taxonomy_term_reference_plain' => array(
'label' => t('Plain text'),
'field types' => array('taxonomy_term_reference'),
),
);
}
/**
* Implements hook_field_formatter_view().
*/
function taxonomy_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
// Terms whose tid is 'autocreate' do not exist
// yet and $item['taxonomy_term'] is not set. Theme such terms as
// just their name.
switch ($display['type']) {
case 'taxonomy_term_reference_link':
foreach ($items as $delta => $item) {
if ($item['tid'] == 'autocreate') {
$element[$delta] = array(
'#markup' => check_plain($item['name']),
);
}
else {
$term = $item['taxonomy_term'];
$uri = entity_uri('taxonomy_term', $term);
$element[$delta] = array(
'#type' => 'link',
'#title' => $term->name,
'#href' => $uri['path'],
'#options' => $uri['options'],
);
}
}
break;
case 'taxonomy_term_reference_plain':
foreach ($items as $delta => $item) {
$name = ($item['tid'] != 'autocreate' ? $item['taxonomy_term']->name : $item['name']);
$element[$delta] = array(
'#markup' => check_plain($name),
);
}
break;
}
return $element;
}
/**
* Returns the set of valid terms for a taxonomy field.
*
* @param $field
* The field definition.
* @return
* The array of valid terms for this field, keyed by term id.
*/
function taxonomy_allowed_values($field) {
$options = array();
foreach ($field['settings']['allowed_values'] as $tree) {
if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
if ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'])) {
foreach ($terms as $term) {
$options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
}
}
}
}
return $options;
}
/**
* Implements hook_field_formatter_prepare_view().
*
* This preloads all taxonomy terms for multiple loaded objects at once and
* unsets values for invalid terms that do not exist.
*/
function taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
$tids = array();
// Collect every possible term attached to any of the fieldable entities.
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
// Force the array key to prevent duplicates.
if ($item['tid'] != 'autocreate') {
$tids[$item['tid']] = $item['tid'];
}
}
}
if ($tids) {
$terms = taxonomy_term_load_multiple($tids);
// Iterate through the fieldable entities again to attach the loaded term data.
foreach ($entities as $id => $entity) {
$rekey = FALSE;
foreach ($items[$id] as $delta => $item) {
// Check whether the taxonomy term field instance value could be loaded.
if (isset($terms[$item['tid']])) {
// Replace the instance value with the term data.
$items[$id][$delta]['taxonomy_term'] = $terms[$item['tid']];
}
// Terms to be created are not in $terms, but are still legitimate.
else if ($item['tid'] == 'autocreate') {
// Leave the item in place.
}
// Otherwise, unset the instance value, since the term does not exist.
else {
unset($items[$id][$delta]);
$rekey = TRUE;
}
}
if ($rekey) {
// Rekey the items array.
$items[$id] = array_values($items[$id]);
}
}
}
}
/**
* Title callback for term pages.
*
* @param $term
* A term object.
*
* @return
* The term name to be used as the page title.
*/
function taxonomy_term_title($term) {
return $term->name;
}
/**
* Implements hook_field_widget_form().
*/
function taxonomy_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$tags = array();
foreach ($items as $item) {
$tags[$item['tid']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
}
$element += array(
'#type' => 'textfield',
'#default_value' => taxonomy_implode_tags($tags),
'#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $field['field_name'],
'#size' => $instance['widget']['settings']['size'],
'#maxlength' => 1024,
'#element_validate' => array('taxonomy_autocomplete_validate'),
);
return $element;
}
/**
* Form element validate handler for taxonomy term autocomplete element.
*/
function taxonomy_autocomplete_validate($element, &$form_state) {
// Autocomplete widgets do not send their tids in the form, so we must detect
// them here and process them independently.
$value = array();
if ($tags = $element['#value']) {
// Collect candidate vocabularies.
$field = field_widget_field($element, $form_state);
$vocabularies = array();
foreach ($field['settings']['allowed_values'] as $tree) {
if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
$vocabularies[$vocabulary->vid] = $vocabulary;
}
}
// Translate term names into actual terms.
$typed_terms = drupal_explode_tags($tags);
foreach ($typed_terms as $typed_term) {
// See if the term exists in the chosen vocabulary and return the tid;
// otherwise, create a new 'autocreate' term for insert/update.
if ($possibilities = taxonomy_term_load_multiple(array(), array('name' => trim($typed_term), 'vid' => array_keys($vocabularies)))) {
$term = array_pop($possibilities);
}
else {
$vocabulary = reset($vocabularies);
$term = array(
'tid' => 'autocreate',
'vid' => $vocabulary->vid,
'name' => $typed_term,
'vocabulary_machine_name' => $vocabulary->machine_name,
);
}
$value[] = (array)$term;
}
}
form_set_value($element, $value, $form_state);
}
/**
* Implements hook_field_widget_error().
*/
function taxonomy_field_widget_error($element, $error, $form, &$form_state) {
form_error($element, $error['message']);
}
/**
* Implements hook_field_settings_form().
*/
function taxonomy_field_settings_form($field, $instance, $has_data) {
// Get proper values for 'allowed_values_function', which is a core setting.
$vocabularies = taxonomy_get_vocabularies();
$options = array();
foreach ($vocabularies as $vocabulary) {
$options[$vocabulary->machine_name] = $vocabulary->name;
}
$form['allowed_values'] = array(
'#tree' => TRUE,
);
foreach ($field['settings']['allowed_values'] as $delta => $tree) {
$form['allowed_values'][$delta]['vocabulary'] = array(
'#type' => 'select',
'#title' => t('Vocabulary'),
'#default_value' => $tree['vocabulary'],
'#options' => $options,
'#required' => TRUE,
'#description' => t('The vocabulary which supplies the options for this field.'),
'#disabled' => $has_data,
);
$form['allowed_values'][$delta]['parent'] = array(
'#type' => 'value',
'#value' => $tree['parent'],
);
}
return $form;
}
/**
* Implements hook_rdf_mapping().
*
* @return array
* The rdf mapping for vocabularies and terms.
*/
function taxonomy_rdf_mapping() {
return array(
array(
'type' => 'taxonomy_term',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('skos:Concept'),
'name' => array(
'predicates' => array('rdfs:label', 'skos:prefLabel'),
),
'description' => array(
'predicates' => array('skos:definition'),
),
'vid' => array(
'predicates' => array('skos:inScheme'),
'type' => 'rel',
),
'parent' => array(
'predicates' => array('skos:broader'),
'type' => 'rel',
),
),
),
array(
'type' => 'taxonomy_vocabulary',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('skos:ConceptScheme'),
'name' => array(
'predicates' => array('dc:title'),
),
'description' => array(
'predicates' => array('rdfs:comment'),
),
),
),
);
}
/**
* @defgroup taxonomy_index Taxonomy indexing
* @{
* Functions to maintain taxonomy indexing.
*
* Taxonomy uses default field storage to store canonical relationships
* between terms and fieldable entities. However its most common use case
* requires listing all content associated with a term or group of terms
* sorted by creation date. To avoid slow queries due to joining across
* multiple node and field tables with various conditions and order by criteria,
* we maintain a denormalized table with all relationships between terms,
* published nodes and common sort criteria such as sticky and created.
* This is used as a lookup table by taxonomy_select_nodes(). When using other
* field storage engines or alternative methods of denormalizing this data
* you should set the variable 'taxonomy_maintain_index_table' to FALSE
* to avoid unnecessary writes in SQL.
*/
/**
* Implements hook_field_presave().
*
* Create any new terms defined in a freetagging vocabulary.
*/
function taxonomy_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
foreach ($items as $delta => $item) {
if ($item['tid'] == 'autocreate') {
$term = (object) $item;
unset($term->tid);
taxonomy_term_save($term);
$items[$delta]['tid'] = $term->tid;
}
}
}
/**
* Implements hook_field_insert().
*/
function taxonomy_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
// We maintain a denor malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
return;
}
}
}
return $operations;
}
/**
* Callback function for admin mass unblocking users.
*/
function user_user_operations_unblock($accounts) {
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip unblocking user if they are already unblocked.
if ($account !== FALSE && $account->status == 0) {
user_save($account, array('status' => 1));
}
}
}
/**
* Callback function for admin mass blocking users.
*/
function user_user_operations_block($accounts) {
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip blocking user if they are already blocked.
if ($account !== FALSE && $account->status == 1) {
// For efficiency manually save the original account before applying any
// changes.
$account->original = clone $account;
user_save($account, array('status' => 0));
}
}
}
/**
* Callback function for admin mass adding/deleting a user role.
*/
function user_multiple_role_edit($accounts, $operation, $rid) {
// The role name is not necessary as user_save() will reload the user
// object, but some modules' hook_user() may look at this first.
$role_name = db_query('SELECT name FROM {role} WHERE rid = :rid', array(':rid' => $rid))->fetchField();
switch ($operation) {
case 'add_role':
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip adding the role to the user if they already have it.
if ($account !== FALSE && !isset($account->roles[$rid])) {
$roles = $account->roles + array($rid => $role_name);
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
user_save($account, array('roles' => $roles));
}
}
break;
case 'remove_role':
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip removing the role from the user if they already don't have it.
if ($account !== FALSE && isset($account->roles[$rid])) {
$roles = array_diff($account->roles, array($rid => $role_name));
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
user_save($account, array('roles' => $roles));
}
}
break;
}
}
function user_multiple_cancel_confirm($form, &$form_state) {
$edit = $form_state['input'];
$form['accounts'] = array('#prefix' => '
', '#suffix' => '
', '#tree' => TRUE);
$accounts = user_load_multiple(array_keys(array_filter($edit['accounts'])));
foreach ($accounts as $uid => $account) {
// Prevent user 1 from being canceled.
if ($uid <= 1) {
continue;
}
$form['accounts'][$uid] = array(
'#type' => 'hidden',
'#value' => $uid,
'#prefix' => '
',
'#suffix' => check_plain($account->name) . "
\n",
);
}
// Output a notice that user 1 cannot be canceled.
if (isset($accounts[1])) {
$redirect = (count($accounts) == 1);
$message = t('The user account %name cannot be cancelled.', array('%name' => $accounts[1]->name));
drupal_set_message($message, $redirect ? 'error' : 'warning');
// If only user 1 was selected, redirect to the overview.
if ($redirect) {
drupal_goto('admin/people');
}
}
$form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
module_load_include('inc', 'user', 'user.pages');
$form['user_cancel_method'] = array(
'#type' => 'item',
'#title' => t('When cancelling these accounts'),
);
$form['user_cancel_method'] += user_cancel_methods();
// Remove method descriptions.
foreach (element_children($form['user_cancel_method']) as $element) {
unset($form['user_cancel_method'][$element]['#description']);
}
// Allow to send the account cancellation confirmation mail.
$form['user_cancel_confirm'] = array(
'#type' => 'checkbox',
'#title' => t('Require e-mail confirmation to cancel account.'),
'#default_value' => FALSE,
'#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
);
// Also allow to send account canceled notification mail, if enabled.
$form['user_cancel_notify'] = array(
'#type' => 'checkbox',
'#title' => t('Notify user when account is canceled.'),
'#default_value' => FALSE,
'#access' => variable_get('user_mail_status_canceled_notify', FALSE),
'#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
);
return confirm_form($form,
t('Are you sure you want to cancel these user accounts?'),
'admin/people', t('This action cannot be undone.'),
t('Cancel accounts'), t('Cancel'));
}
/**
* Submit handler for mass-account cancellation form.
*
* @see user_multiple_cancel_confirm()
* @see user_cancel_confirm_form_submit()
*/
function user_multiple_cancel_confirm_submit($form, &$form_state) {
global $user;
if ($form_state['values']['confirm']) {
foreach ($form_state['values']['accounts'] as $uid => $value) {
// Prevent programmatic form submissions from cancelling user 1.
if ($uid <= 1) {
continue;
}
// Prevent user administrators from deleting themselves without confirmation.
if ($uid == $user->uid) {
$admin_form_state = $form_state;
unset($admin_form_state['values']['user_cancel_confirm']);
$admin_form_state['values']['_account'] = $user;
user_cancel_confirm_form_submit(array(), $admin_form_state);
}
else {
user_cancel($form_state['values'], $uid, $form_state['values']['user_cancel_method']);
}
}
}
$form_state['redirect'] = 'admin/people';
}
/**
* Retrieve a list of all user setting/information categories and sort them by weight.
*/
function _user_categories() {
$categories = module_invoke_all('user_categories');
usort($categories, '_user_sort');
return $categories;
}
function _user_sort($a, $b) {
$a = (array) $a + array('weight' => 0, 'title' => '');
$b = (array) $b + array('weight' => 0, 'title' => '');
return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
}
/**
* List user administration filters that can be applied.
*/
function user_filters() {
// Regular filters
$filters = array();
$roles = user_roles(TRUE);
unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
if (count($roles)) {
$filters['role'] = array(
'title' => t('role'),
'field' => 'ur.rid',
'options' => array(
'[any]' => t('any'),
) + $roles,
);
}
$options = array();
foreach (module_implements('permission') as $module) {
$function = $module . '_permission';
if ($permissions = $function('permission')) {
asort($permissions);
foreach ($permissions as $permission => $description) {
$options[t('@module module', array('@module' => $module))][$permission] = t($permission);
}
}
}
ksort($options);
$filters['permission'] = array(
'title' => t('permission'),
'options' => array(
'[any]' => t('any'),
) + $options,
);
$filters['status'] = array(
'title' => t('status'),
'field' => 'u.status',
'options' => array(
'[any]' => t('any'),
1 => t('active'),
0 => t('blocked'),
),
);
return $filters;
}
/**
* Extends a query object for user administration filters based on session.
*
* @param $query
* Query object that should be filtered.
*/
function user_build_filter_query(SelectQuery $query) {
$filters = user_filters();
// Extend Query with filter conditions.
foreach (isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array() as $filter) {
list($key, $value) = $filter;
// This checks to see if this permission fig the view.
*/
function views_ui_cache_load($name) {
ctools_include('object-cache');
views_include('view');
$view = ctools_object_cache_get('view', $name);
$original_view = views_get_view($name);
if (empty($view)) {
$view = $original_view;
if (!empty($view)) {
// Check to see if someone else is already editing this view.
$view->locked = ctools_object_cache_test('view', $view->name);
// Set a flag to indicate that this view is being edited.
// This flag will be used e.g. to determine whether strings
// should be localized.
$view->editing = TRUE;
}
}
else {
// Keep disabled/enabled status real.
if ($original_view) {
$view->disabled = !empty($original_view->disabled);
}
}
if (empty($view)) {
return FALSE;
}
else {
return $view;
}
}
/**
* Specialized cache function to add a flag to our view, include an appropriate
* include, and cache more easily.
*/
function views_ui_cache_set(&$view) {
if (!empty($view->locked)) {
drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
return;
}
ctools_include('object-cache');
$view->changed = TRUE; // let any future object know that this view has changed.
if (isset($view->current_display)) {
// Add the knowledge of the changed display, too.
$view->changed_display[$view->current_display] = TRUE;
unset($view->current_display);
}
// Unset handlers; we don't want to write these into the cache
unset($view->display_handler);
unset($view->default_display);
$view->query = NULL;
foreach (array_keys($view->display) as $id) {
unset($view->display[$id]->handler);
unset($view->display[$id]->default_display);
}
ctools_object_cache_set('view', $view->name, $view);
}
/**
* Specialized menu callback to load a view that is only a default
* view.
*/
function views_ui_default_load($name) {
$view = views_get_view($name);
if ($view->type == t('Default')) {
return $view;
}
return FALSE;
}
/**
* Theme preprocess for views-view.tpl.php.
*/
function views_ui_preprocess_views_view(&$vars) {
$view = $vars['view'];
if (!empty($view->views_ui_context) && module_exists('contextual')) {
$view->hide_admin_links = TRUE;
foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) {
if (!empty($vars[$section])) {
$vars[$section] = array(
'#theme' => 'views_ui_view_preview_section',
'#view' => $view,
'#section' => $section,
'#content' => $vars[$section],
'#theme_wrappers' => array('views_container'),
'#attributes' => array('class' => 'contextual-links-region'),
);
$vars[$section] = drupal_render($vars[$section]);
}
}
}
}
/**
* Theme preprocess for theme_views_ui_view_preview_section().
*
* @TODO
* Perhaps move this to includes/admin.inc or theme/theme.inc
*/
function template_preprocess_views_ui_view_preview_section(&$vars) {
switch ($vars['section']) {
case 'title':
$vars['title'] = t('Title');
$links = views_ui_view_preview_section_display_category_links($vars['view'], 'title', $vars['title']);
break;
case 'header':
$vars['title'] = t('Header');
$links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
break;
case 'empty':
$vars['title'] = t('No results behavior');
$links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
break;
case 'exposed':
// @todo Sorts can be exposed too, so we may need a better title.
$vars['title'] = t('Exposed Filters');
$links = views_ui_view_preview_section_display_category_links($vars['view'], 'exposed_form_options', $vars['title']);
break;
case 'rows':
// @todo The title needs to depend on what is being viewed.
$vars['title'] = t('Content');
$links = views_ui_view_preview_section_rows_links($vars['view']);
break;p, $account, $language = NULL) {
// By default, we always notify except for canceled and blocked.
$default_notify = ($op != 'status_canceled' && $op != 'status_blocked');
$notify = variable_get('user_mail_' . $op . '_notify', $default_notify);
if ($notify) {
$params['account'] = $account;
$language = $language ? $language : user_preferred_language($account);
$mail = drupal_mail('user', $op, $account->mail, $language, $params);
if ($op == 'register_pending_approval') {
// If a user registered requiring admin approval, notify the admin, too.
// We use the site default language for this.
drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
}
}
return empty($mail) ? NULL : $mail['result'];
}
/**
* Form element process handler for client-side password validation.
*
* This #process handler is automatically invoked for 'password_confirm' form
* elements to add the JavaScript and string translations for dynamic password
* validation.
*
* @see system_element_info()
*/
function user_form_process_password_confirm($element) {
global $user;
$js_settings = array(
'password' => array(
'strengthTitle' => t('Password strength:'),
'hasWeaknesses' => t('To make your password stronger:'),
'tooShort' => t('Make it at least 6 characters'),
'addLowerCase' => t('Add lowercase letters'),
'addUpperCase' => t('Add uppercase letters'),
'addNumbers' => t('Add numbers'),
'addPunctuation' => t('Add punctuation'),
'sameAsUsername' => t('Make it different from your username'),
'confirmSuccess' => t('yes'),
'confirmFailure' => t('no'),
'weak' => t('Weak'),
'fair' => t('Fair'),
'good' => t('Good'),
'strong' => t('Strong'),
'confirmTitle' => t('Passwords match:'),
'username' => (isset($user->name) ? $user->name : ''),
),
);
$element['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.js';
// Ensure settings are only added once per page.
static $already_added = FALSE;
if (!$already_added) {
$already_added = TRUE;
$element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting');
}
return $element;
}
/**
* Implements hook_node_load().
*/
function user_node_load($nodes, $types) {
// Build an array of all uids for node authors, keyed by nid.
$uids = array();
foreach ($nodes as $nid => $node) {
$uids[$nid] = $node->uid;
}
// Fetch name, picture, and data for these users.
$user_fields = db_query("SELECT uid, name, picture, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid');
// Add these values back into the node objects.
foreach ($uids as $nid => $uid) {
$nodes[$nid]->name = $user_fields[$uid]->name;
$nodes[$nid]->picture = $user_fields[$uid]->picture;
$nodes[$nid]->data = $user_fields[$uid]->data;
}
}
/**
* Implements hook_image_style_delete().
*/
function user_image_style_delete($style) {
// If a style is deleted, update the variables.
// Administrators choose a replacement style when deleting.
user_image_style_save($style);
}
/**
* Implements hook_image_style_save().
*/
function user_image_style_save($style) {
// If a style is renamed, update the variables that use it.
if (isset($style['old_name']) && $style['old_name'] == variable_get('user_picture_style', '')) {
variable_set('user_picture_style', $style['name']);
}
}
/**
* Implements hook_action_info().
*/
function user_action_info() {
return array(
'user_block_user_action' => array(
'label' => t('Block current user'),
'type' => 'user',
'configurable' => FALSE,
'triggers' => array('any'),
),
);
}
/**
* Blocks the current user.
*
* @ingroup actions
*/
function user_block_user_action(&$entity, $context = array()) {
// First priority: If there is a $entity->uid, block that user.
// This is most likely a user object or the author if a node or comment.
if (isset($entity->uid)) {
$uid = $entity->uid;
}
elseif (isset($context['uid'])) {
$uid = $context['uid'];
}
// If neither of those are valid, then block the current user.
else {
$uid = $GLOBALS['user']->uid;
}
$account = user_load($uid);
$account = user_save($account, array('status' => 0));
watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a checkbox for the 'user_register_form' instance settings on the 'Edit
* field instance' form.
*/
function user_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
$instance = $form['#instance'];
if ($instance['entity_type'] == 'user') {
$form['instance']['settings']['user_register_form'] = array(
'#type' => 'checkbox',
'#title' => t('Display on user registration form.'),
'#description' => t("This is compulsory for 'required' fields."),
// Field instances created in D7 beta releases before the setting was
// introduced might be set as 'required' and 'not shown on user_register
// form'. We make sure the checkbox comes as 'checked' for those.
'#default_value' => $instance['settings']['user_register_form'] || $instance['required'],
// Display just below the 'required' checkbox.
'#weight' => $form['instance']['required']['#weight'] + .1,
// Disabled when the 'required' checkbox is checked.
'#states' => array(
'enabled' => array('input[name="instance[required]"]' => array('checked' => FALSE)),
),
// Checked when the 'required' checkbox is checked. This is done through
// a custom behavior, since the #states system would also synchronize on
// uncheck.
'#attached' => array(
'js' => array(drupal_get_path('module', 'user') . '/user.js'),
),
);
array_unshift($form['#submit'], 'user_form_field_ui_field_edit_form_submit');
}
}
/**
* Additional submit handler for the 'Edit field instance' form.
*
* Make sure the 'user_register_form' setting is set for required fields.
*/
function user_form_field_ui_field_edit_form_submit($form, &$form_state) {
$instance = $form_state['values']['instance'];
if (!empty($instance['required'])) {
form_set_value($form['instance']['settings']['user_register_form'], 1, $form_state);
}
}
/**
* Form builder; the user registration form.
*
* @ingroup forms
* @see user_account_form()
* @see user_account_form_validate()
* @see user_register_submit()
*/
function user_register_form($form, &$form_state) {
global $user;
$admin = user_access('administer users');
// If we aren't admin but already logged on, go to the user page instead.
if (!$admin && $user->uid) {
drupal_goto('user/' . $user->uid);
}
$form['#user'] = drupal_anonymous_user();
$form['#user_category'] = 'register';
$form['#attached']['library'][] = array('system', 'jquery.cookie');
$form['#attributes']['class'][] = 'user-info-from-cookie';
// Start with the default user account fields.
user_account_form($form, $form_state);
// Attach field widgets, and hide the ones where the 'user_register_form'
// setting is not on.
field_attach_form('user', $form['#user'], $form, $form_state);
foreach (field_info_instances('user', 'user') as $field_name => $instance) {
if (empty($instance['settings']['user_register_form'])) {
$form[$field_name]['#access'] = FALSE;
}
}
if ($admin) {
// Redirect back to page which initiated the create request;
// usually admin/people/create.
$form_state['redirect'] = $_GET['q'];
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Create new account'),
);
$form['#validate'][] = 'user_register_validate';
// Add the final user registration form submit handler.
$form['#submit'][] = 'user_register_submit';
return $form;
}
/**
* Validation function for the user registration form.
*/
function user_register_validate($form, &$form_state) {
entity_form_field_validate('user', $form, $form_state);
}
/**
* Submit handler for the user registration form.
*
* This function is shared by the installation form and the normal registration form,
* which is why it can't be in the user.pages.inc file.
*
* @see user_register_form()
*/
function user_register_submit($form, &$form_state) {
$admin = user_access('administer users');
if (!variable_get('user_email_verification', TRUE) || $admin) {
$pass = $form_state['values']['pass'];
}
else {
$pass = user_password();
}
$notify = !empty($form_state['values']['notify']);
// Remove unneeded values.
form_state_values_clean($form_state);
$form_state['values']['pass'] = $pass;
$form_state['values']['init'] = $form_state['values']['mail'];
$account = $form['#user'];
entity_form_submit_build_entity('user', $account, $form, $form_state);
// Populate $edit with the properties of $account, which have been edited on
// this form by taking over all values, which appear in the form values too.
$edit = array_intersect_key((array) $account, $form_state['values']);
$account = user_save($account, $edit);
// Terminate if an error occurred during user_save().
if (!$account) {
drupal_set_message(t("Error saving user account."), 'error');
$form_state['redirect'] = '';
return;
}
$form_state['user'] = $account;
$form_state['values']['uid'] = $account->uid;
watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
// Add plain text password into user account to generate mail tokens.
$account->password = $pass;
// New administrative account without notification.
$uri = entity_uri('user', $account);
if ($admin && !$notify) {
drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
}
// No e-mail verification required; log in user immediately.
elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
_user_mail_notify('register_no_approval_required', $account);
$form_state['uid'] = $account->uid;
user_login_submit(array(), $form_state);
drupal_set_message(t('Registration successful. You are now logged in.'));
$form_state['redirect'] = '';
}
// No administrator approval required.
elseif ($account->status || $notify) {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
_user_mail_notify($op, $account);
if ($notify) {
drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
}
else {
drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
$form_state['redirect'] = '';
}
}
// Administrator approval required.
else {
_user_mail_notify('register_pending_approval', $account);
drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator. In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
$form_state['redirect'] = '';
}
}
/**
* Implements hook_modules_installed().
*/
function user_modules_installed($modules) {
// Assign all available permissions to the administrator role.
$rid = variable_get('user_admin_role', 0);
if ($rid) {
$permissions = array();
foreach ($modules as $module) {
if ($module_permissions = module_invoke($module, 'permission')) {
$permissions = array_merge($permissions, array_keys($module_permissions));
}
}
if (!empty($permissions)) {
user_role_grant_permissions($rid, $permissions);
}
}
}
/**
* Implements hook_modules_uninstalled().
*/
function user_modules_uninstalled($modules) {
db_delete('role_permission')
->condition('module', $modules, 'IN')
->execute();
}
/**
* Helper function to rewrite the destination to avoid redirecting to login page after login.
*
* Third-party authentication modules may use this function to determine the
* proper destination after a user has been properly logged in.
*/
function user_login_destination() {
$destination = drupal_get_destination();
if ($destination['destination'] == 'user/login') {
$destination['destination'] = 'user';
}
return $destination;
}
/**
* Saves visitor information as a cookie so it can be reused.
*
* @param $values
* An array of key/value pairs to be saved into a cookie.
*/
function user_cookie_save(array $values) {
foreach ($values as $field => $value) {
// Set cookie for 365 days.
setrawcookie('Drupal.visitor.' . $field, rawurlencode($value), REQUEST_TIME + 31536000, '/');
}
}
/**
* Delete a visitor information cookie.
*
* @param $cookie_name
* A cookie name such as 'homepage'.
*/
function user_cookie_delete($cookie_name) {
setrawcookie('Drupal.visitor.' . $cookie_name, '', REQUEST_TIME - 3600, '/');
}
/**
* Implements hook_rdf_mapping().
*/
function user_rdf_mapping() {
return array(
array(
'type' => 'user',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('sioc:UserAccount'),
'name' => array(
'predicates' => array('foaf:name'),
),
'homepage' => array(
'predicates' => array('foaf:page'),
'type' => 'rel',
),
),
),
);
}
/**
* Implements hook_file_download_access().
*/
function user_file_download_access($field, $entity_type, $entity) {
if ($entity_type == 'user') {
return user_view_access($entity);
}
}
/**
* Implements hook_system_info_alter().
*
* Drupal 7 ships with two methods to add additional fields to users: Profile
* module, a legacy module dating back from 2002, and Field API integration
* with users. While Field API support for users currently provides less end
* user features, the inefficient data storage mechanism of Profile module, as
* well as its lack of consistency with the rest of the entity / field based
* systems in Drupal 7, make this a sub-optimal solution to those who were not
* using it in previous releases of Drupal.
*
* To prevent new Drupal 7 sites from installing Profile module, and
* unwittingly ending up with two completely different and incompatible methods
* of extending users, only make the Profile module available if the profile_*
* tables are present.
*
* @todo: Remove in D8, pending upgrade path.
*/
function user_system_info_alter(&$info, $file, $type) {
if orm['user_cancel_confirm'] = array(
'#type' => 'checkbox',
'#title' => t('Require e-mail confirmation to cancel account.'),
'#default_value' => FALSE,
'#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
);
// Also allow to send account canceled notification mail, if enabled.
$form['user_cancel_notify'] = array(
'#type' => 'checkbox',
'#title' => t('Notify user when account is canceled.'),
'#default_value' => FALSE,
'#access' => variable_get('user_mail_status_canceled_notify', FALSE),
'#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
);
return confirm_form($form,
t('Are you sure you want to cancel these user accounts?'),
'admin/people', t('This action cannot be undone.'),
t('Cancel accounts'), t('Cancel'));
}
/**
* Submit handler for mass-account cancellation form.
*
* @see user_multiple_cancel_confirm()
* @see user_cancel_confirm_form_submit()
*/
function user_multiple_cancel_confirm_submit($form, &$form_state) {
global $user;
if ($form_state['values']['confirm']) {
foreach ($form_state['values']['accounts'] as $uid => $value) {
// Prevent programmatic form submissions from cancelling user 1.
if ($uid <= 1) {
continue;
}
// Prevent user administrators from deleting themselves without confirmation.
if ($uid == $user->uid) {
$admin_form_state = $form_state;
unset($admin_form_state['values']['user_cancel_confirm']);
$admin_form_state['values']['_account'] = $user;
user_cancel_confirm_form_submit(array(), $admin_form_state);
}
else {
user_cancel($form_state['values'], $uid, $form_state['values']['user_cancel_method']);
}
}
}
$form_state['redirect'] = 'admin/people';
}
/**
* Retrieve a list of all user setting/information categories and sort them by weight.
*/
function _user_categories() {
$categories = module_invoke_all('user_categories');
usort($categories, '_user_sort');
return $categories;
}
function _user_sort($a, $b) {
$a = (array) $a + array('weight' => 0, 'title' => '');
$b = (array) $b + array('weight' => 0, 'title' => '');
return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
}
/**
* List user administration filters that can be applied.
*/
function user_filters() {
// Regular filters
$filters = array();
$roles = user_roles(TRUE);
unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
if (count($roles)) {
$filters['role'] = array(
'title' => t('role'),
'field' => 'ur.rid',
'options' => array(
'[any]' => t('any'),
) + $roles,
);
}
$options = array();
foreach (module_implements('permission') as $module) {
$function = $module . '_permission';
if ($permissions = $function('permission')) {
asort($permissions);
foreach ($permissions as $permission => $description) {
$options[t('@module module', array('@module' => $module))][$permission] = t($permission);
}
}
}
ksort($options);
$filters['permission'] = array(
'title' => t('permission'),
'options' => array(
'[any]' => t('any'),
) + $options,
);
$filters['status'] = array(
'title' => t('status'),
'field' => 'u.status',
'options' => array(
'[any]' => t('any'),
1 => t('active'),
0 => t('blocked'),
),
);
return $filters;
}
/**
* Extends a query object for user administration filters based on session.
*
* @param $query
* Query object that should be filtered.
*/
function user_build_filter_query(SelectQuery $query) {
$filters = user_filters();
// Extend Query with filter conditions.
foreach (isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array() as $filter) {
list($key, $value) = $filter;
// This checks to see if this permission fig the view.
*/
function views_ui_cache_load($name) {
ctools_include('object-cache');
views_include('view');
$view = ctools_object_cache_get('view', $name);
$original_view = views_get_view($name);
if (empty($view)) {
$view = $original_view;
if (!empty($view)) {
// Check to see if someone else is already editing this view.
$view->locked = ctools_object_cache_test('view', $view->name);
// Set a flag to indicate that this view is being edited.
// This flag will be used e.g. to determine whether strings
// should be localized.
$view->editing = TRUE;
}
}
else {
// Keep disabled/enabled status real.
if ($original_view) {
$view->disabled = !empty($original_view->disabled);
}
}
if (empty($view)) {
return FALSE;
}
else {
return $view;
}
}
/**
* Specialized cache function to add a flag to our view, include an appropriate
* include, and cache more easily.
*/
function views_ui_cache_set(&$view) {
if (!empty($view->locked)) {
drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
return;
}
ctools_include('object-cache');
$view->changed = TRUE; // let any future object know that this view has changed.
if (isset($view->current_display)) {
// Add the knowledge of the changed display, too.
$view->changed_display[$view->current_display] = TRUE;
unset($view->current_display);
}
// Unset handlers; we don't want to write these into the cache
unset($view->display_handler);
unset($view->default_display);
$view->query = NULL;
foreach (array_keys($view->display) as $id) {
unset($view->display[$id]->handler);
unset($view->display[$id]->default_display);
}
ctools_object_cache_set('view', $view->name, $view);
}
/**
* Specialized menu callback to load a view that is only a default
* view.
*/
function views_ui_default_load($name) {
$view = views_get_view($name);
if ($view->type == t('Default')) {
return $view;
}
return FALSE;
}
/**
* Theme preprocess for views-view.tpl.php.
*/
function views_ui_preprocess_views_view(&$vars) {
$view = $vars['view'];
if (!empty($view->views_ui_context) && module_exists('contextual')) {
$view->hide_admin_links = TRUE;
foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) {
if (!empty($vars[$section])) {
$vars[$section] = array(
'#theme' => 'views_ui_view_preview_section',
'#view' => $view,
'#section' => $section,
'#content' => $vars[$section],
'#theme_wrappers' => array('views_container'),
'#attributes' => array('class' => 'contextual-links-region'),
);
$vars[$section] = drupal_render($vars[$section]);
}
}
}
}
/**
* Theme preprocess for theme_views_ui_view_preview_section().
*
* @TODO
* Perhaps move this to includes/admin.inc or theme/theme.inc
*/
function template_preprocess_views_ui_view_preview_section(&$vars) {
switch ($vars['section']) {
case 'title':
$vars['title'] = t('Title');
$links = views_ui_view_preview_section_display_category_links($vars['view'], 'title', $vars['title']);
break;
case 'header':
$vars['title'] = t('Header');
$links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
break;
case 'empty':
$vars['title'] = t('No results behavior');
$links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
break;
case 'exposed':
// @todo Sorts can be exposed too, so we may need a better title.
$vars['title'] = t('Exposed Filters');
$links = views_ui_view_preview_section_display_category_links($vars['view'], 'exposed_form_options', $vars['title']);
break;
case 'rows':
// @todo The title needs to depend on what is being viewed.
$vars['title'] = t('Content');
$links = views_ui_view_preview_section_rows_links($vars['view']);
break;p, $account, $language = NULL) {
// By default, we always notify except for canceled and blocked.
$default_notify = ($op != 'status_canceled' && $op != 'status_blocked');
$notify = variable_get('user_mail_' . $op . '_notify', $default_notify);
if ($notify) {
$params['account'] = $account;
$language = $language ? $language : user_preferred_language($account);
$mail = drupal_mail('user', $op, $account->mail, $language, $params);
if ($op == 'register_pending_approval') {
// If a user registered requiring admin approval, notify the admin, too.
// We use the site default language for this.
drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
}
}
return empty($mail) ? NULL : $mail['result'];
}
/**
* Form element process handler for client-side password validation.
*
* This #process handler is automatically invoked for 'password_confirm' form
* elements to add the JavaScript and string translations for dynamic password
* validation.
*
* @see system_element_info()
*/
function user_form_process_password_confirm($element) {
global $user;
$js_settings = array(
'password' => array(
'strengthTitle' => t('Password strength:'),
'hasWeaknesses' => t('To make your password stronger:'),
'tooShort' => t('Make it at least 6 characters'),
'addLowerCase' => t('Add lowercase letters'),
'addUpperCase' => t('Add uppercase letters'),
'addNumbers' => t('Add numbers'),
'addPunctuation' => t('Add punctuation'),
'sameAsUsername' => t('Make it different from your username'),
'confirmSuccess' => t('yes'),
'confirmFailure' => t('no'),
'weak' => t('Weak'),
'fair' => t('Fair'),
'good' => t('Good'),
'strong' => t('Strong'),
'confirmTitle' => t('Passwords match:'),
'username' => (isset($user->name) ? $user->name : ''),
),
);
$element['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.js';
// Ensure settings are only added once per page.
static $already_added = FALSE;
if (!$already_added) {
$already_added = TRUE;
$element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting');
}
return $element;
}
/**
* Implements hook_node_load().
*/
function user_node_load($nodes, $types) {
// Build an array of all uids for node authors, keyed by nid.
$uids = array();
foreach ($nodes as $nid => $node) {
$uids[$nid] = $node->uid;
}
// Fetch name, picture, and data for these users.
$user_fields = db_query("SELECT uid, name, picture, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid');
// Add these values back into the node objects.
foreach ($uids as $nid => $uid) {
$nodes[$nid]->name = $user_fields[$uid]->name;
$nodes[$nid]->picture = $user_fields[$uid]->picture;
$nodes[$nid]->data = $user_fields[$uid]->data;
}
}
/**
* Implements hook_image_style_delete().
*/
function user_image_style_delete($style) {
// If a style is deleted, update the variables.
// Administrators choose a replacement style when deleting.
user_image_style_save($style);
}
/**
* Implements hook_image_style_save().
*/
function user_image_style_save($style) {
// If a style is renamed, update the variables that use it.
if (isset($style['old_name']) && $style['old_name'] == variable_get('user_picture_style', '')) {
variable_set('user_picture_style', $style['name']);
}
}
/**
* Implements hook_action_info().
*/
function user_action_info() {
return array(
'user_block_user_action' => array(
'label' => t('Block current user'),
'type' => 'user',
'configurable' => FALSE,
'triggers' => array('any'),
),
);
}
/**
* Blocks the current user.
*
* @ingroup actions
*/
function user_block_user_action(&$entity, $context = array()) {
// First priority: If there is a $entity->uid, block that user.
// This is most likely a user object or the author if a node or comment.
if (isset($entity->uid)) {
$uid = $entity->uid;
}
elseif (isset($context['uid'])) {
$uid = $context['uid'];
}
// If neither of those are valid, then block the current user.
else {
$uid = $GLOBALS['user']->uid;
}
$account = user_load($uid);
$account = user_save($account, array('status' => 0));
watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a checkbox for the 'user_register_form' instance settings on the 'Edit
* field instance' form.
*/
function user_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
$instance = $form['#instance'];
if ($instance['entity_type'] == 'user') {
$form['instance']['settings']['user_register_form'] = array(
'#type' => 'checkbox',
'#title' => t('Display on user registration form.'),
'#description' => t("This is compulsory for 'required' fields."),
// Field instances created in D7 beta releases before the setting was
// introduced might be set as 'required' and 'not shown on user_register
// form'. We make sure the checkbox comes as 'checked' for those.
'#default_value' => $instance['settings']['user_register_form'] || $instance['required'],
// Display just below the 'required' checkbox.
'#weight' => $form['instance']['required']['#weight'] + .1,
// Disabled when the 'required' checkbox is checked.
'#states' => array(
'enabled' => array('input[name="instance[required]"]' => array('checked' => FALSE)),
),
// Checked when the 'required' checkbox is checked. This is done through
// a custom behavior, since the #states system would also synchronize on
// uncheck.
'#attached' => array(
'js' => array(drupal_get_path('module', 'user') . '/user.js'),
),
);
array_unshift($form['#submit'], 'user_form_field_ui_field_edit_form_submit');
}
}
/**
* Additional submit handler for the 'Edit field instance' form.
*
* Make sure the 'user_register_form' setting is set for required fields.
*/
function user_form_field_ui_field_edit_form_submit($form, &$form_state) {
$instance = $form_state['values']['instance'];
if (!empty($instance['required'])) {
form_set_value($form['instance']['settings']['user_register_form'], 1, $form_state);
}
}
/**
* Form builder; the user registration form.
*
* @ingroup forms
* @see user_account_form()
* @see user_account_form_validate()
* @see user_register_submit()
*/
function user_register_form($form, &$form_state) {
global $user;
$admin = user_access('administer users');
// If we aren't admin but already logged on, go to the user page instead.
if (!$admin && $user->uid) {
drupal_goto('user/' . $user->uid);
}
$form['#user'] = drupal_anonymous_user();
$form['#user_category'] = 'register';
$form['#attached']['library'][] = array('system', 'jquery.cookie');
$form['#attributes']['class'][] = 'user-info-from-cookie';
// Start with the default user account fields.
user_account_form($form, $form_state);
// Attach field widgets, and hide the ones where the 'user_register_form'
// setting is not on.
field_attach_form('user', $form['#user'], $form, $form_state);
foreach (field_info_instances('user', 'user') as $field_name => $instance) {
if (empty($instance['settings']['user_register_form'])) {
$form[$field_name]['#access'] = FALSE;
}
}
if ($admin) {
// Redirect back to page which initiated the create request;
// usually admin/people/create.
$form_state['redirect'] = $_GET['q'];
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Create new account'),
);
$form['#validate'][] = 'user_register_validate';
// Add the final user registration form submit handler.
$form['#submit'][] = 'user_register_submit';
return $form;
}
/**
* Validation function for the user registration form.
*/
function user_register_validate($form, &$form_state) {
entity_form_field_validate('user', $form, $form_state);
}
/**
* Submit handler for the user registration form.
*
* This function is shared by the installation form and the normal registration form,
* which is why it can't be in the user.pages.inc file.
*
* @see user_register_form()
*/
function user_register_submit($form, &$form_state) {
$admin = user_access('administer users');
if (!variable_get('user_email_verification', TRUE) || $admin) {
$pass = $form_state['values']['pass'];
}
else {
$pass = user_password();
}
$notify = !empty($form_state['values']['notify']);
// Remove unneeded values.
form_state_values_clean($form_state);
$form_state['values']['pass'] = $pass;
$form_state['values']['init'] = $form_state['values']['mail'];
$account = $form['#user'];
entity_form_submit_build_entity('user', $account, $form, $form_state);
// Populate $edit with the properties of $account, which have been edited on
// this form by taking over all values, which appear in the form values too.
$edit = array_intersect_key((array) $account, $form_state['values']);
$account = user_save($account, $edit);
// Terminate if an error occurred during user_save().
if (!$account) {
drupal_set_message(t("Error saving user account."), 'error');
$form_state['redirect'] = '';
return;
}
$form_state['user'] = $account;
$form_state['values']['uid'] = $account->uid;
watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
// Add plain text password into user account to generate mail tokens.
$account->password = $pass;
// New administrative account without notification.
$uri = entity_uri('user', $account);
if ($admin && !$notify) {
drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
}
// No e-mail verification required; log in user immediately.
elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
_user_mail_notify('register_no_approval_required', $account);
$form_state['uid'] = $account->uid;
user_login_submit(array(), $form_state);
drupal_set_message(t('Registration successful. You are now logged in.'));
$form_state['redirect'] = '';
}
// No administrator approval required.
elseif ($account->status || $notify) {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
_user_mail_notify($op, $account);
if ($notify) {
drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
}
else {
drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
$form_state['redirect'] = '';
}
}
// Administrator approval required.
else {
_user_mail_notify('register_pending_approval', $account);
drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator. In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
$form_state['redirect'] = '';
}
}
/**
* Implements hook_modules_installed().
*/
function user_modules_installed($modules) {
// Assign all available permissions to the administrator role.
$rid = variable_get('user_admin_role', 0);
if ($rid) {
$permissions = array();
foreach ($modules as $module) {
if ($module_permissions = module_invoke($module, 'permission')) {
$permissions = array_merge($permissions, array_keys($module_permissions));
}
}
if (!empty($permissions)) {
user_role_grant_permissions($rid, $permissions);
}
}
}
/**
* Implements hook_modules_uninstalled().
*/
function user_modules_uninstalled($modules) {
db_delete('role_permission')
->condition('module', $modules, 'IN')
->execute();
}
/**
* Helper function to rewrite the destination to avoid redirecting to login page after login.
*
* Third-party authentication modules may use this function to determine the
* proper destination after a user has been properly logged in.
*/
function user_login_destination() {
$destination = drupal_get_destination();
if ($destination['destination'] == 'user/login') {
$destination['destination'] = 'user';
}
return $destination;
}
/**
* Saves visitor information as a cookie so it can be reused.
*
* @param $values
* An array of key/value pairs to be saved into a cookie.
*/
function user_cookie_save(array $values) {
foreach ($values as $field => $value) {
// Set cookie for 365 days.
setrawcookie('Drupal.visitor.' . $field, rawurlencode($value), REQUEST_TIME + 31536000, '/');
}
}
/**
* Delete a visitor information cookie.
*
* @param $cookie_name
* A cookie name such as 'homepage'.
*/
function user_cookie_delete($cookie_name) {
setrawcookie('Drupal.visitor.' . $cookie_name, '', REQUEST_TIME - 3600, '/');
}
/**
* Implements hook_rdf_mapping().
*/
function user_rdf_mapping() {
return array(
array(
'type' => 'user',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('sioc:UserAccount'),
'name' => array(
'predicates' => array('foaf:name'),
),
'homepage' => array(
'predicates' => array('foaf:page'),
'type' => 'rel',
),
),
),
);
}
/**
* Implements hook_file_download_access().
*/
function user_file_download_access($field, $entity_type, $entity) {
if ($entity_type == 'user') {
return user_view_access($entity);
}
}
/**
* Implements hook_system_info_alter().
*
* Drupal 7 ships with two methods to add additional fields to users: Profile
* module, a legacy module dating back from 2002, and Field API integration
* with users. While Field API support for users currently provides less end
* user features, the inefficient data storage mechanism of Profile module, as
* well as its lack of consistency with the rest of the entity / field based
* systems in Drupal 7, make this a sub-optimal solution to those who were not
* using it in previous releases of Drupal.
*
* To prevent new Drupal 7 sites from installing Profile module, and
* unwittingly ending up with two completely different and incompatible methods
* of extending users, only make the Profile module available if the profile_*
* tables are present.
*
* @todo: Remove in D8, pending upgrade path.
*/
function user_system_info_alter(&$info, $file, $type) {
if ($type == 'module' && $file->name == 'profile' && db_table_exists('profile_field')) {
$info['hidden'] = FALSE;
}
}
type' => MENU_DEFAULT_LOCAL_TASK,
'load arguments' => array('%map', '%index'),
);
if (($categories = _user_categories()) && (count($categories) > 1)) {
foreach ($categories as $key => $category) {
// 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK.
if ($category['name'] != 'account') {
$items['user/%user_category/edit/' . $category['name']] = array(
'title callback' => 'check_plain',
'title arguments' => array($category['title']),
'page callback' => 'drupal_get_form',
'page arguments' => array('user_profile_form', 1, 3),
'access callback' => isset($category['access callback']) ? $category['access callback'] : 'user_edit_access',
'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(1),
'type' => MENU_LOCAL_TASK,
'weight' => $category['weight'],
'load arguments' => array('%map', '%index'),
'tab_parent' => 'user/%/edit',
'file' => 'user.pages.inc',
);
}
}
}
return $items;
}
/**
* Implements hook_menu_site_status_alter().
*/
function user_menu_site_status_alter(&$menu_site_status, $path) {
if ($menu_site_status == MENU_SITE_OFFLINE) {
// If the site is offline, log out unprivileged users.
if (user_is_logged_in() && !user_access('access site in maintenance mode')) {
module_load_include('pages.inc', 'user', 'user');
user_logout();
}
if (user_is_anonymous()) {
switch ($path) {
case 'user':
// Forward anonymous user to login page.
drupal_goto('user/login');
case 'user/login':
case 'user/password':
// Disable offline mode.
$menu_site_status = MENU_SITE_ONLINE;
break;
default:
if (strpos($path, 'user/reset/') === 0) {
// Disable offline mode.
$menu_site_status = MENU_SITE_ONLINE;
}
break;
}
}
}
if (user_is_logged_in()) {
if ($path == 'user/login') {
// If user is logged in, redirect to 'user' instead of giving 403.
drupal_goto('user');
}
if ($path == 'user/register') {
// Authenticated user should be redirected to user edit page.
drupal_goto('user/' . $GLOBALS['user']->uid . '/edit');
}
}
}
/**
* Implements hook_menu_link_alter().
*/
function user_menu_link_alter(&$link) {
// The path 'user' must be accessible for anonymous users, but only visible
// for authenticated users. Authenticated users should see "My account", but
// anonymous users should not see it at all. Therefore, invoke
// user_translated_menu_link_alter() to conditionally hide the link.
if ($link['link_path'] == 'user' && $link['module'] == 'system') {
$link['options']['alter'] = TRUE;
}
// Force the Logout link to appear on the top-level of 'user-menu' menu by
// default (i.e., unless it has been customized).
if ($link['link_path'] == 'user/logout' && $link['module'] == 'system' && empty($link['customized'])) {
$link['plid'] = 0;
}
}
/**
* Implements hook_translated_menu_link_alter().
*/
function user_translated_menu_link_alter(&$link) {
// Hide the "User account" link for anonymous users.
if ($link['link_path'] == 'user' && $link['module'] == 'system' && user_is_anonymous()) {
$link['hidden'] = 1;
}
}
/**
* Implements hook_admin_paths().
*/
function user_admin_paths() {
$paths = array(
'user/*/cancel' => TRUE,
'user/*/edit' => TRUE,
'user/*/edit/*' => TRUE,
);
return $paths;
}
/**
* Returns $arg or the user ID of the current user if $arg is '%' or empty.
*
* Deprecated. Use %user_uid_optional instead.
*
* @todo D8: Remove.
*/
function user_uid_only_optional_to_arg($arg) {
return user_uid_optional_to_arg($arg);
}
/**
* Load either a specified or the current user account.
*
* @param $uid
* An optional user ID of the user to load. If not provided, the current
* user's ID will be used.
* @return
* A fully-loaded $user object upon sucorm',
'pattern' => 'views_exposed_form__',
'render element' => 'form',
);
$hooks['views_more'] = $base + array(
'template' => 'views-more',
'pattern' => 'views_more__',
'variables' => array('more_url' => NULL, 'link_text' => 'more'),
);
// Add theme suggestions which are part of modules.
foreach (views_get_module_apis() as $info) {
if (isset($info['template path'])) {
$hooks += _views_find_module_templates($hooks, $info['template path']);
}
}
return $hooks;
}
/**
* Scans a directory of a module for template files.
*
* @param $cache
* The existing cache of theme hooks to test against.
* @param $path
* The path to search.
*
* @see drupal_find_theme_templates
*/
function _views_find_module_templates($cache, $path) {
$regex = '/' . '\.tpl\.php' . '$' . '/';
// Because drupal_system_listing works the way it does, we check for real
// templates separately from checking for patterns.
$files = drupal_system_listing($regex, $path, 'name', 0);
foreach ($files as $template => $file) {
// Chop off the remaining extensions if there are any. $template already
// has the rightmost extension removed, but there might still be more,
// such as with .tpl.php, which still has .tpl in $template at this point.
if (($pos = strpos($template, '.')) !== FALSE) {
$template = substr($template, 0, $pos);
}
// Transform - in filenames to _ to match function naming scheme
// for the purposes of searching.
$hook = strtr($template, '-', '_');
if (isset($cache[$hook])) {
$templates[$hook] = array(
'template' => $template,
'path' => dirname($file->filename),
'includes' => isset($cache[$hook]['includes']) ? $cache[$hook]['includes'] : NULL,
);
}
// Ensure that the pattern is maintained from base themes to its sub-themes.
// Each sub-theme will have their templates scanned so the pattern must be
// held for subsequent runs.
if (isset($cache[$hook]['pattern'])) {
$templates[$hook]['pattern'] = $cache[$hook]['pattern'];
}
}
$patterns = array_keys($files);
foreach ($cache as $hook => $info) {
if (!empty($info['pattern'])) {
// Transform _ in pattern to - to match file naming scheme
// for the purposes of searching.
$pattern = strtr($info['pattern'], '_', '-');
$matches = preg_grep('/^'. $pattern .'/', $patterns);
if ($matches) {
foreach ($matches as $match) {
$file = substr($match, 0, strpos($match, '.'));
// Put the underscores back in for the hook name and register this pattern.
$templates[strtr($file, '-', '_')] = array(
'template' => $file,
'path' => dirname($files[$match]->uri),
'variables' => $info['variables'],
'base hook' => $hook,
'includes' => isset($info['includes']) ? $info['includes'] : NULL,
);
}
}
}
}
return $templates;
}
/**
* A theme preprocess function to automatically allow view-based node
* templates if called from a view.
*
* The 'modules/node.views.inc' file is a better place for this, but
* we haven't got a chance to load that file before Drupal builds the
* node portion of the theme registry.
*/
function views_preprocess_node(&$vars) {
// The 'view' attribute of the node is added in views_preprocess_node()
if (!empty($vars['node']->view) && !empty($vars['node']->view->name)) {
$vars['view'] = $vars['node']->view;
$vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name;
if (!empty($vars['node']->view->current_display)) {
$vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name . '__' . $vars['node']->view->current_display;
// If a node is being rendered in a view, and the view does not have a path,
// prevent drupal from accidentally setting the $page variable:
if ($vars['page'] && $vars['view_mode'] == 'full' && !$vars['view']->display_handler->has_path()) {
$vars['page'] = FALSE;
to the user page instead.
if ($user->uid) {
drupal_goto('user/' . $user->uid);
}
// Display login form:
$form['name'] = array('#type' => 'textfield',
'#title' => t('Username'),
'#size' => 60,
'#maxlength' => USERNAME_MAX_LENGTH,
'#required' => TRUE,
);
$form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal')));
$form['pass'] = array('#type' => 'password',
'#title' => t('Password'),
'#description' => t('Enter the password that accompanies your username.'),
'#required' => TRUE,
);
$form['#validate'] = user_login_default_validators();
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
return $form;
}
/**
* Set up a series for validators which check for blocked users,
* then authenticate against local database, then return an error if
* authentication fails. Distributed authentication modules are welcome
* to use hook_form_alter() to change this series in order to
* authenticate against their user database instead of the local users
* table. If a distributed authentication module is successful, it
* should set $form_state['uid'] to a user ID.
*
* We use three validators instead of one since external authentication
* modules usually only need to alter the second validator.
*
* @see user_login_name_validate()
* @see user_login_authenticate_validate()
* @see user_login_final_validate()
* @return array
* A simple list of validate functions.
*/
function user_login_default_validators() {
return array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate');
}
/**
* A FAPI validate handler. Sets an error if supplied username has been blocked.
*/
function user_login_name_validate($form, &$form_state) {
if (isset($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
// Blocked in user administration.
form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
}
}
/**
* A validate handler on the login form. Check supplied username/password
* against local users table. If successful, $form_state['uid']
* is set to the matching user ID.
*/
function user_login_authenticate_validate($form, &$form_state) {
$password = trim($form_state['values']['pass']);
if (!empty($form_state['values']['name']) && !empty($password)) {
// Do not allow any login from the current user's IP if the limit has been
// reached. Default is 50 failed attempts allowed in one hour. This is
// independent of the per-user limit to catch attempts from one IP to log
// in to many different user accounts. We have a reasonably high limit
// since there may be only one apparent IP for all users at an institution.
if (!flood_is_allowed('failed_login_attempt_ip', variable_get('user_failed_login_ip_limit', 50), variable_get('user_failed_login_ip_window', 3600))) {
$form_state['flood_control_triggered'] = 'ip';
return;
}
$account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
if ($account) {
if (variable_get('user_failed_login_identifier_uid_only', FALSE)) {
// Register flood events based on the uid only, so they apply for any
// IP address. This is the most secure option.
$identifier = $account->uid;
}
else {
// The default identifier is a combination of uid and IP address. This
// is less secure but more resistant to denial-of-service attacks that
// could lock out all users with public user names.
$identifier = $account->uid . '-' . ip_address();
}
$form_state['flood_control_user_identifier'] = $identifier;
// Don't allow login if the limit for this user has been reached.
// Default is to allow 5 failed attempts every 6 hours.
if (!flood_is_allowed('failed_login_attempt_user', variable_get('user_failed_login_user_limit', 5), variable_get('user_failed_login_user_window', 21600), $identifier)) {
$form_state['flood_control_triggered'] = 'user';
return;
}
}
// We are not limited by flood control, so try to authenticate.
// Set $form_state['uid'] as a flag for user_login_final_validate().
$form_state['uid'] = user_authenticate($form_state['values']['name'], $password);
}
}
/**
* The final validation handler on the login form.
*
* Sets a form error if user has not been authenticated, or if too many
* logins have been attempted. This validation function should always
* be the last one.
*/
function user_login_final_validate($form, &$form_state) {
if (empty($form_state['uid'])) {
// Always register an IP-based failed login event.
flood_register_event('failed_login_attempt_ip', variable_get('user_failed_login_ip_window', 3600));
// Register a per-user failed login event.
if (isset($form_state['flood_control_user_identifier'])) {
flood_register_event('failed_login_attempt_user', variable_get('user_failed_login_user_window', 21600), $form_state['flood_control_user_identifier']);
}
if (isset($form_state['flood_control_triggered'])) {
if ($form_state['flood_control_triggered'] == 'user') {
form_set_error('name', format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or request a new password.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
}
else {
// We did not find a uid, so the limit is IP-based.
form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or request a new password.', array('@url' => url('user/password'))));
}
}
else {
form_set_error('name', t('Sorry, unrecognized username or password. Have you forgotten your password?', array('@password' => url('user/password'))));
watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
}
}
elseif (isset($form_state['flood_control_user_identifier'])) {
// Clear past failures for this user so as not to block a user who might
// log in and out more than once in an hour.
flood_clear_event('failed_login_attempt_user', $form_state['flood_control_user_identifier']);
}
}
/**
* Try to validate the user's login credentials locally.
*
* @param $name
* User name to authenticate.
* @param $password
* A plain-text password, such as trimmed text from form values.
* @return
* The user's uid on success, or FALSE on failure to authenticate.
*/
function user_authenticate($name, $password) {
$uid = FALSE;
if (!empty($name) && !empty($password)) {
$account = user_load_by_name($name);
if ($account) {
// Allow alternate password hashing schemes.
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
if (user_check_password($password, $account)) {
// Successful authentication.
$uid = $account->uid;
// Update user to new password scheme if needed.
if (user_needs_new_hash($account)) {
user_save($account, array('pass' => $password));
}
}
}
}
return $uid;
}
/**
* Finalize the login process. Must be called when logging in a user.
*
* The function records a watchdog message about the new session, saves the
* login timestamp, calls hook_user op 'login' and generates a new session. *
*/
function user_login_finalize(&$edit = array()) {
global $user;
watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
// Update the user table timestamp noting user has logged in.
// This is also used to invalidate one-time login links.
$user->login = REQUEST_TIME;
db_update('users')
->fields(array('login' => $user->login))
->condition('uid', $user->uid)
->execute();
// Regenerate the session ID to prevent against session fixation attacks.
// This is called before hook_user in case one of those functions fails
// or incorrectly does a redirect which would leave the old session in place.
drupal_session_regenerate();
user_module_invoke('login', $edit, $user);
}
/**
* Submit handler for the login form. Load $user object and perform standard login
* tasks. The user is then redirected to the My Account page. Setting the
* destination in the query string overrides the redirect.
*/
function user_login_submit($form, &$form_state) {
global $user;
$user = user_load($form_state['uid']);
$form_state['redirect'] = 'user/' . $user->uid;
user_login_finalize($form_state);
}
/**
* Helper function for authentication modules. Either logs in or registers
* the current user, based on username. Either way, the global $user object is
* populated and login tasks are performed.
*/
function user_external_login_register($name, $module) {
$account = user_external_load($name);
if (!$account) {
// Register this new user.
$userinfo = array(
'name' => $name,
'pass' => user_password(),
'init' => $name,
'status' => 1,
'access' => REQUEST_TIME
);
$account = user_save(drupal_anonymous_user(), $userinfo);
// Terminate if an error occurred during user_save().
if (!$account) {
drupal_set_message(t("Error saving user account."), 'error');
return;
}
user_set_authmaps($account, array("authname_$module" => $name));
}
// Log user in.
$form_state['uid'] = $account->uid;
user_login_submit(array(), $form_state);
}
/**
* Generates a unique URL for a user to login and reset their password.
*
* @param object $account
* An object containing the user account.
*
* @return
* A unique URL that provides a one-time log in for the user, from which
* they can change their password.
*/
function user_pass_reset_url($account) {
$timestamp = REQUEST_TIME;
return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
}
/**
* Generates a URL to confirm an account cancellation request.
*
* @param object $account
* The user account object, which must contain at least the following
* properties:
* - uid: The user uid number.
* - pass: The hashed user password string.
* - login: The user login name.
*
* @return
* A unique URL that may be used to confirm the cancellation of the user
* account.
*
* @see user_mail_tokens()
* @see user_cancel_confirm()
*/
function user_cancel_url($account) {
$timestamp = REQUEST_TIME;
return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
}
/**
* Creates a unique hash value for use in time-dependent per-user URLs.
*
* This hash is normally used to build a unique and secure URL that is sent to
* the user by email for purposes such as resetting the user's password. In
* order to validate the URL, the same hash can be generated again, from the
* same information, and compared to the hash value from the URL. The URL
* normally contains both the time stamp and the numeric user ID. The login
* name and hashed password are retrieved from the database as necessary. For a
* usage example, see user_cancel_url() and user_cancel_confirm().
*
* @param $password
* The hashed user account password value.
* @param $timestamp
* A unix timestamp.
* @param $login
* The user account login name.
*
* @return
* A string that is safe for use in URLs and SQL statements.
*/
function user_pass_rehash($password, $timestamp, $login) {
return drupal_hmac_base64($timestamp . $login, drupal_get_hash_salt() . $password);
}
/**
* Cancel a user account.
*
* Since the user cancellation process needs to be run in a batch, either
* Form API will invoke it, or batch_process() needs to be invoked after calling
* this function and should define the path to redirect to.
*
* @param $edit
* An array of submitted form values.
* @param $uid
* The user ID of the user account to cancel.
* @param $method
* The account cancellation method to use.
*
* @see _user_cancel()
*/
function user_cancel($edit, $uid, $method) {
global $user;
$account = user_load($uid);
if (!$account) {
drupal_set_message(t('The user account %id does not exist.', array('%id' => $uid)), 'error');
watchdog('user', 'Attempted to cancel non-existing user account: %id.', array('%id' => $uid), WATCHDOG_ERROR);
return;
}
// Initialize batch (to set title).
$batch = array(
'title' => t('Cancelling account'),
'operations' => array(),
);
batch_set($batch);
// Modules use hook_user_delete() to respond to deletion.
if ($method != 'user_cancel_delete') {
// Allow modules to add further sets to this batch.
module_invoke_all('user_cancel', $edit, $account, $method);
}
// Finish the batch and actually cancel the account.
$batch = array(
'title' => t('Cancelling user account'),
'operations' => array(
array('_user_cancel', array($edit, $account, $method)),
),
);
batch_set($batch);
// Batch processing is either handled via Form API or has to be invoked
// manually.
}
/**
* Last batch processing step for cancelling a user account.
*
* Since batch and session API require a valid user account, the actual
* cancellation of a user account needs to happen last.
*
* @see user_cancel()
*/
function _user_cancel($edit, $account, $method) {
global $user;
switch ($method) {
case 'user_cancel_block':
case 'user_cancel_block_unpublish':
default:
// Send account blocked notification if option was checked.
if (!empty($edit['user_cancel_notify'])) {
_user_mail_notify('status_blocked', $account);
}
user_save($account, array('status' => 0));
drupal_set_message(t('%name has been disabled.', array('%name' => $account->name)));
watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
break;
case 'user_cancel_reassign':
case 'user_cancel_delete':
// Send account canceled notification if option was checked.
if (!empty($edit['user_cancel_notify'])) {
_user_mail_notify('status_canceled', $account);
}
user_delete($account->uid);
drupal_set_message(t('%name has been deleted.', array('%name' => $account->name)));
watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
break;
}
// After cancelling account, ensure that user is logged out.
if ($account->uid == $user->uid) {
// Destroy the current session, and reset $user to the anonymous user.
session_destroy();
}
// Clear the cache for anonymous users.
cache_clear_all();
}
/**
* Delete a user.
*
* @param $uid
* A user ID.
*/
function user_delete($uid) {
user_delete_multiple(array($uid));
}
/**
* Delete multiple user accounts.
*
* @param $uids
* An array of user IDs.
*/
function user_delete_multiple(array $uids) {
if (!empty($uids)) {
$accounts = user_load_multiple($uids, array());
$transaction = db_transaction();
try {
foreach ($accounts as $uid => $account) {
module_invoke_all('user_delete', $account);
module_invoke_all('entity_delete', $account, 'user');
field_attach_delete('user', $account);
drupal_session_destroy_uid($account->uid);
}
db_delete('users')
->condition('uid', $uids, 'IN')
->execute();
db_delete('users_roles')
->condition('uid', $uids, 'IN')
->execute();
db_delete('authmap')
->condition('uid', $uids, 'IN')
->execute();
}
catch (Exception $e) {
$transaction->rollback();
watchdog_exception('user', $e);
throw $e;
}
entity_get_controller('user')->resetCache();
}
}
/**
* Page callback wrapper for user_view().
*/
function user_view_page($account) {
// An administrator may try to view a non-existent account,
// so we give them a 404 (versus a 403 for non-admins).
return is_object($account) ? user_view($account) : MENU_NOT_FOUND;
}
/**
* Generate an array for rendering the given user.
*
* When viewing a user profile, the $page array contains:
*
* - $page['content']['Profile Category']:
* Profile categories keyed by their human-readable names.
* - $page['content']['Profile Category']['profile_machine_name']:
* Profile fields keyed by their machine-readable names.
* - $page['content']['user_picture']:
* User's rendered picture.
* - $page['content']['summary']:
* Contains the default "History" profile data for a user.
* - $page['content']['#account']:
* The user account of the profile being viewed.
*
* To theme user profiles, copy modules/user/user-profile.tpl.php
* to your theme directory, and edit it as instructed in that file's comments.
*
* @param $account
* A user object.
* @param $view_mode
* View mode, e.g. 'full'.
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*
* @return
* An array as expected by drupal_render().
*/
function user_view($account, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = $GLOBALS['language_content']->language;
}
// Retrieve all profile fields and attach to $account->content.
user_build_content($account, $view_mode, $langcode);
$build = $account->content;
// We don't need duplicate rendering info in account->content.
unset($account->content);
$build += array(
'#theme' => 'user_profile',
'#account' => $account,
'#view_mode' => $view_mode,
'#language' => $langcode,
);
// Allow modules to modify the structured user.
$type = 'user';
drupal_alter(array('user_view', 'entity_view'), $build, $type);
return $build;
}
/**
* Builds a structured array representing the profile content.
*
* @param $account
* A user object.
* @param $view_mode
* View mode, e.g. 'full'.
* @param $langcode
* (optional) A language code to use for rendering. Defaults to the global
* content language of the current request.
*/
function user_build_content($account, $view_mode = 'full', $langcode = NULL) {
if (!isset($langcode)) {
$langcode = $GLOBALS['language_content']->language;
}
// Remove previously built content, if exists.
$account->content = array();
// Build fields content.
field_attach_prepare_view('user', array($account->uid => $account), $view_mode, $langcode);
entity_prepare_view('user', array($account->uid => $account), $langcode);
$account->content += field_attach_view('user', $account, $view_mode, $langcode);
// Populate $account->content with a render() array.
module_invoke_all('user_view', $account, $view_mode, $langcode);
module_invoke_all('entity_view', $account, 'user', $view_mode, $langcode);
}
/**
* Implements hook_mail().
*/
function user_mail($key, &$message, $params) {
$language = $message['language'];
$variables = array('user' => $params['account']);
$message['subject'] .= _user_mail_text($key . '_subject', $language, $variables);
$message['body'][] = _user_mail_text($key . '_body', $language, $variables);
}
/**
* Returns a mail string for a variable name.
*
* Used by user_mail() and the settings forms to retrieve strings.
*/
function _user_mail_text($key, $language = NULL, $variables = array(), $replace = TRUE) {
$langcode = isset($language) ? $language->language : NULL;
if ($admin_setting = variable_get('user_mail_' . $key, FALSE)) {
// An admin setting overrides the default string.
$text = $admin_setting;
}
else {
// No override, return default string.
switch ($key) {
case 'register_no_approval_required_subject':
$text = t('Account details for [user:name] at [site:name]', array(), array('langcode' => $langcode));
break;
case 'register_no_approval_required_body':
$text = t("[user:name],
Thank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:
[user:one-time-login-url]
This link can only be used once to log in and will lead you to a page where you can set your password.
After setting your password, you will be able to log in at [site:login-url] in the future using:
username: [user:name]
password: Your password
-- [site:name] team", array(), array('langcode' => $langcode));
break;
case 'register_admin_created_subject':
$text = t('An administrator created an account for you at [site:name]', array(), array('langcode' => $langcode));
break;
case 'register_admin_created_body':
$text = t("[user:name],
A site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:
[user:one-time-login-url]
This link can only be used once to log in and will lead you to a page where you can set your password.
After setting your password, you will be able to log in at [site:login-url] in the future using:
username: [user:name]
password: Your password
-- [site:name] team", array(), array('langcode' => $langcode));
break;
case 'register_pending_approval_subject':
case 'register_pending_approval_admin_subject':
$text = t('Account details for [user:name] at [site:name] (pending admin approval)', array(), array('langcode' => $langcode));
break;
case 'register_pending_approval_body':
$text = t("[user:name],
Thank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details.
-- [site:name] team", array(), array('langcode' => $langcode));
break;
case 'register_pending_approval_admin_body':
$text = t("[user:name] has applied for an account.
[user:edit-url]", array(), array('langcode' => $langcode));
break;
case 'password_reset_subject':
$text = t('Replacement login information for [user:name] at [site:name]', array(), array('langcode' => $langcode));
break;
case 'password_reset_body':
$text = t("[user:name],
A request to reset the password for your account has been made at [site:name].
You may now log in by clicking this link or copying and pasting it to your browser:
[user:one-time-login-url]
This link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.
-- [site:name] team", array(), array('langcode' => $langcode));
break;
case 'status_activated_subject':
$text = t('Account details for [user:name] at [site:name] (approved)', array(), array('langcode' => $langcode));
break;
case 'status_activated_body':
$text = t("[user:name],
Your account at [site:name] has been activated.
You may now log in by clicking this link or copying and pasting it into your browser:
[user:one-time-login-url]
This link can only be used once to log in and will lead you to a page where you can set your password.
After setting your password, you will be able to log in at [site:login-url] in the future using:
username: [user:name]
password: Your password
-- [site:name] team", array(), array('langcode' => $langcode));
break;
case 'status_blocked_subject':
$text = t('Account details for [user:name] at [site:name] (blocked)', array(), array('langcode' => $langcode));
break;
case 'status_blocked_body':
$text = t("[user:name],
Your account on [site:name] has been blocked.
-- [site:name] team", array(), array('langcode' => $langcode));
note that
* this returns a reference, so be careful! You can unintentionally modify the
* $view object.
*/
function &views_get_page_view() {
return views_set_page_view();
}
/**
* Set the current 'current view' that is being built/rendered so that it is
* easy for other modules or items in drupal_eval to identify
*/
function &views_set_current_view($view = NULL) {
static $cache = NULL;
if (isset($view)) {
$cache = $view;
}
return $cache;
}
/**
* Find out what, if any, current view is currently in use. Please note that
* this returns a reference, so be careful! You can unintentionally modify the
* $view object.
*/
function &views_get_current_view() {
return views_set_current_view();
}
// ------------------------------------------------------------------
// Include file helpers
/**
* Include views .inc files as necessary.
*/
function views_include($file) {
ctools_include($file, 'views');
}
/**
* Load views files on behalf of modules.
*/
function views_module_include($api, $reset = FALSE) {
if ($reset) {
$cache = &drupal_static('ctools_plugin_api_info');
if (isset($cache['views']['views'])) {
unset($cache['views']['views']);
}
}
ctools_include('plugins');
return ctools_plugin_api_include('views', $api, views_api_minimum_version(), views_api_version());
}
/**
* Get a list of modules that support the current views API.
*/
function views_get_module_apis($api = 'views', $reset = FALSE) {
if ($reset) {
$cache = &drupal_static('ctools_plugin_api_info');
if (isset($cache['views']['views'])) {
unset($cache['views']['views']);
}
}
ctools_include('plugins');
return ctools_plugin_api_info('views', $api, views_api_minimum_version(), views_api_version());
}
/**
* Include views .css files.
*/
function views_add_css($file) {
// We set preprocess to FALSE because we are adding the files conditionally,
// and we don't want to generate duplicate cache files.
// TODO: at some point investigate adding some files unconditionally and
// allowing preprocess.
drupal_add_css(drupal_get_path('module', 'views') . "/css/$file.css", array('preprocess' => FALSE));
}
/**
* Include views .js files.
*/
function views_add_js($file) {
// If javascript has been disabled by the user, never add js files.
if (variable_get('views_no_javascript', FALSE)) {
return;
}
static $base = TRUE, $ajax = TRUE;
if ($base) {
drupal_add_js(drupal_get_path('module', 'views') . "/js/base.js");
$base = FALSE;
}
if ($ajax && in_array($file, array('ajax', 'ajax_view'))) {
drupal_add_library('system', 'drupal.ajax');
drupal_add_library('system', 'jquery.form');
$ajax = FALSE;
}
ctools_add_js($file, 'views');
}
/**
* Load views files on behalf of modules.
*/
function views_include_handlers($reset = FALSE) {
static $finished = FALSE;
// Ensure this only gets run once.
if ($finished && !$reset) {
return;
}
views_include('base');
views_include('handlers');
views_include('cache');
views_include('plugins');
views_module_include('views', $reset);
$finished = TRUE;
}
// -----------------------------------------------------------------------
// Views handler functions
/**
* Fetch a handler from the data cache.
*
* @param $table
* The name of the table this handler is from.
* @param $field
* The name of the field this handler is from.
* @param $key
* The type of handler. i.e, sort, field, argument, filter, relationship
* @param $override
* Override the actual handler object with this class. Used for
* aggregation when the handler is redirected to the aggregation
* handler.
*
* @return views_handler
* An instance of a handler object. May be views_handler_broken.
*/
function views_get_handler($table, $field, $key, $override = NULL) {
static $recursion_protection = array();
$data = views_fetch_data($table, FALSE);
$handler = NULL;
// Support old views_data entries conversion.
// Support conversion on table level.
if (isset($data['moved to'])) {
$moved = array($data['moved to'], $field);
}
// Support conversion on datafield level.
if (isset($data[$field]['moved to'])) {
$moved = $data[$field]['moved to'];
}
// Support conversion on handler level.
if (isset($data[$field][$key]['moved to'])) {
$moved = $data[$field][$key]['moved to'];
}
if (isset($data[$field][$key]) || !empty($moved)) {
if (!empty($moved)) {
list($moved_table, $moved_field) = $moved;
if (!empty($recursion_protection[$moved_table][$moved_field])) {
// recursion detected!
return NULL;
}
$recursion_protection[$moved_table][$moved_field] = TRUE;
$handler = views_get_handler($moved_table, $moved_field, $key, $override);
$recursion_protection = array();
if ($handler) {
// store these values so we know what we were originally called.
$handler->original_table = $table;
$handler->original_field = $field;
if (empty($handler->actual_table)) {
$handler->actual_table = $moved_table;
$handler->actual_field = $moved_field;
}
}
return $handler;
}
// Set up a default handler:
if (empty($data[$field][$key]['handler'])) {
$data[$field][$key]['handler'] = 'views_handler_' . $key;
}
if ($override) {
$data[$field][$key]['override handler'] = $override;
}
$handler = _views_prepare_handler($data[$field][$key], $data, $field, $key);
}
if ($handler) {
return $handler;
}
// DEBUG -- identify missing handlers
vpr("Missing handler: $table $field $key");
$broken = array(
'title' => t('Broken handler @table.@field', array('@table' => $table, '@field' => $field)),
'handler' => 'views_handler_' . $key . '_broken',
'table' => $table,
'field' => $field,
);
return _views_create_handler($broken, 'handler', $key);
}
/**
* Fetch Views' data from the cache
*/
function views_fetch_data($table = NULL, $move = TRUE, $reset = FALSE) {
views_include('cache');
return _views_fetch_data($table, $move, $reset);
}
// -----------------------------------------------------------------------
// Views plugin functions
/**
* Fetch the plugin data from cache.
*/
function views_fetch_plugin_data($type = NULL, $plugin = NULL, $reset = FALSE) {
views_include('cache');
return _views_fetch_plugin_data($type, $plugin, $reset);
}
/**
* Fetch a list of all base tables available
*
* @param $type
* Either 'display', 'style' or 'row'
* @param $key
* For style plugins, this is an optional type to restrict to. May be 'normal',
* 'summary', 'feed' or others based on the neds of the display.
* @param $base
* An array of possible base tables.
*
* @return
* A keyed array of in the form of 'base_table' => 'Description'.
*/
function views_fetch_plugin_names($type, $key = NULL, $base = array()) {
$data = views_fetch_plugin_data();
$plugins[$type] = array();
foreach ($data[$type] as $id => $plugin) {
// Skip plugins that don't conform to our key.
if ($key && (empty($plugin['type']) || $plugin['type'] != $key)) {
continue;
}
if (empty($plugin['no ui']) && (empty($base) || empty($plugin['base']) || array_intersect($base, $plugin['base']))) {
$plugins[$type][$id] = $plugin['title'];
}
}
if (!empty($plugins[$type])) {
asort($plugins[$type]);
return $plugins[$type];
}
// fall-through
return array();
}
/**
* Get a handler for a plugin
*
* @return views_plugin
*
* The created plugin object.
*/
function views_get_plugin($type, $plugin, $reset = FALSE) {
views_include('handlers');
$definition = views_fetch_plugin_data($type, $plugin, $reset);
if (!empty($definition)) {
return _views_create_handler($definition, $type);
}
}
/**
* Load the current enabled localization plugin.
*
* @return The name of the localization plugin.
*/
function views_get_localization_plugin() {
$plugin = variable_get('views_localization_plugin', '');
// Provide sane default values for the localization plugin.
if (empty($plugin)) {
if (module_exists('locale')) {
$p 'access user profiles' => 1, // Grant 'access user profiles'
* 'access content' => TRUE, // Grant 'access content'
* 'access comments' => 'access comments', // Grant 'access comments'
* )
* @endcode
* Existing permissions are not changed, unless specified in $permissions.
*
* @see user_role_grant_permissions()
* @see user_role_revoke_permissions()
*/
function user_role_change_permissions($rid, array $permissions = array()) {
// Grant new permissions for the role.
$grant = array_filter($permissions);
if (!empty($grant)) {
user_role_grant_permissions($rid, array_keys($grant));
}
// Revoke permissions for the role.
$revoke = array_diff_assoc($permissions, $grant);
if (!empty($revoke)) {
user_role_revoke_permissions($rid, array_keys($revoke));
}
}
/**
* Grant permissions to a user role.
*
* @param $rid
* The ID of a user role to alter.
* @param $permissions
* A list of permission names to grant.
*
* @see user_role_change_permissions()
* @see user_role_revoke_permissions()
*/
function user_role_grant_permissions($rid, array $permissions = array()) {
$modules = user_permission_get_modules();
// Grant new permissions for the role.
foreach ($permissions as $name) {
db_merge('role_permission')
->key(array(
'rid' => $rid,
'permission' => $name,
))
->fields(array(
'module' => $modules[$name],
))
->execute();
}
// Clear the user access cache.
drupal_static_reset('user_access');
drupal_static_reset('user_role_permissions');
}
/**
* Revoke permissions from a user role.
*
* @param $rid
* The ID of a user role to alter.
* @param $permissions
* A list of permission names to revoke.
*
* @see user_role_change_permissions()
* @see user_role_grant_permissions()
*/
function user_role_revoke_permissions($rid, array $permissions = array()) {
// Revoke permissions for the role.
db_delete('role_permission')
->condition('rid', $rid)
->condition('permission', $permissions, 'IN')
->execute();
// Clear the user access cache.
drupal_static_reset('user_access');
drupal_static_reset('user_role_permissions');
}
/**
* Implements hook_user_operations().
*/
function user_user_operations($form = array(), $form_state = array()) {
$operations = array(
'unblock' => array(
'label' => t('Unblock the selected users'),
'callback' => 'user_user_operations_unblock',
),
'block' => array(
'label' => t('Block the selected users'),
'callback' => 'user_user_operations_block',
),
'cancel' => array(
'label' => t('Cancel the selected user accounts'),
),
);
if (user_access('administer permissions')) {
$roles = user_roles(TRUE);
unset($roles[DRUPAL_AUTHENTICATED_RID]); // Can't edit authenticated role.
$add_roles = array();
foreach ($roles as $key => $value) {
$add_roles['add_role-' . $key] = $value;
}
$remove_roles = array();
foreach ($roles as $key => $value) {
$remove_roles['remove_role-' . $key] = $value;
}
if (count($roles)) {
$role_operations = array(
t('Add a role to the selected users') => array(
'label' => $add_roles,
),
t('Remove a role from the selected users') => array(
'label' => $remove_roles,
),
);
$operations += $role_operations;
}
}
// If the form has been posted, we need to insert the proper data for
// role editing if necessary.
if (!empty($form_state['submitted'])) {
$operation_rid = explode('-', $form_state['values']['operation']);
$operation = $operation_rid[0];
if ($operation == 'add_role' || $operation == 'remove_role') {
$rid = $operation_rid[1];
if (user_access('administer permissions')) {
$operations[$form_state['values']['operation']] = array(
'callback' => 'user_multiple_role_edit',
'callback arguments' => array($operation, $rid),
);
}
else {
watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
return;
}
}
}
return $operations;
}
/**
* Callback function for admin mass unblocking users.
*/
function user_user_operations_unblock($accounts) {
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip unblocking user if they are already unblocked.
if ($account !== FALSE && $account->status == 0) {
user_save($account, array('status' => 1));
}
}
}
/**
* Callback function for admin mass blocking users.
*/
function user_user_operations_block($accounts) {
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip blocking user if they are already blocked.
if ($account !== FALSE && $account->status == 1) {
// For efficiency manually save the original account before applying any
// changes.
$account->original = clone $account;
user_save($account, array('status' => 0));
}
}
}
/**
* Callback function for admin mass adding/deleting a user role.
*/
function user_multiple_role_edit($accounts, $operation, $rid) {
// The role name is not necessary as user_save() will reload the user
// object, but some modules' hook_user() may look at this first.
$role_name = db_query('SELECT name FROM {role} WHERE rid = :rid', array(':rid' => $rid))->fetchField();
switch ($operation) {
case 'add_role':
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip adding the role to the user if they already have it.
if ($account !== FALSE && !isset($account->roles[$rid])) {
$roles = $account->roles + array($rid => $role_name);
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
user_save($account, array('roles' => $roles));
}
}
break;
case 'remove_role':
$accounts = user_load_multiple($accounts);
foreach ($accounts as $account) {
// Skip removing the role from the user if they already don't have it.
if ($account !== FALSE && isset($account->roles[$rid])) {
$roles = array_diff($account->roles, array($rid => $role_name));
// For efficiency manually save the original account before applying
// any changes.
$account->original = clone $account;
user_save($account, array('roles' => $roles));
}
}
break;
}
}
function user_multiple_cancel_confirm($form, &$form_state) {
$edit = $form_state['input'];
$form['accounts'] = array('#prefix' => '
', '#suffix' => '
', '#tree' => TRUE);
$accounts = user_load_multiple(array_keys(array_filter($edit['accounts'])));
foreach ($accounts as $uid => $account) {
// Prevent user 1 from being canceled.
if ($uid <= 1) {
continue;
}
$form['accounts'][$uid] = array(
'#type' => 'hidden',
'#value' => $uid,
'#prefix' => '
',
'#suffix' => check_plain($account->name) . "
\n",
);
}
// Output a notice that user 1 cannot be canceled.
if (isset($accounts[1])) {
$redirect = (count($accounts) == 1);
$message = t('The user account %name cannot be cancelled.', array('%name' => $accounts[1]->name));
drupal_set_message($message, $redirect ? 'error' : 'warning');
// If only user 1 was selected, redirect to the overview.
if ($redirect) {
drupal_goto('admin/people');
}
}
$form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
module_load_include('inc', 'user', 'user.pages');
$form['user_cancel_method'] = array(
'#type' => 'item',
'#title' => t('When cancelling these accounts'),
);
$form['user_cancel_method'] += user_cancel_methods();
// Remove method descriptions.
foreach (element_children($form['user_cancel_method']) as $element) {
unset($form['user_cancel_method'][$element]['#description']);
}
// Allow to send the account cancellation confirmation mail.
$form['user_cancel_confirm'] = array(
'#type' => 'checkbox',
'#title' => t('Require e-mail confirmation to cancel account.'),
'#default_value' => FALSE,
'#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
);
// Also allow to send account canceled notification mail, if enabled.
$form['user_cancel_notify'] = array(
'#type' => 'checkbox',
'#title' => t('Notify user when account is canceled.'),
'#default_value' => FALSE,
'#access' => variable_get('user_mail_status_canceled_notify', FALSE),
'#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
);
return confirm_form($form,
t('Are you sure you want to cancel these user accounts?'),
'admin/people', t('This action cannot be undone.'),
t('Cancel accounts'), t('Cancel'));
}
/**
* Submit handler for mass-account cancellation form.
*
* @see user_multiple_cancel_confirm()
* @see user_cancel_confirm_form_submit()
*/
function user_multiple_cancel_confirm_submit($form, &$form_state) {
global $user;
if ($form_state['values']['confirm']) {
foreach ($form_state['values']['accounts'] as $uid => $value) {
// Prevent programmatic form submissions from cancelling user 1.
if ($uid <= 1) {
continue;
}
// Prevent user administrators from deleting themselves without confirmation.
if ($uid == $user->uid) {
$admin_form_state = $form_state;
unset($admin_form_state['values']['user_cancel_confirm']);
$admin_form_state['values']['_account'] = $user;
user_cancel_confirm_form_submit(array(), $admin_form_state);
}
else {
user_cancel($form_state['values'], $uid, $form_state['values']['user_cancel_method']);
}
}
}
$form_state['redirect'] = 'admin/people';
}
/**
* Retrieve a list of all user setting/information categories and sort them by weight.
*/
function _user_categories() {
$categories = module_invoke_all('user_categories');
usort($categories, '_user_sort');
return $categories;
}
function _user_sort($a, $b) {
$a = (array) $a + array('weight' => 0, 'title' => '');
$b = (array) $b + array('weight' => 0, 'title' => '');
return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
}
/**
* List user administration filters that can be applied.
*/
function user_filters() {
// Regular filters
$filters = array();
$roles = user_roles(TRUE);
unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
if (count($roles)) {
$filters['role'] = array(
'title' => t('role'),
'field' => 'ur.rid',
'options' => array(
'[any]' => t('any'),
) + $roles,
);
}
$options = array();
foreach (module_implements('permission') as $module) {
$function = $module . '_permission';
if ($permissions = $function('permission')) {
asort($permissions);
foreach ($permissions as $permission => $description) {
$options[t('@module module', array('@module' => $module))][$permission] = t($permission);
}
}
}
ksort($options);
$filters['permission'] = array(
'title' => t('permission'),
'options' => array(
'[any]' => t('any'),
) + $options,
);
$filters['status'] = array(
'title' => t('status'),
'field' => 'u.status',
'options' => array(
'[any]' => t('any'),
1 => t('active'),
0 => t('blocked'),
),
);
return $filters;
}
/**
* Extends a query object for user administration filters based on session.
*
* @param $query
* Query object that should be filtered.
*/
function user_build_filter_query(SelectQuery $query) {
$filters = user_filters();
// Extend Query with filter conditions.
foreach (isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array() as $filter) {
list($key, $value) = $filter;
// This checks to see if this permission fig the view.
*/
function views_ui_cache_load($name) {
ctools_include('object-cache');
views_include('view');
$view = ctools_object_cache_get('view', $name);
$original_view = views_get_view($name);
if (empty($view)) {
$view = $original_view;
if (!empty($view)) {
// Check to see if someone else is already editing this view.
$view->locked = ctools_object_cache_test('view', $view->name);
// Set a flag to indicate that this view is being edited.
// This flag will be used e.g. to determine whether strings
// should be localized.
$view->editing = TRUE;
}
}
else {
// Keep disabled/enabled status real.
if ($original_view) {
$view->disabled = !empty($original_view->disabled);
}
}
if (empty($view)) {
return FALSE;
}
else {
return $view;
}
}
/**
* Specialized cache function to add a flag to our view, include an appropriate
* include, and cache more easily.
*/
function views_ui_cache_set(&$view) {
if (!empty($view->locked)) {
drupal_set_message(t('Changes cannot be made to a locked view.'), 'error');
return;
}
ctools_include('object-cache');
$view->changed = TRUE; // let any future object know that this view has changed.
if (isset($view->current_display)) {
// Add the knowledge of the changed display, too.
$view->changed_display[$view->current_display] = TRUE;
unset($view->current_display);
}
// Unset handlers; we don't want to write these into the cache
unset($view->display_handler);
unset($view->default_display);
$view->query = NULL;
foreach (array_keys($view->display) as $id) {
unset($view->display[$id]->handler);
unset($view->display[$id]->default_display);
}
ctools_object_cache_set('view', $view->name, $view);
}
/**
* Specialized menu callback to load a view that is only a default
* view.
*/
function views_ui_default_load($name) {
$view = views_get_view($name);
if ($view->type == t('Default')) {
return $view;
}
return FALSE;
}
/**
* Theme preprocess for views-view.tpl.php.
*/
function views_ui_preprocess_views_view(&$vars) {
$view = $vars['view'];
if (!empty($view->views_ui_context) && module_exists('contextual')) {
$view->hide_admin_links = TRUE;
foreach (array('title', 'header', 'exposed', 'rows', 'pager', 'more', 'footer', 'empty', 'attachment_after', 'attachment_before') as $section) {
if (!empty($vars[$section])) {
$vars[$section] = array(
'#theme' => 'views_ui_view_preview_section',
'#view' => $view,
'#section' => $section,
'#content' => $vars[$section],
'#theme_wrappers' => array('views_container'),
'#attributes' => array('class' => 'contextual-links-region'),
);
$vars[$section] = drupal_render($vars[$section]);
}
}
}
}
/**
* Theme preprocess for theme_views_ui_view_preview_section().
*
* @TODO
* Perhaps move this to includes/admin.inc or theme/theme.inc
*/
function template_preprocess_views_ui_view_preview_section(&$vars) {
switch ($vars['section']) {
case 'title':
$vars['title'] = t('Title');
$links = views_ui_view_preview_section_display_category_links($vars['view'], 'title', $vars['title']);
break;
case 'header':
$vars['title'] = t('Header');
$links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
break;
case 'empty':
$vars['title'] = t('No results behavior');
$links = views_ui_view_preview_section_handler_links($vars['view'], $vars['section']);
break;
case 'exposed':
// @todo Sorts can be exposed too, so we may need a better title.
$vars['title'] = t('Exposed Filters');
$links = views_ui_view_preview_section_display_category_links($vars['view'], 'exposed_form_options', $vars['title']);
break;
case 'rows':
// @todo The title needs to depend on what is being viewed.
$vars['title'] = t('Content');
$links = views_ui_view_preview_section_rows_links($vars['view']);
break;p, $account, $language = NULL) {
// By default, we always notify except for canceled and blocked.
$default_notify = ($op != 'status_canceled' && $op != 'status_blocked');
$notify = variable_get('user_mail_' . $op . '_notify', $default_notify);
if ($notify) {
$params['account'] = $account;
$language = $language ? $language : user_preferred_language($account);
$mail = drupal_mail('user', $op, $account->mail, $language, $params);
if ($op == 'register_pending_approval') {
// If a user registered requiring admin approval, notify the admin, too.
// We use the site default language for this.
drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
}
}
return empty($mail) ? NULL : $mail['result'];
}
/**
* Form element process handler for client-side password validation.
*
* This #process handler is automatically invoked for 'password_confirm' form
* elements to add the JavaScript and string translations for dynamic password
* validation.
*
* @see system_element_info()
*/
function user_form_process_password_confirm($element) {
global $user;
$js_settings = array(
'password' => array(
'strengthTitle' => t('Password strength:'),
'hasWeaknesses' => t('To make your password stronger:'),
'tooShort' => t('Make it at least 6 characters'),
'addLowerCase' => t('Add lowercase letters'),
'addUpperCase' => t('Add uppercase letters'),
'addNumbers' => t('Add numbers'),
'addPunctuation' => t('Add punctuation'),
'sameAsUsername' => t('Make it different from your username'),
'confirmSuccess' => t('yes'),
'confirmFailure' => t('no'),
'weak' => t('Weak'),
'fair' => t('Fair'),
'good' => t('Good'),
'strong' => t('Strong'),
'confirmTitle' => t('Passwords match:'),
'username' => (isset($user->name) ? $user->name : ''),
),
);
$element['#attached']['js'][] = drupal_get_path('module', 'user') . '/user.js';
// Ensure settings are only added once per page.
static $already_added = FALSE;
if (!$already_added) {
$already_added = TRUE;
$element['#attached']['js'][] = array('data' => $js_settings, 'type' => 'setting');
}
return $element;
}
/**
* Implements hook_node_load().
*/
function user_node_load($nodes, $types) {
// Build an array of all uids for node authors, keyed by nid.
$uids = array();
foreach ($nodes as $nid => $node) {
$uids[$nid] = $node->uid;
}
// Fetch name, picture, and data for these users.
$user_fields = db_query("SELECT uid, name, picture, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid');
// Add these values back into the node objects.
foreach ($uids as $nid => $uid) {
$nodes[$nid]->name = $user_fields[$uid]->name;
$nodes[$nid]->picture = $user_fields[$uid]->picture;
$nodes[$nid]->data = $user_fields[$uid]->data;
}
}
/**
* Implements hook_image_style_delete().
*/
function user_image_style_delete($style) {
// If a style is deleted, update the variables.
// Administrators choose a replacement style when deleting.
user_image_style_save($style);
}
/**
* Implements hook_image_style_save().
*/
function user_image_style_save($style) {
// If a style is renamed, update the variables that use it.
if (isset($style['old_name']) && $style['old_name'] == variable_get('user_picture_style', '')) {
variable_set('user_picture_style', $style['name']);
}
}
/**
* Implements hook_action_info().
*/
function user_action_info() {
return array(
'user_block_user_action' => array(
'label' => t('Block current user'),
'type' => 'user',
'configurable' => FALSE,
'triggers' => array('any'),
),
);
}
/**
* Blocks the current user.
*
* @ingroup actions
*/
function user_block_user_action(&$entity, $context = array()) {
// First priority: If there is a $entity->uid, block that user.
// This is most likely a user object or the author if a node or comment.
if (isset($entity->uid)) {
$uid = $entity->uid;
}
elseif (isset($context['uid'])) {
$uid = $context['uid'];
}
// If neither of those are valid, then block the current user.
else {
$uid = $GLOBALS['user']->uid;
}
$account = user_load($uid);
$account = user_save($account, array('status' => 0));
watchdog('action', 'Blocked user %name.', array('%name' => $account->name));
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a checkbox for the 'user_register_form' instance settings on the 'Edit
* field instance' form.
*/
function user_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
$instance = $form['#instance'];
if ($instance['entity_type'] == 'user') {
$form['instance']['settings']['user_register_form'] = array(
'#type' => 'checkbox',
'#title' => t('Display on user registration form.'),
'#description' => t("This is compulsory for 'required' fields."),
// Field instances created in D7 beta releases before the setting was
// introduced might be set as 'required' and 'not shown on user_register
// form'. We make sure the checkbox comes as 'checked' for those.
'#default_value' => $instance['settings']['user_register_form'] || $instance['required'],
// Display just below the 'required' checkbox.
'#weight' => $form['instance']['required']['#weight'] + .1,
// Disabled when the 'required' checkbox is checked.
'#states' => array(
'enabled' => array('input[name="instance[required]"]' => array('checked' => FALSE)),
),
// Checked when the 'required' checkbox is checked. This is done through
// a custom behavior, since the #states system would also synchronize on
// uncheck.
'#attached' => array(
'js' => array(drupal_get_path('module', 'user') . '/user.js'),
),
);
array_unshift($form['#submit'], 'user_form_field_ui_field_edit_form_submit');
}
}
/**
* Additional submit handler for the 'Edit field instance' form.
*
* Make sure the 'user_register_form' setting is set for required fields.
*/
function user_form_field_ui_field_edit_form_submit($form, &$form_state) {
$instance = $form_state['values']['instance'];
if (!empty($instance['required'])) {
form_set_value($form['instance']['settings']['user_register_form'], 1, $form_state);
}
}
/**
* Form builder; the user registration form.
*
* @ingroup forms
* @see user_account_form()
* @see user_account_form_validate()
* @see user_register_submit()
*/
function user_register_form($form, &$form_state) {
global $user;
$admin = user_access('administer users');
// If we aren't admin but already logged on, go to the user page instead.
if (!$admin && $user->uid) {
drupal_goto('user/' . $user->uid);
}
$form['#user'] = drupal_anonymous_user();
$form['#user_category'] = 'register';
$form['#attached']['library'][] = array('system', 'jquery.cookie');
$form['#attributes']['class'][] = 'user-info-from-cookie';
// Start with the default user account fields.
user_account_form($form, $form_state);
// Attach field widgets, and hide the ones where the 'user_register_form'
// setting is not on.
field_attach_form('user', $form['#user'], $form, $form_state);
foreach (field_info_instances('user', 'user') as $field_name => $instance) {
if (empty($instance['settings']['user_register_form'])) {
$form[$field_name]['#access'] = FALSE;
}
}
if ($admin) {
// Redirect back to page which initiated the create request;
// usually admin/people/create.
$form_state['redirect'] = $_GET['q'];
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Create new account'),
);
$form['#validate'][] = 'user_register_validate';
// Add the final user registration form submit handler.
$form['#submit'][] = 'user_register_submit';
return $form;
}
/**
* Validation function for the user registration form.
*/
function user_register_validate($form, &$form_state) {
entity_form_field_validate('user', $form, $form_state);
}
/**
* Submit handler for the user registration form.
*
* This function is shared by the installation form and the normal registration form,
* which is why it can't be in the user.pages.inc file.
*
* @see user_register_form()
*/
function user_register_submit($form, &$form_state) {
$admin = user_access('administer users');
if (!variable_get('user_email_verification', TRUE) || $admin) {
$pass = $form_state['values']['pass'];
}
else {
$pass = user_password();
}
$notify = !empty($form_state['values']['notify']);
// Remove unneeded values.
form_state_values_clean($form_state);
$form_state['values']['pass'] = $pass;
$form_state['values']['init'] = $form_state['values']['mail'];
$account = $form['#user'];
entity_form_submit_build_entity('user', $account, $form, $form_state);
// Populate $edit with the properties of $account, which have been edited on
// this form by taking over all values, which appear in the form values too.
$edit = array_intersect_key((array) $account, $form_state['values']);
$account = user_save($account, $edit);
// Terminate if an error occurred during user_save().
if (!$account) {
drupal_set_message(t("Error saving user account."), 'error');
$form_state['redirect'] = '';
return;
}
$form_state['user'] = $account;
$form_state['values']['uid'] = $account->uid;
watchdog('user', 'New user: %name (%email).', array('%name' => $form_state['values']['name'], '%email' => $form_state['values']['mail']), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
// Add plain text password into user account to generate mail tokens.
$account->password = $pass;
// New administrative account without notification.
$uri = entity_uri('user', $account);
if ($admin && !$notify) {
drupal_set_message(t('Created a new user account for %name. No e-mail has been sent.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
}
// No e-mail verification required; log in user immediately.
elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
_user_mail_notify('register_no_approval_required', $account);
$form_state['uid'] = $account->uid;
user_login_submit(array(), $form_state);
drupal_set_message(t('Registration successful. You are now logged in.'));
$form_state['redirect'] = '';
}
// No administrator approval required.
elseif ($account->status || $notify) {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
_user_mail_notify($op, $account);
if ($notify) {
drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
}
else {
drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
$form_state['redirect'] = '';
}
}
// Administrator approval required.
else {
_user_mail_notify('register_pending_approval', $account);
drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator. In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
$form_state['redirect'] = '';
}
}
/**
* Implements hook_modules_installed().
*/
function user_modules_installed($modules) {
// Assign all available permissions to the administrator role.
$rid = variable_get('user_admin_role', 0);
if ($rid) {
$permissions = array();
foreach ($modules as $module) {
if ($module_permissions = module_invoke($module, 'permission')) {
$permissions = array_merge($permissions, array_keys($module_permissions));
}
}
if (!empty($permissions)) {
user_role_grant_permissions($rid, $permissions);
}
}
}
/**
* Implements hook_modules_uninstalled().
*/
function user_modules_uninstalled($modules) {
db_delete('role_permission')
->condition('module', $modules, 'IN')
->execute();
}
/**
* Helper function to rewrite the destination to avoid redirecting to login page after login.
*
* Third-party authentication modules may use this function to determine the
* proper destination after a user has been properly logged in.
*/
function user_login_destination() {
$destination = drupal_get_destination();
if ($destination['destination'] == 'user/login') {
$destination['destination'] = 'user';
}
return $destination;
}
/**
* Saves visitor information as a cookie so it can be reused.
*
* @param $values
* An array of key/value pairs to be saved into a cookie.
*/
function user_cookie_save(array $values) {
foreach ($values as $field => $value) {
// Set cookie for 365 days.
setrawcookie('Drupal.visitor.' . $field, rawurlencode($value), REQUEST_TIME + 31536000, '/');
}
}
/**
* Delete a visitor information cookie.
*
* @param $cookie_name
* A cookie name such as 'homepage'.
*/
function user_cookie_delete($cookie_name) {
setrawcookie('Drupal.visitor.' . $cookie_name, '', REQUEST_TIME - 3600, '/');
}
/**
* Implements hook_rdf_mapping().
*/
function user_rdf_mapping() {
return array(
array(
'type' => 'user',
'bundle' => RDF_DEFAULT_BUNDLE,
'mapping' => array(
'rdftype' => array('sioc:UserAccount'),
'name' => array(
'predicates' => array('foaf:name'),
),
'homepage' => array(
'predicates' => array('foaf:page'),
'type' => 'rel',
),
),
),
);
}
/**
* Implements hook_file_download_access().
*/
function user_file_download_access($field, $entity_type, $entity) {
if ($entity_type == 'user') {
return user_view_access($entity);
}
}
/**
* Implements hook_system_info_alter().
*
* Drupal 7 ships with two methods to add additional fields to users: Profile
* module, a legacy module dating back from 2002, and Field API integration
* with users. While Field API support for users currently provides less end
* user features, the inefficient data storage mechanism of Profile module, as
* well as its lack of consistency with the rest of the entity / field based
* systems in Drupal 7, make this a sub-optimal solution to those who were not
* using it in previous releases of Drupal.
*
* To prevent new Drupal 7 sites from installing Profile module, and
* unwittingly ending up with two completely different and incompatible methods
* of extending users, only make the Profile module available if the profile_*
* tables are present.
*
* @todo: Remove in D8, pending upgrade path.
*/
function user_system_info_alter(&$info, $file, $type) {
if ($type == 'module' && $file->name == 'profile' && db_table_exists('profile_fi
Detailed But Not Overwhelming | Kem Knapp Sawyer