I am developing a Drupal module. Part of it opens a pop-up window, displays some elements in it, and uses JavaScript to transfer input back to the main page.
As this is a small window, I don't want it to display the full theme borders from the site's theme.
In Drupal 6 I was able to achieve this with the following:
function MyPopupPageHandler() {
#content = "Content...";
//Add additional content to #content;
print theme("page", #content);
}
However, Drupal 7 expects the second parameter of the theme() function to be an array, and none of the examples I've seen show how I set the main content of the page.
What I'd like is a custom page template in the module, that can be overridden by the site theme if it provides one.
I would like to know:
What elements do I need to put in the array I pass to the theme() function?
What should I call my template file?
How do I tell Drupal where to find my template, as it needs to be in the module by default?
Appreciate any help you can offer.
James
Try this
First of all create a menu in .module file
function MYMODULE_menu()
{
$items['Demo'] =array(
'title' => 'Demo Page',
'page callback' => 'demo_page', // call a function
'access arguments' => array('access content'),
);
return $items;
}
After you have create a function
function demo_page()
{
$select = db_select('node', 'n');
$select = $select->fields('n', array('id'))
->extend('PagerDefault');
$queried_nodes = $select->execute()
->fetchAllAssoc('id');
$pager = theme('pager');
return theme('demo_template', array('nodes' => $queried_nodes , 'pager' => $pager)); // call a theme or you have no pass any argument in theme to change a 'nodes'=> NULL or 'pager'=>NULL
}
after i have create a theme function
function MYMODULE_theme()
{
return array(
'demo_template' => array(
'template' => 'demo-page',//this is file name of template file
'variables' => array('nodes' => NULL,'pager' => NULL), //this is pass avarible of templates file
'path' => drupal_get_path('module', 'MYMODULE_NAME').'/template' // set a path of file
),
);
}
after you have create file name like demo-page.tpl.php in sites/all/modules/MYMODULENAME/template/
and clear caches
1) The second parameter of theme() function must be an associative array. Your function should look like :
function MYMODULE_custom_function() {
$content = "Some stuff";
return theme('MYMODULE_custom_output', array('content' => $content));
}
2) I guess you meant "Where should I call my template file?" In the hook_theme() function in your same .module file :
function MYMODULE_theme() {
return array(
'MYMODULE_custom_output' => array(
'variables' => array('content' => array()),
// You can also use template file here : 'template' => 'MYMODULE-template'
// OR use the following theme_function() if you don't want to create a new file
),
);
}
// If you don't use template file
function theme_MYMODULE_custom_output($variables) {
$output = // Arrange your html output here
return $output;
}
3) To tell where to find your custom template file if you decide to use one, you can read this : https://drupal.org/node/715160 and I hope it will help.
Please stay indulgent because I'm still new with Drupal and I did try to do my best here :o)
Related
I am new to Drupal and PHP. I am trying to create a module that when activated creates a page, then creates a block, configures block to only show on the created page, and then display an included PHP file. The PHP file that is included is for accessing an off site database.
I have managed to code the module to create the page, and create the block. But the following is my issue.
In the hook_block_view, for content I have input a function name, below I defined the function to include testpage.inc. that page loads but the content does not load in the block's specified region. If I just input text for the content, it loads in the region no problem.
When I define the include as the output value, the block loads the file path, and the included file loads at the top of the page outside of the block.
The Included file is a PHP file that accesses an off site web-service to display a company's Inventory.
<?php
//////////////////////////////////////////////////////////////////
/** using hook_menu to create a page for the module - hook_menu**/
/////////////////////////////////////////////////////////////////
function sps_webconnect_menu() {
$items['products']=array(
'title' => 'Products',
'type' => MENU_NORMAL_ITEM,
'page callback' => 'sps_webconnect_products',
'access callback' => TRUE,
);
return $items;
}
/** This function defines the page callback from above**/
function sps_webconnect_products() {
return " ";
}
////////////////////////////////////////////////////////////////
/** Creates and Defines Block - hook_block_info**/
////////////////////////////////////////////////////////////////
function sps_webconnect_block_info() {
$blocks['sps_webconnect_block'] = array(
'info' => t('Products Page - SPS Web Connect'),
'cache' => DRUPAL_NO_CACHE,
'status' => TRUE,
'region' => 'content',
'visibility' => BLOCK_VISIBILITY_LISTED,
'pages' => 'products',
);
return $blocks;
}
//////////////////////////////////////////////////////////////////////
/* This function defines the content of the block - hook_block_view */
/////////////////////////////////////////////////////////////////////
function sps_webconnect_block_view($delta = '') {
$block = array();
switch ($delta) {
case 'sps_webconnect_block':
$block['subject'] = t('SPS Product Page');
$block['content'] = sps_webconnect_file();
break;
}
return $block;
}
function sps_webconnect_file(){
$output = '';
module_load_include('inc', 'sps_webconnect', 'testpage');
return $output;
}
You did not provide the content of the inc file but I am pretty sure it outputs (echo/print) data/html and it should not since the last function is supposed to return output.
So you could have a function inside your inc file that returns some html and then call it from your last function to populate $output.
Correct, The inc File did include an Echo. When I included a function the inc file and called that function for Output the inc file was loaded in the block. I found out that the reason for this is that Drupal uses Output Buffering, which compiles all of the markup before sending it to the browser. The File I was trying to include was not output buffer friendly.
I am looking for input/help on how to do this. Might be some PHP/cake developers could provide some good solutions here. Cakephp 2.3 something :)
Problem; How to put shortcodes in wysiwyg editor (example: [slideshow=1]slideshow here[/slideshow]) and render an element (in this case, loading and displaying the slideshow with ID=1).
ShortcodeHelper.php
App::import('Helper', 'Html', 'Router');
class ShortcodeHelper extends AppHelper {
public $shortcodes = array(
'slideshow' => '/(\[slideshow=)(.+?)(\])(.+?)(\[\/slideshow\])/'
);
public $returncodes = array(
//'slideshow' => $this->render('/elements/slideshow', array('id'=>'\\2'))
'slideshow' => '<strong rel="\\2">\\4</strong>'
);
public function render($content, $render=null) {
$shortcodes = $this->shortcodes;
$returncodes = $this->returncodes;
if(isset($render)) {
$temp_shortcodes = array();
$temp_returncodes = array();
foreach ($render as $key => $value) {
$temp_shortcodes[$key] = $shortcodes[$value];
$temp_returncodes[$key] = $returncodes[$value];
}
$returncodes = $temp_returncodes;
$shortcodes = $temp_shortcodes;
}
$return = preg_replace($shortcodes, $returncodes, $content);
return $this->output($return);
}
}
view.ctp (call render function from helper, and run the page-content trough it):
<?php echo $this->Shortcode->render($page['Page']['body']); ?>
Thanks. You are awesome!! :)
-Tom
You need to turn the short code string into a method call, parse it.
Your helper will need to be able to detect them and then break them up. Your code needs to be mapped somehow to a callback.
// [slideshow=1]slideshow here[/slideshow]
$this->requestAction(array('controller' => 'slideshows', 'action' => 'view', $id);
For example.
I think the best way here would be to just always map the first arg, the "function call" to an element instead and pass all other args to the element. This way you can do there whatever you want and request the data or just simply display HTML only.
I would put the mapping of short codes into something like Configure::write('ShortCodes', $shortCodeArray); this way plugins could even register their callback mapping by simply adding them to that array.
array(
'slideshow' => array('controller' => 'slideshows', 'action' => 'view')
);
You'll have to merge that with args from the parsed short code.
Why requestAction()? You should not violate the MVC pattern, for this reason you'll have to request the data via requestAction().
I am porting a module from Drupal 6 to Drupal 7 and I am trying to pass a variable from my custom module to a template. I have something like this:
function my_callback_function(){
... //some unrelated code
$page_params = array();
$page_params['items_per_page'] = 25;
$page_params['page'] = $_GET['page'] ? $_GET['page'] : 0;
$page_params['total_items'] = $data_provider->getNumItems();
$page_params['total_pages'] = $data_provider->getNumPages($page_params['items_per_page']);
return theme('my_theme', $page_params);
}
function my_module_theme($existing, $type, $theme, $path) {
return array(
'my_theme' => array(
'variables' => array('page_params' => NULL),
'template' => 'theme/my_template_file',
),
);
}
And inside *my_template_file.tpl.php* I try to use $page_params:
<?php print $page_params['total_items']; ?>
All that make my site to throw the following error:
Fatal error: Unsupported operand types in
C:...\includes\theme.inc on line 1075
Which corresponds with these lines of code in theme.inc:
// Merge in argument defaults.
if (!empty($info['variables'])) {
$variables += $info['variables']; // THIS IS THE VERY EXACT LINE
}
elseif (!empty($info['render element'])) {
$variables += array($info['render element'] => array());
}
If I leave the theme() call as it was in Drupal 6, the error doesn't appear but then my template doesn't recognize my $page_params variable:
return theme('my_theme', array('page_params' => $page_params));
I have read half the API trying to figure out what I am doing wrong but as far as I have read, it seems that this is the proper way to pass variables from a custom module to a template.
Thus, any kind of help will be more than welcome.
Thanks in advance.
Finally, I figured out what I was doing wrong. In fact, they were a couple of things:
My theme() call was ok:
return theme('my_theme', $page_params);
But my hook_theme implementation wasn't. If $page_params is my array of variables, i cannot use the whole array as a variable, I have to explicitly specify which are my variables inside the array. Something like this:
function my_module_theme($existing, $type, $theme, $path) {
return array(
'my_theme' => array(
'variables' => array(
'items_per_page' => NULL,
'page' => NULL,
'total_items' => NULL,
'total_pages' => NULL,
),
'template' => 'theme/my_template_file',
);
}
And finally, inside my_template_file.tpl.php I will have to use the variable names directly instead of using them as a component of $page_params:
<?php print $total_items; ?>
It may seem obvious for experienced users but it took me a while until I realized this. I hope it can be useful for other beginners like me.
You can use drupal variable_set() and variable_get() to store data in drupal session and get data from session.
Thanks
This would be most simple functional question in Drupal 7.
Now below mentioned things are mandatory to do and cant change the functionality.
1) I have created a custom block in module.
2) I have included form like below:-
function usercontent_block_view($delta='')
{
$block = array();
switch($delta)
{
case 'user_front_page' :
$block['content'] = drupal_get_form('genre_all_login');
break;
}
return $block;
}
3) Now I have some other content in block but which put me in trouble is shown below:-
function genre_all_login($form) {
$form['new_user'] = array(
'#type' => 'item',
'#markup' => l('Add New User','http://www.google.com'),
);
return $form;
}
4) So it includes a link which will move me to Google once clicked.
5) Now I want to validate certain content inside the block before user navigates to Google.So if the condition doesn't match then I will stop the user by showing an error message and hence user cannot traverse to Google.
Please tell me if it is possible or should I use different technique to handle this scenario.
You can set the #markup like:
l('Add New User','http://www.google.com', array('attributes' => array('class' => 'validate_with_js') ))
And then you can write a JavaScript code to validate:
jQuery(document).ready(function() {
jQuery('.validate_with_js').click(function() {
// Your Validation Cdoe. You can use AJAX here if you need to validate through PHP.
});
});
If you want to validate after submission of the form then you can use #element_validate.
made a simple thankyou page (e.g. /product/3/thankyou) based on a menu callback in a custom module. the content shows up fine in the normal page layout but i want the regions and blocks to show up and they don't. suggestions?
// menu callback
function custom_menu() {
$items = array();
$items['product/%/thankyou'] = array(
'page callback' => 'custom_product_thankyou',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK
);
return $items;
}
// theme function
function custom_theme() {
return array(
'product_review_thankyou' => array(
'variables' => array('node' => NULL),
'template' => 'product_review_thankyou',
),
);
}
// page callback
function custom_product_thankyou() {
$node = node_load(arg(1));
$output = theme('product_review_thankyou', array('node' => $node));
return $output;
}
I just tried your code in a drupal installation and i have no issues with missing blocks. Is it possible that you configured your blocks to be displayed only on certain pages?
The one block that i still couldn't get to show up (no matter what the block visibility settings were) was a 'menu block'. Problem was there wasn't a link for the thank you page in this block. So, I ended up having to add links on the configuration page with paths like product/[node_id]/thankyou, and then I disabled the links so they wouldn't be visible. Refreshed the page, and the block appeared.
To me this is a little annoying, as I kind of wanted this to be dynamic and not have to write in the product node ids. But either way, problem solved.