Drupal Taxonomy Block, Vocabulary Listing - drupal

I looking for the best way to implement a block that lists all terms of a certain vocabulary. Each term should link to page that lists all nodes associated with that term. Any help would be greatly appreciated. Thanks!

See here for a great tutorial to achieve exactly what you want
http://chrisshattuck.com/blog/how-add-block-menu-tags-or-other-taxonomy-terms-drupal-site

The easiest way to approach this would probably be to use Views, and simply create a new view of the type "term". Here's a quick example which assumes that you have some basic familiarity with the Views UI:
Visit Views > Add (build/views/add), give your new view a name, and select Term from the "View type" radio buttons.
On the next page, start by adding a Taxonomy: Vocabulary filter and selecting your vocabulary in the filter settings.
Add a Taxonomy: Term field and enable the Link this field to its taxonomy term page option in the field settings. You might also want to remove the field's label, since this is just a simple listing.
You probably want your display to display all terms in your vocabulary, so change the "Items to display" 0 (unlimited). By default, new views only display 10 items at a time.
Check out the Live preview below to see if it's outputting what you need.
Add a new Block display using the dropdown on the left side of the Views UI.
Give your new block a name in the "Block settings" area. This is the description that will appear on Drupal's block admin page.
Save your view and visit admin/build/block to place and configure your block.
It's worth noting that Views does indeed have some overhead, but in my experience, its flexibility and ease-of-use far outweigh the relatively minor performance hit.
If you'd like to avoid using Views, you could write a pretty simple custom module using hook_block() and adapting http://drupal.org/node/247472. If you'd like, I can edit this answer with an example module based on that.

(Posting this as another answer, since this is a different approach than my first answer.)
As I mentioned above, here's another approach involving a custom module based on the code at http://drupal.org/node/247472. You could also just drop that code into a custom block with the "PHP" input format selected, but that's generally considered to be bad practice.
Add a new folder in sites/all/modules called vocabulary_block. Customize and add the following two files:
vocabulary_block.module
<?php
/**
* #file
* Exposes a block with a simple list of terms from [vocabulary].
* Each term is linked to its respective term page.
*/
/**
* Lists terms for a specific vocabulary without descriptions.
* Each term links to the corresponding /taxonomy/term/tid listing page.
*/
function vocabulary_block_get_terms($vid) {
$items = array();
$terms = taxonomy_get_tree($vid, 0, -1, 1);
foreach($terms as $term) {
$items[]= l($term->name, "taxonomy/term/$term->tid");
}
if(count($items)) {
return theme('item_list', $items);
}
}
/**
* Implementation of hook_block().
*/
function vocabulary_block_block($op = 'list', $delta = 0, $edit = array()) {
switch ($op) {
case 'list':
$blocks[0]['info'] = t('List of [vocabulary] terms');
return $blocks;
case 'view':
if ($delta == 0) {
$vid = 43;
$block['subject'] = t('[Vocabulary]');
$block['content'] = vocabulary_block_get_terms($vid);
}
return $block;
}
}
vocabulary_block.info
name = Vocabulary Block
description = Exposes a block with a simple list of terms from [vocabulary]. Each term is linked to its respective term page.
; Core version (required)
core = 6.x
; Package name (see http://drupal.org/node/101009 for a list of names)
package = Taxonomy
; Module dependencies
dependencies[] = taxonomy
Notes
Be sure to change $vid = 43; to
reflect the ID of the vocabulary that
you'd like to load. You can find the
VID by visiting
admin/content/taxonomy and looking at
the destination of the edit
vocabulary link for your
vocabulary. The VID will be the last
fragment of that URL:
admin/content/taxonomy/edit/vocabulary/[vid].
I wouldn't normally hard-code the
$vid into the module itself. However,
setting up the necessary Drupal
variable and administration form (to
allow users to select a vocabulary
from the Drupal interface) would be
overkill for this answer.
For your own documentation purposes,
don't forget to search/replace
[vocabulary] in those two files and
use your own vocabulary's name
instead.
This method may not necessarily be more performant
than the Views method I described
earlier, especially once you start considering caching,
optimization, etc.
Since performance is a priority,
I recommend thoroughly testing a
variety of different methods on this page and
choosing whichever one is fastest for you.

Related

Drupal: How to restrict apachesolr search results by user/article facets

I have a wiki built with drupal, with a taxonomy category Workgroup, assigned to both the users and the articles. I am using apache solr search module with facet api and my end goal is to set up the search so that by default when users search for the articles, only articles from their workgroup are shown.
That is, when they launch the search from a search box, they should get the same results as for /search/site/hello?f[0]=im_field_kb_workgroups%3A4529 (where 4529 is one workgroup id) instead of just /search/site/hello (current behavior) Users should still be allowed to search in other workgroup facets when they want, by removing the checkbox in the facet block.
I have this working almost by hacking the apachesolr module (not recommended I know but really want this to work). In the function apachesolr_search_custom_page_search_form_submit, I have:
// Get the workgroup id
global $user;
$account = user_load($user->uid);
$user_kb_wg_fieldinfo = field_get_items('user', $account, 'field_kb_workgroups');
$user_kb_wg_tid= '';
if ($user_kb_wg_fieldinfo) {
$user_kb_wg_tid = $user_kb_wg_fieldinfo[0]['tid'];
}
// Add the solr filter for this workgroup facet so that by default, search results are
// fetched only from that workgroup.
if ($user_kb_wg_tid === '4529') {
$get['f[0]'] = 'im_field_kb_workgroups:4529';
}
This does the job but the problem is that this relies on the apachesolr search form. I have users coming to the wiki by searching from sites external to the wiki, where the wiki search form is just a simple POST form pointing to the KB domain and the path /search. So this will work only when people are searching from inside the wiki, where I present them the apachesolr search form.
I have investigated some other options:
In my custom module, I implement this hook (without the user workgroup checks for now, for testing):
function kb_hacks_apachesolr_query_prepare($query) {
$query->addFilter('im_field_kb_workgroups', '4529');
}
This filters the results from searches everywhere, but the filter is applied all the time, and users don't get to deselect this or other filters. (in fact, other filters appear only when passing the filter as a GET param like above with f[0])
I also played with the url_inbound_alter hook, but could not figure out how to pass the solr query param as GET. The following did not work.
function kb_hacks_url_inbound_alter(&$path, $original_path, $path_language) {
if ($path == 'search/site/hello') {
$_GET['f[0]'] = "im_field_kb_workgroups:4529";
//$_GET['f[0]'] = "im_field_kb_workgroups%3A4529";
//$path = 'search/site/hello?f[0]=im_field_kb_workgroups%3A4529;
}
}
Is there a way to set GET params from this hook? But even if this had worked, I would still have to figure out how to do this only by default (when the search form is submitted), and not when the filter itself is deselected. Is there a way to detect checkbox changes in the facet block?
Maybe there's another way to do this? I have been stuck here for the last two days and would really appreciate any help I can get. Thanks!
You can add a relationship to the taxonomy term "Workgroup" and use a contextual filter for the current user. In the contextual filters section, you can change the behavior when the filter is not present.

Referenced node display references

I have a node "Bug/Requests" which references one "Project".
On the project "node" page, I would like to display a list of bugs/requests which link to that project. Is this possible?
here is how I ended up doing it:
Is this good or bad? Is there a better way? (in template.php)
<?php
function digital_preprocess_node(&$vars)
{
$node = $vars['node'];
if ($node->type == 'project' )
{
$bugs_requests_nids = array();
$query = 'SELECT entity_id FROM field_data_field_project WHERE field_project_nid = :project_nid';
$result = db_query($query, array(':project_nid' =>$node->nid));
foreach($result as $row)
{
$bugs_requests_nids[] = $row->entity_id;
}
$vars['tasks'] = node_load_multiple($bugs_requests_nids);
}
}
I think you want the References Module (provides node and user reference fields for Drupal 7)
Apologies I didn't read properly, you also want the Corresponding node reference module which makes the node reference bi-directional (D7 versions of the modules given in another answer).
EDIT to address your new code:
I'm guessing you're pretty new to Drupal from your recent questions but either way you've hit on (in my opinion) the best method to do this. If you're comfortable writing PHP code (which a lot of Drupal users aren't) then grabbing the data directly will always be more efficient than using a contributed module that might have a lot of overhead.
A few minor points:
I'd consider moving your code out of the template file and into a custom module, inside a hook_node_load function instead so this data is available throughout the life of the nodes (that way you can re-use it in many different contexts). However if you don't need to reuse this data anywhere except in the template file then it's fine where it is.
If you're going to go directly into the field tables you should probably use the field_revision_field_x tables instead of field_data_field_x so you can take advantage of the revision system and always grab the most recent data.
As fields can be attached to multiple entity types you should make sure you're getting the right field data for the right entity (you may not plan to attach this field to any other nodes/entities but it's good practice in case you do).
This is a slightly edited version of your code taking into account the proper field types (untested but should work):
function digital_preprocess_node(&$vars) {
$node = $vars['node'];
if ($node->type == 'project' ) {
$bugs_requests_nids = db_select('field_revision_field_project', 'p')
->fields('p', array('entity_id'))
->condition('entity_type', 'node')
->condition('bundle', 'project')
->condition('entity_id', $node->nid)
->condition('revision_id', $node->vid)
->execute()
->fetchCol();
$vars['tasks'] = node_load_multiple($bugs_requests_nids);
}
}

Theming view for multiple value field

I know this is probably a n00b question but I've searched everywhere for an answer and havent found anything.
I have a CCK multiple value field for "Features" where a product can have a random number of multiple features entered for it. I am editing the view so I can style the output of the features on the product page.
Right now in my view I can output the entire list of features at once using:
<?php print $fields['field_features_value']->content ?>
This will give me a list all the features given for a product. But what I want to do is loop through and pull out each individual feature and format/style it separately. How exactly would I do this?
I ran into this yesterday again, and spent hours trying to Google the syntax, to no avail.
I was able to get this to work, but I must admit it is not the best way. It is duplicating some of the work that Views has already potentially done for us, and should be considered a brute-force approach.
My use case involved theming each filefield file in a node separately, per node-based row:
<?php
// $Id: views-view-field.tpl.php,v 1.1 2008/05/16 22:22:32 merlinofchaos Exp $
/**
* This template is used to print a single field in a view. It is not
* actually used in default Views, as this is registered as a theme
* function which has better performance. For single overrides, the
* template is perfectly okay.
*
* Variables available:
* - $view: The view object
* - $field: The field handler object that can process the input
* - $row: The raw SQL result that can be used
* - $output: The processed output that will normally be used.
*
* When fetching output from the $row, this construct should be used:
* $data = $row->{$field->field_alias}
*
* The above will guarantee that you'll always get the correct data,
* regardless of any changes in the aliasing that might happen if
* the view is modified.
*/
?>
<?php
$output = explode('|', $output); // I've rewritten the field output in Views like this: [field_portfolio_image-field_name]|[nid]
$paths = $output[0]; // I've set filefield to show file paths rather than the file
$nid = $output[1]; // The NID is all that's really needed for this approach
$node = node_load($nid);
$slots = $node->field_portfolio_image;
foreach($slots as $prop) {
print ''.$prop[data][description].'';
}
?>
I used the Devel module heavily here (image reference for this example attached), in order to get the nested values I needed.
I know there is a better, proper way of doing this, rather than reloading the node data, since views should already have access to this upon page load.
When theming views is too specific I set conditions, relationships, parameters and all the views stuff except fields. The only field I use is node id.
Then when doing the theming I use...
$node = node_load($nid);
... to get the node object. You can inspect the node object with the dpm function that comes with the devel module.
dpm($node);
This "technique" works well for nodes and when you don't care about optimization or speed, because if you are going to do this with 1000 nodes you should load the nodes on batch.

How do I filter a block view by taxonomy argument in URL?

I have some block views in my sidebar that show events marked as a highlight happening in certain cities. Nodes are organized into cities using taxonomy.
When I load a node directly I have an URL like www.host.com/events/new-york/name-of-my-nice-event
I have some other page views that show teasers for all events in a certain city: www.host.com/events/new-york
Also I have some static pages that are valid for all cities, e.g. www.host.com/about-us
The sidebar blocks showing the highlights are available throughout the whole website. Now I want to make sure that the blocks in my sidebar only show those nodes for the selected city based on the taxonomy provided in the URL. (except for the static pages as there is no taxonomy in the URL, but those are not that important)
So far I tried to pass my view the taxonomy term as an argument using PHP as standard argument:
if (arg(1)) {
$term = arg(1);
return $term;
}
This works fine on the above mentioned page views (e.g. www.host.com/events/new-york). But when I load a node directly www.host.com/events/new-york/name-of-my-nice-event my block only shows the empty text.
I thought that arguments are indexed like this:
events/new-york/name-of-my-nice-event
^0 ^1 ^2
So I don't understand why arg(1) does not return new-york when I am viewing the node detail.
First of all, with path and path auto what you see is not always what you get.
Fx I could setup pathauto for my articles nodes to generate urls like this
content/article/[title]
So if I wanted the title I should use arg(2) right?
No! (arg(2) is actually NULL in this case.)
The reason is that the url that's generated by path auto is a fake url, that gets translated into a Drupal url. In the case above what I get is node/[nid]. So eventhough the node title i in the url, I can't get it by using arg(), but I can get the nid by using arg(1)
I can't guess what your urls map to, it depends how you have set up your site what modules you use etc.
A good advice if you do a lot of these context aware things, is to look into panels. It's made to be able to tell modules like views about the context which it is present. Like fx terms, nodes, etc, and you could use this to pass arguments into views.
Panels can do a lot more and is quite complex, but if you need to do a lot of this stuff, it is probably worth the investment.
Solution to my problem:
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
if (count($node->taxonomy) > 0) {
foreach ($node->taxonomy as $term) {
$term = $term->name;
}
}
$term = strtolower($term); // for some reason needed in my case
}
else {
$term = arg(1);
$term = str_replace('-', ' ', $term); // for some reason needed in my case
}
return $term;
While this was technically possible with Views 2 as described in some of the other answers, Views 3 has this integration built in.
You can create an argument for the taxonomy term id and then choose "Provide default argument". This will then give you an option for "Taxonomy Term ID from URL" and "Load default argument from node page..."
This will allow you to take the taxonomy of a page and pass that as an argument to your view block.
Note: Views 3 is currently in Alpha 3 but in my experience is at a relatively stable state and I am using it on production sites. If it has features like the one above that you find useful please use it, test it and submit bugs/patches if you encounter any issues!

Drupal: retrieve data from multiple node types in views 2?

...or, in other words, how to create a simple join as I would do in SQL?
Suppose I want the following information:
Just as an example:
a person's full name
a person's hobbies.
His full name is in a (content profile) node type 'name_and_address' and his hobbies are in 'hobbies'.
In SQL, I would link them together by node.uid.
I've seen a bit about using relationships, but that goes with user-node-refs.
I just want the same user from one content-type and the other.
Now how could I get his name and his hobbies in 1 view?
There is a how to here does this do the job?
If not...
Views can be extended with custom joins, filters etc. If you are lucky there will be a module for this already. Some modules even provide their own views plugins.
You can write your own views plugins, although the documentation is a little fragmented.
The other thing that should be noted is that views isn't always the answer. Sometimes writing a custom query and display handler will do what you want with much less hassle.
Look at the relationships section of the view. This allows you to relate (ie join) different types of content (ie tables). It's not especially intuitive to someone used to SQL, but this video explains much of it. http://www.drupalove.com/drupal-video/demonstration-how-use-views-2s-relationships
You could use views_embed_view() in your template files to manually specify where they appear (and by extension render one view right below another).
You could override this function in a custom module (modulename_embed_view($name, $display_id)) in order to selectively edit what data is allowed out to the page.
Ex):
function modulename_embed_view($name, $display_id) {
if (strcmp($_GET['q'], 'node/123') === 0) {
$view = views_get_view($name);
$view2 = views_get_view('second view');
$output = $view['some element'] . $view2['element'];
}
return $output;
}
I know that this is very much a hack - I just wanted to show how one might use php to manually render and modify views in your template files.

Resources