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

...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.

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.

Drupal user permissions & odd content types

I have a permissions problem in Drupal. I want users to be able to create a certain node type, but there are two different paths I need to give them permissions for to let them do this. The type is content created by a module called isbn2node, and there are two ways to make content through it, each with different paths:
?=node/add/isbn2node-book
?=node/add/isbn2node_book/isbn2node
One has an underscore and the other one has a hyphen. The first path leads to a form that lets users enter information on a book manually; the second path lets them enter an ISBN, searches for it, and populates the form for them based on the results.
I've changed permissions in the People menu so they can add isbn2node-book content manually using the first path, but there isn't an option to let them use the second method. Aliasing the url so it didn't have node/add in the path didn't work either.
Creating a duplicate content type seems like an ugly solution to this; is there a more elegant way to let users access that second path?
A little code in a custom module using hook_node_access should do it.
$node is either a node object or the machine name of the content type on which to perform the access check (if the node is being created then the $node object is not available so it will be a string instead).
So this should do it:
function MY_MODULE_node_access($node, $op, $account) {
if ($op == 'create') {
$type = $node;
if($type == 'book' && $account->uid) return NODE_ACCESS_ALLOW;
}
}
I figured this out, and the issues I was having were specific to this content type. The ISBN2Node module requires users to have the Administer Nodes permission to use its lookup and bulk import features.
There is some extra code for the module's hook_permission and hook_menu sections submitted as a fix in the module's issues thread.

Nested REST Routing

Simple situation: I have a server with thousands of pictures on it. I want to create a restful business layer which will allow me to add tags (categories) to each picture. That's simple. I also want to get lists of pictures that match a single tag. That's simple too. But now I also want to create a method that accepts a list of tags and which will return only pictures that match all these tags. That's a bit more complex, but I can still do that.
The problem is this, however. Say, my rest service is at pictures.example.com, I want to be able to make the following calls:
pictures.example.com/Image/{ID} - Should return a specific image
pictures.example.com/Images - Should return a list of image IDs.
pictures.example.com/Images/{TAG} - Should return a list of image IDs with this tag.
pictures.example.com/Images/{TAG}/{TAG} - Should return a list of image IDs with these tags.
pictures.example.com/Images/{TAG}/{TAG}/{TAG} - Should return a list of image IDs with these tags.
pictures.example.com/Images/{TAG}/{TAG}/{TAG}/{TAG}/{TAG} - Should return a list of image IDs with these tags.
etcetera...
So, how do I set up a RESTful web service projects that will allow me to nest tags like this and still be able to read them all? Without any limitations for the number of tags, although the URL length would be a limit. I might want to have up to 30 tags in a selection and I don't want to set up 30 different routing thingies to get it to work. I want one routing thingie that could technically allow unlimited tags.
Yes, I know there could be other ways to send such a list back and forth. Better even, but I want to know if this is possible. And if it's easy to create. So the URL cannot be different from above examples.
Must be simple, I think. Just can't come up with a good solution...
The URL structure you choose should be based on whatever is easy to implement with your web framework. I would expect something like:
http://pictures.example.com/images?tags=tag1,tag2,tag3,tag4
Is going to be much easier to handle on the server, and I can see no advantage to the path segment approach that you are having trouble with.
I assume you can figure out how to actually write the SQL or filesystem query to filter by multiple tags. In CherryPy, for example, hooking that up to a URL is as simple as:
class Images:
#cherrypy.tools.json_out()
def index(self):
return [cherrypy.url("/images/" + x.id)
for x in mylib.images()]
index.exposed = True
#cherrypy.tools.json_out()
def default(self, *tags):
return [cherrypy.url("/images/" + x.id)
for x in mylib.images(*tags)]
default.exposed = True
...where the *tags argument is a tuple of all the /{TAG} path segments the client sends. Other web frameworks will have similar options.

How do I prevent a content type from appearing in collections?

How do I prevent a specific content type from appearing in collections (smart folders) site wide in Plone 3? I've looked for relevant options in portal_types and the types and search control panel (turning off the content type for searching doesn't seem to have an effect on collections).
Specific situation: I recently installed plone.app.discussion on a Plone 3.3.5 installation and now comments appear in all the collections. We want to remove them from the collections.
Thanks.
Portal Types criterion is based on plone.app.vocabularies.ReallyUserFriendlyTypes, a vocabulary factory defined in http://svn.plone.org/svn/plone/plone.app.vocabularies/trunk/plone/app/vocabularies/types.py.
If you patch BAD_TYPES by adding discussion comments you'll exclude them from Collections, but you'll also hide them from anywhere this vocabulary factory is used. As far as I know they are also used in contentrules and search control panel.
You can patch BAD_TYPES by adding these lines into __init__.py file of a custom package:
def initialize(context):
"""Initializer called when used as a Zope 2 product."""
from plone.app.vocabularies import types
types.BAD_TYPES = types.BAD_TYPES + ('Discussion Item',)
If you don't have too many collections, the simplest solution might be to add criteria to exclude comments. You can easily get a list of all your collections:
http://your-site/search?portal_type%3Alist=Topic
If you have a lot of collections, you might need to write some custom code to do this. It doesn't have to be a product or anything serious, just some code you can run to add an 'exclude comments' criteria to existing collections. I would start by looking around line 507 of http://svn.plone.org/svn/collective/Products.ATContentTypes/branches/1.3/Products/ATContentTypes/tests/test_criteria.py
You can add criteria to your collections stating which content types you want to display. You can not (without patching/hacking) choose which to exclude.
I.e collections have can have type whitelilsts not blacklists.

Drupal: Sharing content across Multisite

I have 2 websites with similar content types.
Let's say an event-content type with some cck fields in it.
site1: events ( title, body, image )
site2: events ( title, body, image, onsite1)
by the extra field at site2-events i want to give the possibility to the user to post his event on the ( main ) site1.
Site1 and site2 are both on 1 database, although tables from site2 are prefixed.
How can i add content made from site2 to site1?
( Is there an easy way to do this without resorting to sql? I am using the nodeapi at this time to do some extra when an event is submitted. )
Since you say you're already using hook_nodeapi it seems like you could just do:
if ($op == 'insert' && (see if checkbox is checked here))
...then switch to the other site's database, do a node_save, and switch back to the current site's database to let Drupal finish its business.
You might be able to use db_set_active() as Mike-Crittenden describes even if it is within same database, as both $db_url and $db_prefix can be arrays, instead of single strings.
This way you can have the same db_url for both 'default' and 'alternative' db, but use different prefixes to switch between databasees using db_set_active('alternative') and db_set_active() to return to default.
Lots of people use Feed API / Feeds module for this. You can filter by taxonomy terms, content type, whatever you need so that you don't have to import everything from the primary site. Great tool.

Resources