123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873 |
- <?php
- /**
- * @file
- * Implements the Add Tag (create) grant on editing forms.
- *
- * These functions need to be included in three circumstances:
- * - Form building for forms with taxonomy fields.
- * - taxonomy_access_field_widget_form_alter()
- * - taxonomy_access_field_widget_taxonomy_autocomplete_form_alter()
- * - Form validation for forms with taxonomy fields.
- * - taxonomy_access_autocomplete_validate()
- * - taxonomy_access_options_validate()
- * - taxonomy_access_field_attach_validate()
- * - Taxonomy autocomplete AJAX requests.
- * - taxonomy_access_autocomplete()
- */
- /**
- * @defgroup tac_create Taxonomy Access Control: Add tag (create) permission
- * @{
- * Implement access control for taxonomy terms on node editing forms.
- */
- /**
- * Implements the create grant for autocomplete fields.
- *
- * - Denies access if the user cannot alter the field values.
- * - Determines whether the user can autocreate new terms for the field.
- * - Removes default values disallowed by create.
- * - Adds information on autocreate and disallowed defaults to the element so
- * it is available to the validator.
- * - Adds the custom validator.
- * - Sets a custom autocomplete path to filter autocomplete by create.
- *
- * Some of the logic here is borrowed from taxonomy_autocomplete_validate().
- *
- * @see taxonomy_access_field_widget_taxonomy_autocomplete_form_alter()
- */
- function _taxonomy_access_autocomplete_alter(&$element, &$form_state, $context) {
- // Collect a list of terms and filter out those disallowed by create.
- $filtered = array();
- foreach ($context['items'] as $item) {
- $filtered[$item['tid']] = $item;
- }
- $disallowed_defaults = taxonomy_access_create_disallowed(array_keys($filtered));
- foreach ($disallowed_defaults as $tid) {
- unset($filtered[$tid]);
- }
- // Assemble a list of all vocabularies for the field.
- $vids = array();
- foreach ($context['field']['settings']['allowed_values'] as $tree) {
- if ($vocab = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
- $vids[] = $vocab->vid;
- }
- }
- // Determine whether the user has create for any terms in the given vocabs.
- $allowed_terms = FALSE;
- foreach ($vids as $vid) {
- $terms = taxonomy_access_user_create_terms_by_vocab($vid);
- if (!empty($terms)) {
- $allowed_terms = TRUE;
- break;
- }
- }
- // Filter the vids to vocabs in which the user may create new terms.
- $allowed_vids = taxonomy_access_create_default_allowed($vids);
- // If the field already has the maximum number of values, and all of these
- // values are disallowed, deny access to the field.
- if ($context['field']['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
- if (sizeof($disallowed_defaults) >= $context['field']['cardinality']) {
- $element['#access'] = FALSE;
- }
- }
- // If the user may not create any terms on this field, deny access.
- if (empty($allowed_vids) && !$allowed_terms) {
- $element['#access'] = FALSE;
- }
- // Set the default value from the filtered item list.
- $element['#default_value'] =
- taxonomy_access_autocomplete_default_value($filtered);
- // Custom validation. Set values for the validator indicating:
- // 1. Whether the user can autocreate terms in this field (vocab. default).
- // 2. Which tids were removed due to create restrictions.
- $element['#allow_autocreate'] = empty($allowed_vids) ? FALSE : TRUE;
- $element['#disallowed_defaults'] = $disallowed_defaults;
- $element['#element_validate'] =
- array('taxonomy_access_autocomplete_validate');
- // Use a custom autocomplete path to filter by create rather than list.
- $element['#autocomplete_path'] =
- 'taxonomy_access/autocomplete/' . $context['field']['field_name'];
- unset($context);
- }
- /**
- * Implements the create grant for options widgets.
- *
- * - Denies access if the user cannot alter the field values.
- * - Attaches jQuery to disable values disallowed by create.
- * - Adds the disallowed values from the element so they are available to the
- * custom validator.
- * - Adds the custom validator.
- *
- * We use jQuery to disable the options because of FAPI limitations:
- * @see http://drupal.org/node/284917
- * @see http://drupal.org/node/342316
- * @see http://drupal.org/node/12089
- *
- * @see taxonomy_access_field_widget_form_alter()
- */
- function _taxonomy_access_options_alter(&$element, &$form_state, $context) {
- // Check for an existing entity ID.
- $entity_id = 0;
- if (!empty($form_state['build_info']['args'][0])) {
- $info = entity_get_info($context['instance']['entity_type']);
- $pseudo_entity = (object) $form_state['build_info']['args'][0];
- if (isset($pseudo_entity->{$info['entity keys']['id']})) {
- $entity_id = $pseudo_entity->{$info['entity keys']['id']};
- }
- }
- // Collect a list of terms and determine which are allowed
- $tids = array_keys($element['#options']);
- // Ignore the "none" option if present.
- $key = array_search('_none', $tids);
- if ($key !== FALSE) {
- unset($tids[$key]);
- }
- $allowed_tids = taxonomy_access_create_allowed($tids);
- $disallowed_tids = taxonomy_access_create_disallowed($tids);
- // If no options are allowed, deny access to the field.
- if (empty($allowed_tids)) {
- $element['#access'] = FALSE;
- }
- // On node creation, simply remove disallowed default values.
- if (!$entity_id) {
- $disallowed_defaults = array();
- if (is_array($element['#default_value'])) {
- foreach ($element['#default_value'] as $key => $value) {
- if (in_array($value, $disallowed_tids)) {
- unset($element['#default_value'][0]);
- }
- }
- }
- elseif (in_array($element['#default_value'], $disallowed_tids)) {
- unset($element['#default_value']);
- }
- }
- // If the node already exists, check:
- // 1. Whether the field already has the maximum number of values
- // 2. Whether all of these values are disallowed.
- // If both these things are true, then the user cannot edit the field's
- // value, so disallow access.
- else {
- $defaults =
- is_array($element['#default_value'])
- ? $element['#default_value']
- : array($element['#default_value'])
- ;
- $disallowed_defaults =
- array_intersect($defaults, $disallowed_tids);
- if ($context['field']['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
- if (sizeof($disallowed_defaults) >= $context['field']['cardinality']) {
- $element['#access'] = FALSE;
- }
- }
- }
- // If there are disallowed, terms, add CSS and JS for jQuery.
- // We use jQuery because FAPI does not currently support attributes
- // for individual options.
- if (!empty($disallowed_tids)) {
- // Add a css class to the field that we can use in jQuery.
- $class_name = 'tac_' . $element['#field_name'];
- $element['#attributes']['class'][] = $class_name;
- // Add js for disabling create options.
- $settings[] = array(
- 'field' => $class_name,
- 'disallowed_tids' => $disallowed_tids,
- 'disallowed_defaults' => $disallowed_defaults,
- );
- $element['#attached']['js'][] =
- drupal_get_path('module', 'taxonomy_access') . '/tac_create.js';
- $element['#attached']['js'][] = array(
- 'data' => array('taxonomy_access' => $settings),
- 'type' => 'setting',
- );
- }
- $element['#disallowed_defaults'] = $disallowed_defaults;
- $element['#element_validate'] = array('taxonomy_access_options_validate');
- }
- /**
- * Retrieve terms that the current user may create.
- *
- * @return array|true
- * An array of term IDs, or TRUE if the user may create all terms.
- *
- * @see taxonomy_access_user_create_terms_by_vocab()
- * @see _taxonomy_access_user_term_grants()
- */
- function taxonomy_access_user_create_terms() {
- // Cache the terms the current user can create.
- $terms = &drupal_static(__FUNCTION__, NULL);
- if (is_null($terms)) {
- $terms = _taxonomy_access_user_term_grants(TRUE);
- }
- return $terms;
- }
- /**
- * Retrieve terms that the current user may create in specific vocabularies.
- *
- * @param int $vid
- * A vid to use as a filter.
- *
- * @return array|true
- * An array of term IDs, or TRUE if the user may create all terms.
- *
- * @see taxonomy_access_user_create_terms()
- * @see _taxonomy_access_user_term_grants()
- */
- function taxonomy_access_user_create_terms_by_vocab($vid) {
- // Cache the terms the current user can create per vocabulary.
- static $terms = array();
- if (!isset($terms[$vid])) {
- $terms[$vid] = _taxonomy_access_user_term_grants(TRUE, array($vid));
- }
- return $terms[$vid];
- }
- /**
- * Retrieve terms that the current user may create.
- *
- * @return array|true
- * An array of term IDs, or TRUE if the user may create all terms.
- *
- * @see _taxonomy_access_create_defaults()
- */
- function taxonomy_access_user_create_defaults() {
- // Cache the terms the current user can create.
- static $vids = NULL;
- if (is_null($vids)) {
- $vids = _taxonomy_access_create_defaults();
- }
- return $vids;
- }
- /**
- * Check a list of term IDs for terms the user may not create.
- *
- * @param array $tids
- * The array of term IDs.
- *
- * @return array
- * An array of disallowed term IDs.
- */
- function taxonomy_access_create_disallowed(array $tids) {
- $all_allowed = taxonomy_access_user_create_terms();
- // If the user's create grant info is exactly TRUE, no terms are disallowed.
- if ($all_allowed === TRUE) {
- return array();
- }
- return array_diff($tids, $all_allowed);
- }
- /**
- * Filter a list of term IDs to terms the user may create.
- *
- * @param array $tids
- * The array of term IDs.
- *
- * @return array
- * An array of disallowed term IDs.
- */
- function taxonomy_access_create_allowed(array $tids) {
- $all_allowed = taxonomy_access_user_create_terms();
- // If the user's create grant info is exactly TRUE, all terms are allowed.
- if ($all_allowed === TRUE) {
- return $tids;
- }
- return array_intersect($tids, $all_allowed);
- }
- /**
- * Filter a list of vocab IDs to those in which the user may create by default.
- *
- * @param array $vids
- * The array of vocabulary IDs.
- *
- * @return array
- * An array of disallowed vocabulary IDs.
- */
- function taxonomy_access_create_default_allowed(array $vids) {
- $all_allowed = taxonomy_access_user_create_defaults();
- // If the user's create grant info is exactly TRUE, all terms are allowed.
- if ($all_allowed === TRUE) {
- return $vids;
- }
- return array_intersect($vids, $all_allowed);
- }
- /**
- * Retrieve vocabularies in which the current user may create terms.
- *
- * @param object|null $account
- * (optional) The account for which to retrieve grants. If no account is
- * passed, the current user will be used. Defaults to NULL.
- *
- * @return array
- * An array of term IDs, or TRUE if the user has the grant for all terms.
- */
- function _taxonomy_access_create_defaults($account = NULL) {
- // If the user can administer taxonomy, return TRUE for a global grant.
- if (user_access('administer taxonomy', $account)) {
- return TRUE;
- }
- // Build a term grant query.
- $query = _taxonomy_access_grant_query(array('create'), TRUE);
- // Select term grants for the current user's roles.
- if (is_null($account)) {
- global $user;
- $account = $user;
- }
- $query
- ->fields('td', array('vid'))
- ->groupBy('td.vid')
- ->condition('tadg.rid', array_keys($account->roles), 'IN')
- ;
- // Fetch term IDs.
- $r = $query->execute()->fetchAll();
- $vids = array();
- // If there are results, initialize a flag to test whether the user
- // has the grant for all terms.
- $grants_for_all_vocabs = empty($r) ? FALSE : TRUE;
- foreach ($r as $record) {
- // If the user has the grant, add the term to the array.
- if ($record->grant_create) {
- $vids[] = $record->vid;
- }
- // Otherwise, flag that the user does not have the grant for all terms.
- else {
- $grants_for_all_vocabs = FALSE;
- }
- }
- // If the user has the grant for all terms, return TRUE for a global grant.
- if ($grants_for_all_vocabs) {
- return TRUE;
- }
- return $vids;
- }
- /**
- * Autocomplete menu callback: filter allowed terms by create, not list.
- *
- * For now we essentially duplicate the code from taxonomy.module, because
- * it calls drupal_json_output without providing the logic separately.
- *
- * @see http://drupal.org/node/1169964
- * @see taxonomy_autocomplete()
- */
- function taxonomy_access_autocomplete($field_name, $tags_typed = '') {
- // Enforce that list grants do not filter the autocomplete.
- taxonomy_access_disable_list();
- $field = field_info_field($field_name);
- // The user enters a comma-separated list of tags. We only autocomplete the last tag.
- $tags_typed = drupal_explode_tags($tags_typed);
- $tag_last = drupal_strtolower(array_pop($tags_typed));
- $matches = array();
- if ($tag_last != '') {
- // Part of the criteria for the query come from the field's own settings.
- $vids = array();
- $vocabularies = taxonomy_vocabulary_get_names();
- foreach ($field['settings']['allowed_values'] as $tree) {
- $vids[] = $vocabularies[$tree['vocabulary']]->vid;
- }
- $query = db_select('taxonomy_term_data', 't');
- $query->addTag('translatable');
- $query->addTag('term_access');
- // Do not select already entered terms.
- if (!empty($tags_typed)) {
- $query->condition('t.name', $tags_typed, 'NOT IN');
- }
- // Select rows that match by term name.
- $tags_return = $query
- ->fields('t', array('tid', 'name'))
- ->condition('t.vid', $vids)
- ->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE')
- ->range(0, 10)
- ->execute()
- ->fetchAllKeyed();
- // Unset suggestions disallowed by create grants.
- $disallowed = taxonomy_access_create_disallowed(array_keys($tags_return));
- foreach ($disallowed as $tid) {
- unset($tags_return[$tid]);
- }
- $prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
- $term_matches = array();
- foreach ($tags_return as $tid => $name) {
- $n = $name;
- // Term names containing commas or quotes must be wrapped in quotes.
- if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
- $n = '"' . str_replace('"', '""', $name) . '"';
- }
- $term_matches[$prefix . $n] = check_plain($name);
- }
- }
- drupal_json_output($term_matches);
- }
- /**
- * Validates taxonomy autocomplete values for create grants.
- *
- * For now we essentially duplicate the code from taxonomy.module, because
- * it calls form_set_value() without providing the logic separately.
- *
- * We use two properties set in hook_field_widget_form_alter():
- * - $element['#allow_autocreate']
- * - $element['#disallowed_defaults']
- *
- * @todo
- * Specify autocreate per vocabulary?
- *
- * @see taxonomy_autocomplete_validate()
- * @see taxonomy_access_autocomplete()
- * @see taxonomy_access_field_widget_taxonomy_autocomplete_form_alter()
- */
- function _taxonomy_access_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);
- }
- // Only autocreate if the user has create for the vocab. default.
- elseif ($element['#allow_autocreate']) {
- $vocabulary = reset($vocabularies);
- $term = array(
- 'tid' => 'autocreate',
- 'vid' => $vocabulary->vid,
- 'name' => $typed_term,
- 'vocabulary_machine_name' => $vocabulary->machine_name,
- );
- }
- // If they cannot autocreate and this is a new term, set an error.
- else {
- form_error(
- $element,
- t('You may not create new tags in %name.',
- array('%name' => t($element['#title']))
- )
- );
- }
- if ($term) {
- $value[] = (array) $term;
- }
- }
- }
- // Add in the terms that were disallowed.
- // taxonomy.module expects arrays, not objects.
- $disallowed = taxonomy_term_load_multiple($element['#disallowed_defaults']);
- foreach ($disallowed as $key => $term) {
- $disallowed[$key] = (array) $term;
- }
- $value = array_merge($value, $disallowed);
- // Subsequent validation will be handled by hook_field_attach_validate().
- // Set the value in the form.
- form_set_value($element, $value, $form_state);
- }
- /**
- * Form element validation handler for taxonomy option fields.
- *
- * We use a property set in hook_field_widget_form_alter():
- * - $element['#disallowed_defaults']
- *
- * @see options_field_widget_validate()
- * @see taxonomy_access_field_widget_form_alter()
- */
- function _taxonomy_access_options_validate($element, &$form_state) {
- if ($element['#required'] && $element['#value'] == '_none') {
- form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
- }
- // Clone the element and add in disallowed defaults.
- $el = $element;
- if (!empty($element['#disallowed_defaults'])) {
- if (empty($el['#value'])) {
- $el['#value'] = $element['#disallowed_defaults'];
- }
- elseif (is_array($el['#value'])) {
- $el['#value'] = array_unique(array_merge($el['#value'], $element['#disallowed_defaults']));
- }
- else {
- $el['#value'] = array_unique(array_merge(array($el['#value']), $element['#disallowed_defaults']));
- }
- }
- // Transpose selections from field => delta to delta => field, turning
- // multiple selected options into multiple parent elements.
- $items = _options_form_to_storage($el);
- // Subsequent validation will be handled by hook_field_attach_validate().
- // Set the value in the form.
- form_set_value($element, $items, $form_state);
- }
- /**
- * Default value re-generation for autocomplete fields.
- *
- * @param array $items
- * An array of values from form build info, filtered by create grants.
- *
- * @return string
- * Field default value.
- *
- * @see taxonomy_field_widget_form()
- */
- function taxonomy_access_autocomplete_default_value(array $items) {
- // Preserve the original state of the list flag.
- $flag_state = taxonomy_access_list_enabled();
- // Enforce that list grants do not filter the options list.
- taxonomy_access_disable_list();
- // Assemble list of tags.
- $tags = array();
- foreach ($items as $item) {
- $tags[$item['tid']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
- }
- // Assemble the default value using taxonomy.module.
- $tags = taxonomy_implode_tags($tags);
- // Restore list flag to previous state.
- if ($flag_state) {
- taxonomy_access_enable_list();
- }
- return $tags;
- }
- /**
- * Validates form submissions of taxonomy fields for create grants.
- *
- * @todo
- * Use field label and term names in errors rather than field name and tids.
- *
- * @see http://drupal.org/node/1220212
- * @see entity_form_field_validate()
- */
- function _taxonomy_access_field_validate($entity_type, $entity, &$errors) {
- // Check for a pre-existing entity (i.e., the entity is being updated).
- $old_fields = FALSE;
- // The entity is actually a "pseudo-entity," and the user profile form
- // neglects to include the uid. So, we need to load it manually.
- if ($entity_type == 'user') {
- // Some modules which extend the user profile form cause additional
- // validation to happen with "pseudo-entities" that do not include the
- // name. So, check if it exists.
- if (isset($entity->name)) {
- if ($account = user_load_by_name($entity->name)) {
- $entity->uid = $account->uid;
- }
- }
- }
- list($entity_id, , $bundle) = entity_extract_ids($entity_type, $entity);
- if ($entity_id) {
- // Load the entity.
- $old_entity = entity_load($entity_type, array($entity_id));
- $old_entity = $old_entity[$entity_id];
- // Fetch the original entity's taxonomy fields.
- $old_fields =
- _taxonomy_access_entity_fields($entity_type, $old_entity, $bundle);
- }
- // Fetch the updated entity's taxonomy fields.
- $new_fields =
- _taxonomy_access_entity_fields($entity_type, $entity, $bundle);
- // Set errors if there are any disallowed changes.
- $changes = _taxonomy_access_compare_fields($new_fields, $old_fields);
- // We care about the overall value list, so delta is not important.
- $delta = 0;
- // Check each field and langcode for disallowed changes.
- foreach ($changes as $field_name => $langcodes) {
- foreach ($langcodes as $langcode => $disallowed) {
- if ($disallowed) {
- if (!empty($disallowed['added'])) {
- $text = 'You may not add the following tags to %field: %tids';
- $errors[$field_name][$langcode][$delta][] = array(
- 'error' => 'taxonomy_access_disallowed_added',
- 'message' => t($text, array(
- '%field' => $field_name,
- '%tids' => implode(', ', $disallowed['added']),
- )),
- );
- }
- if (!empty($disallowed['removed'])) {
- $text = 'You may not remove the following tags from %field: %tids';
- $errors[$field_name][$langcode][$delta][] = array(
- 'error' => 'taxonomy_access_disallowed_removed',
- 'message' => t($text, array(
- '%field' => $field_name,
- '%tids' => implode(', ', $disallowed['removed']),
- )),
- );
- }
- }
- }
- }
- }
- /**
- * Helper function to extract the taxonomy fields from an entity.
- *
- * @param object $entity
- * The entity object.
- *
- * @return array
- * An associative array of field information, containing:
- * - field_list: A flat array of all this entity's taxonomy fields, with the
- * format $field_name => $field_name.
- * - langcodes: A flat array of all langcodes in this entity's fields, with
- * the format $langcode => $langcode.
- * - data: An associative array of non-empty fields:
- * - $field_name: An associative array keyed by langcode.
- * - $langcode: Array of field values for this field name and langcode.
- *
- * @see http://drupal.org/node/1220168
- */
- function _taxonomy_access_entity_fields($entity_type, $entity, $bundle) {
- // Maintain separate lists of field names and langcodes for quick comparison.
- $fields = array();
- $fields['field_list'] = array();
- $fields['langcodes'] = array();
- $fields['data'] = array();
- // If there is no entity, return the empty structure.
- if (!$entity) {
- return $fields;
- }
- // Get a list of possible fields for the bundle.
- // The bundle does not contain the field type (see #122016), so our only use
- // for it is the field names.
- $possible = array_keys(field_info_instances($entity_type, $bundle));
- // Sort through the entity for relevant field data.
- foreach ($entity as $field_name => $field) {
- // Only proceed if this element is a valid field for the bundle.
- if (in_array($field_name, $possible, TRUE)) {
- // Check whether each entity field is a taxonomy field.
- $info = field_info_field($field_name);
- if ($info['type'] == 'taxonomy_term_reference') {
- foreach ($field as $langcode => $values) {
- // Add non-empty fields to the lists.
- if (!empty($values)) {
- $fields['langcodes'][$langcode] = $langcode;
- $fields['field_list'][$field_name] = $field_name;
- $fields['data'][$field_name][$langcode] = $values;
- }
- unset($values);
- }
- }
- }
- unset($info);
- unset($field);
- }
- unset($entity);
- return $fields;
- }
- /**
- * Helper function to compare field values and look for disallowed changes.
- *
- * @param array $new
- * An associative array of the updated field information as returned by
- * _taxonomy_access_entity_fields().
- * @param array $old
- * (optional) An associative array of the original field information,
- * or FALSE if there is no original field data. Defaults to FALSE.
- *
- * @return array
- * An array of disallowed changes, with the structure:
- * - $field_name: An associative array keyed by langcode.
- * - $langcode: Disallowed changes for this field name and langcode,
- * or FALSE if none.
- * - 'added' => An array of added terms that are disallowed.
- * - 'removed' => An array of removed termss that are disallowed.
- *
- * @see _taxonomy_access_entity_fields()
- * @see _taxonomy_access_disallowed_changes()
- */
- function _taxonomy_access_compare_fields($new, $old = FALSE) {
- $disallowed_changes = array();
- // If there are no original fields, simply process new.
- if (!$old) {
- foreach ($new['data'] as $field_name => $langcodes) {
- foreach ($langcodes as $langcode => $values) {
- $changes = _taxonomy_access_disallowed_changes($values, array());
- if ($changes) {
- if (!isset($disallowed_changes[$field_name])) {
- $disallowed_changes[$field_name] = array();
- }
- $disallowed_changes[$field_name][$langcode] = $changes;
- }
- }
- }
- }
- // Otherwise, aggregate and compare field data.
- else {
- $all_fields = $new['field_list'] + $old['field_list'];
- $all_langcodes = $new['langcodes'] + $old['langcodes'];
- foreach ($all_fields as $field_name) {
- foreach ($all_langcodes as $langcode) {
- $new_values = array();
- if (isset($new['field_list'][$field_name])
- && isset($new['data'][$field_name][$langcode])) {
- $new_values = $new['data'][$field_name][$langcode];
- }
- $old_values = array();
- if (isset($old['field_list'][$field_name])
- && isset($old['data'][$field_name][$langcode])) {
- $old_values = $old['data'][$field_name][$langcode];
- }
- $changes = _taxonomy_access_disallowed_changes($new_values, $old_values);
- if ($changes) {
- if (!isset($disallowed_changes[$field_name])) {
- $disallowed_changes[$field_name] = array();
- }
- $disallowed_changes[$field_name][$langcode] = $changes;
- }
- }
- }
- }
- unset($old);
- unset($new);
- return $disallowed_changes;
- }
- /**
- * Helper function to check for term reference changes disallowed by create.
- *
- * @param array $new_field
- * The entity or form values of the updated field.
- * @param array $old_field
- * The entity or form values of the original field.
- *
- * @return array|false
- * Returns FALSE if there are no disallowed changes. Otherwise, an array:
- * - 'added' => An array of added terms that are disallowed.
- * - 'removed' => An array of removed termss that are disallowed.
- */
- function _taxonomy_access_disallowed_changes(array $new_field, array $old_field) {
- // Assemble a list of term IDs from the original entity, if any.
- $old_tids = array();
- foreach ($old_field as $old_item) {
- // Some things are NULL for some reason.
- if ($old_item['tid']) {
- $old_tids[] = $old_item['tid'];
- }
- }
- // Assemble a list of term IDs from the updated entity.
- $new_tids = array();
- foreach ($new_field as $delta => $new_item) {
- // Some things are NULL for some reason.
- // Allow the special tid "autocreate" so users can create new terms.
- if ($new_item['tid'] && ($new_item['tid'] != 'autocreate')) {
- $new_tids[$delta] = $new_item['tid'];
- }
- }
- // Check for added tids, and unset ones the user may not add.
- $added = array_diff($new_tids, $old_tids);
- $may_not_add = taxonomy_access_create_disallowed($added);
- // Check for removed tids, and restore ones the user may not remove.
- $removed = array_diff($old_tids, $new_tids);
- $may_not_remove = taxonomy_access_create_disallowed($removed);
- // If there were any disallowed changes, return them.
- if (!empty($may_not_add) || !empty($may_not_remove)) {
- return array('added' => $may_not_add, 'removed' => $may_not_remove);
- }
- // Return FALSE if all changes were valid.
- return FALSE;
- }
- /**
- * End of "defgroup tac_create".
- * @}
- */
|