Panels
Calling a system page handler from code (CTools / Panels 3)
Posted 05/07/2009 - 20:28 by Michelle
This is a quick and dirty tutorial for calling a CTools/Panels 3 custom handler from code. It works for handlers for the system pages, such as the user page override. Calling custom/panels pages from code is different. Make sure you replace all instances of MODULE_NAME with your module's name.
Step 1: Create a file named MODULE_NAME.delegator_default.inc and put it somewhere in your module's directory.
Step 2: Paste in the following code.
<?php
/**
* Implementation of hook_default_delegator_handlers()
*/
function MODULE_NAME_default_delegator_handlers() {
// BEGIN HANDLER EXPORT ******************************************************/
// PASTE YOUR HANDLER EXPORT IN HERE
// END HANDLER EXPORT ******************************************************/
$handlers[$handler->name] = $handler;
return $handlers;
}
?>If you have multiple handlers to export, make sure each one ends with $handlers[$handler->name] = $handler; to put it in the array, and return the full array at the end.
Step 3: On the task handler list page, click on "operation" and then choose "export" from the drop down. Copy the contents where it says // PASTE YOUR HANDLER EXPORT IN HERE in the code above.
Step 4: Add the following code to your .module.
<?php
function MODULE_NAME_ctools_plugin_api($module, $api) {
if ($module == 'delegator' && $api == 'delegator_default') {
$module_path = drupal_get_path('module', 'MODULE_NAME');
return array('version' => 1, 'path' => $module_path . '/path/to/include/file');
}
}
?>Change "/path/to/include/file" to the actual path from your module root to MODULE_NAME.delegator_default.inc. You can leave out the 'path' if it's in the module's root.
Step 5: Clear your cache.
At this point, if everything worked, you should see the "Type" change from "Normal" to "Overridden" which means it sees the handler in code but is still using the one in the database.
Bonus step: If you don't like the name MODULE_NAME.delegator_default.inc, you can call it something else by adding 'file' => 'filename' to the array in MODULE_NAME_ctools_plugin_api.
Thanks again to merlinofchaos for patiently getting me through doing this for APK's user page and for adding a couple suggestions to this article. :)
A rough introduction to building a page in Panels 3
Posted 04/27/2009 - 13:57 by Michelle
Note: The CTools / Panels UI has changed drastically since this article was written, so much of it doesn't apply anymore.
This article is for folks used to making Panels pages in Panels 2 feeling bewildered by Panels 3. I wrote it quickly while my son had lunch and it's not polished at all but it may help some folks until there are better docs. I'm assuming you have bleeding edge Panels 3 and CTools installed for this.
If you go looking to add a page under the main Panels menu, you won't find it. Instead, you need to go to Site Building -> Pages (admin/build/pages). On that page, you'll find a list of existing "System Pages" as well as the ability to create your own. If you are overriding an existing Drupal page, such as the user page or node pages, this is the section you want. If you are creating a brand new page, you'll want the other section. So far I've only overridden the user page so that is what I'm going to use as an example.
Find the line that says "User view" and look all the way to the right for the word "Operations". Click on that. You'll see an option pop up for "Task handlers", which is some new lingo we got in Panels 3. My understanding is that a "task handler" is one of possibly several items that potentially handle the task of showing the page. What this means is that, unlike Panels 2 where you had only one Panels page per path, you can have multiple possible Pages for a path and set the rules for which one is used when the page is viewed. So click on "Task handlers" and let's get started making one.
In the "choose" dropdown, choose Panel and then add new handler. You are then taken to a page where you can determine access control. I haven't played with that, yet, so just continue. Then you need to choose a layout. Be aware that only the flexible layout lets you use the fancy layout tool at this time. If you choose any other one, it's static. For our example, choose flexible and then continue. This takes you to the add content page.
Click on "show layout designer" and the screen will change. Flexible starts you out with one region for adding content. Odds are you'll want to add more. Take some time to plan out how you want to do it because it can be tricky to move columns/rows around once you have content in them. How it works is this: The "canvas" lets you add columns. Columns let you add rows. Rows let you add either regions or more columns. Notice that the canvas won't let you add rows. That's very important. If you start out with a left and right column in the canvas and then decide you want to add a footer row across the bottom of them, you're out of luck. To do that, you need to have one column in the canvas, add two rows to that, and then put your side by side column in the first row. It's a bit tricky to get your head around, which is why pre-planning is a good idea. Once you have your structure of columns and rows, you'll want to add regions into each one. Regions are where you actually put the content.
In addition to adding columns, rows, and regions, you can use the grippy bars to make columns wider and narrower and the settings to make them fluid or fixed. You can run into some trouble with some combinations of fluid and fixed. If you get a division by 0 error, you have a combo that doesn't work right. Another neat trick is to hold down the shift key while you drag to get it to jump in whole number increments.
Once you have your structure set up, hide the layout designer. You should now see all your regions laid out. Now you can click the plus and choose "add content". This will pop up a dialog with all the content types available for the context you have. What context do you have? At this point, just the user context because it comes for free when you override the user page. If you want to add, say, cck fields from the related content profile node type, you can't do that yet. This is a bit awkward and hopefully will be fixed before the final version. But, back to adding content. Along the left side are the various categories of content types available to you. Pick a category to see the content types in it. For a handy example, click on the "user" category. Then add "user profile". This particular one only has a setting to override the title. Other content types may have more complex forms with various options to set. Continue adding content this way until you have everything you want. You can drag content from one region to another if you decide you don't like where you put it.
When you're done adding content, click finish. And then make sure you save it on the page that comes up. Now go to user/1 (or any other uid) and you should see it replaced by the page you just created. If you want to add more contexts, say a relationship to the content profile node, you can go back to the task handler page and choose "contexts" from the operations popup. Then you will have more content types to add on the add content page. Warning: Don't use the tabs across the top to jump directly from adding contexts to adding content or it will not save. You need to click the continue button at the bottom to save the new context, even though continue doesn't take you to the add content page. Then you can click the link to go to add content.
This article just barely scratched the surface of what's possible. As more people begin using Panels 3, I'm sure there will be lots more in depth articles getting into all the possibilities. For now, hopefully this will get you started.
Creating a content type with CTools for Panels 3
Posted 04/21/2009 - 14:16 by Michelle
Update: Feb 17, 2010 This article was written nearly a year ago when CTools was still in alpha. I don't know how much of it applies anymore but I've been told there's an up to date plugin example that ships with CTools now so you'll want to look to that as an example.
Note: This article requires the current Panels 3 / CTools development snapshots (or the beta when it comes out) as the alphas do not contain the recent changes to content types.
When you open the dialog to add content to your panel, there are many items already in there which come from core and contributed modules you have installed. But what if you want to add something new? There are a couple of options.
The simple way is to do it right through the UI with the option to add "New custom content" but this is only recommended if you just need to add a few lines of text or want to test something out. For dynamic content, you'll want to make your own content type. A content type is one of several types of CTools plugins along with "relationships", "layouts", and more. It is a discrete bit of content, defined in code, that can be added as a pane to your panel. You define the content type in a module and then it is available to the Panels UI. This article will walk you through creating a content type using my Author Pane as an example. You can also look at ctools/plugins/content_types for examples that come with CTools.
Note that you need to create content types inside of a module. If you have a site module for customizations you can use that or you can create a module just for this.
Step 1: Name your content type
Each content type needs a distinct name that will not conflict with any other content type name, including those coming from other contributed modules. Because of this, it's best to include your module's name in the content type name. If your module is only providing one content type, you can simply use your module name for the content type name. In Author Pane, I only create one content type so I named it simply "author_pane".
Step 2: Create the file and tell CTools where to find it
Your content type needs to live in an include file of the same name. In my case, author_pane.inc. You should put this in a directory that only contains content types. The recommended directory structure is: moduledir/plugins/content_types. For Author Pane, the complete path with file is author_pane/plugins/content_types/author_pane.inc
Once you have the file and directories set up, you need to tell CTools where your plugins are. If you followed the recommended directory structure, you can simply drop this code into your module (be sure to change MODULE_NAME to your module's name.):
<?php
/**
* Implementation of hook_ctools_plugin_directory().
*/
function MODULE_NAME_ctools_plugin_directory($module, $plugin) {
if ($module == 'ctools') {
return 'plugins/' . $plugin;
}
}
?>This code will look for content types in plugins/content_types, relationships in plugins/relationships, and so on for all the various plugins. Don't worry about creating the other directories if your module doesn't make any other plugin types.
Step 3: Create the content type
The rest of this will walk you through the Author Pane content type, explaining what each bit does, and how you should change it for your own content type. All of these functions go in the ct_name.inc file you created in the last step.
Tell CTools about your content type:
<?php
/**
* Callback function to supply a list of content types.
*/
function author_pane_author_pane_ctools_content_types() {
return array(
'single' => TRUE,
'title' => t('Author Pane'),
'icon' => 'icon_user.png',
'description' => t('Author related variables gathered from helper modules.'),
'required context' => new ctools_context_required(t('User'), 'user'),
'category' => t('Advanced Profile Kit'),
'defaults' => array('image_path' => '', 'template_file' => 'author-pane'),
);
}
?>Here's what each bit in that code means:
- The function name is
MODULE_NAME_CT_NAME_ctools_content_types. Because our module name and content type name are the same, we have "author_pane" twice. This looks a little funny but doesn't hurt anything. - We are only creating one content type so we set 'single' to true.
- The title is the name that shows up in the add content dialog.
- The icon is the icon that shows next to the name that shows up in the add content dialog. Since this is user related, we are using the user icon. This icon is in CTools and you will either need to copy it to your plugin directory or put the full path to it (or make your own icon). If you don't provide an icon, CTools will provide a default.
- The description shows up when you hover over the item in the add content dialog.
- Required context is what context this content type needs to work. In this case, we need the user object so we make use of the user context. If the Panel does not have a user context on it, our content type will not show up as an option to add.
- The category defines where our content type shows up in the add content dialog. For a user related item, you could use simply "User". In this case, I wanted it to be grouped with other items provided by Advanced Profile Kit as that is where it will most commonly be used. If you are creating this content type for your own site, you can use whatever category you like. If you are creating it for others to use, be careful what you choose here to avoid cluttering the add content dialog.
- Defaults is an array that contains the default values, if any, for the items on your add/edit form.
Tell CTools how to display your content type:
<?php
/**
* Output function for the 'author pane' content type.
*/
// The function name is <code>MODULE_NAME_CT_NAME_content_type_render
function author_pane_author_pane_content_type_render($subtype, $conf, $panel_args, $context) {
// $context in this case is a user context, so we can get the user object
// from it and put it into $account.
$account = isset($context->data) ? drupal_clone($context->data) : NULL;
// Make a new empty "block" which will be a Pane you can add to your Panel.
$block = new stdClass();
if ($account) {
// Set the title of the block to the name of the user. It can be overridden
// through the UI as well.
$block->title = check_plain($account->name);
// Call the function that makes the author pane and use that for the block
// content. In our case, this is just one line but you can have whatever
// code you need to assemble the content and then assign it to the
// $block->content variable.
$block->content = theme('author_pane', $account, $conf['image_path'], $conf['template_file']);
}
else {
// If somehow the user context is empty, this is a fallback message but
// that should never happen.
$block->content = "User information not available";
}
return $block;
}
?>Define the settings form for the pane:
<?php
/**
* Returns an edit form for the custom type.
*/
// The function name is <code>MODULE_NAME_CT_NAME_content_type_edit_form
function author_pane_author_pane_content_type_edit_form(&$form, &$form_state) {
// The current configuration
$conf = $form_state['conf'];
// This and the next one are normal FAPI form making.
$form['image_path'] = array(
'#type' => 'textfield',
'#title' => t('Image directory'),
'#size' => 50,
'#description' => t('Full path to image directory, not including leading or trailing slashes. Use [theme_path] to substitute the active theme\'s path. If left blank the images in the module directory will be used.'),
'#default_value' => $conf['image_path'],
'#prefix' => '<div class="clear-block no-float">',
'#suffix' => '</div>',
);
$form['template_file'] = array(
'#type' => 'textfield',
'#title' => t('Template file'),
'#size' => 50,
'#description' => t('Template file to use for the author pane.'),
'#default_value' => $conf['template_file'],
'#prefix' => '<div class="clear-block no-float">',
'#suffix' => '</div>',
);
}
?>Prepare the settings form for submission.
<?php
function author_pane_author_pane_content_type_edit_form_submit(&$form, &$form_state) {
// For each part of the form defined in the 'defaults' array set when you
// defined the content type, copy the value from the form into the array
// of items to be saved. We don't ever want to use
// $form_state['conf'] = $form_state['values'] because values contains
// buttons, form id and other items we don't want stored. CTools will handle
// the actual form submission.
foreach (array_keys($form_state['plugin']['defaults']) as $key) {
$form_state['conf'][$key] = $form_state['values'][$key];
}
}
?>Define the title displayed on the layout page of the panel:
This is the title you see when you are editing the Panel. It is not the same as the title
of the pane when you are viewing the panel, which is defined in the render function.
<?php
function author_pane_author_pane_content_type_admin_title($subtype, $conf, $context) {
return t('"@s" author pane', array('@s' => $context->identifier));
}
?>Explore the possibilities
This example has shown the most common bits you will need to create a content type. With it, you can make use of the panel's context as well as settings on the pane and your own code to come up with your own dynamic, custom content. This lives in a file that can be checked into version control rather than just being set in the database. You can also include it with a module for distribution to share with others. From here, there are more complicated things you can do such as multi-page settings and more.
If you maintain a contributed module, consider what parts of it could be encapsulated into content types to make it easier for your users to add them to their own panels. While Panels will make use of blocks you provide, that is only part of the story. Making it a true content type gives you more options for integration such as showing content for a particular user based on the user context or a node based on the node context.
The possibilities are endless.
Much credit goes to merlinofchaos who helped me quite a bit in writing this as well as converting my content types from Panels 2
Site contents are licensed under a