Extending the Standard Confirmation Form
Out of the box, the confirmation form provided by the Flag module does nothing more than display a form containing a submit button to confirm the action and a link to cancel it. We can just use hook_form_alter()
to add a few of additional fields to this a basic implementation of which is shown below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/** * Implements hook_form_alter() */ function example_form_alter(& $form , & $form_state , $form_id ){ if ( $form_id == 'flag_confirm' ){ $form [ 'reason' ] = array ( '#type' => 'select' , '#title' => t( 'Reason for reporting this comment' ), '#options' => array ( 'offensive' => t( 'Comment\'s offensive or unlawful' ), 'spam' => t( 'Advertising / Spam' ), 'other' => t( 'Another reason' ), ), ); $form [ 'other' ] = array ( '#type' => 'textfield' , '#title' => t( 'Reason' ), ); $form [ 'comment_id' ] = array ( '#type' => 'value' , '#value' => arg(4), ); //Add our own submit handler to process this data. $form [ '#submit' ][] = 'example_confirm_form_submit' ; } } |
$form['#submit']
array. The function this points
to will just take the standard arguments we pass to a submit handler so
as a basic example we need to include a function like:/**
* Additional submit handler for the comment confirmation form
*/
function
example_confirm_form_submit(
$form
, &
$form_state
){
$params
=
$form_state
[
'values'
];
//Do something with these submitted values
//For example send them in an email to a moderator using php drupal_mail()
}
AJAX and the Chaos Tool Suite
The reason why this is slightly more complicated than it first sounds is because we want the rendered form but we don’t want to push it through the entire Drupal theme engine as this would mean we’d end up rendering another whole page within the popup when in fact all we want is the markup for the form. Loading forms in modal windows isn’t actually that complicated in itself as the Chaos Tool Suite module provides some nice functionality to do the heavy lifting for us. What makes this particular example more complex is the fact that the form we want to render isn’t being generated by our module and neither are any references to it so we need to extend the Flag module in a way that means our custom module can just slot in to add this modal functionality.To get our module working with Chaos Tools we need to follow a similar technique to that described in our Make a link use ajax in Drupal 7 (it’s easy) article. We begin by defining an implementation of
hook_menu()
as shown below:/**
* Implements hook_menu()
*/
function
example_menu(){
$items
[
'comments/%ctools_js/confirm/%flag/%'
] =
array
(
'title'
=>
'Contact'
,
'page callback'
=>
'example_test_modal'
,
'page arguments'
=>
array
(1, 3, 4),
'access arguments'
=> TRUE,
'type'
=> MENU_CALLBACK,
);
return
$items
;
}
$ctools_js
– this will trigger a function called ctools_js_load()
to run within Chaos Tools
to check whether or not the link is capable of running the JavaScript
required to fire the AJAX request. If it is then this placeholder
becomes ajax
; if not it’s set to nojs
in exactly the same way as is shown in the Make a link use ajax in Drupal 7 (it’s easy) article. However, in that example we explicitly define two callbacks whereas here the %ctools_js
wildcard allows one callback to suffice as the Chaos Tools module will change the argument dynamically.The next wildcard is
%flag
and this is what makes our menu callback work with the Flag module. When rendering the form the Flag
module needs to reference an object that represents the flag that is
being used as the trigger. This object is passed through to the form as
an argument which means that it needs to be loaded as a variable before
we can call the form. By including this placeholder we run the Flag module’s implementation of flag_load()
, which takes the string entered in the path and returns the relevant flag object.The final wildcard is just a simple reference to the ID of the comment we’re interacting with; again this will be needed to allow the Flag module to do its magic, as we’ll see shortly.
Next we need to implement the page callback function we reference in
hook_menu()
as this will be what actually generates the response to any requests
that hit a path matching our definition. We need to make sure it accepts
three arguments corresponding to the wildcards discussed above; these
parameters will have been set by each modules’ _load()
functions by the time we invoke the callback function.function
example_test_modal(
$js
,
$flag
,
$cid
){
//If JavaScript isn't enabled the just go to the standard confirmation form
if
(!
$js
) {
drupal_goto(
'flag/confirm/flag/abuse/'
.
$cid
,
array
(
'query'
=>
array
(
'destination'
,
$_GET
[
'destination'
])));
}
//Include the relevant code from CTools
ctools_include(
'modal'
);
ctools_include(
'ajax'
);
ctools_add_js(
'ajax-responder'
);
//Build up the $form_state array
//This is passed through to the form generated by the Flag module
$form_state
=
array
(
'title'
=> t(
'Report Comment'
),
'ajax'
=> TRUE,
'build_info'
=>
array
(
'args'
=>
array
(
0 =>
'flag'
,
1 =>
$flag
,
3 =>
$cid
,
),
),
);
//Wrap the Flag module's form in a wrapper provided by CTools
$output
= ctools_modal_form_wrapper(
'flag_confirm'
,
$form_state
);
if
(!
empty
(
$form_state
[
'executed'
])) {
$output
=
array
();
//This makes sure we go to the right place once we close the modal window
if
(isset(
$_GET
[
'destination'
])) {
$output
[] = ctools_ajax_command_redirect(
$_GET
[
'destination'
]);
}
else
{
$output
[] = ctools_ajax_command_reload();
}
}
//Return the JSON string ready to be rendered back to the DOM
print
ajax_render(
$output
);
exit
;
}
There’s quite a lot going on in this function. First of all we check
to see if we can use AJAX and render a modal version of the form. If not
then we just redirect to the standard form which will be displayed on
its own page. If JavaScript is enabled we then need to make sure we add
all the code we need from Chaos Tools – this is just done by some simple helper functions provided by the module. The next thing we need to do is build up the
We then use the Chaos Tools wrapper to add the form to the
$form_state
array – this is an important stage as we also need to include the arguments in under a build_info
key in order to get them over to the Flag
module. This differs from how we’d usually do things if we weren’t
trying to render a modal form as it would be possible to just call drupal_get_form()
and pass the arguments through as normal. Because we are using the Chaos Tools wrapper around the form we can’t do this so we need to add them into the $form_state
array. We also set the title of the modal window and the ajax
key to TRUE
.We then use the Chaos Tools wrapper to add the form to the
$output
variable and apply a bit of logic to make sure we still honour the destination
argument in the query string if it’s present. If it’s not then we just
reload the current page when the modal window is closed. Finally we just
print the JSON string and exit the function to stop it running through
the theme engine and having markup added that will break the AJAX
response.Tying Everything Together
Now we have a function that will provide a valid AJAX response if
requested, we need to start tying this into the links already being
rendered by the Flag module. Chaos Tools is clever enough to realise that any link that has a class of
ctools-use-modal
needs to be loaded in a modal window if possible. So we need to add
this class to the ‘Flag’ link on the comment to begin with. Next the
link provided by the Flag module still points at the MENU_CALLBACK
defined in that module so we need to rewrite this to point at our new page callback function defined in our implementation of hook_menu()
. We could do all this using hook_comment_view_alter()
;
however, I opted to use jQuery to add the classes as this means that in
real terms if JavaScript isn’t enabled then the class won’t be added
and the link will never get pointed at our function so it will just work
as normal
function
($) {
Drupal.behaviors.initModalFormsConfirm = {
attach:
function
(context, settings) {
$(
".flag-link-confirm"
, context).once(
'init-modal-forms-contact'
,
function
() {
this.href = this.href.replace(/flag\/confirm\/flag\/abuse/,
'comments/nojs/confirm/abuse'
);
}).addClass(
'ctools-use-modal ctools-modal-modal-popup-confirm'
);
}
};
})(jQuery);
The jQuery code above just looks for any link that has a class of
flag-link-confirm
and then rewrites its href
attribute based on a regular expression matching the entire string up
to the point where the flag placeholder and comment ID are appended. We
then add the ctools-use-modal
class; you’ll notice we also add another class of ctools-modal-modal-popup-confirm
– this is to allow us to control how the modal window is rendered and
we’ll look at this next. To add this code to the comment we just use hook_comment_view()
to call drupal_add_js()
. It is important to notice that we set the weight to -20
– this ensures that this code runs before the Chaos Tools JavaScript. If you didn’t do this then the ctools-use-modal
class won’t have been set in time for the Chaos Tools JavaScript to recognise it when it runs./**
* Implements hook_node_view_alter().
*/
function
example_comment_view_alter(
$comment
,
$view_mode
,
$langcode
) {
drupal_add_js(drupal_get_path(
'module'
,
'example'
) .
'/js/example.js'
,
array
(
'weight'
=> -20));
}
hook_init()
to invoke a function
that will add all the required JavaScript to the current page. We also
wrap it in some logic to stop the code from being added to any of the
Drupal installation pages.**
* Implements hook_init().
*/
function
example_init() {
if
(!drupal_installation_attempted()) {
example_configure();
}
}
The code this calls will just add any JavaScript files that Chaos Tools needs in order to respond to the
ctools-use-modal
class and load the modal window. We also define some settings that will
be added as JavaScript to help us theme the form; the code these
reference is based on the Modal Forms
module. This module provides some nice functionality to get some of the
common core forms rendering in modal windows – for example using a
‘login’ link to load a modal version of the core user_login form. We’re
not actually using the module here but are borrowing the code it uses to
render the modal window.function
example_configure(){
static
$configured
= FALSE;
if
(
$configured
) {
return
;
}
//Include the relevant CTools code
ctools_include(
'ajax'
);
ctools_include(
'modal'
);
ctools_modal_add_js();
$throbber
= theme(
'image'
,
array
(
'path'
=> ctools_image_path(
'loading_animation.gif'
,
'modal_forms'
),
'alt'
=> t(
'Loading...'
),
'title'
=> t(
'Loading'
)));
$js_settings
=
array
(
'modal-popup-confirm'
=>
array
(
'modalSize'
=>
array
(
'type'
=>
'fixed'
,
'width'
=> 500,
'height'
=> 200,
),
'modalOptions'
=>
array
(
'opacity'
=> 0.85,
'background'
=>
'#000'
,
),
'animation'
=>
'fadeIn'
,
'modalTheme'
=>
'ModalFormsPopup'
,
'throbber'
=>
$throbber
,
'closeText'
=> t(
'Close'
),
),
);
drupal_add_js(
$js_settings
,
'setting'
);
//Add in some custom CSS and our jQuery template
ctools_add_css(
'example_popup'
,
'example'
);
ctools_add_js(
'example'
,
'example'
);
$configured
= TRUE;
}
You can see that the
$js_settings
just take the form
of an array with the key corresponding to the second class we added
above. It also specifies that we should render the form using a ‘theme’
called ModalFormsPopup
. This option just references a JavaScript file containing some code to override the standard Chaos Tools theming of the modal window – it’s purely aesthetic. The Drupal.theme.prototype
namespace was added in Drupal 6 to allow provide a method of cleanly
overriding another module’s JavaScript generated HTML code./**
* Provide the HTML to create the modal dialog.
*/
Drupal.theme.prototype.ModalFormsPopup =
function
() {
var
html =
''
html +=
'<div id="ctools-modal">'
;
html +=
' <div>'
;
html +=
' <div>'
;
html +=
' <div>'
;
html +=
' <h3 id="modal-title"></h3>'
;
html +=
' <span>'
+ Drupal.CTools.Modal.currentSettings.closeText +
'</span>'
;
html +=
' </div>'
;
html +=
' <div><div id="modal-content"></div></div>'
;
html +=
' </div>'
;
html +=
' </div>'
;
html +=
'</div>'
;
return
html;
}
That’s pretty much all there is to it. We’ve essentially just written a lot of glue to get the Flag module’s confirmation form working with the Chaos Tools
modal popup functionality. More importantly, we’ve not hacked either
module and all the AJAX degrades gracefully. I’ve used the
implementation of
example_configure()
to also add some CSS
just to polish the final result a bit. I’ve also included a custom
throbber that I generated using an online Tool just to make it tie nicely into my site’s look and feel.
No comments:
Post a Comment