I realize this question has been asked, but I either simply don't understand or the previous answers don't apply (or I don't understand how to apply them) to my circumstance. Here goes:
I have a custom module named:
"my_module" in /sites/all/modules/custom/my_module
I have a module file:
/sites/all/modules/custom/my_module/my_module.module
I have a page template name "page-mypage" which is NOT in my module:
/sites/all/themes/mytheme/pages/page-mypath-mypage.tpl.php
I made the hook menu for this:
$items['mypath/mypage'] = array(
'title' => 'My Page!',
'page callback' => 'my_module_mypage',
'page arguments' => array(1,2),
'access callback' => true,
'type' => MENU_CALLBACK,
);
In the function, I build up some content like so:
function my_module_mypage($x, $y) {
$output = "foo AND bar!";
return $output;
}
In the template (again, NOT in my module folder, but in the THEME subfolder "pages", I have:
<?php print $content ?>
When I go to http://mysite/mypath/mypage I get "foo AND bar!"
Now for the question. I want a new variable, defined in my_module_mypage(), called '$moar_content'. I want to output $moar_content in my page-mypath-mypage.tpl.php. I only need to do this for this module and for this template. I do not need it theme-wide, so I don't think using mytheme's 'template.php' is appropriate.
I think I need to use some kind of preprocessing, but everything I try fails, and everything I read seems to be missing some kind of magic ingredient.
My thinking was:
function my_module_preprocess_page_mypath_mypage(&$variables) {
$variables['moar_content'] = 'OATMEAL';
}
or
function my_module_preprocess_my_module_mypage(&$variables) {
$variables['moar_content'] = 'OATMEAL';
}
or something. I'm pretty sure I'm on the right track, but I'm hitting a brick wall.
To do the job, you must follow Drupal's best practices, supposing you are using D6, so you can insert some variables to your template like this :
// You menu path is good
$items['mypath/mypage'] = array(
'title' => 'My Page!',
'page callback' => 'my_module_mypage',
'page arguments' => array(1,2),
'access callback' => true,
'type' => MENU_CALLBACK,
);
Second thing, we define the theme hook for our page
// We define here a new theme file for your your page
// Your theme file must be located in your module's folder
// you can use a subfolder to group all your module's theme files
// E.g : themes/my-module-theme.tpl.php
// Note that in theme files, we change _ by -
function my_module_theme() {
return array(
'my_module_theme' => array( // Keep that name in your mind
'template' => 'my_module_theme',
'arguments' => array(
'my_var' => NULL,
'my_var2' => NULL,
),
)
);
}
Now we can create a file "my-module-theme.tpl.php" in the root folder of our module, and paste something like "foo AND bar!"
Back to our my_module.module, the callback must be something like :
function my_module_mypage($x, $y) {
// $x and $y are optionnal, so this is the first manner
// to inject variables into your theme's file
$output = theme("my_module_theme", $x, $y);
return $output;
}
Also you can use preprocess hook to insert variables
// The hook must be named like this : template_preprocess_NAME_OF_THEME_FILE
// where NAME_OF_THEME_FILE is the name that you kept in your mind ;)
function template_preprocess_my_module_theme(&$variables) {
// Do some job
$var1 = 'Foobar';
// So in "my-module-theme.tpl.php", $my_var1 will print Foobar
$variables['my_var1'] = $var1;
}
Related
I am trying to write my first Drupal custom module (using drupal 6). I got the basic module working but I would like to add another page to the project. For example, right now my module's path looks like this: www.mysite.com/my_custom_module. I would like to add another page like this: www.mysite.com/my_custom_module/my_sub_page. I've tried adding a new .module and .info file but this does not work. I've also tried adding new items to the menu hook, like this:
my_custom_module.module:
function my_custom_module_menu(){
$items = array();
$items['my_custom_module'] = array(
'title' => "My Custom Module",
'page callback' => "my_custom_module_info",
'access callback' => true,
'type' => MENU_NORMAL_ITEM,
);
$items['my_custom_module/my_sub_page'] = array(
'title' => "My Sub Page",
'page callback' => "my_custom_module_sub_page_info",
'access callback' => true,
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
function my_custom_module_info(){
$result = 'My Page URL was hit';
return $result;
}
function my_custom_module_sub_page_info(){
$result = 'My Sub Page URL was hit';
return $result;
}
In this example, if I go to .../my_custom_module it works fine. But, if I go to .../my_custom_module/my_sub_page, it still load, and displays my_custom_module. When I debug with a break point in each function, only my_custom_module_info is hit. Not the sub page. What am I doing wrong? I this even the correct way to create multi pages in a module? Just an FYI, each of these pages will have different audiences. The first page is to allow a user to submit some form data. The second is to allow an elevated user view the data.
Thanks
jason
I would like to use the node-menu (see image) on a page in Drupal.
Is this possible and, if yes, how?
If the page you are referring is a custom page output from your module, and "mymodule/page" is the path for that page, for which you want the tabs "View" and "Edit," then you should implement hook_menu() using code similar to the following one:
function mymodule_menu() {
$items = array();
$items['mymodule/page'] = array(
'page callback' => 'mymodule_page_view',
'access arguments' => array('view mymodule page'),
);
$items['mymodule/page/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['mymodule/page/edit'] = array(
'title' => 'Edit',
'page callback' => 'mymodule_page_edit',
'access arguments' => array('edit mymodule page'),
'weight' => 0,
'type' => MENU_LOCAL_TASK,
);
return $items;
}
If the page you are referring is a page that is shown at example.com/mymodule/page, and that should show what you see at example.com/node/7, then you can implement the following code, in Drupal 7:
function mymodule_url_inbound_alter(&$path, $original_path, $path_language) {
if (preg_match('|^mymodule/page|', $path)) {
$path = 'node/7';
}
}
The equivalent for Drupal 6 is writing the following code in the settings.php file:
function custom_url_rewrite_inbound(&$result, $path, $path_language) {
if (preg_match('|^mymodule/page|', $path)) {
$result = 'node/7';
}
}
I didn't write the symmetric code for hook_url_outbound_alter(), and custom_url_rewrite_outbound() as I suppose you are not interested in rewriting example.com/node/7 to make it appear as example.com/mymodule/page, but you are interested in making appear example.com/mymodule/page the same as example.com/node/7.
Okay; a node page usually has those View and Edit tabs. So my next question is to wonder why they aren't appearing on your node page already. Have you by chance created a custom page template for this node type and removed the code that prints the tabs? Or is there a chance you're logged in as a user that doesn't have permission to edit this type of node?
There must be a reason why you're not getting those tabs; they should be there, by default.
If you do have a custom page template for this node type, look for code that looks something like this:
<?php if ($tabs): ?>
<div class="tabs"><?php print $tabs; ?></div>
<?php endif; ?>
If you don't see code like that, try adding it.
If you DO see code like that, try to isolate what's different about this content type compared to other content types where you DO see those tabs.
What I am trying to do is generate some raw output within a module.
I would like to pass an array of data through to a template file, and then use that data to populate the code from the template. The template is represented by a file in my theme folder.
I have a hook set up for a certain URL (/itunes):
$items['itunes'] = array(
'page callback' => 'itunespromo_buildpage',
'type' => MENU_SUGGESTED_ITEM,
'access arguments' => array('access content'),
);
..inside itunespromo_buildpage...
function itunespromo_buildpage() {
//grab some data to pass through to template file, put into $promo_data
$details = theme('itunes_page', array(
'promo_data' => $promo_data,
));
return $details;
}
Here is the hook_theme():
function itunespromo_theme() {
return array(
'itunes_page' => array(
'template' => 'itunes_page',
),
);
}
Inside my theme's template.php:
function geddystyle_itunes_page($vars) {
return print_r($vars['promo_data'], true);
}
Right now, $promo_data is being passed through fine, and it is print_r'd on to the result page. However, I'd like to then take this $promo_data variable and use it in my itunes_page.tpl.php template file.
I'm kind of certain I'm close here. Am I supposed to call some sort of render function and pass the $promo_data variable to it from function itunespromo_theme()?
I believe you just need to update your hook_theme() to provide the ability to send variables to your template file.
Something like this should do the trick:
function itunespromo_theme($existing, $type, $theme, $path) {
return array(
'itunes_page' => array(
'variables' => array(
'promo_data' => NULL,
),
'template' => 'itunes_page',
)
);
}
Also, instead of calling the theme() function directly what you want to be doing is actually constructing a renderable array and letting Drupal call the theme() function. What you should be doing is calling drupal_render which in turn calls theme() for you. Look at this piece of advice here for a little more clarity:
http://drupal.org/node/1351674#comment-5288046
In your case you would change your function itunespromo_buildpage to look something like this:
function itunespromo_buildpage() {
//grab some data to pass through to template file, put into $promo_data
$output = array(
'#theme' => 'itunes_page',
'#promo_data' => $promo_data //call $promo_data from the tpl.php page to access the variable
);
$details = drupal_render($output);
return $details;
}
I am having a hard time understanding what hook_theme() does.
My understanding is that it has something to do with making it possible to override templates.
I was looking at:
$theme_hooks = array(
'poll_vote' => array(
'template' => 'poll-vote',
'render element' => 'form',
),
'poll_choices' => array(
'render element' => 'form',
),
'poll_results' => array(
'template' => 'poll-results',
'variables' => array('raw_title' => NULL, 'results' => NULL, 'votes' => NULL, 'raw_links' => NULL, 'block' => NULL, 'nid' => NULL, 'vote' => NULL),
),
'poll_bar' => array(
'template' => 'poll-bar',
'variables' => array('title' => NULL, 'votes' => NULL, 'total_votes' => NULL, 'vote' => NULL, 'block' => NULL),
),
);
Could you provide an example of how it works?
It provides a place for a module to define its themes, which can then be overridden by any other module/theme. It will also provide the opportunity for any module to use a hook such as mymodule_preprocess_theme_name to change the variables passed to the eventual theme function or template file.
There are basically two ways to initialise a theme function:
theme('poll_results', array('raw_title' => 'title', 'results' => $results, etc...));
and
$build = array(
'#theme' => 'poll_results',
'#raw_title' => 'title',
'#results' => $results,
etc...
); // Note the '#' at the beginning of the argument name, this tells Drupal's `render` function that this is an argument, not a child element that needs to be rendered.
$content = render($build); // Exact equivalent of calling the previous example now that you have a render array.
Please keep in mind, you should avoid calling theme() directly (per the documentation in theme.inc) since it:
Circumvents caching.
Circumvents defaults of types defined in hook_element_info(), including attached assets
Circumvents the pre_render and post_render stages.
Circumvents JavaScript states information.
In Drupal 8, theme() is a private function, _theme(). For more detail, please see www.drupal.org/node/2173655.
When you compare the two of these to the poll_results element in the example you give above you can probably work out what's happening...since PHP is not a strongly typed language Drupal is providing 'named arguments' through either a keyed array passed to the theme function, or as hashed keys in a render array.
As far as 'render element' is concerned, this basically tells the theme system that this theme function will be called using a render array, with one named argument (in this case form). The code would look something like this:
$build = array(
'#theme' => 'poll_choices',
'#form' => $form
);
This will pass whatever's in the $form variable to the theme function as it's sole argument.
Regarding the template key:
'poll_vote' => array(
'template' => 'poll-vote',
'render element' => 'form',
)
defines a theme called poll_vote which uses a template file (hence the template key) with a name of 'poll-vote.tpl.php' (this is by convention). The path to that template file will be found by using the path to the module that implements it (e.g. modules/poll/poll-vote.tpl.php), so it's fine to put template files in sub-folders of the main module folder.
There are two ways to actually return the output for a theme function, by implementing the physical function name (in this case it would be theme_poll_vote) or by using a template file. If the template key is empty Drupal will assume you've implemented a physical function and will try to call it.
Template files are preferable if you have a fair bit of HTML to output for a theme, or you simply don't like writing HTML in strings inside PHP (personally I don't). In either case though, the variables passed when you call the theme (either using theme() or a render array as described above) are themselves passed through to the template file or theme function. So:
function theme_poll_results(&$vars) {
$raw_title = $vars['raw_title'];
$results = $vars['results'];
// etc...
}
If you were using a template file instead for the same method the variables would be available as $raw_title, $results, etc, as Drupal runs extract on the $vars before parsing the template file.
I'm sure there's a lot I've missed out here but if you have any more specific questions ask away and I'll try to help out.
Drupal 6
I was stuck all day with this and now successfully implemented, so sharing my finding here, may it will help understand hook_theme.
There are 3 steps involved:
hook_theme
function YOURMODULENAME_theme() {
return array(
'xxx_xxx' => array(
'template' => 'xxx-xxx', // define xxx-xxx.tpl.php inside module
'arguments' => array('xxx' => null), //define $xxx so it will available in your xxx-xxx.tpl.php
),
);
}
echo/return the theme in your .tpl or any .module
$output = theme('xxx_xxx', $xxx);
Now variable are magically available in you xxx-xxx.tpl.php.
<?php echo $xxx ?>
Note: you can pass $xxx as array,object or anything :)
There is yet another way: (can be found in Bartik theme)
The scenario here is that we have created our own module and want to override the default output for let's say a node with title 'zzz' only.We don't know and don't really care how the default output is generated. All we need is to tell Drupal to use our own custom template file (node--custom--name.tpl.php) to render that specific node.
These are the steps:
Tell Drupal where our template file lives. (Keep in mind that this function will take effect only once and after clearing Drupal's cache):
// Implements hook_theme()
function mymodulename_theme() {
$theme = array();
$theme['node__custom__name'] = array(
'render element' => 'node',
'template' => 'path_from_mymodule_root/node__custom__name',
);
return $theme;
}
Tell Drupal where and when to use it
//Implements hook_preprocess_node()
function mymodulename_preprocess_node($vars) {
if($vars['node']->title == 'zzzz') {
$vars['theme_hook_suggestions'][] = 'node__custom__name';
... your other code ...
}
}
Now Drupal will use our template file for that specific case only, provided that 'node--custom--name.tpl.php' file is in the declared path, otherwise it will keep searching according to the suggestions naming conventions for a fallback template.
Is there anyway I can customize the paths to a node's tabs?
When using either pathauto or hook_menu_alter, I can change the path to the node view to from node/node_id to, well pretty much anything, but let's say xyz/node_title.
The node tabs, however, still stay with the path /node/node_id/tab_name
I'm trying to add a custom tab to the node, and keep the custom path as well (e.g.: xyz/node_title/tab_name instead of node/node_id/tab_name).
I manage to add the custom tab via hook_menu:
$items['node/%node/members'] = array(
'title' => 'Manage Membership',
'page callback' => 'mymodule_members',
'page arguments' => array(1),
'access callback' => 'mymembers_members_access',
'access arguments' => array(1),
'type' => MENU_LOCAL_TASK
);
but if I try to customize the path, either at hook_menu or at hook_menu_alter, the tab just disappears.
Any ideas?
PS, I've posted the same question on the Drupal forums, no answer so far. I'll cross-update.
You can add two functions in settings.php: custom_url_rewrite_inbound(), and custom_url_rewrite_outbound().
The examples in those pages should make clear how to use them.
function custom_url_rewrite_inbound(&$result, $path, $path_language) {
global $user;
// Change all article/x requests to node/x
if (preg_match('|^article(/.*)|', $path, $matches)) {
$result = 'node'. $matches[1];
}
// Redirect a path called 'e' to the user's profile edit page.
if ($path == 'e') {
$result = 'user/'. $user->uid .'/edit';
}
}
function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
global $user;
// Change all 'node' to 'article'.
if (preg_match('|^node(/.*)|', $path, $matches)) {
$path = 'article'. $matches[1];
}
// Create a path called 'e' which lands the user on her profile edit page.
if ($path == 'user/'. $user->uid .'/edit') {
$path = 'e';
}
}
Drupal 7 uses two new hooks, instead of those functions: hook_url_inbound_alter() and hook_url_outbound_alter().
I think this module will help you form the tab path the way you want: Sub-path URL Aliases.