diff --git a/HEAD_IS_EMPTY_USE_7.x-2.x_BRANCH.txt b/HEAD_IS_EMPTY_USE_7.x-2.x_BRANCH.txt new file mode 100644 index 0000000000000000000000000000000000000000..485fc70549de58ec776865a2250ddf5bd53f6b35 --- /dev/null +++ b/HEAD_IS_EMPTY_USE_7.x-2.x_BRANCH.txt @@ -0,0 +1 @@ +Development takes place in the 7.x-2.x branch diff --git a/README.txt b/README.txt deleted file mode 100644 index f5c08ebbf901989f94e233ea253c4e6db419a8e8..0000000000000000000000000000000000000000 --- a/README.txt +++ /dev/null @@ -1,37 +0,0 @@ -$Id$ - --- SUMMARY -- - -The References project contains straight ports of the node_reference and -user_reference modules to the Drupal 7 API. - -For a full description of the module, visit the project page: - http://drupal.org/project/references - --- REQUIREMENTS -- - -None. - -CCK for Drupal 7 is /not/ a requirement for these modules. - --- GOALS AND LIMITATIONS -- - -It is not envisioned as a final solution, but as a way to actually deploy -Drupal 7 from release day on sites using node and user references much as on -Drupal 6, until a native entity relationships Drupal 7 module becomes a usable -alternative. - -As of 2010-11-30, is looks like a candidate for that usable alternative might -someday be project Relation: - http://drupal.org/project/relation - -In short: use these modules now, but be ready to migrate to a different entity -referencing solution during the D7 life cycle. - --- CONTACT -- - -Current maintainers: - -* References: Frederic G. MARAND (fgm) - http://drupal.org/user/27985 -* CCK D7: Yves CHEDEMOIS (yched) - http://drupal.org/user/39567 - diff --git a/node_reference/node_reference.info b/node_reference/node_reference.info deleted file mode 100644 index 7e737977b4a3803207910388d2277e3e83f2ae4b..0000000000000000000000000000000000000000 --- a/node_reference/node_reference.info +++ /dev/null @@ -1,6 +0,0 @@ -; $Id$ -name = Node Reference -description = Defines a field type for referencing one node from another. -files[]=node_reference.module -package = CCK -core = 7.x diff --git a/node_reference/node_reference.module b/node_reference/node_reference.module deleted file mode 100644 index 6d0d2f85041f3c4910d24da9e4612d6f4a88503b..0000000000000000000000000000000000000000 --- a/node_reference/node_reference.module +++ /dev/null @@ -1,841 +0,0 @@ - 'node_reference autocomplete', - 'page callback' => 'node_reference_autocomplete', - 'page arguments' => array(2, 3), - 'access callback' => 'node_reference_autocomplete_access', - 'access arguments' => array(2, 3), - 'type' => MENU_CALLBACK, - ); - return $items; -} - -/** - * Implements hook_field_info(). - */ -function node_reference_field_info() { - return array( - 'node_reference' => array( - 'label' => t('Node reference'), - 'description' => t('This field stores the ID of a related node as an integer value.'), - 'settings' => array('referenceable_types' => array()), - // It probably make more sense to have the referenceable types be per-field than per-instance - // 'instance settings' => array('referenceable_types' => array()), - 'default_widget' => 'options_select', // node_reference_autocomplete', - 'default_formatter' => 'node_reference_default', - ), - ); -} - -/** - * Implements hook_field_schema(). - */ -function node_reference_field_schema($field) { - $columns = array( - 'nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - ), - ); - return array( - 'columns' => $columns, - 'indexes' => array('nid' => array('nid')), - 'foreign keys' => array( - 'nid' => array( - 'table' => 'node', - 'columns' => array('nid' => 'nid'), - ), - ), - ); -} - -/** - * Implements hook_field_settings_form(). - */ -function node_reference_field_settings_form($field, $instance, $has_data) { - $settings = $field['settings']; - - $form = array(); - $form['referenceable_types'] = array( - '#type' => 'checkboxes', - '#title' => t('Content types that can be referenced'), - '#multiple' => TRUE, - '#default_value' => is_array($settings['referenceable_types']) - ? $settings['referenceable_types'] - : array(), - '#options' => array_map('check_plain', node_type_get_names()), - '#disabled' => $has_data, - ); - return $form; -} - -/** - * Implements hook_field_validate(). - * - * Possible error codes: - * - 'invalid_nid': nid is not valid for the field (not a valid node id, or the node is not referenceable). - */ -function node_reference_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { - // Extract nids to check. - $ids = array(); - - // First check non-numeric "nid's to avoid losing time with them. - foreach ($items as $delta => $item) { - if (is_array($item) && !empty($item['nid'])) { - if (is_numeric($item['nid'])) { - $ids[] = $item['nid']; - } - else { - $errors[$field['field_name']][$langcode][$delta][] = array( - 'error' => 'invalid_nid', - 'message' => t("%name: invalid input.", - array('%name' => $instance['label'])), - ); - } - } - } - // Prevent performance hog if there are no ids to check. - if ($ids) { - $refs = _node_reference_potential_references($field, '', NULL, $ids); - foreach ($items as $delta => $item) { - if (is_array($item)) { - if (!empty($item['nid']) && !isset($refs[$item['nid']])) { - $errors[$field['field_name']][$langcode][$delta][] = array( - 'error' => 'invalid_nid', - 'message' => t("%name: this post can't be referenced.", - array('%name' => $instance['label'])), - ); - } - } - } - } -} - -/** - * Implements hook_field_prepare_view(). - */ -function node_reference_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) { - // @todo : do we need the static ? - $fetched_nodes = &drupal_static(__FUNCTION__, array()); - - // Extract nids to check. - $ids = array(); - foreach ($items as $id => $entity_items) { - foreach ($entity_items as $delta => $item) { - if (is_array($item)) { - // Default to 'not accessible'. - $items[$id][$delta]['access'] = FALSE; - if (!empty($item['nid']) && is_numeric($item['nid'])) { - $ids[$item['nid']] = $item['nid']; - } - } - } - } - - if ($ids) { - // Load information about nids that we haven't already loaded during - // this page request. - $missing_ids = array_diff($ids, array_keys($fetched_nodes)); - if (!empty($missing_ids)) { - $query = db_select('node', 'n') - ->fields('n') - ->condition('n.nid', $missing_ids) - ->addTag('node_access'); - if (!user_access('administer nodes')) { - $query->condition('status', 1); - } - $fetched_nodes = $query->execute()->fetchAllAssoc('nid'); - } - - foreach ($items as $id => $entity_items) { - foreach ($entity_items as $delta => $item) { - if (is_array($item) && !empty($item['nid']) && isset($fetched_nodes[$item['nid']])) { - $items[$id][$delta]['node'] = $fetched_nodes[$item['nid']]; - $items[$id][$delta]['access'] = TRUE; - } - } - } - } -} - -/** - * Implements hook_field_is_empty(). - */ -function node_reference_field_is_empty($item, $field) { - // nid = 0 is empty too, which is exactly what we want. - return empty($item['nid']); -} - -/** - * Implements hook_field_formatter_info(). - */ -function node_reference_field_formatter_info() { - $ret = array( - 'node_reference_default' => array( - 'label' => t('Title (link)'), - 'description' => t('Display the title of the referenced node as a link to the node page.'), - 'field types' => array('node_reference'), - ), - 'node_reference_plain' => array( - 'label' => t('Title (no link)'), - 'description' => t('Display the title of the referenced node as plain text.'), - 'field types' => array('node_reference'), - ), - 'node_reference_node' => array( - 'label' => t('Rendered node'), - 'description' => t('Display the referenced node in a specific view mode'), - 'field types' => array('node_reference'), - 'settings' => array('node_reference_view_mode' => 'full'), - ), - ); - return $ret; -} - -/** - * Implements hook_field_formatter_prepare_view(). - * - * Preload all nodes referenced by items using 'full entity' formatters. - */ -function node_reference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) { - // Collect nids to load. - // @todo : can we make clever use of the recursion queue here ? - $nids = array(); - foreach ($displays as $id => $display) { - if ($display['type'] == 'node_reference_node') { - foreach ($items[$id] as $delta => $item) { - if ($item['access']) { - $nids[$item['nid']] = $item['nid']; - } - } - } - } - $nodes = node_load_multiple($nids); - - // Add the loaded nodes to the items. - foreach ($displays as $id => $display) { - if ($display['type'] == 'node_reference_node') { - foreach ($items[$id] as $delta => $item) { - if ($item['access']) { - $items[$id][$delta]['node'] = $nodes[$item['nid']]; - } - } - } - } -} - -/** - * Implements hook_field_formatter_settings_form(). - */ -function node_reference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { - $display = $instance['display'][$view_mode]; - $settings = $display['settings']; - - // Only build this information for node_reference_node displays - if ($display['type'] !== 'node_reference_node') { - return NULL; - } - - $entity_info = entity_get_info('node'); - $modes = $entity_info['view modes']; - $options = array(); - foreach ($modes as $name => $mode) { - $options[$name] = $mode['label']; - } - $element['node_reference_view_mode'] = array( - '#title' => t('View mode'), - '#type' => 'select', - '#options' => $options, - '#default_value' => $settings['node_reference_view_mode'], - // Never empty, so no #empty_option - ); - return $element; -} - -/** - * Implements hook_field_formatter_settings_summary(). - */ -function node_reference_field_formatter_settings_summary($field, $instance, $view_mode) { - $display = $instance['display'][$view_mode]; - $settings = $display['settings']; - - if ($display['type'] === 'node_reference_node') { - $entity_info = entity_get_info('node'); - $modes = $entity_info['view modes']; - $mode = $modes[$settings['node_reference_view_mode']]['label']; - $summary = t('View mode: %mode', array('%mode' => $mode)); - } - else { - $summary = NULL; - } - return $summary; -} - -/** - * Implements hook_field_formatter_view(). - */ -function node_reference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { - $result = array(); - - switch ($display['type']) { - case 'node_reference_default': - case 'node_reference_plain': - foreach ($items as $delta => $item) { - if ($item['access']) { - $node = $item['node']; - if ($display['type'] == 'node_reference_default') { - $uri = entity_uri('node', $node); - $result[$delta] = array( - '#type' => 'link', - '#title' => $node->title, - '#href' => $uri['path'], - '#options' => $uri['options'], - ); - } - else { - $result[$delta] = array( - '#markup' => check_plain($node->title), - ); - } - if (!$node->status) { - $result[$delta]['#prefix'] = ''; - $result[$delta]['#suffix'] = ''; - } - } - } - break; - - case 'node_reference_node': - $view_mode = $display['settings']['node_reference_view_mode']; - // To prevent infinite recursion caused by reference cycles, we store - // diplayed nodes in a recursion queue. - $recursion_queue = &drupal_static(__FUNCTION__, array()); - - // If no 'referencing entity' is set, we are starting a new 'reference - // thread' and need to reset the queue. - // @todo Bug: $entity->referencing_entity on nodes referenced in a different - // thread on the page. E.g: 1 references 1+2 / 2 references 1+2 / visit homepage. - // We'd need a more accurate way... - if (!isset($entity->referencing_entity)) { - $recursion_queue = array(); - } - - // The recursion queue only needs to track nodes. - if ($entity_type == 'node') { - list($id) = entity_extract_ids($entity_type, $entity); - $recursion_queue[$id] = $id; - } - - // Check the recursion queue to determine which nodes should be fully - // displayed, and which nodes will only be displayed as a title. - $nodes_display = array(); - foreach ($items as $delta => $item) { - if ($item['access'] && !isset($recursion_queue[$item['nid']])) { - $nodes_display[$item['nid']] = $item['node']; - } - } - - // Load and build the fully displayed nodes. - if ($nodes_display) { - foreach ($nodes_display as $nid => $node) { - $nodes_display[$nid]->referencing_entity = $entity; - $nodes_display[$nid]->referencing_field = $field['field_name']; - } - $nodes_built = node_view_multiple($nodes_display, $view_mode); - } - - // Assemble the render array. - foreach ($items as $delta => $item) { - if ($item['access']) { - if (isset($nodes_display[$item['nid']])) { - $result[$delta] = $nodes_built['nodes'][$item['nid']]; - } - else { - $node = $item['node']; - $uri = entity_uri('node', $node); - $result[$delta] = array( - '#type' => 'link', - '#title' => $node->title, - '#href' => $uri['path'], - '#options' => $uri['options'], - ); - if (!$node->status) { - $result[$delta]['#prefix'] = ''; - $result[$delta]['#suffix'] = ''; - } - } - } - } - break; - } - - return $result; -} - -/** - * Implements hook_field_widget_info(). - */ -function node_reference_field_widget_info() { - return array( - 'node_reference_autocomplete' => array( - 'label' => t('Autocomplete text field'), - 'description' => t('Display the list of referenceable nodes as a textfield with autocomplete behaviour.'), - 'field types' => array('node_reference'), - 'settings' => array( - 'autocomplete_match' => 'contains', - 'size' => 60, - 'autocomplete_path' => 'node_reference/autocomplete', - ), - ), - ); -} - -/** - * Implements hook_field_widget_info_alter(). - */ -function node_reference_field_widget_info_alter(&$info) { - $info['options_select']['field types'][] = 'node_reference'; - $info['options_buttons']['field types'][] = 'node_reference'; -} - -/** - * Implements hook_field_widget_settings_form(). - */ -function node_reference_field_widget_settings_form($field, $instance) { - $widget = $instance['widget']; - $defaults = field_info_widget_settings($widget['type']); - $settings = array_merge($defaults, $widget['settings']); - - $form = array(); - if ($widget['type'] == 'node_reference_autocomplete') { - $form['autocomplete_match'] = array( - '#type' => 'select', - '#title' => t('Autocomplete matching'), - '#default_value' => $settings['autocomplete_match'], - '#options' => array( - 'starts_with' => t('Starts with'), - 'contains' => t('Contains'), - ), - '#description' => t('Select the method used to collect autocomplete suggestions. Note that Contains can cause performance issues on sites with thousands of nodes.'), - ); - $form['size'] = array( - '#type' => 'textfield', - '#title' => t('Size of textfield'), - '#default_value' => $settings['size'], - '#element_validate' => array('_element_validate_integer_positive'), - '#required' => TRUE, - ); - } - return $form; -} - -/** - * Implements hook_field_widget_form(). - */ -function node_reference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { - switch ($instance['widget']['type']) { - case 'node_reference_autocomplete': - $element += array( - '#type' => 'textfield', - '#default_value' => isset($items[$delta]['nid']) ? $items[$delta]['nid'] : NULL, - '#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $instance['entity_type'] . '/' . $field['field_name'], - '#size' => $instance['widget']['settings']['size'], - '#element_validate' => array('node_reference_autocomplete_validate'), - '#value_callback' => 'node_reference_autocomplete_value', - ); - break; - } - - return array('nid' => $element); -} - -/** - * Value callback for a node_reference autocomplete element. - * - * Replace the node nid with a node title. - */ -function node_reference_autocomplete_value($element, $input = FALSE, $form_state) { - if ($input === FALSE) { - // We're building the displayed 'default value': expand the raw nid into - // "node title [nid:n]". - $nid = $element['#default_value']; - if (!empty($nid)) { - $q = db_select('node', 'n'); - $node_title_alias = $q->addField('n', 'title'); - $q->addTag('node_access') - ->condition('n.nid', $nid) - ->range(0, 1); - $result = $q->execute(); - // @todo If no result (node doesn't exist or no access). - $value = $result->fetchField(); - $value .= ' [nid:' . $nid . ']'; - return $value; - } - } -} - -/** - * Validation callback for a node_reference autocomplete element. - */ -function node_reference_autocomplete_validate($element, &$form_state, $form) { - $field = $form_state['field'][$element['#field_name']][$element['#language']]['field']; - $instance = $form_state['field'][$element['#field_name']][$element['#language']]['instance']; - - $value = $element['#value']; - $nid = NULL; - - if (!empty($value)) { - // Check whether we have an explicit "[nid:n]" input. - preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches); - if (!empty($matches)) { - // Explicit nid. Check that the 'title' part matches the actual title for - // the nid. - list(, $title, $nid) = $matches; - if (!empty($title)) { - $real_title = db_select('node', 'n') - ->fields('n', array('title')) - ->condition('n.nid', $nid) - ->execute() - ->fetchField(); - if (trim($title) != trim($real_title)) { - form_error($element, t('%name: title mismatch. Please check your selection.', array('%name' => $instance['label']))); - } - } - } - else { - // No explicit nid (the submitted value was not populated by autocomplete - // selection). Get the nid of a referencable node from the entered title. - $reference = _node_reference_potential_references($field, $value, 'equals', NULL, 1); - if ($reference) { - // @todo The best thing would be to present the user with an - // additional form, allowing the user to choose between valid - // candidates with the same title. ATM, we pick the first - // matching candidate... - $nid = key($reference); - } - else { - form_error($element, t('%name: found no valid post with that title.', array('%name' => $instance['label']))); - } - } - } - - // Set the element's value as the node id that was extracted from the entered - // input. - form_set_value($element, $nid, $form_state); -} - -/** - * Implements hook_field_widget_error(). - */ -function node_reference_field_widget_error($element, $error, $form, &$form_state) { - form_error($element['nid'], $error['message']); -} - -/** - * Fetch an array of all candidate referenced nodes. - * - * This info is used in various places (allowed values, autocomplete - * results, input validation...). Some of them only need the nids, - * others nid + titles, others yet nid + titles + rendered row (for - * display in widgets). - * - * The array we return contains all the potentially needed information, - * and lets consumers use the parts they actually need. - * - * @param $field - * The field description. - * @param $string - * Optional string to filter titles on (used by autocomplete). - * @param $match - * Operator to match filtered name against, can be any of: - * 'contains', 'equals', 'starts_with' - * @param $ids - * Optional node ids to lookup (the $string and $match arguments will be - * ignored). - * @param $limit - * If non-zero, limit the size of the result set. - * - * @return - * An array of valid nodes in the form: - * array( - * nid => array( - * 'title' => The node title, - * 'rendered' => The text to display in widgets (can be HTML) - * ), - * ... - * ) - * @todo Check whether we still need the 'rendered' value (hook_options_list() - * does not need it anymore). Should probably be clearer after the 'Views' - * mode is ported. - */ -function _node_reference_potential_references($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) { - $results = &drupal_static(__FUNCTION__, array()); - - // Create unique id for static cache. - $cid = $field['field_name'] . ':' . $match . ':' - . ($string !== '' ? $string : implode('-', $ids)) - . ':' . $limit; - if (!isset($results[$cid])) { - $references = _node_reference_potential_references_standard($field, $string, $match, $ids, $limit); - - // Store the results. - $results[$cid] = !empty($references) ? $references : array(); - } - - return $results[$cid]; -} - -/** - * Helper function for _node_reference_potential_references(). - * - * List of referenceable nodes defined by content types. - */ -function _node_reference_potential_references_standard($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) { - // Avoid useless work - if (!count($field['settings']['referenceable_types'])) { - return array(); - } - - $query = db_select('node', 'n'); - $node_nid_alias = $query->addField('n', 'nid'); - $node_title_alias = $query->addField('n', 'title', 'node_title'); - $node_type_alias = $query->addField('n', 'type', 'node_type'); - $query->addTag('node_access'); - $query->addMetaData('id', ' _node_reference_potential_references_standard'); - - if (is_array($field['settings']['referenceable_types'])) { - $query->condition('n.type', $field['settings']['referenceable_types'], 'IN'); - } - - if ($string !== '') { - switch ($match) { - case 'contains': - $query->condition('n.title', '%' . $string . '%', 'LIKE'); - break; - - case 'starts_with': - $query->condition('n.title', $string . '%', 'LIKE'); - break; - - case 'equals': - default: // no match type or incorrect match type: use "=" - $query->condition('n.title', $string); - break; - } - } - elseif ($ids) { - $query->condition('n.nid', $ids, 'IN', $ids); - } - - $query - ->orderBy($node_title_alias) - ->orderBy($node_type_alias); - - if ($limit) { - $query->range(0, $limit); - } - - $result = $query->execute(); - $references = array(); - foreach ($result->fetchAll() as $node) { - $references[$node->nid] = array( - 'title' => $node->node_title, - 'rendered' => check_plain($node->node_title), - ); - } - return $references; -} - -/** - * Menu access callback for the autocomplete path. - * - * Check for both 'edit' and 'view' access in the unlikely event - * a user has edit but not view access. - */ -function node_reference_autocomplete_access($entity_type, $field_name) { - return user_access('access content') && ($field = field_info_field($field_name)) && field_access('view', $field, $entity_type) && field_access('edit', $field, $entity_type); -} - -/** - * Menu callback for the autocomplete results. - */ -function node_reference_autocomplete($entity_type, $field_name, $string = '') { - $field = field_info_field($field_name); - - // @todo broken - this is in $field['widget']['settings']['autocomplete_match'] - we'd need the $instance. - $match = isset($field['widget']['autocomplete_match']) ? $field['widget']['autocomplete_match'] : 'contains'; - $matches = array(); - - $references = _node_reference_potential_references($field, $string, $match, array(), 10); - foreach ($references as $id => $row) { - // Add a class wrapper for a few required CSS overrides. - $matches[$row['title'] . " [nid:$id]"] = '
' . $row['rendered'] . '
'; - } - drupal_json_output($matches); -} - -/** - * Implements hook_node_type_update(). - * - * Reflect type name changes to the 'referenceable types' settings: when - * the name of a type changes, the change needs to be reflected in the - * "referenceable types" setting for any node_reference field - * referencing it. - */ -function node_reference_node_type_update($info) { - if (!empty($info->old_type) && $info->old_type != $info->type) { - $fields = field_info_fields(); - foreach ($fields as $field_name => $field) { - if ($field['type'] == 'node_reference' && isset($field['settings']['referenceable_types'][$info->old_type])) { - $field['settings']['referenceable_types'][$info->type] = empty($field['settings']['referenceable_types'][$info->old_type]) ? 0 : $info->type; - unset($field['settings']['referenceable_types'][$info->old_type]); - field_update_field($field); - } - } - } -} - -/** - * Theme preprocess function. - * - * Allows specific node templates for nodes displayed as values of a - * node_reference field with the 'full node' / 'teaser' formatters. - */ -function node_reference_preprocess_node(&$vars) { - // The 'referencing_field' attribute of the node is added by the 'teaser' - // and 'full node' formatters. - if (!empty($vars['node']->referencing_field)) { - $node = $vars['node']; - $field = $node->referencing_field; - $vars['theme_hook_suggestions'][] = 'node-reference'; - $vars['theme_hook_suggestions'][] = 'node-reference__' . $field['field_name']; - $vars['theme_hook_suggestions'][] = 'node-reference__' . $node->type; - $vars['theme_hook_suggestions'][] = 'node-reference__' . $field['field_name'] . '__' . $node->type; - } -} - -/** - * Implements hook_field_prepare_translation(). - * - * When preparing a translation, load any translations of existing - * references. - * @todo Correctly implement after http://drupal.org/node/362021 is fixed. - */ -function node_reference_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items) { - $addition = array(); - $addition[$field['field_name']] = array(); - if (isset($entity->translation_source->$field['field_name']) - && is_array($entity->translation_source->$field['field_name'])) { - foreach ($entity->translation_source->$field['field_name'] as $key => $reference) { - $reference_node = node_load($reference['nid']); - // Test if the referenced node type is translatable and, if so, - // load translations if the reference is not for the current language. - // We can assume the translation module is present because it invokes 'prepare translation'. - if (translation_supported_type($reference_node->type) - && !empty($reference_node->language) - && $reference_node->language != $node->language - && $translations = translation_node_get_translations($reference_node->tnid)) { - // If there is a translation for the current language, use it. - $addition[$field['field_name']][] = array( - 'nid' => isset($translations[$node->language]) - ? $translations[$node->language]->nid - : $reference['nid'], - ); - } - } - } - - return $addition; -} - -/** - * Implements hook_options_list(). - */ -function node_reference_options_list($field) { - $references = _node_reference_potential_references($field); - - // @todo Support optgroups ? I think this was added in late CCK D6. - $options = array(); - foreach ($references as $key => $value) { - $options[$key] = $value['title']; - } - - return $options; -} - -/** - * Implements hook_content_migrate_field_alter(). - * - * Use this to tweak the conversion of field settings from the D6 style to the - * D7 style for specific situations not handled by basic conversion, as when - * field types or settings are changed. - * - * $field_value['widget_type'] is available to - * see what widget type was originally used. - */ -function node_reference_content_migrate_field_alter(&$field_value, $instance_value) { - switch ($field_value['module']) { - case 'nodereference': - $field_value['module'] = 'node_reference'; - $field_value['type'] = 'node_reference'; - break; - } -} - -/** - * Implements hook_content_migrate_instance_alter(). - * - * Use this to tweak the conversion of instance or widget settings from the D6 - * style to the D7 style for specific situations not handled by basic - * conversion, as when formatter or widget names or settings are changed. - */ - function node_reference_content_migrate_instance_alter(&$instance_value, $field_value) { - switch ($field_value['type']) { - case 'nodereference': - // Massage formatters. - foreach ($instance_value['display'] as $context => &$display) { - switch ($display['type']) { - case 'full': - case 'teaser': - // Those two formatters have been merged into - // 'node_reference_view_mode', with a formatter setting. - $display['type'] = 'node_reference_node'; - $display['settings']['node_reference_view_mode'] = $display['type']; - break; - - default: - // The formatter names changed, all are prefixed with - // 'node_reference_'. - $display['type'] = 'node_reference_' . $display['type']; - break; - } - } - // Massage the widget. - switch ($instance_value['widget']['type']) { - case 'nodereference_autocomplete': - $instance_value['widget']['type'] = 'node_reference_autocomplete'; - $instance_value['widget']['module'] = 'node_reference'; - break; - case 'nodereference_select': - $instance_value['widget']['type'] = 'options_select'; - $instance_value['widget']['module'] = 'options'; - break; - case 'nodereference_buttons': - $instance_value['widget']['type'] = 'options_buttons'; - $instance_value['widget']['module'] = 'options'; - } - break; - } -} diff --git a/user_reference/user_reference.info b/user_reference/user_reference.info deleted file mode 100644 index e5dedd0fc6fea30c9f8c09a19b2274f24bdfc964..0000000000000000000000000000000000000000 --- a/user_reference/user_reference.info +++ /dev/null @@ -1,6 +0,0 @@ -; $Id$ -name = User Reference -description = Defines a field type for referencing a user from a node. -files[]=user_reference.module -package = CCK -core = 7.x diff --git a/user_reference/user_reference.module b/user_reference/user_reference.module deleted file mode 100644 index f5c42f0a320268070cebea8d59bf3e81a121de9d..0000000000000000000000000000000000000000 --- a/user_reference/user_reference.module +++ /dev/null @@ -1,666 +0,0 @@ - 'user_reference autocomplete', - 'page callback' => 'user_reference_autocomplete', - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK - ); - return $items; -} - -/** - * Implements hook_field_info(). - */ -function user_reference_field_info() { - return array( - 'user_reference' => array( - 'label' => t('User reference'), - 'description' => t('This field stores the ID of a related user as an integer value.'), - 'settings' => array('referenceable_roles' => array(), 'referenceable_status' => array()), - 'default_widget' => 'user_reference_autocomplete', - 'default_formatter' => 'user_reference_default', - ), - ); -} - -/** - * Implements hook_field_schema(); - */ -function user_reference_field_schema($field) { - $columns = array( - 'uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - ), - ); - return array( - 'columns' => $columns, - 'indexes' => array('uid' => array('uid')), - 'foreign keys' => array( - 'uid' => array( - 'table' => 'users', - 'columns' => array('uid' => 'uid'), - ), - ), - ); -} - -/** - * Implements hook_field_settings_form(). - */ -function user_reference_field_settings_form($field, $instance, $has_data) { - $settings = $field['settings']; - - $form = array(); - $form['referenceable_roles'] = array( - '#type' => 'checkboxes', - '#title' => t('User roles that can be referenced'), - '#default_value' => is_array($settings['referenceable_roles']) - ? array_filter($settings['referenceable_roles']) - : array(), - '#options' => user_roles(1), - '#disabled' => $has_data, - ); - $form['referenceable_status'] = array( - '#type' => 'checkboxes', - '#title' => t('User status that can be referenced'), - '#default_value' => is_array($settings['referenceable_status']) - ? array_filter($settings['referenceable_status']) - : array(1), - '#options' => array(1 => t('Active'), 0 => t('Blocked')), - '#disabled' => $has_data, - ); - return $form; -} - -/** - * Implements hook_field_validate(). - * - * Possible error codes: - * - 'invalid_uid': uid is not valid for the field (not a valid user id, or the user is not referenceable). - */ -function user_reference_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { - // Extract uids to check. - $ids = array(); - - // First check non-numeric uid's to avoid losing time with them. - foreach ($items as $delta => $item) { - if (is_array($item) && !empty($item['uid'])) { - if (is_numeric($item['uid'])) { - $ids[] = $item['uid']; - } - else { - $errors[$field['field_name']][$langcode][$delta][] = array( - 'error' => 'invalid_uid', - 'message' => t('%name: invalid input.', - array('%name' => $instance['label'])), - ); - } - } - } - // Prevent performance hog if there are no ids to check. - if ($ids) { - $refs = _user_reference_potential_references($field, '', NULL, $ids); - foreach ($items as $delta => $item) { - if (is_array($item)) { - if (!empty($item['uid']) && !isset($refs[$item['uid']])) { - $errors[$field['field_name']][$langcode][$delta][] = array( - 'error' => 'invalid_uid', - 'message' => t("%name: this user can't be referenced.", - array('%name' => $instance['label'])), - ); - } - } - } - } -} - -/** - * Implements hook_field_is_empty(). - */ -function user_reference_field_is_empty($item, $field) { - return empty($item['uid']); -} - -/** - * Implements hook_field_formatter_info(). - */ -function user_reference_field_formatter_info() { - return array( - 'user_reference_default' => array( - 'label' => t('Default'), - 'description' => t("Display the name of the referenced user as a link to the user's profile page."), - 'field types' => array('user_reference'), - ), - 'user_reference_plain' => array( - 'label' => t('Plain text'), - 'description' => t('Display the name of the referenced user as plain text.'), - 'field types' => array('user_reference'), - ), - ); -} - -/** - * Implements hook_field_formatter_view(). - */ -function user_reference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { - $result = array(); - - // @todo Optimisation: use hook_field_formatter_prepare_view() to load - // user names or full user entities in 'multiple' mode. - - // Collect the list of user ids. - $uids = array(); - foreach ($items as $delta => $item) { - $uids[$item['uid']] = $item['uid']; - } - - switch ($display['type']) { - case 'user_reference_default': - case 'user_reference_plain': - $titles = _user_reference_get_user_names($uids); - foreach ($items as $delta => $item) { - if ($display['type'] == 'user_reference_default') { - $result[$delta] = array( - '#type' => 'link', - '#title' => $titles[$item['uid']], - '#href' => 'user/' . $item['uid'], - ); - } - else { - $result[$delta] = array( - '#markup' => check_plain($titles[$item['uid']]), - ); - } - } - break; - } - - return $result; -} - -/** - * Helper function for widgets and formatters. - * - * Store user names collected in the curent request. - */ -function _user_reference_get_user_names($uids, $known_titles = array()) { - $titles = &drupal_static(__FUNCTION__, array()); - - // Save titles we receive. - $titles += $known_titles; - - // Collect nids to retrieve from database. - $uids_query = array(); - foreach ($uids as $uid) { - if (!isset($titles[$uid])) { - $uids_query[] = $uid; - } - } - if ($uids_query) { - $query = db_select('users', 'u') - ->fields('u', array('uid', 'name')) - ->condition('u.uid', $uids); - $titles += $query->execute()->fetchAllKeyed(); - } - - // Build the results array. - $return = array(); - foreach ($uids as $uid) { - $return[$uid] = isset($titles[$uid]) ? $titles[$uid] : ''; - } - - return $return; -} - -/** - * Implements hook_field_widget_info(). - */ -function user_reference_field_widget_info() { - return array( - 'user_reference_autocomplete' => array( - 'label' => t('Autocomplete text field'), - 'description' => t('Display the list of referenceable users as a textfield with autocomplete behaviour.'), - 'field types' => array('user_reference'), - 'settings' => array( - 'autocomplete_match' => 'contains', - 'size' => 60, - 'autocomplete_path' => 'user_reference/autocomplete', - ), - ), - ); -} - -/** - * Implements hook_field_widget_info_alter(). - */ -function user_reference_field_widget_info_alter(&$info) { - $info['options_select']['field types'][] = 'user_reference'; - $info['options_buttons']['field types'][] = 'user_reference'; -} - -/** - * Implements hook_field_widget_settings_form(). - */ -function user_reference_field_widget_settings_form($field, $instance) { - $widget = $instance['widget']; - $defaults = field_info_widget_settings($widget['type']); - $settings = array_merge($defaults, $widget['settings']); - - $form = array(); - if ($widget['type'] == 'user_reference_autocomplete') { - $form['autocomplete_match'] = array( - '#type' => 'select', - '#title' => t('Autocomplete matching'), - '#default_value' => $settings['autocomplete_match'], - '#options' => array( - 'starts_with' => t('Starts with'), - 'contains' => t('Contains'), - ), - '#description' => t('Select the method used to collect autocomplete suggestions. Note that Contains can cause performance issues on sites with thousands of users.'), - ); - $form['size'] = array( - '#type' => 'textfield', - '#title' => t('Size of textfield'), - '#default_value' => $settings['size'], - '#element_validate' => array('_element_validate_integer_positive'), - '#required' => TRUE, - ); - } - return $form; -} - -/** - * Implements hook_field_widget_form(). - */ -function user_reference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { - switch ($instance['widget']['type']) { - case 'user_reference_autocomplete': - $element += array( - '#type' => 'textfield', - '#default_value' => isset($items[$delta]['uid']) ? $items[$delta]['uid'] : NULL, - '#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $field['field_name'], - '#size' => $instance['widget']['settings']['size'], - '#element_validate' => array('user_reference_autocomplete_validate'), - '#value_callback' => 'user_reference_autocomplete_value', - ); - break; - } - return array('uid' => $element); -} - -/** - * Value callback for a user_reference autocomplete element. - * - * Substitute in the user name for the uid. - */ -function user_reference_autocomplete_value($element, $input = FALSE, $form_state) { - if ($input === FALSE) { - // We're building the displayed 'default value': expand the raw uid into - // "user name [uid:n]". - $uid = $element['#default_value']; - if (!empty($uid)) { - $q = db_select('users', 'u'); - $q->addField('u', 'name'); - - $q->condition('u.uid', $uid) - ->range(0, 1); - $result = $q->execute(); - // @todo If no result (user doesn't exist). - $value = $result->fetchField(); - $value .= ' [uid:' . $uid . ']'; - return $value; - } - } -} - -/** - * Validation callback for a user_reference autocomplete element. - */ -function user_reference_autocomplete_validate($element, &$form_state, $form) { - $field = $form_state['field'][$element['#field_name']][$element['#language']]['field']; - $instance = $form_state['field'][$element['#field_name']][$element['#language']]['instance']; - - $value = $element['#value']; - $uid = NULL; - - if (!empty($value)) { - // Check whether we have an explicit "[uid:n]" input. - preg_match('/^(?:\s*|(.*) )?\[\s*uid\s*:\s*(\d+)\s*\]$/', $value, $matches); - if (!empty($matches)) { - // Explicit uid. Check that the 'name' part matches the actual name for - // the uid. - list(, $name, $uid) = $matches; - if (!empty($name)) { - $names = _user_reference_get_user_names(array($uid)); - if ($name != $names[$uid]) { - form_error($element, t('%name: name mismatch. Please check your selection.', array('%name' => $instance['label']))); - } - } - } - else { - // No explicit uid (the submitted value was not populated by autocomplete - // selection). Get the uid of a referencable user from the entered name. - $reference = _user_reference_potential_references($field, $value, 'equals', NULL, 1); - if ($reference) { - // @todo The best thing would be to present the user with an - // additional form, allowing the user to choose between valid - // candidates with the same name. ATM, we pick the first - // matching candidate... - $uid = key($reference); - } - else { - form_error($element, t('%name: found no valid user with that name.', array('%name' => $instance['label']))); - } - } - } - - // Set the element's value as the user id that was extracted from the entered - // input. - form_set_value($element, $uid, $form_state); -} - -/** - * Implements hook_field_widget_error(). - */ -function user_reference_field_widget_error($element, $error, $form, &$form_state) { - form_error($element['uid'], $error['message']); -} - -/** - * Fetch an array of all candidate referenced users. - * - * This info is used in various places (aloowed values, autocomplete results, - * input validation...). Some of them only need the uids, others nid + names, - * others yet uid + names + rendered row (for display in widgets). - * The array we return contains all the potentially needed information, and lets - * consumers use the parts they actually need. - * - * @param $field - * The field description. - * @param $string - * Optional string to filter usernames on (used by autocomplete) - * @param $match - * Operator to match filtered name against, can be any of: - * 'contains', 'equals', 'starts_with' - * @param $ids - * Optional user ids to lookup (the $string and $match arguments will be - * ignored). - * @param $limit - * If non-zero, limit the size of the result set. - * - * @return - * An array of valid users in the form: - * array( - * uid => array( - * 'title' => The user name, - * 'rendered' => The text to display in widgets (can be HTML) - * ), - * ... - * ) - */ -function _user_reference_potential_references($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) { - $results = &drupal_static(__FUNCTION__, array()); - - // Create unique id for static cache. - $cid = $field['field_name'] . ':' . $match . ':' - . ($string !== '' ? $string : implode('-', $ids)) - . ':' . $limit; - if (!isset($results[$cid])) { - $references = _user_reference_potential_references_standard($field, $string, $match, $ids, $limit); - - // Store the results. - $results[$cid] = !empty($references) ? $references : array(); - } - - return $results[$cid]; -} - -/** - * Helper function for _user_reference_potential_references(). - * - * List of referenceable users defined by user role and status. - */ -function _user_reference_potential_references_standard($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) { - // Avoid useless work - if (!count($field['settings']['referenceable_status']) || !count($field['settings']['referenceable_roles'])) { - return array(); - } - - $query = db_select('users', 'u'); - $user_uid_alias = $query->addField('u', 'uid'); - $user_name_alias = $query->addField('u', 'name'); - $user_status_alias = $query->addField('u', 'status'); - $query->addMetaData('id', ' _user_reference_potential_references_standard'); - - if (is_array($field['settings']['referenceable_status'])) { - $query->condition('u.status', $field['settings']['referenceable_status']); - } - - if (is_array($field['settings']['referenceable_roles']) && (!in_array(DRUPAL_AUTHENTICATED_RID, $field['settings']['referenceable_roles']))) { - $query->join('users_roles', 'r', 'u.uid = r.uid'); - $query->addField('r', 'rid'); - $query->condition('r.rid', $field['settings']['referenceable_roles']); - } - - if ($string !== '') { - $args = array(); - switch ($match) { - case 'contains': - $name_clause = 'u.name LIKE :match'; - $args['match'] = '%' . $string . '%'; - break; - - case 'starts_with': - $name_clause = 'u.name LIKE :match'; - $args['match'] = $string . '%'; - break; - - case 'equals': - default: // no match type or incorrect match type: use "=" - $name_clause = 'u.name = :match'; - $args['match'] = $string; - break; - } - $query->where($name_clause, $args); - } - elseif ($ids) { - $query->condition($user_uid_alias, $ids, 'IN', $ids); - } - - $query - ->orderBy($user_name_alias); - - if ($limit) { - $query->range(0, $limit); - } - - $result = $query->execute(); - $references = array(); - foreach ($result->fetchAll() as $user) { - $references[$user->uid] = array( - 'title' => $user->name, - 'rendered' => check_plain($user->name), - ); - } - return $references; -} - -/** - * Menu callback; Retrieve a pipe delimited string of autocomplete suggestions for existing users - */ -function user_reference_autocomplete($field_name, $string = '') { - $field = field_info_field($field_name); - - $match = isset($field['widget']['autocomplete_match']) ? $field['widget']['autocomplete_match'] : 'contains'; - $matches = array(); - - $references = _user_reference_potential_references($field, $string, $match, array(), 10); - foreach ($references as $id => $row) { - // Add a class wrapper for a few required CSS overrides. - $matches[$row['title'] . " [uid:$id]"] = '
' . $row['rendered'] . '
'; - } - drupal_json_output($matches); -} - -/** - * Implements hook_options_list(). - */ -function user_reference_options_list($field) { - $references = _user_reference_potential_references($field); - - // @todo Support optgroups ? I think this was added in late CCK D6. - $options = array(); - foreach ($references as $key => $value) { - $options[$key] = $value['title']; - } - - return $options; -} - -/** - * Implementation of hook_user_load(). - */ -/*function user_reference_user_load(&$accounts) { - - // Only add links if we are on the user 'view' page. - if (arg(0) != 'user' || arg(2)) { - return; - } - - foreach ($accounts as $uid => $account) { - - // find CCK user_reference field tables - // search through them for matching user ids and load those nodes - $additions = array(); - $fields = field_info_instances('user'); - - // TODO : replace with field_attach_query() + synchronize with latest D6 code. - - // Find the table and columns to search through, if the same - // table comes up in more than one field type, we only need - // to search it once. - $search_tables = array(); - $search_links = array(); - foreach ($fields as $field) { - if ($field['type'] == 'user_reference' && !empty($field['widget']['reverse_link'])) { - $db_info = content_database_info($field); - $search_tables[$db_info['table']] = $db_info['columns']['uid']['column']; - $search_links[$db_info['table']] = $field['widget']['reverse_link']; - } - } - foreach ($search_tables as $table => $column) { - $ids = db_query(db_rewrite_sql("SELECT DISTINCT(n.nid) FROM {node} n LEFT JOIN {". $table ."} f ON n.vid = f.vid WHERE f.". $column ."=". $account->uid. " AND n.status = 1")); - while ($data = db_fetch_object($ids)) { - // TODO, do we really want a complete node_load() here? We only need the title to create a link. - $node = node_load($data->nid); - $node->reverse_link = $search_links[$table]; - $additions[$node->type][] = $node; - } - } - $accounts[$uid]->user_reference = $additions; - } - return; -}*/ - -/** - * Implementation of hook_user_view(). - */ -/*function user_reference_user_view(&$account) { - if (!empty($account->user_reference)) { - $node_types = content_types(); - $additions = array(); - $values = array(); - foreach ($account->user_reference as $node_type => $nodes) { - foreach ($nodes as $node) { - if ($node->reverse_link) { - $values[$node_type][] = l($node->title, 'node/' . $node->nid); - } - } - if (isset($values[$node_type])) { - $additions[] = array( - '#type' => 'user_profile_item', - '#title' => check_plain($node_types[$node_type]['name']), - '#value' => theme('item_list', $values[$node_type]), - ); - } - } - if ($additions) { - $account->content['user_reference'] = $additions + array( - '#type' => 'user_profile_category', - '#attributes' => array('class' => array('user-member')), - '#title' => t('Related content'), - '#weight' => 10, - ); - } - } -}*/ - -/** - * Implements hook_content_migrate_field_alter(). - * - * Use this to tweak the conversion of field settings - * from the D6 style to the D7 style for specific - * situations not handled by basic conversion, - * as when field types or settings are changed. - * - * $field_value['widget_type'] is available to - * see what widget type was originally used. - */ -function user_reference_content_migrate_field_alter(&$field_value, $instance_value) { - switch ($field_value['module']) { - case 'userreference': - $field_value['module'] = 'user_reference'; - $field_value['type'] = 'user_reference'; - break; - } -} - -/** - * Implements hook_content_migrate_instance_alter(). - * - * Use this to tweak the conversion of instance or widget settings - * from the D6 style to the D7 style for specific - * situations not handled by basic conversion, as when - * formatter or widget names or settings are changed. - */ -function user_reference_content_migrate_instance_alter(&$instance_value, $field_value) { - // The module name for the instance was corrected - // by the change in user_reference_content_migrate_field_alter(). - switch ($field_value['type']) { - case 'userreference': - // The formatter names changed, all are prefixed - // with 'user_reference_'. - foreach ($instance_value['display'] as $context => $settings) { - $instance_value['display'][$context]['type'] = 'user_reference_'. $settings['type']; - } - // Massage the widget. - switch ($instance_value['widget']['type']) { - case 'userreference_autocomplete': - $instance_value['widget']['type'] = 'user_reference_autocomplete'; - $instance_value['widget']['module'] = 'user_reference'; - break; - case 'userreference_select': - $instance_value['widget']['type'] = 'options_select'; - $instance_value['widget']['module'] = 'options'; - break; - case 'userreference_buttons': - $instance_value['widget']['type'] = 'options_buttons'; - $instance_value['widget']['module'] = 'options'; - } - break; - } -} \ No newline at end of file