CTools Modal Windows - Part I
At work, I've become the 'modal guy,' and it's true. If you're doing modals, I am your guy. In building my first big contrib module, Report Builder, I grew pretty acquainted with the CTools framework for modal windows / forms. It's complicated stuff, but it's super powerful. Unfortunately, there isn't a lot of good documentation / tutorials out there for creating CTools modals. There's a sample module that comes with the module, but the examples are pretty confusing. The best tutorial I could find was here, but it's out of date (many of the ajax stuff from CTools in D6 is now in core). Thus, I'm going to start a short series on how to do it.
We'll tackle forms later, but in Part I, we'll just put some random text in a CTools modal.
First things first, you'll need to create a module for all this. <n00b> You do that the usual way: add a folder in the sites/all/modules directory with a .info and a .module file in it. Our module is called mymodule. So I created a folder in sites/all/modules called mymodule. Then I put two empty files in there: mymodule.info and mymodule.module. </n00b> The .info file should be normal, but make sure to include ctools as a dependency.
name = My Module<br>core = 7.x<br>description = HEY! It's my module<br>dependencies[] = ctools
Now onto the module file. We'll set this up using a module-defined block. To define a block in a module, bare minimum, you need two hooks: hook_block_info() and hook_block_view().
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">mymodule_block_info</span><span style="color: #007700">() {<br> </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">[</span><span style="color: #DD0000">'modal_test'</span><span style="color: #007700">] = array(<br> </span><span style="color: #DD0000">'info' </span><span style="color: #007700">=> </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Modal Test Block'</span><span style="color: #007700">),<br> );<br><br> return </span><span style="color: #0000BB">$blocks</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?></span></span>
This info hook tells Drupal about our new block, and gives it a new administrative name 'Modal Test Block' and a machine name of 'modal_test'. If you enable your new module, you should see your new block on the blocks administration page. Stick your new block somewhere on the page. You won't be able to see the new block though, because we haven't told Drupal what content to put in it! For that, we need hook_block_view().
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">mymodule_block_view</span><span style="color: #007700">(</span><span style="color: #0000BB">$block_name</span><span style="color: #007700">) {<br> if (</span><span style="color: #0000BB">$block_name </span><span style="color: #007700">== </span><span style="color: #DD0000">'modal_test'</span><span style="color: #007700">) {<br> <br> </span><span style="color: #0000BB">ctools_include</span><span style="color: #007700">(</span><span style="color: #DD0000">'modal'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">ctools_include</span><span style="color: #007700">(</span><span style="color: #DD0000">'ajax'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">ctools_modal_add_js</span><span style="color: #007700">();<br> <br> return array(<br> </span><span style="color: #DD0000">'subject' </span><span style="color: #007700">=> </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Modal Test Block Title!'</span><span style="color: #007700">),<br> </span><span style="color: #DD0000">'content' </span><span style="color: #007700">=> </span><span style="color: #0000BB">ctools_modal_text_button</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Click Here!'</span><span style="color: #007700">), </span><span style="color: #DD0000">'modal-test-callback'</span><span style="color: #007700">, </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Click Here!'</span><span style="color: #007700">)),<br> );<br> }<br>}<br></span><span style="color: #0000BB">?></span></span>
Let's walk through this. The function takes the block's machine name as an argument. This is because your module could define multiple blocks, but this function will handle displaying all of them. Use if statements or switch {} blocks to separate the login in this hook for each of the blocks your module might define. The ctools_include lines are necessary whenever you're using any of this CTools modal stuff. ctools_modal_add_js() does exactly what it says - it adds the javascript to the page that CTools uses to generate the modal window. Then we return an array with the subject (block title) and the content of the block. Our only block content is generated by ctools_modal_text_button(), which adds a link that creates the pop up when the user clicks. The first and 3rd arguments are the link's label, and the 2nd argument is the path to a callback that will deliver the modal's content.
Give that a go (you may need to clear your cache). When you click on the link in your new block, you should get a crazy looking 404 popup. That's because the path 'modal-test-callback' doesn't exist yet. Time to make it. Whenever you want drupal to respond to a new path, you use hook_menu()
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">mymodule_menu</span><span style="color: #007700">() {<br><br> </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #DD0000">'modal-test-callback'</span><span style="color: #007700">] = array(<br> </span><span style="color: #DD0000">'page callback' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'mymodule_modal_callback'</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'access arguments' </span><span style="color: #007700">=> array(</span><span style="color: #DD0000">'access content'</span><span style="color: #007700">),<br> </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=> </span><span style="color: #0000BB">MENU_CALLBACK</span><span style="color: #007700">,<br> );<br><br> return </span><span style="color: #0000BB">$items</span><span style="color: #007700">; <br>}<br></span><span style="color: #0000BB">?></span></span>
If you don't know hook_menu() well, you should. Any time you want to create your own page or ajax callback, you'll need it. The key in the $items array is the path for our new callback. The page callback is the name of the function that will handle this request. Access arguments is an array of permissions that a user needs to have to access the callback. Setting the type to MENU_CALLBACK tells Drupal that this page isn't intended to be a full Drupal page, but rather an ajax callback. We've registered a new hook, and added a new menu router with this code, so make sure you clear the cache if you want it to work.
We're almost there, but we still need to define mymodule_modal_callback() which is the function that will actually handle the request.
<span style="color: #000000"><span style="color: #0000BB"><?php<br><br></span><span style="color: #007700">function </span><span style="color: #0000BB">mymodule_modal_callback</span><span style="color: #007700">() {<br> </span><span style="color: #0000BB">ctools_include</span><span style="color: #007700">(</span><span style="color: #DD0000">'ajax'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">ctools_include</span><span style="color: #007700">(</span><span style="color: #DD0000">'modal'</span><span style="color: #007700">);<br> </span><span style="color: #0000BB">ctools_modal_render</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Modal Window Title'</span><span style="color: #007700">),</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Hello World'</span><span style="color: #007700">));<br>}<br></span><span style="color: #0000BB">?></span></span>
Remember to use ctools_include whenever you're using the CTools modal functions. The function doing all of the work here is ctools_modal_render(), which take two arguments. The first argument is the title that will appear at the top of the modal. The second is the content of the modal window itself. Go back to your site, click the link, and have a look. You should see the modal window pictured above.
You may be asking: "Whew, that's a lot of work for a simple modal pop up, why the heck should I do this?" The answer: you shouldn't. This is intended to be a simple example to get you started. We'll extend this code in part II to do some more fun stuff.