multiple autocomplete
| Project: | Addnode |
| Version: | 5.x-2.x-dev |
| Component: | User interface |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | active |
Jump to:
I think that autocomplete would be a great addition, as it may have already been mentioned.
One method I think that would set this apart and blow it's usability wide open is a "Add To Reference" autocomplete field.
Example:
Have an autocomplete field, and an "attached references" field (like addnode_beta, multi select)
beside the autocomplete field, have an "add" button.
User types in title, if it exists and they hit add, it goes to attached list (like beta multi select)
if it doesn't exist, then it opens the add reference modal as per usual, fills in title with the one entered in autocomplete, user submits new node, and it's added to attached (like beta multi select).
My understanding of code unfortunately isn't that great, but pretty much the only functionality your adding to the beta multi select, is autocomplete widget that pops up the appropriate window when no match found.
Unfortunately with the 2.1 version from what I see, that "attached reference" box has dissapeared, making this entire idea, less plausible.

#1
I've done some searching and found a module that allows autocomplete on nodereferences with multiples. When you add a reference, another box becomes available, so there's no limit to what you can add.
It had it's own node creation functions, and imported info from wikipedia. I've gone ahead and stripped the wikipedia code, along with the autocreation that was built in.
This version is great because it removes the unpleasant NID that's often associated.
I believe it wouldn't be too hard to lock into node_assist, but unfortunately I'm at a loss of how to do this. I tried moving over some of the functions, but ran into issues. Plus I'm using a frankenstein of your beta and new release. (I liked the dual boxes)
We would want to do:
copy over theme(node_assist_link)
and make it within the module.
If input title doesn't exist, call created node_assist_link loading pop-up.
Auto populate title in addnode form.
Since we already typed in the name, we shouldn't have to worry about populating the autocomplete field with the new entry like we do the "Available References" in addnode.
Here's my altered code for the multi autocomplete, with removed ID. THIS IS NOT MY MODULE, I just hacked it to pieces.
It's from a preview (not even alpha) that I found by googling. In it's downloaded form it was completely unusable choking on wiki imports and more. Another reason I stripped it all, in it's current state it can be installed, and used without issue. If you can help me tie into the addnode api that would be awesome! This feature has been asked for on the forums for a very very long time.
<?php
/**
* @file
* Defines a new widget for the node reference cck type
* Based on the Autocomplete widget, this widget creates a node if it doesn't exist <-Removed
* Attempts to pull some text from wikipedia for the newly created node. <-Removed
*/
/**
* Implementation of hook_widget_info().
*/
function autocreate_noderef_widget_info() {
return array(
'autocreate_noderef_autocomplete' => array(
'label' => 'Autocreate Autocomplete Text Field',
'field types' => array('nodereference'),
),
);
}
/**
* Implementation of hook_widget().
*/
function autocreate_noderef_widget($op, &$node, $field, &$items) {
switch ($op) {
case 'prepare form values':
foreach ($items as $delta => $item) {
if (!empty($items[$delta]['nid'])) {
$items[$delta]['default node_name'] = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $items[$delta]['nid']));
//$items[$delta]['default node_name'] .= ' [nid:'. $items[$delta]['nid'] .']';
}
}
break;
case 'form':
$form = array();
$form[$field['field_name']] = array('#tree' => TRUE);
if ($field['multiple']) {
$form[$field['field_name']]['#type'] = 'fieldset';
$form[$field['field_name']]['#description'] = t($field['widget']['description']);
$delta = 0;
foreach ($items as $item) {
if ($item['nid']) {
$form[$field['field_name']][$delta]['node_name'] = array(
'#type' => 'textfield',
'#title' => ($delta == 0) ? t($field['widget']['label']) : '',
'#autocomplete_path' => 'autocreate_noderef/autocomplete/'. $field['field_name'],
'#default_value' => $item['default node_name'],
'#required' => ($delta == 0) ? $field['required'] : FALSE,
);
$delta++;
}
}
foreach (range($delta, $delta + 2) as $delta) {
$form[$field['field_name']][$delta]['node_name'] = array(
'#type' => 'textfield',
'#title' => ($delta == 0) ? t($field['widget']['label']) : '',
'#autocomplete_path' => 'autocreate_noderef/autocomplete/'. $field['field_name'],
'#default_value' => '',
'#required' => ($delta == 0) ? $field['required'] : FALSE,
);
}
}
else {
$form[$field['field_name']][0]['node_name'] = array(
'#type' => 'textfield',
'#title' => t($field['widget']['label']),
'#autocomplete_path' => 'autocreate_noderef/autocomplete/'. $field['field_name'],
'#default_value' => $items[0]['default node_name'],
'#required' => $field['required'],
'#description' => t($field['widget']['description']),
);
}
return $form;
case 'validate':
foreach ($items as $delta => $item) {
$error_field = $field['field_name'] .']['. $delta .'][node_name';
if (!empty($item['node_name'])) {
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
if (!empty($matches)) {
// explicit nid
list(, $title, $nid) = $matches;
if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
form_set_error($error_field, t('%name : Title mismatch. Please check your selection.'), array('%name' => t($field['widget']['label'])));
}
}
}
}
return;
case 'process form values':
global $user;
foreach ($items as $delta => $item) {
$nid = 0;
if (!empty($item['node_name'])) {
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
if (!empty($matches)) {
// explicit nid
$nid = $matches[2];
}
else {
// no explicit nid - There's not going to be - I removed nid from the autocomplete
// 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...
$nids = _autocreate_noderef_potential_references($field, FALSE, $item['node_name'], TRUE);
$nid = (!empty($nids)) ? array_shift(array_keys($nids)) : 0;
}
}
// Remove the widget's data representation so it isn't saved.
unset($items[$delta]['node_name']);
if (!empty($nid)) {
$items[$delta]['nid'] = $nid;
$items[$delta]['error_field'] = $field['field_name'] .']['. $delta .'][node_name';
}
elseif ($delta > 0) {
// Don't save empty fields when they're not the first value (keep '0' otherwise)
unset($items[$delta]);
}
}
break;
}
}
/**
* Fetch an array of all candidate referenced nodes, for use in presenting the selection form to the user.
*/
function _autocreate_noderef_potential_references($field, $return_full_nodes = FALSE, $string = '', $exact_string = false) {
if (module_exists('views') && isset($field['advanced_view']) && $field['advanced_view'] != '--' && ($view = views_get_view($field['advanced_view']))) {
// advanced field : referenceable nodes defined by a view
// let views.module build the query
// arguments for the view
$view_args = array();
if (isset($field['advanced_view_args'])) {
// TODO: Support Tokens using token.module ?
$view_args = array_map(trim, explode(',', $field['advanced_view_args']));
}
if (isset($string)) {
views_view_add_filter($view, 'node', 'title', $exact_string ? '=' : 'contains', $string, null);
}
// we do need title field, so add it if not present (unlikely, but...)
$has_title = array_reduce($view->field, create_function('$a, $b', 'return ($b["field"] == "title") || $a;'), false);
if (!$has_title) {
views_view_add_field($view, 'node', 'title', '');
}
views_load_cache();
views_sanitize_view($view);
// make sure the fields get included in the query
$view->page = true;
$view->page_type = 'list';
// make sure the query is not cached
unset($view->query); // Views 1.5-
$view->is_cacheable = FALSE; // Views 1.6+
$view_result = views_build_view('result', $view, $view_args);
$result = $view_result['result'];
}
else {
// standard field : referenceable nodes defined by content types
// build the appropriate query
$related_types = array();
$args = array();
if (isset($field['referenceable_types'])) {
foreach ($field['referenceable_types'] as $related_type) {
if ($related_type) {
$related_types[] = " n.type = '%s'";
$args[] = $related_type;
}
}
}
$related_clause = implode(' OR ', $related_types);
if (!count($related_types)) {
return array();
}
if (isset($string)) {
$string_clause = $exact_string ? " AND n.title = '%s'" : " AND n.title LIKE '%%%s%'";
$related_clause = "(". $related_clause .")". $string_clause;
$args[] = $string;
}
$result = db_query(db_rewrite_sql("SELECT n.nid, n.title AS node_title, n.type AS node_type FROM {node} n WHERE ". $related_clause ." ORDER BY n.title, n.type"), $args);
}
if (db_num_rows($result) == 0) {
return array();
}
$rows = array();
while ($node = db_fetch_object($result)) {
if ($return_full_nodes) {
$rows[$node->nid] = $node;
}
else {
$rows[$node->nid] = $node->node_title;
}
}
return $rows;
}
/**
* Retrieve a pipe delimited string of autocomplete suggestions
*/
function autocreate_noderef_autocomplete($field_name, $string = '') {
$fields = content_fields();
$field = $fields[$field_name];
$matches = array();
foreach (_autocreate_noderef_potential_references($field, TRUE, $string) as $row) {
$matches[$row->node_title] = _autocreate_noderef_item($field, $row, TRUE);
//$matches[$row->node_title .' [nid:'. $row->nid .']'] = _autocreate_noderef_item($field, $row, TRUE);
}
print drupal_to_js($matches);
exit();
}
function _autocreate_noderef_item($field, $item, $html = false) {
if (module_exists('views') && isset($field['advanced_view']) && $field['advanced_view'] != '--' && ($view = views_get_view($field['advanced_view']))) {
$output = theme('autocreate_noderef_item_advanced', $item, $view);
if (!$html) {
// Views theming runs check_plain (htmlentities) on the values.
// We reverse that with html_entity_decode.
$output = html_entity_decode(strip_tags($output), ENT_QUOTES);
}
}
else {
$output = theme('autocreate_noderef_item_simple', $item);
}
return $output;
}
function theme_autocreate_noderef_item_advanced($item, $view) {
$fields = _views_get_fields();
$item_fields = array();
foreach ($view->field as $field) {
$value = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $item, $view);
// remove link tags (ex : for node titles)
$value = preg_replace('/<a[^>]*>(.*)<\/a>/iU', '$1', $value);
if (!empty($value)) {
$item_fields[] = "<span class='view-field view-data-$field[queryname]'>$value</span>";;
}
}
$output = implode(' - ', $item_fields);
$output = "<span class='view-item view-item-$view->name'>$output</span>";
return $output;
}
function theme_autocreate_noderef_item_simple($item) {
return check_plain($item->node_title);
}
/**
* Provide a list of users to filter on.
*/
function _autocreate_noderef_filter_handler($op, $filterinfo) {
$options = array(0 => t('<empty>'));
$options = $options + _autocreate_noderef_potential_references($filterinfo['extra']['field']);
return $options;
}
If I figure out anything I will be sure to post it. I will also let the other developer know that node_assist is a viable solution for node creation (if he wants to steer away from wikipedia fetching that is).