Projet

Général

Profil

Paste
Télécharger (35,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / modules / overlay / overlay.module @ 76597ebf

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_FORM_ID_alter().
83
 */
84
function overlay_form_user_profile_form_alter(&$form, &$form_state) {
85
  if ($form['#user_category'] == 'account') {
86
    $account = $form['#user'];
87
    if (user_access('access overlay', $account)) {
88
      $form['overlay_control'] = array(
89
        '#type' => 'fieldset',
90
        '#title' => t('Administrative overlay'),
91
        '#weight' => 4,
92
        '#collapsible' => TRUE,
93
      );
94

    
95
      $form['overlay_control']['overlay'] = array(
96
        '#type' => 'checkbox',
97
        '#title' => t('Use the overlay for administrative pages.'),
98
        '#description' => t('Show administrative pages on top of the page you started from.'),
99
        '#default_value' => isset($account->data['overlay']) ? $account->data['overlay'] : 1,
100
      );
101
    }
102
  }
103
}
104

    
105
/**
106
 * Implements hook_user_presave().
107
 */
108
function overlay_user_presave(&$edit, $account, $category) {
109
  if (isset($edit['overlay'])) {
110
    $edit['data']['overlay'] = $edit['overlay'];
111
  }
112
}
113

    
114
/**
115
 * Implements hook_init().
116
 *
117
 * Determine whether the current page request is destined to appear in the
118
 * parent window or in the overlay window, and format the page accordingly.
119
 *
120
 * @see overlay_set_mode()
121
 */
122
function overlay_init() {
123
  global $user;
124

    
125
  $mode = overlay_get_mode();
126

    
127
  // Only act if the user has access to the overlay and a mode was not already
128
  // set. Other modules can also enable the overlay directly for other uses.
129
  $use_overlay = !isset($user->data['overlay']) || $user->data['overlay'];
130
  if (empty($mode) && user_access('access overlay') && $use_overlay) {
131
    $current_path = current_path();
132
    // After overlay is enabled on the modules page, redirect to
133
    // <front>#overlay=admin/modules to actually enable the overlay.
134
    if (isset($_SESSION['overlay_enable_redirect']) && $_SESSION['overlay_enable_redirect']) {
135
      unset($_SESSION['overlay_enable_redirect']);
136
      drupal_goto('<front>', array('fragment' => 'overlay=' . $current_path));
137
    }
138

    
139
    if (isset($_GET['render']) && $_GET['render'] == 'overlay') {
140
      // If a previous page requested that we close the overlay, close it and
141
      // redirect to the final destination.
142
      if (isset($_SESSION['overlay_close_dialog'])) {
143
        call_user_func_array('overlay_close_dialog', $_SESSION['overlay_close_dialog']);
144
        unset($_SESSION['overlay_close_dialog']);
145
      }
146
      // If this page shouldn't be rendered inside the overlay, redirect to the
147
      // parent.
148
      elseif (!path_is_admin($current_path)) {
149
        // Prevent open redirects by ensuring the current path is not an absolute URL.
150
        if (url_is_external($current_path)) {
151
          $current_path = '<front>';
152
        }
153
        overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('q', 'render'))));
154
      }
155

    
156
      // Indicate that we are viewing an overlay child page.
157
      overlay_set_mode('child');
158

    
159
      // Unset the render parameter to avoid it being included in URLs on the page.
160
      unset($_GET['render']);
161
    }
162
    // Do not enable the overlay if we already are on an admin page.
163
    elseif (!path_is_admin($current_path)) {
164
      // Otherwise add overlay parent code and our behavior.
165
      overlay_set_mode('parent');
166
    }
167
  }
168
}
169

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

    
200
/**
201
 * Implements hook_library().
202
 */
203
function overlay_library() {
204
  $module_path = drupal_get_path('module', 'overlay');
205

    
206
  // Overlay parent.
207
  $libraries['parent'] = array(
208
    'title' => 'Overlay: Parent',
209
    'website' => 'http://drupal.org/documentation/modules/overlay',
210
    'version' => '1.0',
211
    'js' => array(
212
      $module_path . '/overlay-parent.js' => array(),
213
    ),
214
    'css' => array(
215
      $module_path . '/overlay-parent.css' => array(),
216
    ),
217
    'dependencies' => array(
218
      array('system', 'ui'),
219
      array('system', 'jquery.bbq'),
220
    ),
221
  );
222
  // Overlay child.
223
  $libraries['child'] = array(
224
    'title' => 'Overlay: Child',
225
    'website' => 'http://drupal.org/documentation/modules/overlay',
226
    'version' => '1.0',
227
    'js' => array(
228
      $module_path . '/overlay-child.js' => array(),
229
    ),
230
    'css' => array(
231
      $module_path . '/overlay-child.css' => array(),
232
    ),
233
  );
234

    
235
  return $libraries;
236
}
237

    
238
/**
239
 * Implements hook_drupal_goto_alter().
240
 */
241
function overlay_drupal_goto_alter(&$path, &$options, &$http_response_code) {
242
  if (overlay_get_mode() == 'child') {
243
    // The authorize.php script bootstraps Drupal to a very low level, where
244
    // the PHP code that is necessary to close the overlay properly will not be
245
    // loaded. Therefore, if we are redirecting to authorize.php inside the
246
    // overlay, instead redirect back to the current page with instructions to
247
    // close the overlay there before redirecting to the final destination; see
248
    // overlay_init().
249
    if ($path == system_authorized_get_url() || $path == system_authorized_batch_processing_url()) {
250
      $_SESSION['overlay_close_dialog'] = array($path, $options);
251
      $path = current_path();
252
      $options = drupal_get_query_parameters();
253
    }
254

    
255
    // If the current page request is inside the overlay, add ?render=overlay
256
    // to the new path, so that it appears correctly inside the overlay.
257
    if (isset($options['query'])) {
258
      $options['query'] += array('render' => 'overlay');
259
    }
260
    else {
261
      $options['query'] = array('render' => 'overlay');
262
    }
263
  }
264
}
265

    
266
/**
267
 * Implements hook_batch_alter().
268
 *
269
 * If the current page request is inside the overlay, add ?render=overlay to
270
 * the success callback URL, so that it appears correctly within the overlay.
271
 *
272
 * @see overlay_get_mode()
273
 */
274
function overlay_batch_alter(&$batch) {
275
  if (overlay_get_mode() == 'child') {
276
    if (isset($batch['url_options']['query'])) {
277
      $batch['url_options']['query']['render'] = 'overlay';
278
    }
279
    else {
280
      $batch['url_options']['query'] = array('render' => 'overlay');
281
    }
282
  }
283
}
284

    
285
/**
286
 * Implements hook_page_alter().
287
 */
288
function overlay_page_alter(&$page) {
289
  // If we are limiting rendering to a subset of page regions, deny access to
290
  // all other regions so that they will not be processed.
291
  if ($regions_to_render = overlay_get_regions_to_render()) {
292
    $skipped_regions = array_diff(element_children($page), $regions_to_render);
293
    foreach ($skipped_regions as $skipped_region) {
294
      $page[$skipped_region]['#access'] = FALSE;
295
    }
296
  }
297

    
298
  $mode = overlay_get_mode();
299
  if ($mode == 'child') {
300
    // Add the overlay wrapper before the html wrapper.
301
    array_unshift($page['#theme_wrappers'], 'overlay');
302
  }
303
  elseif ($mode == 'parent' && ($message = overlay_disable_message())) {
304
    $page['page_top']['disable_overlay'] = $message;
305
  }
306
}
307

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

    
331
/**
332
 * Returns a renderable array representing a message for disabling the overlay.
333
 *
334
 * If the current user can access the overlay and has not previously indicated
335
 * that this message should be dismissed, this function returns a message
336
 * containing a link to disable the overlay. Nothing is returned for anonymous
337
 * users, because the links control per-user settings. Because some screen
338
 * readers are unable to properly read overlay contents, site builders are
339
 * discouraged from granting the "access overlay" permission to the anonymous
340
 * role.
341
 *
342
 * @see http://drupal.org/node/890284
343
 */
344
function overlay_disable_message() {
345
  global $user;
346

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

    
391
  return $build;
392
}
393

    
394
/**
395
 * Returns the HTML for the message about how to disable the overlay.
396
 *
397
 * @param $variables
398
 *   An associative array with an 'element' element, which itself is an
399
 *   associative array containing:
400
 *   - profile_link: The link to this user's account.
401
 *   - dismiss_message_link: The link to dismiss the overlay.
402
 *
403
 * @ingroup themeable
404
 */
405
function theme_overlay_disable_message($variables) {
406
  $element = $variables['element'];
407

    
408
  // Add CSS classes to hide the links from most sighted users, while keeping
409
  // them accessible to screen-reader users and keyboard-only users. To assist
410
  // screen-reader users, this message appears in both the parent and child
411
  // documents, but only the one in the child document is part of the tab order.
412
  foreach (array('profile_link', 'dismiss_message_link') as $key) {
413
    $element[$key]['#options']['attributes']['class'][] = 'element-invisible';
414
    if (overlay_get_mode() == 'child') {
415
      $element[$key]['#options']['attributes']['class'][] = 'element-focusable';
416
    }
417
  }
418

    
419
  // Render the links.
420
  $output = drupal_render($element['profile_link']) . ' ' . drupal_render($element['dismiss_message_link']);
421

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

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

    
429
  return $output;
430
}
431

    
432
/**
433
 * Implements hook_block_list_alter().
434
 */
435
function overlay_block_list_alter(&$blocks) {
436
  // If we are limiting rendering to a subset of page regions, hide all blocks
437
  // which appear in regions not on that list. Note that overlay_page_alter()
438
  // does a more comprehensive job of preventing unwanted regions from being
439
  // displayed (regardless of whether they contain blocks or not), but the
440
  // reason for duplicating effort here is performance; we do not even want
441
  // these blocks to be built if they are not going to be displayed.
442
  if ($regions_to_render = overlay_get_regions_to_render()) {
443
    foreach ($blocks as $bid => $block) {
444
      if (!in_array($block->region, $regions_to_render)) {
445
        unset($blocks[$bid]);
446
      }
447
    }
448
  }
449
}
450

    
451
/**
452
 * Implements hook_system_info_alter().
453
 *
454
 * Add default regions for the overlay.
455
 */
456
function overlay_system_info_alter(&$info, $file, $type) {
457
  if ($type == 'theme') {
458
    $info['overlay_regions'][] = 'content';
459
    $info['overlay_regions'][] = 'help';
460
  }
461
}
462

    
463
/**
464
 * Implements hook_preprocess_html().
465
 *
466
 * If the current page request is inside the overlay, add appropriate classes
467
 * to the <body> element, and simplify the page title.
468
 *
469
 * @see overlay_get_mode()
470
 */
471
function overlay_preprocess_html(&$variables) {
472
  if (overlay_get_mode() == 'child') {
473
    // Add overlay class, so themes can react to being displayed in the overlay.
474
    $variables['classes_array'][] = 'overlay';
475
  }
476
}
477

    
478
/**
479
 * Implements hook_preprocess_maintenance_page().
480
 *
481
 * If the current page request is inside the overlay, add appropriate classes
482
 * to the <body> element, and simplify the page title.
483
 *
484
 * @see overlay_preprocess_maintenance_page()
485
 */
486
function overlay_preprocess_maintenance_page(&$variables) {
487
  overlay_preprocess_html($variables);
488
}
489

    
490
/**
491
 * Implements template_preprocess_HOOK() for overlay.tpl.php
492
 *
493
 * If the current page request is inside the overlay, add appropriate classes
494
 * to the <body> element, and simplify the page title.
495
 *
496
 * @see template_process_overlay()
497
 * @see overlay.tpl.php
498
 */
499
function template_preprocess_overlay(&$variables) {
500
  $variables['tabs'] = menu_primary_local_tasks();
501
  $variables['title'] = drupal_get_title();
502
  $variables['disable_overlay'] = overlay_disable_message();
503
  $variables['content_attributes_array']['class'][] = 'clearfix';
504
}
505

    
506
/**
507
 * Implements template_process_HOOK() for overlay.tpl.php
508
 *
509
 * Places the rendered HTML for the page body into a top level variable.
510
 *
511
 * @see template_preprocess_overlay()
512
 * @see overlay.tpl.php
513
 */
514
function template_process_overlay(&$variables) {
515
  $variables['page'] = $variables['page']['#children'];
516
}
517

    
518
/**
519
 * Implements hook_preprocess_page().
520
 *
521
 * If the current page request is inside the overlay, hide the tabs.
522
 *
523
 * @see overlay_get_mode()
524
 */
525
function overlay_preprocess_page(&$variables) {
526
  if (overlay_get_mode() == 'child') {
527
    unset($variables['tabs']['#primary']);
528
  }
529
}
530

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

    
563
/**
564
 * Implements hook_page_delivery_callback_alter().
565
 */
566
function overlay_page_delivery_callback_alter(&$callback) {
567
  if (overlay_display_empty_page()) {
568
    $callback = 'overlay_deliver_empty_page';
569
  }
570
}
571

    
572
/**
573
 * Prints an empty page.
574
 *
575
 * This function is used to print out a bare minimum empty page which still has
576
 * the scripts and styles necessary in order to trigger the overlay to close.
577
 */
578
function overlay_deliver_empty_page() {
579
  $empty_page = '<html><head><title></title>' . drupal_get_css() . drupal_get_js() . '</head><body class="overlay"></body></html>';
580
  print $empty_page;
581
  drupal_exit();
582
}
583

    
584
/**
585
 * Gets the current overlay mode.
586
 *
587
 * @see overlay_set_mode()
588
 */
589
function overlay_get_mode() {
590
  return overlay_set_mode(NULL);
591
}
592

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

    
629
  // Make sure external resources are not included more than once. Also return
630
  // the current mode, if no mode was specified.
631
  if (isset($overlay_mode) || !isset($mode)) {
632
    return $overlay_mode;
633
  }
634
  $overlay_mode = $mode;
635

    
636
  switch ($overlay_mode) {
637
    case 'parent':
638
      drupal_add_library('overlay', 'parent');
639

    
640
      // Allow modules to act upon overlay events.
641
      module_invoke_all('overlay_parent_initialize');
642
      break;
643

    
644
    case 'child':
645
      drupal_add_library('overlay', 'child');
646

    
647
      // Allow modules to act upon overlay events.
648
      module_invoke_all('overlay_child_initialize');
649
      break;
650
  }
651
  return $overlay_mode;
652
}
653

    
654
/**
655
 * Implements hook_overlay_parent_initialize().
656
 */
657
function overlay_overlay_parent_initialize() {
658
  // Let the client side know which paths are administrative.
659
  $paths = path_get_admin_paths();
660
  foreach ($paths as &$type) {
661
    $type = str_replace('<front>', variable_get('site_frontpage', 'node'), $type);
662
  }
663
  drupal_add_js(array('overlay' => array('paths' => $paths)), 'setting');
664
  $path_prefixes = array();
665
  if (module_exists('locale') && variable_get('locale_language_negotiation_url_part', LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) == LOCALE_LANGUAGE_NEGOTIATION_URL_PREFIX) {
666
    // Get languages grouped by status and select only the enabled ones.
667
    $languages = language_list('enabled');
668
    $languages = $languages[1];
669

    
670
    $path_prefixes = array();
671
    foreach ($languages as $language) {
672
      if ($language->prefix) {
673
        $path_prefixes[] = $language->prefix;
674
      }
675
    }
676
  }
677
  drupal_add_js(array('overlay' => array('pathPrefixes' => $path_prefixes)), 'setting');
678
  // Pass along the Ajax callback for rerendering sections of the parent window.
679
  drupal_add_js(array('overlay' => array('ajaxCallback' => 'overlay-ajax')), 'setting');
680
}
681

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

    
710
/**
711
 * Requests that the overlay closes when the page is displayed.
712
 *
713
 * @param $redirect
714
 *   (optional) The path that should open in the parent window after the
715
 *   overlay closes. If not set, no redirect will be performed on the parent
716
 *   window.
717
 *
718
 * @param $redirect_options
719
 *   (optional) An associative array of options to use when generating the
720
 *   redirect URL.
721
 */
722
function overlay_close_dialog($redirect = NULL, $redirect_options = array()) {
723
  $settings = array(
724
    'overlayChild' => array(
725
      'closeOverlay' => TRUE,
726
    ),
727
  );
728

    
729
  // Tell the child window to perform the redirection when requested to.
730
  if (isset($redirect)) {
731
    $settings['overlayChild']['redirect'] = url($redirect, $redirect_options);
732
  }
733

    
734
  drupal_add_js($settings, array('type' => 'setting'));
735

    
736
  // Since we are closing the overlay as soon as the page is displayed, we do
737
  // not want to show any of the page's actual content.
738
  overlay_display_empty_page(TRUE);
739
}
740

    
741
/**
742
 * Returns a list of page regions that appear in the overlay.
743
 *
744
 * Overlay regions correspond to the entire contents of the overlay child
745
 * window and are refreshed each time a new page request is made within the
746
 * overlay.
747
 *
748
 * @return
749
 *   An array of region names that correspond to those which appear in the
750
 *   overlay, within the theme that is being used to display the current page.
751
 *
752
 * @see overlay_supplemental_regions()
753
 */
754
function overlay_regions() {
755
  return _overlay_region_list('overlay_regions');
756
}
757

    
758
/**
759
 * Returns a list of supplemental page regions for the overlay.
760
 *
761
 * Supplemental overlay regions are those which are technically part of the
762
 * parent window, but appear to the user as being related to the overlay
763
 * (usually because they are displayed next to, rather than underneath, the
764
 * main overlay regions) and therefore need to be dynamically refreshed if any
765
 * administrative actions taken within the overlay change their contents.
766
 *
767
 * An example of a typical overlay supplemental region would be the 'page_top'
768
 * region, in the case where a toolbar is being displayed there.
769
 *
770
 * @return
771
 *   An array of region names that correspond to supplemental overlay regions,
772
 *   within the theme that is being used to display the current page.
773
 *
774
 * @see overlay_regions()
775
 */
776
function overlay_supplemental_regions() {
777
  return _overlay_region_list('overlay_supplemental_regions');
778
}
779

    
780
/**
781
 * Returns a list of page regions related to the overlay.
782
 *
783
 * @param $type
784
 *   The type of regions to return. This can either be 'overlay_regions' or
785
 *   'overlay_supplemental_regions'.
786
 *
787
 * @return
788
 *   An array of region names of the given type, within the theme that is being
789
 *   used to display the current page.
790
 *
791
 * @see overlay_regions()
792
 * @see overlay_supplemental_regions()
793
 */
794
function _overlay_region_list($type) {
795
  // Obtain the current theme. We need to first make sure the theme system is
796
  // initialized, since this function can be called early in the page request.
797
  drupal_theme_initialize();
798
  $themes = list_themes();
799
  $theme = $themes[$GLOBALS['theme']];
800
  // Return the list of regions stored within the theme's info array, or an
801
  // empty array if no regions of the appropriate type are defined.
802
  return !empty($theme->info[$type]) ? $theme->info[$type] : array();
803
}
804

    
805
/**
806
 * Returns a list of page regions that rendering should be limited to.
807
 *
808
 * @return
809
 *   An array containing the names of the regions that will be rendered when
810
 *   drupal_render_page() is called. If empty, then no limits will be imposed,
811
 *   and all regions of the page will be rendered.
812
 *
813
 * @see overlay_page_alter()
814
 * @see overlay_block_list_alter()
815
 * @see overlay_set_regions_to_render()
816
 */
817
function overlay_get_regions_to_render() {
818
  return overlay_set_regions_to_render();
819
}
820

    
821
/**
822
 * Sets the regions of the page that rendering will be limited to.
823
 *
824
 * @param $regions
825
 *   (Optional) An array containing the names of the regions that should be
826
 *   rendered when drupal_render_page() is called. Pass in an empty array to
827
 *   remove all limits and cause drupal_render_page() to render all page
828
 *   regions (the default behavior). If this parameter is omitted, no change
829
 *   will be made to the current list of regions to render.
830
 *
831
 * @return
832
 *   The current list of regions to render, or an empty array if the regions
833
 *   are not being limited.
834
 *
835
 * @see overlay_page_alter()
836
 * @see overlay_block_list_alter()
837
 * @see overlay_get_regions_to_render()
838
 */
839
function overlay_set_regions_to_render($regions = NULL) {
840
  $regions_to_render = &drupal_static(__FUNCTION__, array());
841
  if (isset($regions)) {
842
    $regions_to_render = $regions;
843
  }
844
  return $regions_to_render;
845
}
846

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

    
904
/**
905
 * Returns any rendered content that was stored earlier in the page request.
906
 *
907
 * @return
908
 *   An array of all rendered HTML that was stored earlier in the page request,
909
 *   keyed by the identifier with which it was stored. If no content was
910
 *   stored, an empty array is returned.
911
 *
912
 * @see overlay_store_rendered_content()
913
 */
914
function overlay_get_rendered_content() {
915
  return overlay_store_rendered_content();
916
}
917

    
918
/**
919
 * Stores strings representing rendered HTML content.
920
 *
921
 * This function is used to keep a static cache of rendered content that can be
922
 * referred to later in the page request.
923
 *
924
 * @param $id
925
 *   (Optional) An identifier for the content which is being stored, which will
926
 *   be used as an array key in the returned array. If omitted, no change will
927
 *   be made to the current stored data.
928
 * @param $content
929
 *   (Optional) A string representing the rendered data to store. This only has
930
 *   an effect if $id is also provided.
931
 *
932
 * @return
933
 *   An array representing all data that is currently being stored, or an empty
934
 *   array if there is none.
935
 *
936
 * @see overlay_get_rendered_content()
937
 */
938
function overlay_store_rendered_content($id = NULL, $content = NULL) {
939
  $rendered_content = &drupal_static(__FUNCTION__, array());
940
  if (isset($id)) {
941
    $rendered_content[$id] = $content;
942
  }
943
  return $rendered_content;
944
}
945

    
946
/**
947
 * Requests that the parent window refreshes a particular page region.
948
 *
949
 * @param $region
950
 *   The name of the page region to refresh. The parent window will trigger a
951
 *   refresh of this region on the next page load.
952
 *
953
 * @see overlay_trigger_refresh()
954
 * @see Drupal.overlay.refreshRegions()
955
 */
956
function overlay_request_refresh($region) {
957
  $class = drupal_region_class($region);
958
  $_SESSION['overlay_regions_to_refresh'][] = array($class => $region);
959
}
960

    
961
/**
962
 * Requests that the entire parent window is reloaded when the overlay closes.
963
 *
964
 * @see overlay_trigger_refresh()
965
 */
966
function overlay_request_page_refresh() {
967
  $_SESSION['overlay_refresh_parent'] = TRUE;
968
}
969

    
970
/**
971
 * Checks if the parent window needs to be refreshed on this page load.
972
 *
973
 * If the previous page load requested that any page regions be refreshed, or
974
 * if it requested that the entire page be refreshed when the overlay closes,
975
 * pass that request via JavaScript to the child window, so it can in turn pass
976
 * the request to the parent window.
977
 *
978
 * @see overlay_request_refresh()
979
 * @see overlay_request_page_refresh()
980
 * @see Drupal.overlay.refreshRegions()
981
 */
982
function overlay_trigger_refresh() {
983
  if (!empty($_SESSION['overlay_regions_to_refresh'])) {
984
    $settings = array(
985
      'overlayChild' => array(
986
        'refreshRegions' => $_SESSION['overlay_regions_to_refresh'],
987
      ),
988
    );
989
    drupal_add_js($settings, array('type' => 'setting'));
990
    unset($_SESSION['overlay_regions_to_refresh']);
991
  }
992
  if (!empty($_SESSION['overlay_refresh_parent'])) {
993
    drupal_add_js(array('overlayChild' => array('refreshPage' => TRUE)), array('type' => 'setting'));
994
    unset($_SESSION['overlay_refresh_parent']);
995
  }
996
}
997

    
998
/**
999
 * Prints the markup obtained by rendering a single region of the page.
1000
 *
1001
 * This function is intended to be called via Ajax.
1002
 *
1003
 * @param $region
1004
 *   The name of the page region to render.
1005
 *
1006
 * @see Drupal.overlay.refreshRegions()
1007
 */
1008
function overlay_ajax_render_region($region) {
1009
  print overlay_render_region($region);
1010
}