Hi,

I've been using Drupal for a week now and I am very happy with what it has me allowed to do so far. Now I've run into a problem that I haven't been able to figure out myself:

I have a view that takes an argument from the URL and filters nodes by that term.
The view works great by itself, but now I want to embed it in a different node.

More specific: I have a node "Paris" and I want to embed a view "Sights". The view should filter all available "Sights" nodes to only those that are in Paris.

As I said: I have a view that works, but when I embed it into the "Paris" node using "View Reference" it looses its filtering ability.

The argument code I am using is very basic (but I don't know PHP and can't come up with something else):
$args[0] = arg(2);

arg(2) is the city in my URLs.

I'd be grateful for any pointers in the right direction. I'm starting to learn PHP right now, but I guess it'll take me sometime to really get it.

Comments

Anonymous’s picture

-----------------------------------------
Joep
CompuBase, Drupal websites and design

pulpzebra’s picture

if you display a views as a block inside a page, no arguments are passed to that. That's why you're not filtering anything. You're correctly using arguments handling code to retrieve the url for your view. your code means that www.yoursite.com/ARG0/ARG1/ARG2 you want to do things with your ARG2. If this is ok, then you just have to add "return $args" at the bottom of your arguments handling code.

Consider that if your url is www.yoursite.com/PARIS, there is no arg(2), but I assume you've already come up with this.

arien101’s picture

Thanks for your answer! I have tried to implement it so now my views argument handling code reads as follows:

$args[0] = arg(2);
return $args;

This still works for the view itself, but as soon as the view is part of a node it stops working. Note that I am not using a block, but the cck field "View reference". I have also tried a block, however that also leeds to the same situation.
When I configure the "view reference" in the node there is also a field "Arguments". Will that somehow help me?

On a related note: I'm using German Umlaute (ä, ö, ü etc.). My URLs are rewritten correctly to ae, oe, ue. My taxonomy is still using a,ö,ü though. Apparently the filter doesn't think that the ae from the URL is the same as the ä in the taxonomy. Any hints on that?

Sorry for pestering you and thanks for your quick replies!

arien101’s picture

Correction: It does work, but only if I use a view instead of a node.

When I place the Paris node at france/ile-de-france/paris/ it doesn't work.
When I place a view that only contains the node at the same URL it works.

I would still prefer using just the node though. Otherwise I will have to take an extra step and create a view for every city node.

arien101’s picture

Hey, sorry to bump this, but I still haven't managed to make any progress. I've tried several approaches, but the only one that works is not really what I am looking for.

Here's the lowdown:

- I have a cck node that holds information about a city. In this node I have embedded a view that shows all sights in that city
- The sights have their own cck content type
- The view takes an argument from the URL and filters the sights accordingly. This works great per se, but once the view is embedded into the node, the argument filter stops working
- Now here's something funny: When I use another view to display the full node with the embedded view it again works
- Doe anybody have an idea how I can get the embedded view with just the node?

Here's the argument handling code:

$args[0] = arg(2);
return $args;

pulpzebra’s picture

if you display a views as a block inside a page, no arguments are passed to that. That's why you're not filtering anything. You're correctly using arguments handling code to retrieve the url for your view. your code means that www.yoursite.com/ARG0/ARG1/ARG2 you want to do things with your ARG2. If this is ok, then you just have to add "return $args" at the bottom of your arguments handling code.

Consider that if your url is www.yoursite.com/PARIS, there is no arg(2), but I assume you've already come up with this.

arien101’s picture

Hi,

unfortunately I haven't been able to make progress on this. I tried working with "build-type == 'embed'" but that didn't help. I also tried assigning the type of my cck node to the $view, but that didn't do anything either. It still works if the page I'm looking at is a view, but as soon as I embed the view in a node using viewfield it stops working.

Since I have no clue how to make it work I am looking for alternatives:
1. Can I maybe pass something like %tid or %taxonomy_term using the "arguments" section of the viewfield? I couldn't get %tid to work yet, but is it even possible?
2. Is it possible to have the same view at multiple url locations? Like can I tell the same view to be at all URLs with this make-up: "continent/country/region/*(any_city)*/". That way instead of calling a node with views, I could call a view that holds other views.
3. Would it be possible to embed the whole view into the cck node using views_build_view like it is decribed here http://drupal.org/node/99721? Or would that just lead to the same problem?

Andy advice is highly welcome :-)

dbassendine’s picture

I have exactly the same problem as you, and I've not yet found a way to do it cleanly.

I have views embedded inside og nodes. When the og node is rendered, it calls phptemplate_og_view() (in my template.php file), which prints an embedded view. The only way I've found to pass arguments to the embedded view, is to use $_GET variables in the url ie. your url would read:

www.yoururl.com/sights?city=paris

Then inside the "sights" node you have code which gets the "city" variable, and passes it on to the embedded view eg.:

<?php
//gets the city variable
$city = $_GET['city'];
//passes the variable on as an argument
$view_args = array($city);
$view = views_get_view('your_view');
$output .= views_build_view('embed', $view, $view_args, $view->use_pager, $view->nodes_per_page);
print $output;
?>

You need to change "your_view" here to the name of the view you want to call. Also, you won't need to use any argument handling code is this case.

It's ugly, but it works.

I'd also like to pass a url argument to a node, giving clean urls, but it seems it just isn't possible (at least in 4.7, which I'm using here). For example, if you create a page at node/xxx and insert eg:

<?php
$city = arg(2);
print $city;
?>

... and then try to access on node/xxx/paris, you always just get the full node listing (same as node/). It seems any arguments passed to the node actually invalidate that address, and redirect to node/ instead.

Can this be changed in core (presuming it is a core issue) to allow embedded views to work properly?

doc2@drupalfr.org’s picture

Suscribing. Same problem.

I need a viewfield embed view in my nodes but as far as no arguments are passed into the url of a node, the view doesn't show up... unless using another view. As for you, I cannot have a view per node... views are for customizing requests, not for content.

I don't know yet how to handle this. I thought the views_argument_api.module would help me, but I don't see how yet.

pulpzebra’s picture

In Drupal I use views to retrieve values in a complex way: so, eg, my view newest_product will retrieve nodes that are of type "product" and that have been added in the last two months. I might also want to filter this view by category, so I would keep my view "newest_product" and pass to it the "category" argument: that, is newest_product/cars, newest_product/bikes...

in this case "cars" and "bikes" are arguments to my view. I may also add further arguments to the view, as long as my content type would be suitable to that.

If I get right what you are trying to do, you're making nodes /A/B/C style... so you want all arguments to be passed to the view. Of course, if the view is embedded in the page, this is no easy thing, since the embedded view won't get the url arguments. You can do this in view argument handling code:

1) check the REAL path you get ex: list($a,$b,$c) = explode("/", $_GET["q"]);
2) if $a == "node" than no arguments decoding can be done by the view. In this case you have to manually get the path alias you want: $alias = drupal_get_path_alias("node/".$b) (where $b is the node->nid);

doc2@drupalfr.org’s picture

Of course, if the view is embedded in the page, this is no easy thing, since the embedded view won't get the url arguments.

Great! This might have been too obvious for experienced people, but it is indeed a very useful hint.

By the way, I would like to warn every viewfield user that the module encounters a few issues worth to know about, such as:


You can do this [getting the url rags of a node] in view argument handling code (...)

Thank you for this help. I wish I could make a better use of it... Yet I found in this topic and the ones I've just quoted a few interesting turnarounds or patches to answer my needs, though not tested yet.

I fear loosing a lot of time testing all of them as I'm far from being an expert (ex: I'm not sure on how to pass 1) & 2) to my view argument handling code without the comments). I just bet on the future developments of the viewfield module for the moment. But please go on suggesting hints because it will surely help other drupalers too, and myself later on!

    Thanks again
kristi wachter’s picture

This thread very helpfully provides the following code, which you can put in the Argument Handling Code section for your block view:

if ($view->build_type == 'block' && arg(0) == 'node' && is_numeric(arg(1))) {
  $args[0] = arg(1);
}
return $args;

Note that this example is passing the node is (the first argument after "node/"). If city is your second argument, you might be able to use

  $args[0] = arg(1);

and you might want to get rid of the "arg(0) == 'node' part - play around with it and see.

˚ ˚ ˚ ˚ ˚
Kristi Wachter
Drupal consultant, evangelist, and enthusiast

dbassendine’s picture

Unfortunately the embedded view never receives the url arguments - they have already been filtered out by the Drupal core! So it seems to me there's nothing you can do in Views' Argument Handling Code or anywhere else in views.

There has to be a change in core that passes on arguments after node/XXX. Thinking about it now, though, the node/XXX/edit and node/XXX/revisions arguments are passed on - so possibly we can write some module code that defines additional arguments and passes them through. I'll investigate how revisions (for example) does it.

dbassendine’s picture

I think I've figured it out - but it requires a hook_menu call in your node xxx module, and a patch to path.inc in drupal core. This is in 4.7.

First of all you have to allow the node (the one you want to embed your view, in my case, og nodes) to take arguments from the url, and spell out what to do with them. So, in og.module's hook_menu (ie og_menu), you add:

// pull out arguments from url, and pass on to og_views_arguments
   if (arg(0) == 'node' && is_numeric(arg(1))) {
      $node = node_load(arg(1));
      if (og_is_group_type($node->type)) { //checks that this is an og node
        $items[] = array('path' => 'node/' . arg(1), 
          'title' => t('email'), 
          'callback' => 'og_views_arguments', 
          'callback arguments' => array($node, arg(2), arg(3)), 
          'access' => node_access('access content', $node), 
          'type' => MENU_CALLBACK);
      }
    }

'Callback arguments' get passed on to the function you specify in 'callback', and in this case I've only specified arg(2) and arg(3), although you could add more. Note that hook_menu receives the args in internal path form (not as an alias), so arg(0) is node and arg(1) is the node id.

Then you have to create your callback function in the xxxx.module for your node (ie. og.module). This takes the 'callback arguments' in the same order as they're specified in hook_menu, and then you can do something useful with them:

function og_views_arguments($node, $arg2, $arg3){
  //uncomment to check you're getting the args throug h correctly
  //print 'arg2: '. $arg2;
  //print 'arg3: '. $arg3;

  //assign to $_POST global variable, so you can access from elsewhere
  $_POST['arg2'] = $arg2;
  $_POST['arg3'] = $arg3; 

  //display the node as normal
  og_set_group_context($node); //just for og
  $output = node_show($node, $cid = FALSE);
  return $output;

}

Here I've put the args in $_POST variable, and I then link this up with the embedded view by saying $arg2 = $_POST['arg2'] and $arg3 = $_POST['arg3'], and then passing these to the view (as in the comment above).

At this point you should be able to go to node/xxx/arg2/arg3 and get both your arguments passed through to the view.

Next, if you use aliases, is to make sure your added arguments don't get cut off before you even get to the hook_menu call. By default the drupal_lookup_path function in path.inc (in the /includes folder) looks for a direct match between the path entered ($path), and the aliases in the {url_alias} table. So if you add anything onto the end of an alias (like an argument), you get redirectd immediately to "page not found".

Here, I've modified path.inc: first it looks for a direct match (as normal), but if it doesn't I then break up the $path and check if any of its components match the aliases in {url_alias}. We're trying to find if the root of $path is an alias, and if it matches, we then assume that all subsequent pieces of $path are arguments. Finally if none of the roots (here, up to three hierarchical levels) match an alias, we then go to "page not found".

    // Check $no_src for this $path in case we've already determined that there
    // isn't a path that has this alias
    elseif ($action == 'source' && !isset($no_src[$path])) {
      // Look for the value $path within the cached $map
      if (!$src = array_search($path, $map)) {
        if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $path))) {
          $map[$src] = $path;
        }
        else {
          // There is no direct match to known aliases, but check if the alias roots
          // exist. If they do presume additional hierarchies are added arguments, 
          // & add them onto the $src url ie. for any given path (h1/h2/h3 etc.)we 
          // are trying to distinguish between the root alias components (r1/r2) and the 
          // argument components (a1/a2), by trying to match root path against the 
          // {url_alias} table  
          $path_pieces = explode('/', $path);
          if(count($path_pieces > 1)){ //path has 2 or more hierarchies ie h1./h2. etc. ...
            //check if the root is a recognised alias ...
            //at one root hierarchical level r1/a1/a2 etc.
            if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $path_pieces[0]))){
              $root_h_number = 1;
            //at two root hierarchical levels r1/r2/a1/a2 etc.
            }elseif($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $path_pieces[0]. '/'. $path_pieces[1]))){
              $root_h_number = 2;    
            //at three root hierarchical levels r1/r2/r3/a1/a2 etc.        
            }elseif($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s'", $path_pieces[0]. '/'. $path_pieces[1]. '/'. $path_pieces[2]))){
              $root_h_number = 3;
            }
          }
          if($src){ //one of the roots is an alias
            //add any remaining a. onto the $src path
            foreach($path_pieces as $path_piece_key => $path_piece){
              //add the arguments, but don't duplicate root pieces
              if($path_piece_key > ($root_h_number - 1)) $src .= '/'. $path_piece;
            } 
            //add to cache
            $map[$src] = $path;
          }else{
            // Even the roots (up to 3 hierarchies) cannot be matched to known aliases, hence ... 
            // We can't record anything into $map because we do not have a valid
            // index and there is no need because we have not learned anything
            // about any Drupal path. Thus cache to $no_src.
            $no_src[$path] = TRUE;
          }
        }
      }
      return $src;

This should go in around line 68 (on 4.7) in the "action='source'" section. Note this only properly distinguish arguments added onto aliases three or less levels deep (ie. r1/r2/r3 or less). Also, if you have a root alias that is more than one level (r1/r2 or greater), then you shouldn't have aliases using r1 that link to different nodes - because the search will pick up the r1 root (and redirect) before you get to r1/r2.

Note also that this prioritises matches to already-defined aliases ie. if you have two paths r1/r2 and r1/a1 which are the same, then it will to r1/r2. This is good, probably, because it means there are no extra sql overheads for aliases that are already defined - the extra queries only kick in if the alias is not found by a direct match.

NB. I know very little about security, and haven't really thought through the implications of this. I don't think there's any harm passing arguments from aliases to the internal paths, as arguments could be added to internal paths anyway. However, I've restricted the pick-up of arguments from internal paths (in hook_menu) to the node type I'm using to embed views in (og). And these only get passed via $_POST into the views calls - but probably they should be filtered in some way, because the data can be entered directly into the url. Any advice appreciated!

Phew - don't you just love open source! I just hope you can make some sense of this :)

doc2@drupalfr.org’s picture

I wish so much I could :'(

I ever you come accross some ready-to-use module, or even patch, let us know!

Just 2 remarks (that's my cases):
1) some people may use drupal 5.
2) some may use standard node types.
If so, they might ask what to do... But that will already be useful to many. But personaly, I wouldn't understand. I just love OS anyways!

dbassendine’s picture

Yes, sorry! It does get a bit involved. I'm sure it can be done as a simpler patch or module, but I'll have to have a think.

Its probably better not to use the path.inc patch, as it probably a bit of bloat (I have loads of spam hits looking for paths that aren't there, and those would trigger the search queries). Writing the aliases directly to the url_alias table would make it quicker, and avoid conflicts between paths - that would require an addition to the pathauto module. And it would only work if you knew what your arguments were going to be (ie all possible arguments), and there weren't too many of them!

doc2@drupalfr.org’s picture

0- Remark :
0.0.0 You're thinking of using the viewfield.module, right?
0.0.1 There are a few concerning issues on the embedding subject for this module, and a development timeline
0.0.2 What about blocks set to show on the nodetype only (php or module required) and displaied wherever including the content top bottom (cf. infra)?

0.1.1 What about the views_argument_API.module?

0.1.2 What about the token.module?

1- I have:
1.1 One standard node type. Let's call it "substance" (not to mix up with content although that's what I mean)

1.2 Each substance node is tagged with one and one term only, which designs the substance, and therefore is the title of the node as well (I use auto_nodetitle to do that.)
NB.: they can still hold other kind of contents, such as fields or even other terms from other vocabularies.

1.3 This "node title" term comes from a single vocabulary which ID is 1 and name is... substance, of course.
NB.: Each one of the terms which come from that vocabulary can be assigned to only one node only.

NODE TYPE = "SUBSTANCE" <=> VOCABULARY NAME = "SUBSTANCE" & ID=1 (voc1)

Substance Node Title = "a substance X" <=> Term name from voc1 = "a substance X"

2- At the bottom of a substance node will be referenced documents nodetypes for that substance.

I am not sure yet whether I'll use (the default taxonomy) terms or cck_taxonomy terms to reference those documents. Let's say we use taxonomy terms though.

2.1 Arguments would be:

    2.1.1 - the node ID (NID)
    2.1.2 - term (from predefined voc1) for NID.

    2.1.3 I don't know whether the pre-defined vocabulary has to be an argument...

2.2 (very optional, but seems to match your idea of using the url_alias table :) Ideally, we'd have in the url (regardless of what the process uses):

    2.2.1 - the node title of the substance node (nodes containing the embed views).
    2.2.2 - the node title followed by the node type: substance-X/videos for the stand-alone view.
    2.2.3 I don't know whether displaying the 2.1.2 - term for NID in the URLs is default. I *just* guess it is not for the node but it is for the view.
    2.2.4 The previous 2.2.3 remark would lead to a redundancy as I use node_autopath with nodeauto_title to define my node title and url according to term (from voc1) for NID.
    2.2.5 Sorry, it gets a bit complicated here... :)

3- In fact, those documents are Images, Videos, etc... that is to say that those related documents are from different node types (yet similar as documents being referenced by the substance nodes).

3.1 I intended to embed into my substance node, for each document nodetype, one viewfield embed view (or, according to 0.0.2, one highly customized block). Doing so would have enabled me to (less true for blocks):

    3.1.1 easily control the number of nodes to show according to the document node-type
    3.1.2 custom theming of the resulting view: a related image node in its resulting embed view (table type) would have a kind of "preview" image that would take more room than a default list of node of another type. Thus, in order to keep an equilibrium between the surface taken by each document type on the substance node page, there would be less image node referenced in the "related images" viewfield embed view than in the "other types" ones.
    3.1.3 display the related node types embed views in an arbitrary order. *Eventually*, in different places of a panel content type (Panels.module v.2 aims at being integrated into nodes!)

3.2 I'm not sure viewfield allows multiple views embeding...

marcvangend’s picture

I'm not sure if it adds something to this conversation, but maybe you're interested to read what I did with views/arguments, pathauto and panels. I just posted it here: http://drupal.org/node/70145#comment-738448.

doc2@drupalfr.org’s picture

Thanks Marc, but this is much too complicated for me.

I managed something with a combination of the modules:
- viewfield
- ReferencedByFilter (dependency on the nodereference.module included in CCK)

Sorry, I don't have time to precise, but read carefully the ReferencedByFilter.module's documentation, and you might come out with great features!

Hope this help

(EDIT: July, 1st 2008) It does works for other things than galeries!!! Any field from your view may be used!

socialnicheguru’s picture

subscribing

http://SocialNicheGuru.com
Delivering inSITE(TM), we empower you to deliver the right product and the right message to the right NICHE at the right time across all product, marketing, and sales channels.

summit’s picture

Subscribing, very much interested in using $args in the node-title and body.
Greetings,
Martijn

Kenny Johnston’s picture

I am very much interested in a solution to this. Right now I'm trying to develop a SlateCard view that would display the candidates (created as a candidate node type with a CCK field of "District") in given districts through a view that would be referenced something like this:www.ksdp.org/slatecard/$County/$CongressionalDistrict/$StateSenate/$StateHouse

So an example would be
www.ksdp.org/slatecard/WY/3/4/38

I started out trying to create a view for each race and then putting the block views for each on one page. Of course the arguments don't get passed. I've heard a lot about Panels and how they might be a solution but I haven't quite figured them out yet. Any advice?

Kenny Johnston
Kansas Democratic Party
www.ksdp.org