cms/drupal/modules/overlay/overlay.module
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     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 }