Shell Multimedia
Hello and welcome to Shell Multimedia! Your host in this strand of the web is Michelle Cox, user 23570 at Drupal.org. On this site you'll find tutorials and other writings on web development, primarily relating to Drupal, my CMS of choice.

Live in or near the Coulee Region? Be sure to check out my local community site, Coulee Region ONLINE.

MyBB style for Advanced Forum

Posted 08/01/2009 - 21:14 by Michelle

I've always loved the default MyBB theme. Personally, I think it looks much better than PHPBB's default theme. Since MyBB is GPL, I've often thought about porting it over for use with Advanced Forum 2.x. Last night, I spent a couple hours and got pretty far. It still has some rough edges but it's not bad. I can't really spend any more time on it right now so I decided to put what I have up here in my blog both to show what I have so far and share it in case someone wants to finish it off.

If anyone polishes this up and uses this on their site, I'd be interested in seeing it. I got it probably 80% there and but all know the last 20% can be a killer. ;)

In the attachments below are screenshots as well as a zip of the style.

Enjoy!

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

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

Taking a support break

Posted 04/10/2009 - 23:10 by Michelle

I've been with Drupal just about four years. Almost from the start I've been helping people. I spent a long time active in #drupal-support on IRC. I wrote tutorials about things I was working on. I answered emailed support requests. Eventually I moved on to writing three modules and all the support that goes along with those. Everywhere I look I see people needing help with Drupal and I want so badly to help them all.

I think of myself as a generally friendly person. Of course I'm not perfect and I have a temper now and then. But I'm mostly easy going and want to get along with everyone. I've had tons of people seek me out for support just because they saw me helping someone else and I was nice. I thought I was fairly well known in this community for being nice and helpful, especially to new people.

But lately I've been told by several people that I'm not. They say I'm harsh, condesending, defensive, cocky. That I lack empathy. When it was just one person, it was easy to dismiss. Tonight, it was two different people on two different posts bringing the total up to 5 that I'm aware of.

I sat here in tears for a while after that. I try so hard to help people and yet I'm obviously failing somewhere. I know I can give curt, short answers often. I frequently answer posts in my queue with a couple of screaming children clinging to me and can barely get a sentence out before having to leave the computer. So I dash off answers, do my best to point them in the right direction. I know how in so many queues questions sit for weeks or even months unanswered. I thought people would appreciate me giving my best attempt, letting them know if I need more info or if it's something I can't help with. If it's another module at fault, I move the issue. If it's something weird that I'm pretty sure isn't caused by my modules and I'm unable to reproduce it, I let them know. I thought people would want a response so they aren't sitting waiting and wondering. But it seems all I'm doing with my quick responses is pissing people off.

But what more can I do? I've already been told to suck it up and stop whining about how I have no time because of my kids but that's my reality. My computer time is stolen moments here and there in a loud crazy house. Even now, at 11pm, I write this with a toddler by my side who is refusing to go to sleep and just whipped his sippy cup at my monitor. This is my life. My family is my priority. I squeeze helping people in where I can, as much as I can, to the detriment of my own sites that sit there unfinished. But it's not enough. It's never enough. The support requests just keep coming at me from all over.

And I'm getting burnt out. I try drawing lines. Saying, yes, "this" is a problem in my module and I can fix it and, no, "that" is some weirdness on your site that I can't reproduce. I hate setting won't fix. It's a horrible status, admitting defeat. But I must or I will go crazy. I can't fix everyone's problems. Perhaps "can't fix" would be a better name. "won't" implies refusal. It doesn't say how much I desperately wish I could wave a magic wand and solve your problem.

But I'm only human. I have no magic. I only have my code and my not infallable Drupal knowledge. I have my love of helping people and I have my limits.

And I'm drawing another line.

Effective immediately, I am taking a break from support. I am going to work on my sites for a while, solve my own web problems. Aside from APK for D6 my modules are stable and don't have any known bugs. Certainly there's nothing critical that can't wait a while. I'll keep working on APK D6 because I need it for my own site and it only makes sense to publish what I have. But I will not be supporting any of it for a while. I don't know, yet, what will become of the eBook I am writing. Documenting everything has slowed my rebuild to a crawl. I suspect that will either be canceled or massively scaled down. We'll see. All I know is I can't keep going like this. I can't keep giving and giving only to be told I'm not giving nicely enough. It's tearing me up inside.

I need a break.

I'm not going to put this on Planet but will link to it from my projects to explain my absence. I'll leave comments open here just in case something needs clarifying but please don't post to say how I've helped you and that sort of thing. I'm not posting this for attention or fishing for compliments; I'm not an attention whore who gets all dramatic just to get praise. I'm posting this to let you all know why I'm dropping out for a while and that is all.

Michelle

Syndicate content