Drupal - Getting node id from view to customise link in block - drupal

How can I build a block in Drupal which is able to show the node ID of the view page the block is currently sitting on?
I'm using views to build a large chunk of my site, but I need to be able to make "intelligent" blocks in PHP mode which will have dynamic content depending on what the view is displaying.
How can I find the $nid which a view is currently displaying?

Here is a more-robust way of getting the node ID:
<?php
// Check that the current URL is for a specific node:
if(arg(0) == 'node' && is_numeric(arg(1))) {
return arg(1); // Return the NID
}
else { // Whatever it is we're looking at, it's not a node
return NULL; // Return an invalid NID
}
?>
This method works even if you have a custom path for your node with the path and/or pathauto modules.
Just for reference, if you don't turn on the path module, the default URLs that Drupal generates are called "system paths" in the documentation. If you do turn on the path module, you are able to set custom paths which are called "aliases" in the documentation.
Since I always have the path module turned on, one thing that confused me at first was whether it was ever possible for the arg function to return part of an alias rather than part of system path.
As it turns out, the arg function will always return a system path because the arg function is based on $_GET['q']... After a bit of research it seems that $_GET['q'] will always return a system path.
If you want to get the path from the actual page request, you need to use $_REQUEST['q']. If the path module is enabled, $_REQUEST['q'] may return either an alias or a system path.

For a solution, especially one that involves a view argument in the midst of a path like department/%/list, see the blog post Node ID as View Argument from SEO-friendly URL Path.

In the end this snippet did the job - it just stripped the clean URL and reported back the very last argument.
<?php
$refer= $_SERVER ['REQUEST_URI'];
$nid = explode("/", $refer);
$nid = $nid[3];
?>
Given the comment reply, the above was probably reduced to this, using the Drupal arg() function to get a part of the request path:
<?php
$nid = arg(3);
?>

You should considder the panels module. It is a very big module and requires some work before you really can tap into it's potential. So take that into considderation.
You can use it to setup a page containing several views/blocks that can be placed in different regions. It uses a concept called context which can be anything related to what you are viewing. You can use that context to determine which node is being viewed and not only change blocks but also layout. It is also a bit more clean since you can move the PHP code away from admin interface.
On a side note, it's also written by the views author.

There are a couple of ways to go about this:
You can make your blocks with Views and pass the nid in through an argument.
You can manually pass in the nid by accessing the $view object using the code below. It's an array at $view->result. Each row in the view is an object in that array, and the nid is in that object for each one. So you could run a foreach on that and get all of the nid of all rows in the view pretty easily.
The first option is a lot easier, so if that suits your needs I would go with that.

New about Drupal 7: The correct way to get the node id is using the function menu_get_object();
Example:
$node = menu_get_object();
$contentType = node_type_get_name($node);
Drupal 8 has another method. Check this out:
arg() is deprecated

Related

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.

Set PHP Variables for Drupal 7 Theme Files

I want to set custom php variables that can be used throughout my drupal theme (html.tpl.php, page.tpl.php etc.) I need the variables set based on the $title of the node. I'm not an expert on how Drupal works, the moduling and hooks, and I just need to know the simplest/easiest way to accomplish this. I keep reading about implementing some sort of hook in the template.php file where you can set variables, but I've been unsuccesful with everything I've tried.
So, basically, how would you accomplish this:
Get $title of Node
Set variables that will be passed along into theme files (for example, to do basic things like: if($title == 'news_page') { $siteSection = "news"; } )
Have $siteSection be available to use in theme files
Any help would be great.. thanks!
Before Drupal builds the HTML for a page from a theme's template (.tpl.php file), it runs preprocess "hooks". Hooks are basically a naming convention for functions that let modules and themes override or "hook" onto Drupal core processes.
E.g., if you want to display a message to a user when they log in, you can use hook_user_login.
function MODULENAME_user_login(&$edit, $account) {
drupal_set_message("Welcome, ". $account->name);
}
When a user logs in, Drupal looks for all loaded functions that end in "_user_login" and it runs them. If this function is in an enabled module, it has been loaded, so it will get run as well.
If you want to make a variable named $site_section available in your page.tpl.php file, you can hook into template_preprocess_page. This is a theme hook, so the name is a little different, but it functions pretty much the same way. To call this hook from your theme, you need to create a file called template.php in your theme's directory. Inside template.php, we'll add:
<?php
function THEMENAME_preprocess_page(&$vars){
switch (drupal_strtolower($vars['node']->title)) {
case "about page":
$site_section = "about";
break;
case "news page":
case "news page1":
case "news page2":
$site_section = "news";
break;
default:
$site_section = "none";
break;
}
$vars['site_section'] = $site_section;
}
The <?php is used to tell the server the treat all of the proceeding code as PHP. We then declare our hook function with the intention of loading Drupal's array of page variables into a local variable called $vars. By adding the & before $vars, we'll be allowed to modify the values for use outside of this function.
The switch statement will let us efficiently test the page title for multiple values. The value of the node's title may contain uppercase letters, lowercase letters, and symbols, so to avoid a case-sensitive mismatch, we're going to convert the title to lowercase and only test that (symbols will still be in the title, though). After the switch statement, we set the value of our $site_section local value into the referenced $vars array for use in page.tpl.php.
However, if it's just your intention to break the site up into sections for theming purposes, there are other ways of accomplishing that. My answer to a similar situation a few months ago might be helpful.

In Drupal 7, how do I get a list of all blocks being used on the page?

I'm building a module that manages ad units in the form of blocks that all need to be aware of each other and pass information around.
Thus I need to find a simple hook or other function to get a listing of every block that will be used on the page, so I can be sure to know the entire list of ad units on the page.
I've tried hook_block_list_alter() but this seems to return the ENTIRE list of blocks that exist in my Drupal install, with no designation of which blocks are actually going to be rendered or not on this page.
So, now what do I do?
I think it's got something to do with the order hook_block_list_alter() is called in but I'm not entirely sure. Either way the following code will get you a list of all blocks in the current page context. Be sure not to put this in a hook_block_list_alter() function or you'll get an exception caused by infinite nesting.
global $theme;
$all_regions = system_region_list($theme);
$blocks = array();
foreach (array_keys($all_regions) as $region) {
$blocks += block_list($region);
}
If you only need the blocks in a particular region you can ditch most of the code above and just go with:
$blocks = block_list('region_name');
I ran into the same problem when calling block_list('content'). Looking at the code for this function I found it calls two other functions, _block_load_blocks() and _block_render_blocks(). The problem seems to occur in _block_render_blocks() as the display text is not added to the content object. This is different from the other block objects that pass through the function.
To get around this, instead of calling block_list(), I called _block_load_blocks() directly. This returns an array of blocks grouped by region, bypassing the _block_render_blocks() call.
Now we can check for blocks in the content region without the content text disappearing. Huzzar!
I had a similar requirement when implementing an adserver (DFP). My solution was to define an array as a global variable, and include php code in each ad unit block that added a new element to the array.
Then, once all blocks on the page have been executed, you can simply access the global variable to see which ad blocks were called. Because the code to build the list of blocks being called is part of each block, it doesn't matter whether the blocks are displayed in a region, in a panel, or anywhere else.
In my case, I wanted to use the information to add scripts to the <head> section that reference only the add units from the blocks being placed. My complete solution was as follows:
1) Implement hook init to create a global variable in which to store information about which blocks are being displayed (you need to create a custom module to contain this code):
YOURMODULE_custom_init() {
$GLOBALS['dfp-ads'] = array();
}
2) Enable the core php module
3) Add php code at the end of each ad block to add a row to the array created in step 1
<?php
$GLOBALS['dfp-ads']['AD_OR_BLOCK_NAME_GOES_HERE']="AD SPECIFIC SCRIPT GOES HERE";
?>
4) Implement THEME_preprocess_html in my template.php file to access the global variable, build the script, and add the script to <head> section with a call to drupal_add_html_head
function YOURTHEME_preprocess_html(&$vars) {
$inline_script = LOGIC TO ACCESS $GLOBALS['dfp-ads'] AND BUILD SCRIPT GOES HERE;
$element = array(
'#type' => 'markup',
'#markup' => $inline_script,
);
drupal_add_html_head($element, 'google-dfp');
}
It sounds from your description that you don't need the list of ad blocks to build javascript for the head section, but instead want to use the information to modify the contents of the blocks themselves.
In that case insteaad of THEME_preprocess_html, you could try hook_page_alter(&page)
The api page for that hook states that individual "Blocks may be referenced by their module/delta pair within a region:"
// The login block in the first sidebar region.
$page['sidebar_first']['user_login']['#block'];
Hope that helps someone!

How do you use a view with arguments as the site front page in Drupal?

I have a Drupal site and I have setup a view to power the front page.
My goal is to be able to pass 0-2 arguments to the home page, that get passed into the view. However, I still need the normal Drupal pages to work. The list of arguments is known.
For example:
mysite.com/berlin/birds would pass in "berlin" as the first argument and "birds" as the second argument to the view that powers the front page.
mysite.com/berlin would just pass in one argument, "berlin"
mysite.com/admin would load the normal admin pages in Drupal
I'm not clear on how to achieve this. Is there a hook I can use? I can't find one or think of one. Is there a way to specify this in the argument for the view itself? Perhaps I can write a hook that interjects when the URL is being loaded, and rewrite in the background?
The solution I currently have is to add these paths (since my arguments are known) to the menu system. This works, except that when I the pages they aren't the front page, so the pages don't use the node themes I want (they use the node details theme).
I don't think you can really do this without custom code. The View needs a path before it starts taking arguments, and your URLs start with the arguments. What you can do is fake it with custom code. I've used something like this:
/**
* Implements hook_init().
*/
function mymodule_custom_init() {
$item = menu_get_item(); // Matching Drupal path
if (!$item) { // There is no matching Drupal path.
$_GET['q'] = 'view_path/' . $_GET['q']; // Fake path path.
} // if
} // mymodule_custom_init
Then you give the view a path of "view_path" and it responds to everything that doesn't match anything else in Drupal's paths.
There is a spot in the views UI for "Argument handling code" that takes a small snippet of php - http://drupal.org/node/70145
You could run some code there that checks to see if you are on the front page (or arguments are not present or whatever)and insert the arguments you want.
Another way is to set arguments via a hook_views_pre_view or hook_views_pre_build. Is better because you are sure you don't break other stuff (like another view block).
function MYMODULE_views_pre_view(&$view){
if ($view->name == 'your_view_name' && drupal_is_front_page()) {
$view->set_arguments(array('you_argument','you_second_argument'));
}
}

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!

Resources