diff --git a/wysiwyg-dialog-page.tpl.php b/wysiwyg-dialog-page.tpl.php index 0811ae7..3e77d79 100644 --- a/wysiwyg-dialog-page.tpl.php +++ b/wysiwyg-dialog-page.tpl.php @@ -2,83 +2,27 @@ /** * @file - * Theme template to display a single Wysiwyg (plugin) dialog page. - * - * Available variables: - * - * General utility variables: - * - $base_path: The base URL path of the Drupal installation. At the very - * least, this will always default to /. - * - $css: An array of CSS files for the current page. - * - $directory: The directory the theme is located in, e.g. themes/garland or - * themes/garland/minelli. - * - $logged_in: TRUE if the user is registered and signed in. - * - $is_admin: TRUE if the user has permission to access administration pages. - * - * Page metadata: - * - $language: (object) The language the site is being displayed in. - * $language->language contains its textual representation. - * $language->dir contains the language direction. It will either be 'ltr' or 'rtl'. - * - $head_title: A modified version of the page title, for use in the TITLE tag. - * - $head: Markup for the HEAD section (including meta tags, keyword tags, and - * so on). - * - $styles: Style tags necessary to import all CSS files for the page. - * - $scripts: Script tags necessary to load the JavaScript files and settings - * for the page. - * - * Site identity: - * - $site_name: The name of the site, empty when display has been disabled - * in theme settings. - * - * Page content (in order of occurrance in the default page.tpl.php): - * - $breadcrumb: The breadcrumb trail for the current page. - * - $title: The page title, for use in the actual HTML content. - * - $help: Dynamic help text, mostly for admin pages. - * - $messages: HTML for status and error messages. Should be displayed prominently. - * - $tabs: Tabs linking to any sub-pages beneath the current page (e.g., the view - * and edit tabs when displaying a node). - * - * - $content: The main content of the current Drupal page. - * - * Footer/closing data: - * - $footer : The footer region. - * - $closure: Final closing markup from any modules that have altered the page. - * This variable should always be output last, after all other dynamic content. - * - * @see template_preprocess() - * @see template_preprocess_wysiwyg_dialog_page() + * Theme implementation to display a single Wysiwyg (plugin) dialog page. */ ?> - - - - <?php print $head_title; ?> - - - - - -
-
-
- -
-

-
- - -
- -
-
+ -
-
-
- - - +
+ +
+ + +

+ +
+ + + +
+ +
+ + diff --git a/wysiwyg.dialog.inc b/wysiwyg.dialog.inc index 500f78d..8c42bc4 100644 --- a/wysiwyg.dialog.inc +++ b/wysiwyg.dialog.inc @@ -6,7 +6,68 @@ */ /** - * Menu callback; Output a wysiwyg plugin dialog page. + * Page callback; Outputs a dialog page for a wysiwyg plugin. + * + * A Wysiwyg dialog is a bare minimum, simple HTML page; presented in a + * modal/popup window, triggered via JavaScript. + * + * However, Drupal core does not support such a concept, at all. + * Insanity happens on two separate layers: + * - All HTML pages go through the default delivery callback of + * drupal_deliver_html_page(), which calls into drupal_render_page(), which + * in turn *unconditionally* invokes hook_page_build() implementations. Thus, + * block_page_build() and similar implementations add the entirety of their + * page regions and blocks to our simple dialog page. + * Obviously, we don't want that. + * - There is a nice default 'page' theme template implementation, which + * performs all the heavy-lifting that is required for outputting a sane HTML + * page through preprocess and process functions. The theme system does not + * support to "inherit" preprocess and process hooks to alternative + * implementations. Even a very basic HTML page requires almost all of that. + * However, the default page template (normally overridden by a theme) + * contains too many regions and usually also huge a header and footer. + * Obviously, we don't want that. + * + * The poor workaround would be to follow the Overlay module's implementation in + * core: override the theme, build everything, and after doing all of that, + * strip away what isn't needed. Obviously, we don't want that. + * + * Instead, we bend Drupal to sane rules: + * - This page callback returns the actual main content. + * - wysiwyg_menu() defines a custom delivery callback that replaces + * drupal_deliver_html_page(), just because we need to replace + * drupal_render_page(). + * - Our replacement for drupal_render_page() builds a $page that does not use + * #type 'page' but #type 'wysiwyg_dialog_page' instead. + * - #type 'wysiwyg_dialog_page' is defined like #type 'page' in + * system_element_info(), but is required, because there's no way to inherit + * a theme definition but override the page template file to be used. + * - As a consequence, #type 'wysiwyg_dialog_page' uses + * #theme 'wysiwyg_dialog_page', for which we have to implement stub + * preprocess and process callbacks in order to call into the ones for + * #theme 'page'. + * + * As a result we get: + * - A HTML response. + * - A HTML page wrapped into html.tpl.php. + * - A page title, title prefix/suffix, messages, help, etc.pp. + * - A simple page without regions and blocks (neither built nor rendered). + * + * @see wysiwyg_menu() + * @see wysiwyg_deliver_dialog_page + * @see wysiwyg_render_dialog_page() + * @see wysiwyg_element_info() + * @see wysiwyg_theme() + * @see template_preprocess_wysiwyg_dialog_page() + * @see template_process_wysiwyg_dialog_page() + * + * @see drupal_deliver_page() + * @see drupal_deliver_html_page() + * @see drupal_render_page() + * @see system_element_info() + * @see drupal_common_theme() + * @see template_preprocess_page() + * @see template_process_page() */ function wysiwyg_dialog($plugin, $instance) { $plugins = wysiwyg_get_all_plugins(); @@ -27,7 +88,74 @@ function wysiwyg_dialog($plugin, $instance) { ); drupal_add_js(array('wysiwyg' => $settings), 'setting'); - echo theme('wysiwyg_dialog_page', $callback($instance)); + $build = $callback($instance); + if (!is_array($build)) { + $build = array('#markup' => $build); + } + $build += array( + '#instance' => $instance, + '#plugin' => $plugin, + ); + return $build; +} + +/** + * @see drupal_deliver_html_page() + */ +function wysiwyg_deliver_dialog_page($page_callback_result) { + // Menu status constants are integers; page content is a string or array. + if (is_int($page_callback_result)) { + return drupal_deliver_html_page($page_callback_result); + } + + // Emit the correct charset HTTP header, but not if the page callback + // result is NULL, since that likely indicates that it printed something + // in which case, no further headers may be sent, and not if code running + // for this page request has already set the content type header. + if (isset($page_callback_result) && is_null(drupal_get_http_header('Content-Type'))) { + drupal_add_http_header('Content-Type', 'text/html; charset=utf-8'); + } + + // Send appropriate HTTP-Header for browsers and search engines. + global $language; + drupal_add_http_header('Content-Language', $language->language); + + if (isset($page_callback_result)) { + // Print anything besides a menu constant, assuming it's not NULL or + // undefined. + print wysiwyg_render_dialog_page($page_callback_result); + } + + // Perform end-of-request tasks. + drupal_page_footer(); +} + +/** + * @see drupal_render_page() + */ +function wysiwyg_render_dialog_page($page) { + $main_content_display = &drupal_static('system_main_content_added', FALSE); + + // Allow menu callbacks to return strings or arbitrary arrays to render. + // If the array returned is not of #type page directly, we need to fill + // in the page with defaults. + if (is_string($page) || (is_array($page) && (!isset($page['#type']) || ($page['#type'] != 'page')))) { + drupal_set_page_content($page); + $page = element_info('wysiwyg_dialog_page'); + } + + // Modules alter the $page as needed. Blocks are populated into regions like + // 'sidebar_first', 'footer', etc. + drupal_alter(array('wysiwyg_dialog_page', 'page'), $page); + + // If no module has taken care of the main content, add it to the page now. + // This allows the site to still be usable even if no modules that + // control page regions (for example, the Block module) are enabled. + if (!$main_content_display) { + $page['content']['system_main'] = drupal_set_page_content(); + } + + return drupal_render($page); } /** @@ -35,29 +163,21 @@ function wysiwyg_dialog($plugin, $instance) { * * @see wysiwyg_dialog() * @see wysiwyg-dialog-page.tpl.php - * @see template_preprocess() + * @see template_preprocess_page() */ function template_preprocess_wysiwyg_dialog_page(&$variables) { - // Construct page title - $head_title = array(strip_tags(drupal_get_title()), variable_get('site_name', 'Drupal')); - - $variables['head_title'] = implode(' | ', $head_title); - $variables['base_path'] = base_path(); - $variables['front_page'] = url(); - // @todo Would a breadcrumb make sense / possible at all? - // $variables['breadcrumb'] = theme('breadcrumb', drupal_get_breadcrumb()); - $variables['head'] = drupal_get_html_head(); - $variables['help'] = theme('help'); - $variables['language'] = $GLOBALS['language']; - $variables['language']->dir = $GLOBALS['language']->direction ? 'rtl' : 'ltr'; - $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : ''; - $variables['site_name'] = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : ''); - $variables['css'] = drupal_add_css(); - $variables['styles'] = drupal_get_css(); - $variables['scripts'] = drupal_get_js(); - $variables['tabs'] = theme('menu_local_tasks'); - $variables['title'] = drupal_get_title(); - // Closure should be filled last. - $variables['closure'] = theme('closure'); + template_preprocess_page(&$variables); +} + + +/** + * Template process function for theme_wysiwyg_dialog_page(). + * + * @see wysiwyg_dialog() + * @see wysiwyg-dialog-page.tpl.php + * @see template_process_page() + */ +function template_process_wysiwyg_dialog_page(&$variables) { + template_process_page(&$variables); } diff --git a/wysiwyg.module b/wysiwyg.module index ec483e0..b6a4027 100644 --- a/wysiwyg.module +++ b/wysiwyg.module @@ -79,9 +79,11 @@ function wysiwyg_menu() { 'type' => MENU_LOCAL_TASK, 'weight' => 10, ); + // @see wysiwyg_dialog() $items['wysiwyg/%'] = array( 'page callback' => 'wysiwyg_dialog', 'page arguments' => array(1), + 'delivery callback' => 'wysiwyg_deliver_dialog_page', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, 'file' => 'wysiwyg.dialog.inc', @@ -90,6 +92,19 @@ function wysiwyg_menu() { } /** + * Implements hook_element_info(). + */ +function wysiwyg_element_info() { + // @see wysiwyg_dialog() + $types['wysiwyg_dialog_page'] = array( + '#theme' => 'wysiwyg_dialog_page', + '#theme_wrappers' => array('html'), + '#show_messages' => TRUE, + ); + return $types; +} + +/** * Implementation of hook_theme(). * * @see drupal_common_theme(), common.inc @@ -103,8 +118,9 @@ function wysiwyg_theme() { 'wysiwyg_admin_button_table' => array( 'render element' => 'form', ), + // @see wysiwyg_dialog() 'wysiwyg_dialog_page' => array( - 'variables' => array('content' => NULL, 'show_messages' => TRUE), + 'render element' => 'page', 'file' => 'wysiwyg.dialog.inc', 'template' => 'wysiwyg-dialog-page', ),