diff --git node_reference/node_reference.module node_reference/node_reference.module
index 11db51b..c4e8d51 100644
--- node_reference/node_reference.module
+++ node_reference/node_reference.module
@@ -29,7 +29,13 @@ function node_reference_field_info() {
'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()),
+ 'settings' => array(
+ 'referenceable_types' => array(),
+ 'views' => array(
+ 'view' => '',
+ 'view_args' => '',
+ ),
+ ),
// 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',
@@ -79,6 +85,42 @@ function node_reference_field_settings_form($field, $instance, $has_data) {
: array(),
'#options' => array_map('check_plain', node_type_get_names()),
);
+
+ if (module_exists('views')) {
+ $form['views'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Views - Nodes that can be referenced'),
+ '#collapsible' => TRUE,
+ '#collapsed' => !isset($settings['views']['view']) || $settings['views']['view'] == '',
+ );
+
+ $views_options = references_get_views('node');
+ if ($views_options) {
+ $views_options = array('' => '<' . t('none') . '>') + $views_options;
+ $form['views']['view'] = array(
+ '#type' => 'select',
+ '#title' => t('View used to select the nodes'),
+ '#options' => $views_options,
+ '#default_value' => isset($settings['views']['view']) ? $settings['views']['view'] : '',
+ '#description' => t('
Choose the "Views module" view that selects the nodes that can be referenced.
Note:
') .
+ t('- This will discard the "Content types" settings above. Use the view\'s "filters" section instead.
- Use the view\'s "fields" section to display additional informations about candidate nodes on node creation/edition form.
- Use the view\'s "sort criteria" section to determine the order in which candidate nodes will be displayed.
'),
+ );
+ $form['views']['view_args'] = array(
+ '#type' => 'textfield',
+ '#title' => t('View arguments'),
+ '#default_value' => isset($settings['views']['view_args']) ? $settings['views']['view_args'] : '',
+ '#required' => FALSE,
+ '#description' => t('Provide a comma separated list of arguments to pass to the view.'),
+ );
+ }
+ else {
+ $form['views']['no_view_help'] = array(
+ '#value' => t('The list of nodes that can be referenced can be based on a "Views module" view but no appropriate views were found.
Note:
') .
+ t('- This will discard the "Content types" settings above. Use the view\'s "filters" section instead.
- Use the view\'s "fields" section to display additional informations about candidate nodes on node creation/edition form.
- Use the view\'s "sort criteria" section to determine the order in which candidate nodes will be displayed.
'),
+ );
+ }
+ }
+
return $form;
}
@@ -561,7 +603,7 @@ function _node_reference_options($field) {
$options = array();
foreach ($references as $key => $value) {
- $options[$key] = $value['title'];
+ $options[$key] = $value['rendered'];
}
return $options;
@@ -612,7 +654,14 @@ function _node_reference_potential_references($field, $string = '', $match = 'co
. ($string !== '' ? $string : implode('-', $ids))
. ':' . $limit;
if (!isset($results[$cid])) {
- $references = _node_reference_potential_references_standard($field, $string, $match, $ids, $limit);
+ $references = FALSE;
+ if (module_exists('views') && !empty($field['settings']['views']['view'])) {
+ $references = _node_reference_potential_references_views($field, $string, $match, $ids, $limit);
+ }
+
+ if ($references === FALSE) {
+ $references = _node_reference_potential_references_standard($field, $string, $match, $ids, $limit);
+ }
// Store the results.
$results[$cid] = !empty($references) ? $references : array();
@@ -622,6 +671,89 @@ function _node_reference_potential_references($field, $string = '', $match = 'co
}
/**
+ * Helper function for _nodereference_potential_references().
+ *
+ * Case of Views-defined referenceable nodes.
+ */
+function _node_reference_potential_references_views($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) {
+ list($view_name, $display_name) = explode(':', $field['settings']['views']['view']);
+
+ // Check that the view is valid and the display still exists.
+ $view = views_get_view($view_name);
+ if (!$view || $view->base_table != 'node' || !isset($view->display[$display_name])) {
+ return FALSE;
+ }
+
+ // Collect arguments if any.
+ $view_args = array();
+ if (!empty($field['settings']['views']['view_args'])) {
+ $view_args = array_map('trim', explode(',', $field['settings']['views']['view_args']));
+ }
+
+ // Temporary backwards compatibility for fields migrated from CCK D6: accept
+ // 'default' display.
+ // @todo Test
+ // @todo Add the corresponding migration code in hook_content_migrate_instance_alter().
+ if ($display_name == 'default') {
+ $display_name = $view->add_display('references');
+ $view->display[$display_name]->set_option('style_plugin', 'references_plugin_style');
+ }
+
+ $view->set_display($display_name);
+
+ // @todo From merlinofchaos on IRC : arguments using summary view can defeat
+ // the style setting.
+ // We might also need to check if there's an argument, and set its
+ // style_plugin as well.
+
+ // @todo : isn't there a way to force this ?
+ $view->display_handler->set_option('row_plugin', 'fields');
+
+ // Additional options to let references_plugin_display::query()
+ // narrow the results.
+ $options = array(
+ 'ids' => $ids,
+ 'title_field' => 'title',
+ 'string' => $string,
+ 'match' => $match,
+ );
+ $view->display_handler->set_option('references_options', $options);
+
+ // We do need the title field, so add it if not present (unlikely, but...)
+ $fields = $view->get_items('field', $display_name);
+ if (!isset($fields['title'])) {
+ // @todo : exclude from display ? (at least, no 'Title' label)
+ $view->add_item($display_name, 'field', 'node', 'title');
+ }
+
+ // Limit result set size.
+ $limit = isset($limit) ? $limit : 0;
+ // @todo check this ('type' => 'some' ??)
+ $view->display_handler->set_option('pager', array('type' => 'some', 'options' => array('items_per_page' => $limit)));
+
+ // If not set, make all fields inline and define a separator.
+ // @todo doesn't work ?
+ $row_options = $view->display_handler->get_option('row_options');
+ if (empty($row_options['inline'])) {
+ $row_options['inline'] = drupal_map_assoc(array_keys($view->get_items('field', $display_name)));
+ }
+ if (empty($row_options['separator'])) {
+ $row_options['separator'] = '-';
+ }
+ $view->display_handler->set_option('row_options', $row_options);
+
+ // Make sure the query is not cached
+ $view->is_cacheable = FALSE;
+
+ // Get the results.
+ $results = $view->execute_display($display_name, $view_args);
+
+ // @todo : remove links ?
+
+ return $results;
+}
+
+/**
* Helper function for _node_reference_potential_references().
*
* List of referenceable nodes defined by content types.
diff --git references.info references.info
index c5a0dfe..b6cc1c5 100644
--- references.info
+++ references.info
@@ -6,3 +6,5 @@ dependencies[] = field
dependencies[] = options
files[] = views/references_handler_relationship.inc
files[] = views/references_handler_argument.inc
+files[] = views/references_plugin_display.inc
+files[] = views/references_plugin_style.inc
diff --git references.module references.module
index 550b9a6..4e75bdf 100644
--- references.module
+++ references.module
@@ -10,6 +10,78 @@
*/
function references_views_api() {
return array(
- 'api' => '3.0',
+ 'api' => 3,
+ 'path' => drupal_get_path('module', 'references') . '/views',
);
}
+
+/**
+ * Implements hook_views_plugins().
+ *
+ * Defines some plugins used by the Views modes for
+ * user_reference.
+ */
+function references_views_plugins() {
+ $plugins = array(
+ 'display' => array(
+ 'references' => array(
+ // @todo Why doesn't this display appear in the main Views list ?
+ 'title' => 'References',
+ 'help' => 'Selects referenceable entities for a reference field (node_reference, user_reference...)',
+ 'handler' => 'references_plugin_display',
+ 'uses hook menu' => FALSE,
+ 'use ajax' => FALSE,
+ 'use pager' => FALSE,
+ 'accept attachments' => FALSE,
+ // Custom property, used with views_get_applicable_views() to retrieve
+ // all views with a 'References' display.
+ 'references display' => TRUE,
+ // @todo : is there a way to prevent having a 'view title' ?
+ ),
+ ),
+ 'style' => array(
+ 'references_style' => array(
+ 'title' => 'References list',
+ 'help' => '@todo Returns the view as a PHP array of names + rendered rows.',
+ 'handler' => 'references_plugin_style',
+ 'theme' => 'views_view_unformatted',
+ 'uses row plugin' => TRUE,
+ 'uses fields' => TRUE,
+// @todo ?
+// 'uses row class' => TRUE,
+// 'uses grouping' => TRUE,
+ // @todo : 'Feed' displays get automatically created with the correct
+ // overriden style.
+ 'type' => 'references',
+ 'even empty' => TRUE,
+ ),
+ ),
+ );
+ return $plugins;
+}
+
+/**
+ * Retrieves the list of views
+ * @param $entity_type
+ * The entity type.
+ *
+ * @return
+ * @todo return format is not great
+ */
+function references_get_views($entity_type) {
+ // Filter views that contain a 'references' display.
+ $views = views_get_applicable_views('references display');
+
+ // Filter views that list the entity type we want.
+ $entity_info = entity_get_info($entity_type);
+ foreach ($views as $data) {
+ list($view, $display_id) = $data;
+ if ($view->base_table == $entity_info['base table']) {
+ $id = $view->name . ':' . $display_id;
+ $display_title = $view->name ." ". strtolower($view->display[$display_id]->display_title) . ' ['. $id . ']';
+ $result[$id] = $display_title;
+ }
+ }
+
+ return $result;
+}
diff --git user_reference/user_reference.module user_reference/user_reference.module
index fe442c7..7175cef 100644
--- user_reference/user_reference.module
+++ user_reference/user_reference.module
@@ -27,7 +27,14 @@ function user_reference_field_info() {
'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()),
+ 'settings' => array(
+ 'referenceable_roles' => array(),
+ 'referenceable_status' => array(),
+ 'views' => array(
+ 'view' => '',
+ 'view_args' => '',
+ ),
+ ),
'default_widget' => 'user_reference_autocomplete',
'default_formatter' => 'user_reference_default',
// Support hook_entity_property_info() from contrib "Entity API".
@@ -82,6 +89,42 @@ function user_reference_field_settings_form($field, $instance, $has_data) {
: array(1),
'#options' => array(1 => t('Active'), 0 => t('Blocked')),
);
+
+ if (module_exists('views')) {
+ $form['views'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Views - Users that can be referenced'),
+ '#collapsible' => TRUE,
+ '#collapsed' => !isset($settings['views']['view']) || $settings['views']['view'] == '',
+ );
+
+ $views_options = references_get_views('users');
+ if ($views_options) {
+ $views_options = array('' => '<' . t('none') . '>') + $views_options;
+ $form['views']['view'] = array(
+ '#type' => 'select',
+ '#title' => t('View used to select the users'),
+ '#options' => $views,
+ '#default_value' => isset($settings['views']['view']) ? $settings['views']['view'] : '',
+ '#description' => t('Choose the "Views module" view that selects the users that can be referenced.
Note:
') .
+ t('- This will discard the "Referenceable Roles" and "Referenceable Status" settings above. Use the view\'s "filters" section instead.
- Use the view\'s "fields" section to display additional informations about candidate users on user creation/edition form.
- Use the view\'s "sort criteria" section to determine the order in which candidate users will be displayed.
'),
+ );
+ $form['views']['view_args'] = array(
+ '#type' => 'textfield',
+ '#title' => t('View arguments'),
+ '#default_value' => isset($settings['views']['view_args']) ? $settings['views']['view_args'] : '',
+ '#required' => FALSE,
+ '#description' => t('Provide a comma separated list of arguments to pass to the view.'),
+ );
+ }
+ else {
+ $form['views']['no_view_help'] = array(
+ '#value' => t('The list of user that can be referenced can be based on a "Views module" view but no appropriate views were found.
Note:
') .
+ t('- This will discard the "Referenceable Roles" and "Referenceable Status" settings above. Use the view\'s "filters" section instead.
- Use the view\'s "fields" section to display additional informations about candidate users on user creation/edition form.
- Use the view\'s "sort criteria" section to determine the order in which candidate users will be displayed.
'),
+ );
+ }
+ }
+
return $form;
}
@@ -397,7 +440,7 @@ function _user_reference_options($field) {
$options = array();
foreach ($references as $key => $value) {
- $options[$key] = $value['title'];
+ $options[$key] = $value['rendered'];
}
return $options;
@@ -443,7 +486,14 @@ function _user_reference_potential_references($field, $string = '', $match = 'co
. ($string !== '' ? $string : implode('-', $ids))
. ':' . $limit;
if (!isset($results[$cid])) {
- $references = _user_reference_potential_references_standard($field, $string, $match, $ids, $limit);
+ $references = FALSE;
+ if (module_exists('views') && !empty($field['settings']['views']['view'])) {
+ $references = _user_reference_potential_references_views($field, $string, $match, $ids, $limit);
+ }
+
+ if ($references === FALSE) {
+ $references = _user_reference_potential_references_standard($field, $string, $match, $ids, $limit);
+ }
// Store the results.
$results[$cid] = !empty($references) ? $references : array();
@@ -455,6 +505,88 @@ function _user_reference_potential_references($field, $string = '', $match = 'co
/**
* Helper function for _user_reference_potential_references().
*
+ * Case of Views-defined referenceable users.
+ */
+function _user_reference_potential_references_views($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) {
+ list($view_name, $display_name) = explode(':', $field['settings']['views']['view']);
+
+ // Check that the view is valid and the display still exists.
+ $view = views_get_view($view_name);
+ if (!$view || $view->base_table != 'node' || !isset($view->display[$display_name])) {
+ return FALSE;
+ }
+
+ // Collect arguments if any.
+ $view_args = array();
+ if (!empty($field['settings']['views']['view_args'])) {
+ $view_args = array_map('trim', explode(',', $field['settings']['views']['view_args']));
+ }
+
+ // Temporary backwards compatibility for fields migrated from CCK D6: accept
+ // 'default' display.
+ // @todo Test
+ // @todo Add the corresponding migration code in hook_content_migrate_instance_alter().
+ if ($display_name == 'default') {
+ $display_name = $view->add_display('references');
+ $view->display[$display_name]->set_option('style_plugin', 'references_plugin_style');
+ }
+
+ $view->set_display($display_name);
+ // @todo From merlinofchaos on IRC : arguments using summary view can defeat
+ // the style setting.
+ // We might also need to check if there's an argument, and set its
+ // style_plugin as well.
+
+ // @todo : isn't there a way to force this ?
+ $view->display_handler->set_option('row_plugin', 'fields');
+
+ // Additional options to let references_plugin_display::query()
+ // narrow the results.
+ $options = array(
+ 'ids' => $ids,
+ 'title_field' => 'name',
+ 'string' => $string,
+ 'match' => $match,
+ );
+ $view->display_handler->set_option('references_options', $options);
+
+ // We do need the name field, so add it if not present (unlikely, but...)
+ $fields = $view->get_items('field', $display_name);
+ if (!isset($fields['name'])) {
+ // @todo : exclude from display ? (at least, no 'Title' label)
+ $view->add_item($display_name, 'field', 'users', 'name');
+ }
+
+ // Limit result set size.
+ $limit = isset($limit) ? $limit : 0;
+ // @todo check this ('type' => 'some' ??)
+ $view->display_handler->set_option('pager', array('type' => 'some', 'options' => array('items_per_page' => $limit)));
+
+ // If not set, make all fields inline and define a separator.
+ // @todo doesn't work ?
+ $row_options = $view->display_handler->get_option('row_options');
+ if (empty($row_options['inline'])) {
+ $row_options['inline'] = drupal_map_assoc(array_keys($view->get_items('field', $display_name)));
+ }
+ if (empty($row_options['separator'])) {
+ $row_options['separator'] = '-';
+ }
+ $view->display_handler->set_option('row_options', $row_options);
+
+ // Make sure the query is not cached
+ $view->is_cacheable = FALSE;
+
+ // Get the results.
+ $results = $view->execute_display($display_name, $view_args);
+
+ // @todo : remove links ?
+
+ return $results;
+}
+
+/**
+ * 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) {
diff --git views/references_plugin_display.inc views/references_plugin_display.inc
new file mode 100644
index 0000000..c0e6940
--- /dev/null
+++ views/references_plugin_display.inc
@@ -0,0 +1,63 @@
+view->render($this->display->id);
+ }
+
+ function render() {
+ if (!empty($this->view->result) || !empty($this->view->style_plugin->definition['even empty'])) {
+ return $this->view->style_plugin->render($this->view->result);
+ }
+ return '';
+ }
+
+ function uses_exposed() {
+ return FALSE;
+ }
+
+ function query() {
+ // Make sure the id field is included in the results, and save its alias
+ // so that references_plugin_style can retrieve it.
+ $this->id_field_alias = $this->view->query->add_field($this->view->base_table, $this->view->base_field);
+
+ if ($options = $this->get_option('references_options')) {
+ // Restrict on the incoming string, or incoming ids.
+ if ($options['string'] !== '') {
+ switch ($options['match']) {
+ case 'equals':
+ $operator = '=';
+ $value = $options['string'];
+ break;
+
+ case 'starts_with':
+ $operator = 'LIKE';
+ $value = db_like($options['string']) . '%';
+ break;
+
+ case 'contains':
+ default:
+ $operator = 'LIKE';
+ $value = '%' . db_like($options['string']) . '%';
+ break;
+ }
+
+ $table_alias = $this->view->query->ensure_table($this->view->base_table);
+ $this->view->query->add_where(NULL, $table_alias . '.' . $options['title_field'], $value, $operator);
+ }
+ elseif ($options['ids']) {
+ $table_alias = $this->view->query->ensure_table($this->view->base_table);
+ $this->view->query->add_where(NULL, $table_alias . '.' . $this->view->base_field, $options['ids'], 'IN');
+ }
+ }
+ }
+}
diff --git views/references_plugin_style.inc views/references_plugin_style.inc
new file mode 100644
index 0000000..fa78af2
--- /dev/null
+++ views/references_plugin_style.inc
@@ -0,0 +1,42 @@
+display->handler->get_option('references_options')) {
+ // Group the rows according to the grouping field, if specified.
+ $sets = $this->render_grouping($this->view->result, $this->options['grouping']);
+
+ $title_field = $references_options['title_field'];
+ $title_field_alias = $this->view->field[$title_field]->field_alias;
+ $id_field_alias = $this->display->handler->id_field_alias;
+
+ // @todo We don't display grouping info for now. Could be useful for select
+ // widget, though.
+ $results = array();
+ $this->view->row_index = 0;
+ foreach ($sets as $title => $records) {
+ foreach ($records as $label => $row) {
+ $results[$row->{$id_field_alias}] = array(
+ // @todo : should this use $this->get_value() ?
+ 'title' => $row->{$title_field_alias},
+ 'rendered' => $this->row_plugin->render($row),
+ );
+ $this->view->row_index++;
+ }
+ }
+ unset($this->view->row_index);
+
+ return $results;
+ }
+ else {
+ // No 'references_options' : we are not being rendered through
+ // _*reference_potential_references_views() ('preview', for instance).
+ // Just display the HTML.
+ return parent::render();
+ }
+ }
+}