I am building a custom module in Drupal 7. I have a URL which is like "review/edit/2"; I want that number in the end of the URL.
How can I access it using hook_menu()? A small snippet would be good.
This is a very simple example that will pass everything after review/edit/ to your page callback function:
function mymodule_menu() {
$items['review/edit/%'] = array(
'title' => 'Title',
'page callback' => 'mymodule_page_callback',
'page arguments' => array(2),
'access arguments' => array('access administration pages')
);
return $items;
}
function mymodule_page_callback($arg) {
// For the URL review/edit/2, $arg is equal to 2
}
The question is effectively two questions:
How do I access page arguments that are passed through the URL?
How do I create a menu that is used for more than one URL?
The answer for the first question is that every number that is used to define the page arguments of a menu callback (this is valid also for the access arguments, the title arguments, and the theme arguments) is replaced from the equivalent part of the path.
For example, a module can implement hook_menu() using the following code (the example is used in the documentation for hook_menu()):
function mymodule_menu() {
$items['abc/def'] = array(
'page callback' => 'mymodule_abc_view',
'page arguments' => array(1, 'foo'),
);
return $items;
}
When users access example.com/abc/def, the arguments passed to the page callback are "abc" and "foo". If I wanted to pass 1 to the page callback, I would have to define the menu callback as following:
function mymodule_menu() {
$items['abc/def'] = array(
'page callback' => 'mymodule_abc_view',
'page arguments' => array('1', 'foo'),
);
return $items;
}
In this case, the arguments passed to the page callback are "1" and "foo" because only the numbers used in the argument array are replaced. (It doesn't include strings that can be casted to numbers.)
The answer to the second question is to use wildcards to define the menu callback, which are of two types: the generic %, or the wildcard associated with an autoloader function (e.g. %node, %user).
Using wildcards, I can define a menu callback associated with a path such as "test/event/%" that will match more than one URL, such as example.com/test/event/string example.com/test/event/234q3, or example.com/test/event/369014, where the % character is replaced with any string made of all the characters until the first slash.
Using a wildcard such as %node, the value matched from the wildcard is passed to a function (in this case, node_load()), and the value returned from that value is passed to the page callback.
As your menu callback replies to a path starting with "review/edit," I get that the last number is the node ID of a review node that needs to be edit; in such case, I would implement hook_menu() using the following code:
function mymodule_menu() {
$items['review/edit/%node'] = array(
'page callback' => 'mymodule_review_edit',
'page arguments' => array(2),
);
return $items;
}
function mymodule_review_edit($node) {
// $node contains the node object for the node ID passed in the URL.
}
There is a difference between using the generic wildcard, and using a more specific wildcard: When the auto-loader function returns FALSE, the user gets a "page not found" error. This is useful to filter out the wrong arguments. For example, if I would call the menu callback I previously defined with example.com/review/edit/fersgdsf, I would get a "page not found" error because node_load() would not find a node with ID equal to "fersgdsf" (node IDs are numbers, not strings). I would not need to first verify that the argument is an integer, because node_load() will return FALSE when it doesn't find a node with the passed ID, which happens when I pass a string that isn't equal to a number.
Drupal 6 and higher defines other wildcards, such as %user, _%taxonomy_vocabulary_; the list for Drupal 6 is reported at the bottom of Wildcard usage and core's wildcards, which also explain how to use wildcards. That documentation page is for Drupal 6, but it is still valid for Drupal 7; as far as I know, Drupal 7 doesn't define new wildcards.
Related
I am new to Drupal, I have made a custom module using PHP, which shows List of Student's with Information, and want to call it, on click of Submenu item, named, student Info. Please guide me by step wise step procedure.
The starting place to look for generating a "page callback" (essentially making a url active in drupal) would be hook_menu. As suggested take a look at the documentation but a starting point to actually make your callback work would be this in a my_module.module file:
/**
* Implements hook_menu().
*/
function my_module__menu() {
$items = array();
$items['student-info'] = array(
'title' => 'Student Info', // This becomes the page title
'description' => 'Information about students.', // this is the link description
'page callback' => 'function_name_that_outputs_content', // this is the page callback function that will fire
'type' => MENU_CALLBACK, // this is the type of menu callback, there are several that you can use depending on what your needs are.
);
return $items; // make sure you actually return the items.
}
/**
* Output the page contents when someone visits http://example.com/student-info.
*/
function function_name_that_outputs_content() {
$output = 'My page content'
return $output;
}
How can I set PHP condition for access argument in my own module?
If it returns true, then we have access to particular page. If false, we have not access to.
Added: I have variable for each user, true or false. So I need to restrict access to page(also menu should not be shown) if it is false.
You may find more details in Menu example in examples project. But the important part is the "access callback":
# in hook menu:
$items['beeroclock'] = array(
'title' => 'Beer-o-clock!',
'page callback' => '_menu_example_menu_page',
'page arguments' => array(),
'access callback' => "is_it_friday_yet",
);
#in your module, global scope
function is_it_friday_yet() {
$access = FALSE;
if (date('w') == 5) {
$access = TRUE;
}
return $access;
}
For D6 look at the following hooks:
http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_perm/6
http://api.drupal.org/api/drupal/developer--hooks--node.php/function/hook_access/6
You find a lot of example code in every module, that gives you specific permissions.
You can also put your own php code inside a singular page if you don't need something "special"
look here
I created a hook in order to add an item to the administrator's menu. When the user clicks on the item, I want to return the content of a specific view I created. How should I return the view?
My current code looks like:
function my_view_menu(){
$items['view'] = array(
'title' => 'Report',
'page callback' => 'return_my_view',
'access arguments' => array('access content'),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
function return_my_view(){
return t("Hello!");
}
EDIT:
As suggested by Berdir, this the correct way to call a view:
function return_my_view(){
$viewName = 'my_report'; // use the machine readable name of the view
return views_embed_view($viewName);
}
You could just add a menu item in the view itself...and restrict access (to the view) to the admin role of choice :)
In your view choose "page" and click
on the "Add Display" button (if there isn't already a page display).
Under
"Page Settings" add a Path and a
Normal Menu-Entry in the Navigation
Menu
Next Under Basic Settings
change the access to Role based and
choose the role(s) that should have
access
Finally go to the
navigation menu settings and drag
the new menu item to the desired
place in the Administer menu
You want views_embed_view(), see http://web.archive.org/web/20110213234806/http://thedrupalblog.com/embedding-view-drupal-6-using-views-embed-view
views_embed_view() is the correct call. If you are getting a blank page, try checking your apache error log to see if there are any php errors. I also notice that in your revised example you used $viewName = "my-report", but views_embed_view() expects the machine readable name of the view, which only allows for alphanumeric and underscore characters. Perhaps you are using the incorrect name?
Third technique: Once you have created a Page Display for a View, Views will provision that page with a menu entry. Once that exists, it is possible to duplicate that menu entry for your own purposes.
Create a module with a weight of at least 11 (higher weight than Views)
Implement hook_menu_alter() and duplicate the View entry.
function example_menu_alter(&$items) {
$items['admin/new/path'] = $items['original/view/path'];
}
This approach is somewhat convoluted, but is sometimes a useful alternative for Views or other "page" content you want to clone.
In addition to berdir's comment, you can also skip the intermediate callback function and just call views_embed_view directly from your menu router:
function hook_menu(){
$items['path/to/my/view'] = array(
'title' => 'Report',
'page callback' => 'views_embed_view',
'page arguments' => array('my-view-name'),
'access arguments' => array('access content'),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
I want to pass
var x = 10; to drupal/php variable $x without page refresh.
Can anyone suggest?
Thank you so much for reply.
Actually I am developing custom module for E commerce site(Drupal 6).
On product page, I have 4 attributes as text field (Width inch, width foot, height inch, height foot) By doing some calculation I got new price for that product. I did that calculation in j query.
Now I alter "uc_add_to_cart_form" and add 1 hidden field and get jquery new price into that hidden field using $_SESSION. Upto this point everything is working fine.
I want to save this new price to product node. I am not able set this new price to $node->price.
I am using uc_ajax_cart for "Add to cart" button.
Can anyone please suggest me how can I proceed?
Passing the variable data from JavaScript to Drupal/PHP is one thing. Passing the information from Drupal/PHP to JavaScript is another separate thing. I think that you meant the first one in your question. So I'll go ahead and answer that.
Passing variables from JavaScript to Drupal/PHP
1) Implement a menu hook. You can do that on an existing contributed or new custom module, it depends what you are doing. It's pretty easy and straight forward, it is also well documented on the Drupal site.
$items = array();
$items['my_custom_callback/%'] = array(
'title' => 'My Custom Callback',
'description' => 'Listing of blogs.',
'page callback' => 'my_custom_php_function',
'page arguments' => array(1),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
Let's break down the example above.
$items['my_custom_callback/%']
$items is just an array, and it can be named anything you like. If the URL of my website is www.alexanderallen.name, then http://www.alexanderallen.name/my_custom_callback would be the resource that I would call from my JavaScript application.
In the array's key, the percentage symbol after the word my_custom_callback is a placeholder. This is where you will pass your data from JS to PHP. So if the value of variable x is 10, the call from your JQuery will look like this:
http://www.alexanderallen.name/my_custom_callback/10
When you load that URL, Drupal will find your menu hook, and it will call the function you defined in the 'page callback' array key. In this case that would be:
<?php
/**
* Function that gets called from JQuery asynchronously.
*/
function my_custom_php_function($argument) {
// Do something with $argument...
echo $argument;
}
?>
There you can do anything you want with the value of variable x, like storing it in the database.
If you were to put the menu hook on a custom module, and the name of your module were example_module, the menu hook and custom PHP callback would look like this:
<?php
/**
* #file example_module.module Handles fancy AJAX functionality.
*
*/
/**
* Implementation of hook_menu
*
* #see http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_menu/6
*/
function example_module_menu() {
$items = array();
$items['my_custom_callback/%'] = array(
'title' => 'My Custom Callback',
'description' => 'Listing of blogs.',
'page callback' => 'my_custom_php_function',
'page arguments' => array(1),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Function that gets called from JQuery asynchronously.
*/
function my_custom_php_function($argument) {
// Do something with $argument...
echo $argument;
}
?>
If you wanted to pass multiple variables from JQuery to Drupal/PHP, you could consider JSONifying them in JavaScript, before passing it to PHP. Remember that if the payload is very large you should consider JQuery .post() instead of .get(). The PHP functions for decoding JSON are here on php.net.
Don't forget to return $items at the end of the example_module_menu() function. If you wanted to pass two or more arguments from JavaScript to PHP then the menu item would look similar to this:
function example_module_menu() {
$items = array();
$items['my_custom_callback/%/%/%'] = array(
'title' => 'My Custom Callback',
'description' => 'Listing of blogs.',
'page callback' => 'my_custom_php_function',
'page arguments' => array(1, 2, 3),
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
function my_custom_php_function($arg1, $arg2, $arg3) {
// Do something ...
$sum = $arg2 + $arg3;
echo "Hi". $arg1 .", X + Z = ". $sum;
}
?>
In this case I am passing three arguments to PHP. 'page arguments' specifies the part of the URL that you want to pass to PHP. If you were to write 'page arguments' => array(0),, then the argument to your PHP function would be the string my_custom_callback.
2) Call the menu hook from your JS code using JQuery. JQuery provides various AJAX methods. Which one you use will depend on the format of the data your dealing with and it's size, amongst other things.
Most likely you will use one of two JQuery methods. .post() or .get(), bearing in mind that that post requests will allow for a bigger data payload than get requests. So if the size of variable x = 10, you might want to stick with .get(). Both .post() and .get() methods are an extension of, or rely on the .ajax() method. The .ajax() method is more flexible because it provides you with more options, like the ability to specify the format of the data (JSON or plain-text), send a username/password along with your request, choose between synchronous or asynchronous requests, and whether the browser should cache or not the request.
This is the method signature for the JQuery .get() method:
jQuery.get( url, [ data ], [ callback(data, textStatus, XMLHttpRequest) ], [ dataType ] )
If you were going to send your data using GET to Drupal/PHP, using the example above you could do something like this:
<script type='text/javascript'>
var name = 'alexander';
var x = 10;
var z = 20;
$.get(
// Callback URL.
"http://www.alexanderallen.name/my_custom_callback/"+ name +"/"+ x +"/"+ z
);
</script>
Note that the usage of the data argument on the .get() method is optional, and in my example I omitted it, but achieved the same effect by concatenating the data to pass to Drupal/PHP with the rest of the URL.
If I were to run that, the HTTP GET request would look like:
http://www.alexanderallen.name/my_custom_callback/alexander/10/20
and the response would look like:
Hi Alexander, X + Z = 30
Passing data from Drupal to JavaScript
For that then you would use Behaviors, I am not entering into details since I think that wasn't your question, but you can go to the official documentation for Drupal 6 behaviors here.
You need to know:
The menu API:
http://api.drupal.org/api/drupal/includes--menu.inc/group/menu/7
Some Drupal JSON functions (maybe)
http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_json_encode/7
variable_set()
http://api.drupal.org/api/drupal/includes--bootstrap.inc/function/variable_set/7
And jQuery ajax API:
http://api.jquery.com/category/ajax/
Essentially, you need to create a menu item that delegates to a callback (be careful with your permissions) and then call it with $.ajax(). If you need to do something with the response, see the json_encode functionality.
You might want to take a look here for some menu examples:
http://drupal.org/project/examples
I think there is a Drupal interface to the jQuery ajax API. Perhaps someone can comment if they know a better way.
A node loads a profile of a user (external database + views). All of this works when I visit: node/123/profile/id/3. Now I have implemented hook_menu() in order to load any profile page and have nicer URLs.
When I load it myself for some reason $left in page.tpl.php is suddenly empty and many more variables seem not to be loading. I have tried many different functions to render and create the correct $output but realized that node_show() seems to be the function of choice.
Testing has shown now that for some reason hook_nodeapi() calls are ignored.
My code:
/**
* Implementation of hook_menu
*/
function modulename_menu() {
$items = array();
$items['my/nice/url/profile'] = array(
'description' => 'This page holds a view that shows profiles based on the %',
'page callback' => 'website_profile_load',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Menu path wildcard callback
*/
function website_profile_load() {
$output = node_show(node_load(1221), false, true);
return $output;
}
So what is the correct way to do this and get Panels (see comment below) to load correctly?
UPDATE:
I am using table wizard and Views 2 to connect to another database with information about people that aren't users of the system. This is an alumni page, the page is administered externally and showed internally (nothing I can do about, have to make this work :)
Just discovered that Panels aren't loaded at all. So even if the node I am trying to load is uses panels for some reason none of that is loaded.
/**
* Menu path wildcard callback
*/
function website_profile_load($uid = null) {
if (!$uid) {
global $user; // if no user passed in argument, show current user profile
$uid = $user->uid;
}
$output = drupal_render(content_profile_show_profiles($uid));
}
There are many reasons why rendering the result of a node_load is different from going to the stock Drupal path /node. It is way too much to go over here honestly but the short answer is that you have to define a template/theme and blocks etc for each page you create. Just because you make a new path and do a node_load in the callback for that path doesn't mean Drupal can automagically know how you want to display that content. It simply loads data from the node and it is available to do whatever you please with it after that. This is why you get a blankish looking page instead of what you'd expect from going through /node.
However I will offer this simple solution since it sounds like you want the exact same page you'd get when you go to 'node/123/profile/id/3' but accessible through a link you define yourself. You just need to setup a redirect in your hook_menu like so:
$items['my/nice/url/profile'] = array(
'description' => 'This page holds a view that shows profiles based on the %',
'page callback' => 'drupal_goto',
'page arguments' => 'node/123/profile/id/3',
'access callback' => TRUE,
'type' => MENU_CALLBACK);
This is essentially saying that when you navigate to 'my/nicer/url/profile' it runs: drupal_goto('node/123/profile/id/3');
I found the answer to be that apparently somewhere in the pipeline of creating a node Drupal uses $path (that is originally set by $_GET['q']) and sometimes also $_GET['q'] to determine how to render the page. NOTE that I am using Panels and Ctools Page Manager modules in order to get my things working correctly.
It turns out Panels, if you search the code, looks at $_GET['q'] for quite an amount of things.
Here is what I ended up with:
/**
* Implementation of hook_menu
*/
function modulename_menu() {
$items = array();
// For department and having nice URL's for their profile pages.
$items['my/nice/url/profile/%'] = array(
'description' => 'This page holds a view that shows profiles based on the %',
'page callback' => 'website_profile_load',
'page arguments' => arg(4),
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Menu path callback
*/
function website_profile_load($id = NULL) {
// Rename the query internally since other functions base the design
// on the way the query is structured and not simply by the node which
// is currently loading.
if(!empty($id)) {
$path = $_GET['q'] = 'node/1221/profile/id/' . $id;
}
// Use ctools function to correctly display node view since
// this site heavily uses ctools rendering for panels and
// web parts / web pages.
drupal_load('module', 'page_manager');
ctools_include('node_view', 'page_manager', 'plugins/tasks');
if(function_exists('page_manager_node_view')) {
$output = page_manager_node_view(node_load(1221));
} else {
// Will display incorrectly but still load the UI
$output = node_page_view(node_load(1221));
}
return $output;
}
And it works :)