Project

General

Profile

Paste
Download (35.6 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / overlay / overlay.module @ b0dc3a2e

1
<?php
2

    
3
/**
4
 * @file
5
 * Displays the Drupal administration interface in an overlay.
6
 */
7

    
8
/**
9
 * Implements hook_help().
10
 */
11
function overlay_help($path, $arg) {
12
  switch ($path) {
13
    case 'admin/help#overlay':
14
      $output = '';
15
      $output .= '<h3>' . t('About') . '</h3>';
16
      $output .= '<p>' . t('The Overlay module makes the administration pages on your site display in a JavaScript overlay of the page you were viewing when you clicked the administrative link, instead of replacing the page in your browser window. Use the close link on the overlay to return to the page you were viewing when you clicked the link. For more information, see the online handbook entry for <a href="@overlay">Overlay module</a>.', array('@overlay' => 'http://drupal.org/documentation/modules/overlay')) . '</p>';
17
      return $output;
18
  }
19
}
20

    
21
/**
22
 * Implements hook_menu().
23
 */
24
function overlay_menu() {
25
  $items['overlay-ajax/%'] = array(
26
    'title' => '',
27
    'page callback' => 'overlay_ajax_render_region',
28
    'page arguments' => array(1),
29
    'access arguments' => array('access overlay'),
30
    'type' => MENU_CALLBACK,
31
  );
32
  $items['overlay/dismiss-message'] = array(
33
    'title' => '',
34
    'page callback' => 'overlay_user_dismiss_message',
35
    'access arguments' => array('access overlay'),
36
    'type' => MENU_CALLBACK,
37
  );
38
  return $items;
39
}
40

    
41
/**
42
 * Implements hook_admin_paths().
43
 */
44
function overlay_admin_paths() {
45
  $paths = array(
46
    // This is marked as an administrative path so that if it is visited from
47
    // within the overlay, the user will stay within the overlay while the
48
    // callback is being processed.
49
    'overlay/dismiss-message' => TRUE,
50
  );
51
  return $paths;
52
}
53

    
54
/**
55
 * Implements hook_permission().
56
 */
57
function overlay_permission() {
58
  return array(
59
    'access overlay' => array(
60
      'title' => t('Access the administrative overlay'),
61
      'description' => t('View administrative pages in the overlay.'),
62
    ),
63
  );
64
}
65

    
66
/**
67
 * Implements hook_theme().
68
 */
69
function overlay_theme() {
70
  return array(
71
    'overlay' => array(
72
      'render element' => 'page',
73
      'template' => 'overlay',
74
    ),
75
    'overlay_disable_message' => array(
76
      'render element' => 'element',
77
    ),
78
  );
79
}
80

    
81
/**
82
 * Implements hook_form_alter().
83
 */
84
function overlay_form_alter(&$form, &$form_state) {
85
  // Add a hidden element to prevent dropping out of the overlay when a form is
86
  // submitted inside the overlay using a GET method.
87
  if (isset($form['#method']) && $form['#method'] == 'get' && isset($_REQUEST['render']) && $_REQUEST['render'] == 'overlay' && !isset($form['render'])) {
88
    $form['render'] = array(
89
      '#type' => 'hidden',
90
      '#value' => 'overlay',
91
    );
92
  }
93
}
94

    
95
/**
96
 * Implements hook_form_FORM_ID_alter().
97
 */
98
function overlay_form_user_profile_form_alter(&$form, &$form_state) {
99
  if ($form['#user_category'] == 'account') {
100
    $account = $form['#user'];
101
    if (user_access('access overlay', $account)) {
102
      $form['overlay_control'] = array(
103
        '#type' => 'fieldset',
104
        '#title' => t('Administrative overlay'),
105
        '#weight' => 4,
106
        '#collapsible' => TRUE,
107
      );
108

    
109
      $form['overlay_control']['overlay'] = array(
110
        '#type' => 'checkbox',
111
        '#title' => t('Use the overlay for administrative pages.'),
112
        '#description' => t('Show administrative pages on top of the page you started from.'),
113
        '#default_value' => isset($account->data['overlay']) ? $account->data['overlay'] : 1,
114
      );
115
    }
116
  }
117
}
118

    
119
/**
120
 * Implements hook_user_presave().
121
 */
122
function overlay_user_presave(&$edit, $account, $category) {
123
  if (isset($edit['overlay'])) {
124
    $edit['data']['overlay'] = $edit['overlay'];
125
  }
126
}
127

    
128
/**
129
 * Implements hook_init().
130
 *
131
 * Determine whether the current page request is destined to appear in the
132
 * parent window or in the overlay window, and format the page accordingly.
133
 *
134
 * @see overlay_set_mode()
135
 */
136
function overlay_init() {
137
  global $user;
138

    
139
  $mode = overlay_get_mode();
140

    
141
  // Only act if the user has access to the overlay and a mode was not already
142
  // set. Other modules can also enable the overlay directly for other uses.
143
  $use_overlay = !isset($user->data['overlay']) || $user->data['overlay'];
144
  if (empty($mode) && user_access('access overlay') && $use_overlay) {
145
    $current_path = current_path();
146
    // After overlay is enabled on the modules page, redirect to
147
    // <front>#overlay=admin/modules to actually enable the overlay.
148
    if (isset($_SESSION['overlay_enable_redirect']) && $_SESSION['overlay_enable_redirect']) {
149
      unset($_SESSION['overlay_enable_redirect']);
150
      drupal_goto('<front>', array('fragment' => 'overlay=' . $current_path));
151
    }
152

    
153
    if (isset($_GET['render']) && $_GET['render'] == 'overlay') {
154
      // If a previous page requested that we close the overlay, close it and
155
      // redirect to the final destination.
156
      if (isset($_SESSION['overlay_close_dialog'])) {
157
        call_user_func_array('overlay_close_dialog', $_SESSION['overlay_close_dialog']);
158
        unset($_SESSION['overlay_close_dialog']);
159
      }
160
      // If this page shouldn't be rendered inside the overlay, redirect to the
161
      // parent.
162
      elseif (!path_is_admin($current_path)) {
163
        // Prevent open redirects by ensuring the current path is not an absolute URL.
164
        if (url_is_external($current_path)) {
165
          $current_path = '<front>';
166
        }
167
        overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('q', 'render'))));
168
      }
169

    
170
      // Indicate that we are viewing an overlay child page.
171
      overlay_set_mode('child');
172

    
173
      // Unset the render parameter to avoid it being included in URLs on the page.
174
      unset($_GET['render']);
175
    }
176
    // Do not enable the overlay if we already are on an admin page.
177
    elseif (!path_is_admin($current_path)) {
178
      // Otherwise add overlay parent code and our behavior.
179
      overlay_set_mode('parent');
180
    }
181
  }
182
}
183

    
184
/**
185
 * Implements hook_exit().
186
 *
187
 * When viewing an overlay child page, check if we need to trigger a refresh of
188
 * the supplemental regions of the overlay on the next page request.
189
 */
190
function overlay_exit() {
191
  // Check that we are in an overlay child page. Note that this should never
192
  // return TRUE on a cached page view, since the child mode is not set until
193
  // overlay_init() is called.
194
  if (overlay_get_mode() == 'child') {
195
    // Load any markup that was stored earlier in the page request, via calls
196
    // to overlay_store_rendered_content(). If none was stored, this is not a
197
    // page request where we expect any changes to the overlay supplemental
198
    // regions to have occurred, so we do not need to proceed any further.
199
    $original_markup = overlay_get_rendered_content();
200
    if (!empty($original_markup)) {
201
      // Compare the original markup to the current markup that we get from
202
      // rendering each overlay supplemental region now. If they don't match,
203
      // something must have changed, so we request a refresh of that region
204
      // within the parent window on the next page request.
205
      foreach (overlay_supplemental_regions() as $region) {
206
        if (!isset($original_markup[$region]) || $original_markup[$region] != overlay_render_region($region)) {
207
          overlay_request_refresh($region);
208
        }
209
      }
210
    }
211
  }
212
}
213

    
214
/**
215
 * Implements hook_library().
216
 */
217
function overlay_library() {
218
  $module_path = drupal_get_path('module', 'overlay');
219

    
220
  // Overlay parent.
221
  $libraries['parent'] = array(
222
    'title' => 'Overlay: Parent',
223
    'website' => 'http://drupal.org/documentation/modules/overlay',
224
    'version' => '1.0',
225
    'js' => array(
226
      $module_path . '/overlay-parent.js' => array(),
227
    ),
228
    'css' => array(
229
      $module_path . '/overlay-parent.css' => array(),
230
    ),
231
    'dependencies' => array(
232
      array('system', 'ui'),
233
      array('system', 'jquery.bbq'),
234
    ),
235
  );
236
  // Overlay child.
237
  $libraries['child'] = array(
238
    'title' => 'Overlay: Child',
239
    'website' => 'http://drupal.org/documentation/modules/overlay',
240
    'version' => '1.0',
241
    'js' => array(
242
      $module_path . '/overlay-child.js' => array(),
243
    ),
244
    'css' => array(
245
      $module_path . '/overlay-child.css' => array(),
246
    ),
247
  );
248

    
249
  return $libraries;
250
}
251

    
252
/**
253
 * Implements hook_drupal_goto_alter().
254
 */
255
function overlay_drupal_goto_alter(&$path, &$options, &$http_response_code) {
256
  if (overlay_get_mode() == 'child') {
257
    // The authorize.php script bootstraps Drupal to a very low level, where
258
    // the PHP code that is necessary to close the overlay properly will not be
259
    // loaded. Therefore, if we are redirecting to authorize.php inside the
260
    // overlay, instead redirect back to the current page with instructions to
261
    // close the overlay there before redirecting to the final destination; see
262
    // overlay_init().
263
    if ($path == system_authorized_get_url() || $path == system_authorized_batch_processing_url()) {
264
      $_SESSION['overlay_close_dialog'] = array($path, $options);
265
      $path = current_path();
266
      $options = drupal_get_query_parameters();
267
    }
268

    
269
    // If the current page request is inside the overlay, add ?render=overlay
270
    // to the new path, so that it appears correctly inside the overlay.
271
    if (isset($options['query'])) {
272
      $options['query'] += array('render' => 'overlay');
273
    }
274
    else {
275
      $options['query'] = array('render' => 'overlay');
276
    }
277
  }
278
}
279

    
280
/**
281
 * Implements hook_batch_alter().
282
 *
283
 * If the current page request is inside the overlay, add ?render=overlay to
284
 * the success callback URL, so that it appears correctly within the overlay.
285
 *
286
 * @see overlay_get_mode()
287
 */
288
function overlay_batch_alter(&$batch) {
289
  if (overlay_get_mode() == 'child') {
290
    if (isset($batch['url_options']['query'])) {
291
      $batch['url_options']['query']['render'] = 'overlay';
292
    }
293
    else {
294
      $batch['url_options']['query'] = array('render' => 'overlay');
295
    }
296
  }
297
}
298

    
299
/**
300
 * Implements hook_page_alter().
301
 */
302
function overlay_page_alter(&$page) {
303
  // If we are limiting rendering to a subset of page regions, deny access to
304
  // all other regions so that they will not be processed.
305
  if ($regions_to_render = overlay_get_regions_to_render()) {
306
    $skipped_regions = array_diff(element_children($page), $regions_to_render);
307
    foreach ($skipped_regions as $skipped_region) {
308
      $page[$skipped_region]['#access'] = FALSE;
309
    }
310
  }
311

    
312
  $mode = overlay_get_mode();
313
  if ($mode == 'child') {
314
    // Add the overlay wrapper before the html wrapper.
315
    array_unshift($page['#theme_wrappers'], 'overlay');
316
  }
317
  elseif ($mode == 'parent' && ($message = overlay_disable_message())) {
318
    $page['page_top']['disable_overlay'] = $message;
319
  }
320
}
321

    
322
/**
323
 * Page callback: Dismisses the overlay accessibility message for this user.
324
 *
325
 * @return
326
 *   A render array for a page containing a list of content.
327
 */
328
function overlay_user_dismiss_message() {
329
  global $user;
330
  // It's unlikely, but possible that "access overlay" permission is granted to
331
  // the anonymous role. In this case, we do not display the message to disable
332
  // the overlay, so there is nothing to dismiss. Also, protect against
333
  // cross-site request forgeries by validating a token.
334
  if (empty($user->uid) || !isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'overlay')) {
335
    return MENU_ACCESS_DENIED;
336
  }
337
  else {
338
    user_save(user_load($user->uid), array('data' => array('overlay_message_dismissed' => 1)));
339
    drupal_set_message(t('The message has been dismissed. You can change your overlay settings at any time by visiting your profile page.'));
340
    // Destination is normally given. Go to the user profile as a fallback.
341
    drupal_goto('user/' . $user->uid . '/edit');
342
  }
343
}
344

    
345
/**
346
 * Returns a renderable array representing a message for disabling the overlay.
347
 *
348
 * If the current user can access the overlay and has not previously indicated
349
 * that this message should be dismissed, this function returns a message
350
 * containing a link to disable the overlay. Nothing is returned for anonymous
351
 * users, because the links control per-user settings. Because some screen
352
 * readers are unable to properly read overlay contents, site builders are
353
 * discouraged from granting the "access overlay" permission to the anonymous
354
 * role.
355
 *
356
 * @see http://drupal.org/node/890284
357
 */
358
function overlay_disable_message() {
359
  global $user;
360

    
361
  if (!empty($user->uid) && empty($user->data['overlay_message_dismissed']) && (!isset($user->data['overlay']) || $user->data['overlay']) && user_access('access overlay')) {
362
    $build = array(
363
      '#theme' => 'overlay_disable_message',
364
      '#weight' => -99,
365
      // Link to the user's profile page, where the overlay can be disabled.
366
      'profile_link' => array(
367
        '#type' => 'link',
368
        '#title' => t('If you have problems accessing administrative pages on this site, disable the overlay on your profile page.'),
369
        '#href' => 'user/' . $user->uid . '/edit',
370
        '#options' => array(
371
          'query' => drupal_get_destination(),
372
          'fragment' => 'edit-overlay-control',
373
          'attributes' => array(
374
            'id' => 'overlay-profile-link',
375
            // Prevent the target page from being opened in the overlay.
376
            'class' => array('overlay-exclude'),
377
          ),
378
        ),
379
      ),
380
      // Link to a menu callback that allows this message to be permanently
381
      // dismissed for the current user.
382
      'dismiss_message_link' => array(
383
        '#type' => 'link',
384
        '#title' => t('Dismiss this message.'),
385
        '#href' => 'overlay/dismiss-message',
386
        '#options' => array(
387
          'query' => drupal_get_destination() + array(
388
            // Add a token to protect against cross-site request forgeries.
389
            'token' => drupal_get_token('overlay'),
390
          ),
391
          'attributes' => array(
392
            'id' => 'overlay-dismiss-message',
393
            // If this message is being displayed outside the overlay, prevent
394
            // this link from opening the overlay.
395
            'class' => (overlay_get_mode() == 'parent') ? array('overlay-exclude') : array(),
396
          ),
397
        ),
398
      )
399
    );
400
  }
401
  else {
402
    $build = array();
403
  }
404

    
405
  return $build;
406
}
407

    
408
/**
409
 * Returns the HTML for the message about how to disable the overlay.
410
 *
411
 * @param $variables
412
 *   An associative array with an 'element' element, which itself is an
413
 *   associative array containing:
414
 *   - profile_link: The link to this user's account.
415
 *   - dismiss_message_link: The link to dismiss the overlay.
416
 *
417
 * @ingroup themeable
418
 */
419
function theme_overlay_disable_message($variables) {
420
  $element = $variables['element'];
421

    
422
  // Add CSS classes to hide the links from most sighted users, while keeping
423
  // them accessible to screen-reader users and keyboard-only users. To assist
424
  // screen-reader users, this message appears in both the parent and child
425
  // documents, but only the one in the child document is part of the tab order.
426
  foreach (array('profile_link', 'dismiss_message_link') as $key) {
427
    $element[$key]['#options']['attributes']['class'][] = 'element-invisible';
428
    if (overlay_get_mode() == 'child') {
429
      $element[$key]['#options']['attributes']['class'][] = 'element-focusable';
430
    }
431
  }
432

    
433
  // Render the links.
434
  $output = drupal_render($element['profile_link']) . ' ' . drupal_render($element['dismiss_message_link']);
435

    
436
  // Add a heading for screen-reader users. The heading doesn't need to be seen
437
  // by sighted users.
438
  $output = '<h3 class="element-invisible">' . t('Options for the administrative overlay') . '</h3>' . $output;
439

    
440
  // Wrap in a container for styling.
441
  $output = '<div id="overlay-disable-message" class="clearfix">' . $output . '</div>';
442

    
443
  return $output;
444
}
445

    
446
/**
447
 * Implements hook_block_list_alter().
448
 */
449
function overlay_block_list_alter(&$blocks) {
450
  // If we are limiting rendering to a subset of page regions, hide all blocks
451
  // which appear in regions not on that list. Note that overlay_page_alter()
452
  // does a more comprehensive job of preventing unwanted regions from being
453
  // displayed (regardless of whether they contain blocks or not), but the
454
  // reason for duplicating effort here is performance; we do not even want
455
  // these blocks to be built if they are not going to be displayed.
456
  if ($regions_to_render = overlay_get_regions_to_render()) {
457
    foreach ($blocks as $bid => $block) {
458
      if (!in_array($block->region, $regions_to_render)) {
459
        unset($blocks[$bid]);
460
      }
461
    }
462
  }
463
}
464

    
465
/**
466
 * Implements hook_system_info_alter().
467
 *
468
 * Add default regions for the overlay.
469
 */
470
function overlay_system_info_alter(&$info, $file, $type) {
471
  if ($type == 'theme') {
472
    $info['overlay_regions'][] = 'content';
473
    $info['overlay_regions'][] = 'help';
474
  }
475
}
476

    
477
/**
478
 * Implements hook_preprocess_html().
479
 *
480
 * If the current page request is inside the overlay, add appropriate classes
481
 * to the <body> element, and simplify the page title.
482
 *
483
 * @see overlay_get_mode()
484
 */
485
function overlay_preprocess_html(&$variables) {
486
  if (overlay_get_mode() == 'child') {
487
    // Add overlay class, so themes can react to being displayed in the overlay.
488
    $variables['classes_array'][] = 'overlay';
489
  }
490
}
491

    
492
/**
493
 * Implements hook_preprocess_maintenance_page().
494
 *
495
 * If the current page request is inside the overlay, add appropriate classes
496
 * to the <body> element, and simplify the page title.
497
 *
498
 * @see overlay_preprocess_maintenance_page()
499
 */
500
function overlay_preprocess_maintenance_page(&$variables) {
501
  overlay_preprocess_html($variables);
502
}
503

    
504
/**
505
 * Implements template_preprocess_HOOK() for overlay.tpl.php
506
 *
507
 * If the current page request is inside the overlay, add appropriate classes
508
 * to the <body> element, and simplify the page title.
509
 *
510
 * @see template_process_overlay()
511
 * @see overlay.tpl.php
512
 */
513
function template_preprocess_overlay(&$variables) {
514
  $variables['tabs'] = menu_primary_local_tasks();
515
  $variables['title'] = drupal_get_title();
516
  $variables['disable_overlay'] = overlay_disable_message();
517
  $variables['content_attributes_array']['class'][] = 'clearfix';
518
}
519

    
520
/**
521
 * Implements template_process_HOOK() for overlay.tpl.php
522
 *
523
 * Places the rendered HTML for the page body into a top level variable.
524
 *
525
 * @see template_preprocess_overlay()
526
 * @see overlay.tpl.php
527
 */
528
function template_process_overlay(&$variables) {
529
  $variables['page'] = $variables['page']['#children'];
530
}
531

    
532
/**
533
 * Implements hook_preprocess_page().
534
 *
535
 * If the current page request is inside the overlay, hide the tabs.
536
 *
537
 * @see overlay_get_mode()
538
 */
539
function overlay_preprocess_page(&$variables) {
540
  if (overlay_get_mode() == 'child') {
541
    unset($variables['tabs']['#primary']);
542
  }
543
}
544

    
545
/**
546
 * Stores and returns whether an empty page override is needed.
547
 *
548
 * This is used to prevent a page request which closes the overlay (for
549
 * example, a form submission) from being fully re-rendered before the overlay
550
 * is closed. Instead, we store a variable which will cause the page to be
551
 * rendered by a delivery callback function that does not actually print
552
 * visible HTML (but rather only the bare minimum scripts and styles necessary
553
 * to trigger the overlay to close), thereby allowing the dialog to be closed
554
 * faster and with less interruption, and also allowing the display of messages
555
 * to be deferred to the parent window (rather than displaying them in the
556
 * child window, which will close before the user has had a chance to read
557
 * them).
558
 *
559
 * @param $value
560
 *   By default, an empty page will not be displayed. Set to TRUE to request
561
 *   an empty page display, or FALSE to disable the empty page display (if it
562
 *   was previously enabled on this page request).
563
 *
564
 * @return
565
 *   TRUE if the current behavior is to display an empty page, or FALSE if not.
566
 *
567
 * @see overlay_page_delivery_callback_alter()
568
 */
569
function overlay_display_empty_page($value = NULL) {
570
  $display_empty_page = &drupal_static(__FUNCTION__, FALSE);
571
  if (isset($value)) {
572
    $display_empty_page = $value;
573
  }
574
  return $display_empty_page;
575
}
576

    
577
/**
578
 * Implements hook_page_delivery_callback_alter().
579
 */
580
function overlay_page_delivery_callback_alter(&$callback) {
581
  if (overlay_display_empty_page()) {
582
    $callback = 'overlay_deliver_empty_page';
583
  }
584
}
585

    
586
/**
587
 * Prints an empty page.
588
 *
589
 * This function is used to print out a bare minimum empty page which still has
590
 * the scripts and styles necessary in order to trigger the overlay to close.
591
 */
592
function overlay_deliver_empty_page() {
593
  $empty_page = '<html><head><title></title>' . drupal_get_css() . drupal_get_js() . '</head><body class="overlay"></body></html>';
594
  print $empty_page;
595
  drupal_exit();
596
}
597

    
598
/**
599
 * Gets the current overlay mode.
600
 *
601
 * @see overlay_set_mode()
602
 */
603
function overlay_get_mode() {
604
  return overlay_set_mode(NULL);
605
}
606

    
607
/**
608
 * Sets the overlay mode and adds proper JavaScript and styles to the page.
609
 *
610
 * Note that since setting the overlay mode triggers a variety of behaviors
611
 * (including hooks being invoked), it can only be done once per page request.
612
 * Therefore, the first call to this function which passes along a value of the
613
 * $mode parameter controls the overlay mode that will be used.
614
 *
615
 * @param $mode
616
 *   To set the mode, pass in one of the following values:
617
 *   - 'parent': This is used in the context of a parent window (a regular
618
 *     browser window). If set, JavaScript is added so that administrative
619
 *     links in the parent window will open in an overlay.
620
 *   - 'child': This is used in the context of the child overlay window (the
621
 *     page actually appearing within the overlay iframe). If set, JavaScript
622
 *     and CSS are added so that Drupal behaves nicely from within the overlay.
623
 *   - 'none': This is used to avoid adding any overlay-related code to the
624
 *     page at all. Modules can set this to explicitly prevent the overlay from
625
 *     being used. For example, since the overlay module itself sets the mode
626
 *     to 'parent' or 'child' in overlay_init() when certain conditions are
627
 *     met, other modules which want to override that behavior can do so by
628
 *     setting the mode to 'none' earlier in the page request - e.g., in their
629
 *     own hook_init() implementations, if they have a lower weight.
630
 *   This parameter is optional, and if omitted, the current mode will be
631
 *   returned with no action taken.
632
 *
633
 * @return
634
 *   The current mode, if any has been set, or NULL if no mode has been set.
635
 *
636
 * @ingroup overlay_api
637
 * @see overlay_init()
638
 */
639
function overlay_set_mode($mode = NULL) {
640
  global $base_path;
641
  $overlay_mode = &drupal_static(__FUNCTION__);
642

    
643
  // Make sure external resources are not included more than once. Also return
644
  // the current mode, if no mode was specified.
645
  if (isset($overlay_mode) || !isset($mode)) {
646
    return $overlay_mode;
647
  }
648
  $overlay_mode = $mode;
649

    
650
  switch ($overlay_mode) {
651
    case 'parent':
652
      drupal_add_library('overlay', 'parent');
653

    
654
      // Allow modules to act upon overlay events.
655
      module_invoke_all('overlay_parent_initialize');
656
      break;
657

    
658
    case 'child':
659
      drupal_add_library('overlay', 'child');
660

    
661
      // Allow modules to act upon overlay events.
662
      module_invoke_all('overlay_child_initialize');
663
      break;
664
  }
665
  return $overlay_mode;
666
}
667

    
668
/**
669
 * Implements hook_overlay_parent_initialize().
670
 */
671
function overlay_overlay_parent_initialize() {
672
  // Let the client side know which paths are administrative.
673
  $paths = path_get_admin_paths();
674
  foreach ($paths as &$type) {
675
    $type = str_replace('<front>', variable_get('site_frontpage', 'node'), $type);
676
  }
677
  drupal_add_js(array('overlay' => array('paths' => $paths)), 'setting');
678
  $path_prefixes = array();
679
  if (module_exists('locale') && variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) {
680
    // Get languages grouped by status and select only the enabled ones.
681
    $languages = language_list('enabled');
682
    $languages = $languages[1];
683

    
684
    $path_prefixes = array();
685
    foreach ($languages as $language) {
686
      if ($language->prefix) {
687
        $path_prefixes[] = $language->prefix;
688
      }
689
    }
690
  }
691
  drupal_add_js(array('overlay' => array('pathPrefixes' => $path_prefixes)), 'setting');
692
  // Pass along the Ajax callback for rerendering sections of the parent window.
693
  drupal_add_js(array('overlay' => array('ajaxCallback' => 'overlay-ajax')), 'setting');
694
}
695

    
696
/**
697
 * Implements hook_overlay_child_initialize().
698
 */
699
function overlay_overlay_child_initialize() {
700
  // Check if the parent window needs to refresh any page regions on this page
701
  // request.
702
  overlay_trigger_refresh();
703
  // If this is a POST request, or a GET request with a token parameter, we
704
  // have an indication that something in the supplemental regions of the
705
  // overlay might change during the current page request. We therefore store
706
  // the initial rendered content of those regions here, so that we can compare
707
  // it to the same content rendered in overlay_exit(), at the end of the page
708
  // request. This allows us to check if anything actually did change, and, if
709
  // so, trigger an immediate Ajax refresh of the parent window.
710
  if (!empty($_POST) || isset($_GET['token'])) {
711
    foreach (overlay_supplemental_regions() as $region) {
712
      overlay_store_rendered_content($region, overlay_render_region($region));
713
    }
714
    // In addition, notify the parent window that when the overlay closes,
715
    // the entire parent window should be refreshed.
716
    overlay_request_page_refresh();
717
  }
718
  // Indicate that when the main page rendering occurs later in the page
719
  // request, only the regions that appear within the overlay should be
720
  // rendered.
721
  overlay_set_regions_to_render(overlay_regions());
722
}
723

    
724
/**
725
 * Requests that the overlay closes when the page is displayed.
726
 *
727
 * @param $redirect
728
 *   (optional) The path that should open in the parent window after the
729
 *   overlay closes. If not set, no redirect will be performed on the parent
730
 *   window.
731
 *
732
 * @param $redirect_options
733
 *   (optional) An associative array of options to use when generating the
734
 *   redirect URL.
735
 */
736
function overlay_close_dialog($redirect = NULL, $redirect_options = array()) {
737
  $settings = array(
738
    'overlayChild' => array(
739
      'closeOverlay' => TRUE,
740
    ),
741
  );
742

    
743
  // Tell the child window to perform the redirection when requested to.
744
  if (isset($redirect)) {
745
    $settings['overlayChild']['redirect'] = url($redirect, $redirect_options);
746
  }
747

    
748
  drupal_add_js($settings, array('type' => 'setting'));
749

    
750
  // Since we are closing the overlay as soon as the page is displayed, we do
751
  // not want to show any of the page's actual content.
752
  overlay_display_empty_page(TRUE);
753
}
754

    
755
/**
756
 * Returns a list of page regions that appear in the overlay.
757
 *
758
 * Overlay regions correspond to the entire contents of the overlay child
759
 * window and are refreshed each time a new page request is made within the
760
 * overlay.
761
 *
762
 * @return
763
 *   An array of region names that correspond to those which appear in the
764
 *   overlay, within the theme that is being used to display the current page.
765
 *
766
 * @see overlay_supplemental_regions()
767
 */
768
function overlay_regions() {
769
  return _overlay_region_list('overlay_regions');
770
}
771

    
772
/**
773
 * Returns a list of supplemental page regions for the overlay.
774
 *
775
 * Supplemental overlay regions are those which are technically part of the
776
 * parent window, but appear to the user as being related to the overlay
777
 * (usually because they are displayed next to, rather than underneath, the
778
 * main overlay regions) and therefore need to be dynamically refreshed if any
779
 * administrative actions taken within the overlay change their contents.
780
 *
781
 * An example of a typical overlay supplemental region would be the 'page_top'
782
 * region, in the case where a toolbar is being displayed there.
783
 *
784
 * @return
785
 *   An array of region names that correspond to supplemental overlay regions,
786
 *   within the theme that is being used to display the current page.
787
 *
788
 * @see overlay_regions()
789
 */
790
function overlay_supplemental_regions() {
791
  return _overlay_region_list('overlay_supplemental_regions');
792
}
793

    
794
/**
795
 * Returns a list of page regions related to the overlay.
796
 *
797
 * @param $type
798
 *   The type of regions to return. This can either be 'overlay_regions' or
799
 *   'overlay_supplemental_regions'.
800
 *
801
 * @return
802
 *   An array of region names of the given type, within the theme that is being
803
 *   used to display the current page.
804
 *
805
 * @see overlay_regions()
806
 * @see overlay_supplemental_regions()
807
 */
808
function _overlay_region_list($type) {
809
  // Obtain the current theme. We need to first make sure the theme system is
810
  // initialized, since this function can be called early in the page request.
811
  drupal_theme_initialize();
812
  $themes = list_themes();
813
  $theme = $themes[$GLOBALS['theme']];
814
  // Return the list of regions stored within the theme's info array, or an
815
  // empty array if no regions of the appropriate type are defined.
816
  return !empty($theme->info[$type]) ? $theme->info[$type] : array();
817
}
818

    
819
/**
820
 * Returns a list of page regions that rendering should be limited to.
821
 *
822
 * @return
823
 *   An array containing the names of the regions that will be rendered when
824
 *   drupal_render_page() is called. If empty, then no limits will be imposed,
825
 *   and all regions of the page will be rendered.
826
 *
827
 * @see overlay_page_alter()
828
 * @see overlay_block_list_alter()
829
 * @see overlay_set_regions_to_render()
830
 */
831
function overlay_get_regions_to_render() {
832
  return overlay_set_regions_to_render();
833
}
834

    
835
/**
836
 * Sets the regions of the page that rendering will be limited to.
837
 *
838
 * @param $regions
839
 *   (Optional) An array containing the names of the regions that should be
840
 *   rendered when drupal_render_page() is called. Pass in an empty array to
841
 *   remove all limits and cause drupal_render_page() to render all page
842
 *   regions (the default behavior). If this parameter is omitted, no change
843
 *   will be made to the current list of regions to render.
844
 *
845
 * @return
846
 *   The current list of regions to render, or an empty array if the regions
847
 *   are not being limited.
848
 *
849
 * @see overlay_page_alter()
850
 * @see overlay_block_list_alter()
851
 * @see overlay_get_regions_to_render()
852
 */
853
function overlay_set_regions_to_render($regions = NULL) {
854
  $regions_to_render = &drupal_static(__FUNCTION__, array());
855
  if (isset($regions)) {
856
    $regions_to_render = $regions;
857
  }
858
  return $regions_to_render;
859
}
860

    
861
/**
862
 * Renders an individual page region.
863
 *
864
 * This function is primarily intended to be used for checking the content of
865
 * supplemental overlay regions (e.g., a region containing a toolbar). Passing
866
 * in a region that is intended to display the main page content is not
867
 * supported; the region will be rendered by this function, but the main page
868
 * content will not appear in it. In addition, although this function returns
869
 * the rendered HTML for the provided region, it does not place it on the final
870
 * page, nor add any of its associated JavaScript or CSS to the page.
871
 *
872
 * @param $region
873
 *   The name of the page region that should be rendered.
874
 *
875
 * @return
876
 *   The rendered HTML of the provided region.
877
 */
878
function overlay_render_region($region) {
879
  // Indicate the region that we will be rendering, so that other regions will
880
  // be hidden by overlay_page_alter() and overlay_block_list_alter().
881
  overlay_set_regions_to_render(array($region));
882
  // Do what is necessary to force drupal_render_page() to only display HTML
883
  // from the requested region. Specifically, declare that the main page
884
  // content does not need to automatically be added to the page, and pass in
885
  // a page array that has all theme functions removed (so that overall HTML
886
  // for the page will not be added either).
887
  $system_main_content_added = &drupal_static('system_main_content_added');
888
  $system_main_content_added = TRUE;
889
  $page = array(
890
    '#type' => 'page',
891
    '#theme' => NULL,
892
    '#theme_wrappers' => array(),
893
  );
894
  // Render the region, but do not cache any JavaScript or CSS associated with
895
  // it. This region might not be included the next time drupal_render_page()
896
  // is called, and we do not want its JavaScript or CSS to erroneously appear
897
  // on the final rendered page.
898
  $original_js = drupal_add_js();
899
  $original_css = drupal_add_css();
900
  $original_libraries = drupal_static('drupal_add_library');
901
  $js = &drupal_static('drupal_add_js');
902
  $css = &drupal_static('drupal_add_css');
903
  $libraries = &drupal_static('drupal_add_library');
904
  $markup = drupal_render_page($page);
905
  $js = $original_js;
906
  $css = $original_css;
907
  $libraries = $original_libraries;
908
  // Indicate that the main page content has not, in fact, been displayed, so
909
  // that future calls to drupal_render_page() will be able to render it
910
  // correctly.
911
  $system_main_content_added = FALSE;
912
  // Restore the original behavior of rendering all regions for the next time
913
  // drupal_render_page() is called.
914
  overlay_set_regions_to_render(array());
915
  return $markup;
916
}
917

    
918
/**
919
 * Returns any rendered content that was stored earlier in the page request.
920
 *
921
 * @return
922
 *   An array of all rendered HTML that was stored earlier in the page request,
923
 *   keyed by the identifier with which it was stored. If no content was
924
 *   stored, an empty array is returned.
925
 *
926
 * @see overlay_store_rendered_content()
927
 */
928
function overlay_get_rendered_content() {
929
  return overlay_store_rendered_content();
930
}
931

    
932
/**
933
 * Stores strings representing rendered HTML content.
934
 *
935
 * This function is used to keep a static cache of rendered content that can be
936
 * referred to later in the page request.
937
 *
938
 * @param $id
939
 *   (Optional) An identifier for the content which is being stored, which will
940
 *   be used as an array key in the returned array. If omitted, no change will
941
 *   be made to the current stored data.
942
 * @param $content
943
 *   (Optional) A string representing the rendered data to store. This only has
944
 *   an effect if $id is also provided.
945
 *
946
 * @return
947
 *   An array representing all data that is currently being stored, or an empty
948
 *   array if there is none.
949
 *
950
 * @see overlay_get_rendered_content()
951
 */
952
function overlay_store_rendered_content($id = NULL, $content = NULL) {
953
  $rendered_content = &drupal_static(__FUNCTION__, array());
954
  if (isset($id)) {
955
    $rendered_content[$id] = $content;
956
  }
957
  return $rendered_content;
958
}
959

    
960
/**
961
 * Requests that the parent window refreshes a particular page region.
962
 *
963
 * @param $region
964
 *   The name of the page region to refresh. The parent window will trigger a
965
 *   refresh of this region on the next page load.
966
 *
967
 * @see overlay_trigger_refresh()
968
 * @see Drupal.overlay.refreshRegions()
969
 */
970
function overlay_request_refresh($region) {
971
  $class = drupal_region_class($region);
972
  $_SESSION['overlay_regions_to_refresh'][] = array($class => $region);
973
}
974

    
975
/**
976
 * Requests that the entire parent window is reloaded when the overlay closes.
977
 *
978
 * @see overlay_trigger_refresh()
979
 */
980
function overlay_request_page_refresh() {
981
  $_SESSION['overlay_refresh_parent'] = TRUE;
982
}
983

    
984
/**
985
 * Checks if the parent window needs to be refreshed on this page load.
986
 *
987
 * If the previous page load requested that any page regions be refreshed, or
988
 * if it requested that the entire page be refreshed when the overlay closes,
989
 * pass that request via JavaScript to the child window, so it can in turn pass
990
 * the request to the parent window.
991
 *
992
 * @see overlay_request_refresh()
993
 * @see overlay_request_page_refresh()
994
 * @see Drupal.overlay.refreshRegions()
995
 */
996
function overlay_trigger_refresh() {
997
  if (!empty($_SESSION['overlay_regions_to_refresh'])) {
998
    $settings = array(
999
      'overlayChild' => array(
1000
        'refreshRegions' => $_SESSION['overlay_regions_to_refresh'],
1001
      ),
1002
    );
1003
    drupal_add_js($settings, array('type' => 'setting'));
1004
    unset($_SESSION['overlay_regions_to_refresh']);
1005
  }
1006
  if (!empty($_SESSION['overlay_refresh_parent'])) {
1007
    drupal_add_js(array('overlayChild' => array('refreshPage' => TRUE)), array('type' => 'setting'));
1008
    unset($_SESSION['overlay_refresh_parent']);
1009
  }
1010
}
1011

    
1012
/**
1013
 * Prints the markup obtained by rendering a single region of the page.
1014
 *
1015
 * This function is intended to be called via Ajax.
1016
 *
1017
 * @param $region
1018
 *   The name of the page region to render.
1019
 *
1020
 * @see Drupal.overlay.refreshRegions()
1021
 */
1022
function overlay_ajax_render_region($region) {
1023
  print overlay_render_region($region);
1024
}