-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathflag_dialog.module
282 lines (260 loc) · 9.79 KB
/
flag_dialog.module
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
<?php
/**
* @file
* Provides menu callbacks for displaying the flagging forms in a dialog box.
*
* <h3>Overview</h3>
*
* There are two different methods for displaying dialogs. One is to fetch the
* content via AJAX and put it inside a DIV. The other is to to load the
* document into an IFRAME.
*
* This module uses the first method, of using AJAX. It uses jQuery UI's dialog
* widget. jQuery UI is shipped with D7. This module also necessitates the
* <em>Dialog API</em> module, which provides a thin layer of supporting code.
*
* <h3>Alternatives</h3>
*
* The AJAX method is not as robust as the IFRAME one: the additional "magic" it
* involves may not be fully compatible with the JavaScript of the widgets shown
* in the dialog (search flag_dialog.js for "@todo" comments to learn about known
* problems).
*
* We should eventually provide another module (or replace this one?) implementing
* the "other method", of using an IFRAME. At the time of this writing it seems
* it could utilize either D7's <em>Overlay</em> module or the <em>Modal Frame
* API</em> module. However, both modules have bugs that must be fixed first.
*/
/**
* Implements hook_menu().
*/
function flag_dialog_menu() {
// We wrap the CRUD forms of flag_form.module with ajax versions:
//
// 1. We duplicate the paths of flag_form.module, but append '/ajax' to them.
//
// 2. To the outgoing flag_form.module links we append '/nojs', which Drupal
// replaces with '/ajax' if JavaScript is used.
// Editing a flagging.
$items['flag/flagging/%flag/%content_id/edit/ajax'] = array(
'page callback' => 'flag_dialog_edit_flagging',
'page arguments' => array(2, 3),
'title callback' => '_flag_menu_title',
'title arguments' => array(2),
'access callback' => '_flag_flagging_access',
'access arguments' => array('update', 2, 3),
'delivery callback' => 'ajax_deliver',
);
// Creating a flagging.
$items['flag/flagging/%flag/%content_id/create/ajax'] = array(
'page callback' => 'flag_dialog_edit_flagging',
'page arguments' => array(2, 3),
'title callback' => '_flag_menu_title',
'title arguments' => array(2),
'access callback' => '_flag_flagging_access',
'access arguments' => array('create', 2, 3),
'delivery callback' => 'ajax_deliver',
);
// Deleting a flagging.
$items['flag/flagging/%flag/%content_id/delete/ajax'] = array(
'page callback' => 'flag_dialog_delete_flagging',
'page arguments' => array(2, 3),
'title callback' => '_flag_menu_title',
'title arguments' => array(2),
'access callback' => '_flag_flagging_access',
'access arguments' => array('delete', 2, 3),
'delivery callback' => 'ajax_deliver',
);
return $items;
}
/**
* Implements hook_flag_form_interactions().
*/
function flag_dialog_flag_form_interactions() {
return array(
'flag_dialog' => array(
'title' => t('Dialog box'),
'description' => t('Forms are displayed in a dialog box, if JavaScript is available.'),
'weight' => -1,
),
);
}
/**
* Implements hook_flag_form_link_alter().
*
* Alters the links flag_form.module generates.
*/
function flag_dialog_flag_form_link_alter(&$link, $flag) {
if ($flag->form_interaction == 'flag_dialog') {
$link['href'] .= '/nojs/'; // Bug in Drupal's ajax.js: we must have a trailing '/'.
$link['attributes']['class'] = 'use-dialog use-ajax';
// @todo: See Amitai's http://drupal.org/node/858764, which tries to
// standardize js/css inclusion.
// We need the CSS for .flag-message:
drupal_add_css(drupal_get_path('module', 'flag') . '/theme/flag.css');
// We need the anonyous-user handling (but nothing else):
// @todo: split flag.js into flag-common.js and flag-toggle.js?
drupal_add_js(drupal_get_path('module', 'flag') . '/theme/flag.js');
// Finally, our own code:
drupal_add_library('flag_dialog', 'flag_dialog');
}
}
/**
* Implements hook_library().
*/
function flag_dialog_library() {
$libraries['flag_dialog'] = array(
'title' => 'Flag Dialog',
'version' => '1.0',
'js' => array(
// Omitting the 'weight' bellow will use a default weight of JS_LIBRARY,
// which, since lighter than ajax.js's weight, won't enable us to add our
// command(s) to the non-yet-existing Drupal.ajax.prototype.commands.
drupal_get_path('module', 'flag_dialog') . '/flag_dialog.js' => array('weight' => JS_DEFAULT),
),
'css' => array(
drupal_get_path('module', 'flag_dialog') . '/flag_dialog.css' => array(),
),
'dependencies' => array(
array('dialog', 'dialog'),
),
);
return $libraries;
}
/**
* Implements hook_form_alter().
*/
function flag_dialog_form_alter(&$form, &$form_state, $form_id) {
if (!empty($form_state['flag_is_using_ajax'])) {
// Make the buttons submit via ajax. (This could also be done by adding class="use-ajax-submit"
// to buttons, but bugs in Drupal's misc/ajax.js prevent this from working.)
if (isset($form['actions'])) {
foreach (element_children($form['actions']) as $button) {
if ($button != 'cancel') { // For performance, we handle cancel buttons in our JavaScript.
$form['actions'][$button]['#ajax']['path'] = $_GET['q'];
}
}
}
// Make sure #ids don't clash with existing ones.
$form = dialog_process_ajax_form($form);
}
}
// Menu callback.
function flag_dialog_edit_flagging($flag, $content_id) {
$flagging = $flag->get_flagging($content_id);
if (!$flagging) {
// New flagging.
$flagging = $flag->new_flagging($content_id);
}
$form_state = array(
'no_redirect' => TRUE,
'flag_is_using_ajax' => TRUE, // Tell our hook_form_alter() to kick in.
'flag_suppress_messages' => TRUE,
'build_info' => array(
'args' => array($flagging),
),
);
$form = drupal_build_form('flag_flagging_form', $form_state);
return flag_dialog_process_form_result($form, $form_state, $flag, $content_id);
}
// Menu callback.
function flag_dialog_delete_flagging($flag, $content_id) {
$flagging = $flag->get_flagging($content_id);
if (!$flagging) {
// The item isn't flagged. The form function will deal with this error.
$flagging = $flag->new_flagging($content_id);
}
$form_state = array(
'no_redirect' => TRUE,
'flag_is_using_ajax' => TRUE, // Tell our hook_form_alter() to kick in.
'flag_suppress_messages' => TRUE,
'build_info' => array(
'args' => array($flagging),
),
);
$form = drupal_build_form('flag_flagging_delete_form', $form_state);
return flag_dialog_process_form_result($form, $form_state, $flag, $content_id);
}
/**
* Handles submission of the form.
*/
function flag_dialog_process_form_result($form, $form_state, $flag, $content_id) {
$commands = array();
// The following line reloads the 'dialog' and 'flag_dialog' libraries.
// Why? Because there's a bug in Drupal/DialogAPI:
//
// When the JavaScript aggregator is turned on, DialogAPI (in
// dialog_ajax_render_alter()) tells the browser to load drupal.js and
// ajax.js as well, and this blows away the additions dialog.js did to some
// data structures there (Drupal.{theme,ajax}.prototype). So we re-introduce
// these additions.
//
// See http://drupal.org/node/XXXX
drupal_add_library('flag_dialog', 'flag_dialog');
if (!empty($form_state['executed'])) {
// The form has been submitted. Either redirect to a new url or close the dialog.
if (is_array($form_state['redirect'])) {
$target = $form_state['redirect'][0];
}
else {
$target = $form_state['redirect'];
}
// If there's an ajax version for the target path, use it.
$try = menu_get_item($target . '/ajax');
if ($try && strpos($try['path'], '/ajax') !== FALSE) {
// Yes, there is. Do an "internal" redirect.
$target = $target . '/ajax';
_flag_dialog_set_drupal_path($target);
menu_execute_active_handler($target);
drupal_exit();
// An alternative is to do a roundtrip using dialog_command_boxed_redirect()
// See http://drupal.org/node/XXXX
}
else {
// No, there's no ajax page to go to. Close the dialog, and
// update the flag link.
$commands[] = dialog_command_dismiss();
$commands[] = flag_dialog_command_update_link($flag, $content_id, !empty($form_state['flag_status_has_changed']));
}
}
else {
// The form hasn't been submitted. So we just need to display it.
dialog_display(TRUE); // Make dialog_ajax_render_alter() run.
$commands[] = dialog_command_display($form, array('title' => drupal_get_title()));
}
$output = array(
'#type' => 'ajax',
'#commands' => $commands,
);
return $output;
}
/**
* Fool the system to think the current URL is $path.
*
* Used for "internal" redirections. This doesn't need to be foolproof: just enough
* to handle our own code.
*/
function _flag_dialog_set_drupal_path($path) {
// Used by our form_alter():
$_GET['q'] = $path;
// Used as the action='...' for <form> tags:
$_SERVER['REQUEST_URI'] = url($path);
drupal_static_reset('element_info');
}
/**
* Creates a Drupal AJAX command to update a flag link.
*/
function flag_dialog_command_update_link($flag, $content_id, $status_has_changed) {
// @todo: We're mimicing here the JavsScript structure flag_page() builds. Let's
// factor out the flag_page() code and use it instead.
return array(
'command' => 'flag_dialog_update_link',
'flagName' => $flag->name,
'contentId' => $content_id,
// @todo: it was pointed out already, in a comment in flag_form.module,
// that we don't have a special message to print when a flagging is updated
// (as opposed to created or deleted). When we factor flag_page() we should
// make things future-proof to also work when we do have such a message.
'newLink' => $flag->theme($flag->is_flagged($content_id) ? 'unflag' : 'flag', $content_id, $status_has_changed),
);
}