Removing [nid:n] in nodereference autocomplete - drupal

Using the autocomplete field for a cck nodereference always displays the node id as a cryptic bracketed extension:
Page Title [nid:23]
I understand that this ensures that selections are unique in case nodes have the same title, but obviously this is a nasty thing to expose to the user.
Has anyone had any success in removing these brackets, or adding a different unique identifier?

Ultimately, you need to change the output of nodereference_autocomplete() in nodereference.module.
To do this properly, you want a custom module to cleanly override the function.
This function is defined as a menu callback, thus,
/**
* Implementation of hook_menu_alter().
*/
function custom_module_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'custom_module_new_nodereference_autocomplete';
}
Then, copy the nodereference_autocomplete function into your custom module, changing it's name to match your callback. Then change this one line:
$matches[$row['title'] ." [nid:$id]"] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
Dropping the nid reference.
$matches[$row['title']] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
I believe the identifier is purely cosmetic at this point, which means you could also change the text however you like. If it is not purely cosmetic, well, I haven't tested to see what will happen in the wrong conditions.
I always meant to identify how to do this. Thank you for motivating me with your question.

What Grayside has posted will work... as long as you don't have two nodes with the same title. In other words, if you want to do as Grayside has proposed, you need to be aware that the nid is not entirely unimportant. The nodereference_autocomplete_validate() function does two things. It checks to see if there is a node that matches, and if so, it passes the nid on, setting it to the $form_state array. If it can't find a node, it will set an error. If the nid is present, it will be used to get the node, which also is faster, the code is here:
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
if (!empty($matches)) {
// Explicit [nid:n].
list(, $title, $nid) = $matches;
if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
form_error($element[$field_key], t('%name: title mismatch. Please check your selection.', array('%name' => t($field['widget']['label']))));
}
}
This just checks to see if there is a nid and checks if that node matches with the title, if so the nid is passed on.
The 2nd option is a bit slower, but it is here errors can happen. If you follow the execution, you will see, that if will try to find a node based on title alone, and will take the first node that matches. The result of this, is that if you have two nodes with the same title, one of them will always be used. This might not be a problem for you, but the thing is, that you will never find out if this happens. Everything will work just fine and the user will think that he selected the node he wanted to. This might be the case, but he might as well have chosen the wrong node.
So in short, you can get rid of the nid in the autocomplete callback, but it has 2 drawbacks:
performance (little)
uncertainty in selecting the correct node.
So you have to think about it, before going this route. Especially, since you most likely wont be able to find the problem of the selection of the wrong nodes, should it happen. Another thing to be aware of, is that the nid showing up, also brings some valuable info to the users, a quick way to lookup the node, should they be in doubt if it is the one they want, if several nodes have similar titles.

I got Grayside's answer to work, but I had to use MENU alter, instead of the FORM alter he posted. No biggy!
function custommodule_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'fp_tweaks_nodereference_autocomplete';
}

I've found an alternative solution is to change your widget type to select list and then use the chosen module to convert your list to an autocomplete field.
This handles nodes with the same title, and actually I think the UI is better than the one provided by the autocomplete widget.

To anyone coming across this (rather old) topic by way of a google search - for Drupal 7 please consider using entityreference module and "Entity Reference" field type if possible.
You can acheive a lot more in configuration with an "Entity Reference" field. It doesn't have this problem with the nid in square brackets.

Here is the full Drupal 7 version (References 7.x-2.1) of Grayside's answer. This goes in your custom module:
/**
* Implementation of hook_menu_alter().
*/
function custom_menu_alter(&$items) {
$items['node_reference/autocomplete/%/%/%']['page callback'] = 'custom_new_node_reference_autocomplete';
}
/**
* Implementation of Menu callback for the autocomplete results.
*/
function custom_new_node_reference_autocomplete($entity_type, $bundle, $field_name, $string = '') {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
$options = array(
'string' => $string,
'match' => $instance['widget']['settings']['autocomplete_match'],
'limit' => 10,
);
$references = node_reference_potential_references($field, $options);
$matches = array();
foreach ($references as $id => $row) {
// Markup is fine in autocompletion results (might happen when rendered
// through Views) but we want to remove hyperlinks.
$suggestion = preg_replace('/<a href="([^<]*)">([^<]*)<\/a>/', '$2', $row['rendered']);
// Add a class wrapper for a few required CSS overrides.
$matches[$row['title']] = '<div class="reference-autocomplete">' . $suggestion . '</div>'; // this is the line that was modified to remove the "[nid:XX]" disambiguator
}
drupal_json_output($matches);
}

Related

Processing Drupal Node Body

I'm new to Drupal. I looked here and on google for a while before asking, but I'm sure I can't find the answer because I don't know how to ask the question.
Here is what's going on. I'm using a custom module to load certain entities and then output them in a specific format for an application to access. The problem is that the NODE BODY contains special information and media files that should be converted. My goal is to obtain the HTML output that would normally be used on this field.
// Execute an EntityFieldQuery
$result = $query->execute();
if (isset($result['node'])) {
$article_items_nids = array_keys($result['node']);
$article_items = entity_load('node', $news_items_nids);
}
// Loop through each article
foreach ($article_items as $article) {
return $article->body[LANGUAGE_NONE]['0']['value'];
}
All of this works great. The only problem is that I get things like this in the output:
[[{"type":"media","view_mode":"media_original","fid":"283","attributes":{"alt":"","class":"media-image","data-thmr":"thmr_32","height":"400","width":"580"}}]]
or
*protoss_icon*
My goal is to find a way that these items are converted just like they are when these articles are viewed normally.
I've tried doing things such as:
render(field_view_field('node', $article, 'body'));
or
render($article->body[LANGUAGE_NONE]['0']['value']);
without success. Thanks for any help, I'm learning so I don't have a complete grasp of the process drupal uses to build output.
You can try something like this (this works only with nodes not with other custom entity types):
$node = node_load($nid);
$field = field_get_items('node', $node, 'your_field_name');
$output = field_view_value('node', $node, 'your_field_name', $field[$delta]);
the field_view_value returns a renderable array for a single field value. (from drupal api documentation)

Generating doctrine slugs manually

I'm using sluggable behavior in my Symfony2 project, but now I would like to make many slugs for one page, based on different texts (current title, old title(s), users text from form input), and keep it in another table. And my question is - how to manually use doctrine extensions for any text? I can't find it anywhere. Perfect would be something like:
/* careful - it's not a real, working code! */
$sluggable = new DoctrineSluggable();
$slug = $sluggable->generate('My own text!');
echo $slug; // my-own-text
I found solution by accident here.
Code:
use Gedmo\Sluggable\Util as Sluggable;
$string = 'My own text!';
$slug = Sluggable\Urlizer::urlize($string, '-');
if(empty($slug)) // if $string is like '=))' or 'トライアングル・サービス' an empty slug will be returned, that causes troubles and throws no exception
echo 'error, empty slug!!!';
else
echo $slug;
Find the doctrine code for generating a slug here: l3pp4rd/DoctrineExtensions. Playing around with that class could do as you desire but you will probable need to create your own service to implement an easy use as you want. See the Service Container section of the docs for more details about services.
The Sluggable\Urlizer::urlize seems to replace ' with -.
I had to use Sluggable\Urlizer::transliterate to be closer to the SluggableListener behaviour.

Drupal 7 node_save not saving computed fields during cron

I have a Drupal content type which contains a number of computed fields. Some (but not all) items are being added to this content type via a cron-triggered RSS feed importer. I'm trying to trigger computed field generation for new items in hook_cron. The following code grabs all items that haven't been tagged as 'submitted', loads and re-saves the node, and then marks the node as 'submitted'.
$query = db_select('node', 'n');
$query->fields('n', array('nid'));
$table_alias = $query->join('field_data_field_submitted', 'r', 'n.nid = r.entity_id AND r.field_submitted_value = 0');
$result = $query->execute();
foreach ($result as $record){
$q = $record->nid;
$n = node_load($q);
node_save($n);
$query = db_update('field_data_field_submitted')
->fields(array('field_submitted_value' => 1))
->condition('entity_id', $q)
->execute();
}
This code works the way I expect it to if I call it from a module-generated page (created using hook_menu with a page callback function). Nodes are resubmitted, and the computed field data is generated. When I put this code in my hook_cron function, the query works, it loops through the records and updates the 'submitted' value, but the computed fields are not computed. I'm confused as to why this would not get triggered in cron. Any help?
Doh! Finally realized that this was completely my own doing. Due to the nature of this content type, where we allow anonymous users to create new content, but explicitly do not trigger the computed fields when they create the content (long story, but short form is that authenticated users then verify & enhance this content, which is where the computed fields come in). So, as I was setting up the initial content, I disabled the computed fields for anonymous users (if $user->uid > 0), and completely forgot about that. Once I tweaked that logic to allow computed fields to be processed on import (triggering it with a field that has a value for the imported content, but not for other content), the problem was solved.
The cron run has access to the full bootstrap so there's no logical reason why your code would produce different results in that context.
That said, you're only updating the field_data_field_submitted table when you also need to update the field_revision_field_submitted table, so that might somehow account for the discrepancy.
Drupal provides an API for the field system so that these sorts of problems can be avoided completely. The same code you've used, rewritten the 'Drupal' way, would be:
$query = new EntityFieldQuery;
$query->entityCondition('entity_type', 'node')
->fieldCondition('field_submitted', 'value', 0);
$results = $query->execute();
if (!empty($results['node'])) {
$nodes = node_load_multiple(array_keys($results['node']));
foreach ($nodes as $node) {
$node->field_submitted[$node->language][0]['value'] = 1;
node_save($node);
}
}
I can't think of a good reason why the above code would fail on cron either so it might be worth giving it a whirl.

Drupal: hook_search only for a content type

I would like to build a custom search module in Drupal 6 for searching through CCK. I need the user to search between his nodes (node.uid=x) and of a certain type (type='xyz'). I think I have to implement hook_search but I don't know where to put my filters. Can anyone help me?
You already accepted an answer (which is probably the best option for you), but there are a few other ways to accomplish this.
IIRC, the Custom Search module will work for what you want.
You can copy the stock hook_search function to a custom module and modify the query. You can do something like this:
// ...
case 'search':
// Build matching conditions
list($join1, $where1) = _db_rewrite_sql();
$arguments1 = array();
$conditions1 = 'n.status = 1';
// NEW BIT START
$allowed = array(
'content_type_1',
'content_type_2',
'content_type_3',
);
$types = array();
foreach ($allowed as $t) {
$types[] = "n.type = '%s'";
$arguments1[] = $t;
}
$conditions1 .= ' AND ('. implode(' OR ', $types) .')';
$keys = search_query_insert($keys, 'type');
// NEW BIT END
This replaces the bit that extracts the type from the actual query string.
You would have to add in the bit to restruct to a particular n.uid. I have been using this method lately, rather that Custom Search, because it simpler from the user's perspective.
HTH
You might try creating a Views with an exposed filter, it's the absolute easiest way to implementing your idea.
Also you can try use CCK Facets. But Views - of course simple.

Drupal 6 getting the node title from a submitted form

I am using form_alter to edit the submit function when editing content. In my custom function I wish to edit a custom message to the screen with the title name. I thought a way I could do this is something as follows
function mymodule_myfunction(&$form) {
drupal_set_message(t('Some text ' . $form['#node']->title));
}
The title is not being joined to the 'Some text'
I am calling my function by using the following line in my form_alter:
$form['#submit'][] = 'mymodule_myfunction';
All submit functions get two parameters passed to them: $form, which is the final form array after all of the adjustments for hook_form_alter and the like, and $form_state which among other values contains the submitted values, which have been cleaned and checked for ranges. (For instance, if you have three items in a select box, the data in $form_state['values'] already has made sure that the value for that input is one of the three legal values.)
Generally, you shouldn't use $form['#post'] - it's not part of the published way to get at values, and an update to the core to handle some problem with FAPI could conceivably break your code.
Try this:
function mymodule_myfunction($form, &$form_state) {
drupal_set_message(t('Some Message #title'),
array('#title' => $form_state['values']['title'])));
}
Note the corrected use of the t() function - the intent of that function is to allow other users to translate text, and so by using 'Some Message #title' the translator knows more about what is going on. Additionally you get the advantage that text fed through the t function in this way also is fed through check_plain(), which prevents someone from doing something malicious with the input.
DKinzer recommended using dsm($form)to see the variables. The Node title is not populated. It can be found in the Post array. The following line allowed me to do what I wanted.
drupal_set_message(t('Some Text '.$form['#post']['title']));
Try changing the signature of your
function mymodule_myfunction(&$form) {
drupal_set_message(t('Some text ' . $form['#node']->title));
}
To:
function mymodule_myfunction($form, &$form_state) {
drupal_set_message(t('Some text ' . $form['#node']->title));
}
Also try installing the devel module so you can do things like
dsm($form);
dsm($form_state);
And see exactly what you are dealing with.
Also, if all you want to do is give a message when a new node of type 'X' is created a better way is to use hook_nodeapi;
It could look something like this;
function modulename_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
if ($op == 'insert' && $node->type == 'my node type') {
drupal_set_message($node-title . ' is cool.');
}
}

Resources