Index: modules/popuppage/popuppage.info =================================================================== RCS file: modules/popuppage/popuppage.info diff -N modules/popuppage/popuppage.info --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/popuppage/popuppage.info 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,6 @@ +; $Id:$ +name = Popups +description = General dialog creation utilities +package = User Interface +core = 6.x + Index: modules/popuppage/popuppage.module =================================================================== RCS file: modules/popuppage/popuppage.module diff -N modules/popuppage/popuppage.module --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/popuppage/popuppage.module 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,294 @@ + Add vocab. Adding second item to page does not trigger d-n-d transformation. + * Might be a problem with Taxonomy. Menus doesn't have problem (adds d-n-d on first item). + */ + + +// ************************************************************************** +// CORE HOOK FUNCTIONS **************************************************** +// ************************************************************************** + +/** + * hook_menu + * + * @return array of new menu items. + */ +function popuppage_menu() { + $items['admin/settings/popuppage'] = array( + 'page callback' => 'drupal_get_form', + 'page arguments' => array('popuppage_admin_settings'), + 'title' => 'Popups', + 'description' => 'Configure the page-in-a-dialog behavior.', + ); + return $items; +} + +/** + * hook_init + * + * Look at the page path and see if popup behavior has been requested for any links in this page. + */ +function popuppage_init() { + $popups = popuppage_get_popups(); + if (isset($popups[$_GET['q']])) { + popuppage_add_popups( $popups[$_GET['q']] ); + } +} + +/** + * hook_form_alter + * + * Look at the form_id and see if popup behavior has been requested for any links in this form. + * + * @param form_array $form + * @param array $form_state + * @param str $form_id: + */ +function popuppage_form_alter(&$form, $form_state, $form_id) { +// print $form_id; + // Add popup behavior to the form if requested. + $popups = popuppage_get_popups(); + if (isset($popups[$form_id])) { + popuppage_add_popups( $popups[$form_id] ); + } +} + +// ************************************************************************** +// UTILITY FUNCTIONS ****************************************************** +// ************************************************************************** + +/** + * Build the list of popup rules from all modules that implement hook_popups. + * + * @todo - Add some caching so we don't rebuild everytime. + */ +function popuppage_get_popups() { + static $popups = NULL; + if (!isset($popups)) { + $popups = module_invoke_all('popups'); + } + return $popups; +} + +/** + * Attach the popup behavior to the page. + * + * The default behavoir of a popup is to open a form that will modify the original page. The popup submits + * the form and reloads the original page with the resulting new content. The popup then replaces + * the original page's content area with the new copy of that content area. + * + * @param array $rule: Array of rules to apply to the page or form, keyed by jQuery link selector. + * Options: + * noReload: Does the popup NOT modify the original page (Default: false). + * updateTitle: Does the popup modify the title of the current page (Default: false). + * surpressMessages: Don't show the messages the form returns in a popup (Default: false). + * targetSelector: Defines the area on the original page that will be updated (Default: system-wide setting) + * resultsSubselector: Defines the resulting new content to put into the targetSelector (Default: use the entire new results) + * TODO - come up with a good use cases for resultsSubselector and targetSelector. + * singleRow: Array of selectors descripting the elements inside a row to be replaced (Default: replace entire targetSelector) + * additionalJavascript: Array of JavaScript files that must be included to correctly run the page in the popup. + * additionalCss: Array of CSS files that must be included to correctly style the page in the popup. + * + * Rule Format Example: + * 'admin/content/taxonomy' => array( // Act only on the links on this page. + * 'div#tabs-wrapper a:eq(1)', // No options, so use defaults. Note: Selector must select element(s). + * 'table td:nth-child(2) a' => array( + * 'noReload' => true, // Popup will not modify original page. + * ), + * ) + * + */ +function popuppage_add_popups($rules=null) { + static $added = FALSE; + if (!$added) { +// drupal_add_css(drupal_get_path('module', 'popups') .'/popups.css'); + drupal_add_js('misc/popupapi.js', 'core'); + drupal_add_js(drupal_get_path('module', 'popuppage') .'/popuppage.js'); + drupal_add_js('misc/jquery.form.js'); + if (is_array($rules)) { + $settings = array( 'popups' => array( + 'defaultTargetSelector' => variable_get('popups_content_selector', 'div.left-corner > div.clear-block:last'), + 'defaultTitleSelector' => variable_get('popups_title_selector', 'div.left-corner > h2:eq(0)'), + 'links' => array(), + )); + foreach ($rules as $popup_selector => $options) { + if (is_array($options)) { + $settings['popups']['links'][$popup_selector] = $options; + if (isset($options['additionalJavascript']) && is_array($options['additionalJavascript'])) { + foreach ($options['additionalJavascript'] as $file) { + drupal_add_js($file); + } + } + if (isset($options['additionalCss']) && is_array($options['additionalCss'])) { + foreach ($options['additionalCss'] as $file) { + drupal_add_css($file); + } + } + } + else { + $settings['popups']['links'][$options] = array(); + } + } + drupal_add_js( $settings, 'setting' ); + } + + $added = TRUE; + } +} + +/** + * hook_popups + * + * This implements hook_popups, defined in popuppage_get_popups. + * It adds page-in-popup behavior to the core admin pages. + * See the comments in popuppage_add_popups for explination of the options. + * + * @return: Array of link selectors to apply popup behavior to. + * Keyed by path or form_id. + */ +function popuppage_popups() { + $operations = preg_replace('/[\W]+/', '-', strtolower(t('Operations'))); + + return array( + 'admin/build/block' => array( // Blocks admin page. + '#tabs-wrapper a[href$=admin/build/block/add]', // Add Block + '#blocks a[href~=admin/build/block/configure/block]', // configure + '#blocks a[href~=admin/build/block/delete]', // delete + ), + 'admin/build/path' => array( // URL aliases admin page. + '#tabs-wrapper a[href$=admin/build/path/add]', // Add alias + 'td:nth-child(3) a[href~=admin/build/path/edit]', // edit alias + 'td:nth-child(4) a[href~=admin/build/path/delete]', // delete alias + ), + 'admin/content/taxonomy' => array( // Taxonomy admin page. + // TODO: If there are not more than one items to start with, d-n-d files aren't loaded into page. + // This causes trouble when the 2nd item is added, no d-n-d. + // Might be bug in taxonomy table building (or at least inconsistancy). + '#tabs-wrapper a[href$=admin/content/taxonomy/add/vocabulary]' => array( // Add vocabulary + 'additionalJavascript' => array('misc/tabledrag.js'), + ), + '#taxonomy-overview-vocabularies td a:contains('. t('edit vocabulary') .')', // edit vocabulary + '#taxonomy-overview-vocabularies td a:contains('. t('list terms') .')' => array( // list terms + 'noReload' => true, + 'additionalJavascript' => array('misc/tabledrag.js'), + ), + '#taxonomy-overview-vocabularies td a:contains('. t('add terms') .')' => array( // add terms + 'noReload' => true, + 'additionalJavascript' => array('misc/collapse.js'), + ), + ), + 'admin/content/types' => array( // Content Type admin page + '#tabs-wrapper a[href$=admin/content/types/add]' => array( // Add content type + 'additionalJavascript' => array('misc/collapse.js'), + ), + 'table td:nth-child(4) a, table td:nth-child(5) a' // edit, delete + ), + 'admin/content/node' => array( // Existing Content admin page + '#node-admin-content td a:contains('. t('edit') .')' => array( // edit + 'additionalJavascript' => array('misc/collapse.js'), + // TODO: teaser.js not working: Drupal.settings.teaser has no properties + // 'additionalJavascript' => array('misc/collapse.js', 'misc/teaser.js'), + ) + ), + 'page_node_form' => array( // Node edit form + 'a[href$=filter/tips]' => array( // Fixes insane "More information..." link + 'noReload' => true, + //TODO 'addCloseButton' => true - just an idea. + ) + ), + 'admin/content/comment' => array( // Comments admin page. + 'table td:nth-child(2) a' => array( // view (TODO: popup too small) + 'noReload' => true, + 'additionalCss' => array(), //TODO: needs some custom css to get #comments to stay in the popup (no -25px margin). + ), + '#comment-admin-overview td a:contains('. t('edit') .')' => array( // edit + 'additionalJavascript' => array('misc/collapse.js'), + ), + ), + 'admin/user/rules' => array( // Access rules admin page. + '#tabs-wrapper a[href$=admin/user/rules/add]', // Add rule + 'table td:nth-child(4) a, table td:nth-child(5) a', // edit, delete + '#tabs-wrapper a[href$=/admin/user/rules/check]' => array( // Check rule + 'noReload' => true, + ), + ), + 'admin/user/user' => array( // Manage all users admin page. + //Add user (TODO: Can't test, keeps crashing apache!) + '#tabs-wrapper a[href$=admin/user/user/create]' => array( + // TODO: "translate has no properties" user.js line 16. + 'additionalJavascript' => array(drupal_get_path('module', 'user') .'/user.js'), + ), + '#user-admin-account td:nth-child(2) a' => array( // View the user + 'noReload' => true, + ), + + ), + 'menu_overview_form' => array( // Menu admin form. + // Add Item, , edit, delete + '#tabs-wrapper a:eq(1), table#menu-overview td:nth-child(5) a, table#menu-overview td:nth-child(6) a', + '#tabs-wrapper a:eq(2)' => array( // Edit menu: update just page title. + 'updateTitle' => true, + 'noReload' => true, + ), + ), + + // CCK - Manage fields page. + 'content_admin_field_overview_form' => array( + 'div#tabs-wrapper a:eq(0)' => array( // Edit + 'updateTitle' => true, + 'noReload' => true, + ), + 'div#tabs-wrapper a:eq(2)' => array( // Display fields + 'noReload' => true, + ), + 'div#tabs-wrapper a:eq(3), div#tabs-wrapper a:eq(4)', // Add field, Add group + 'table td:nth-child(5) a' => array( // configure + 'singleRow' => array( 'td:eq(0)' ), // Just update the row (still expiremental). + ), + 'table td:nth-child(6) a', // remove + ), + ); +} + +// ************************************************************************** +// ADMIN SETTINGS ********************************************************* +// ************************************************************************** + +function popuppage_admin_settings() { + drupal_set_title("Popups Settings"); + $form = array(); + + $form['popuppage_title_selector'] = array( + '#type' => 'textfield', + '#title' => t('Title Selector'), + '#default_value' => variable_get('popuppage_title_selector', 'div.left-corner > h2:eq(0)'), + '#description' => t("jQuery selector to define the pag'e title on your Admin theme."), + ); + $form['popuppage_content_selector'] = array( + '#type' => 'textfield', + '#title' => t('Content Selector'), + '#default_value' => variable_get('popuppage_content_selector', 'div.left-corner > div.clear-block:last'), + '#description' => t('jQuery selector to define the page\'s content area on your Admin theme.'), + ); + + return system_settings_form($form); +} Index: modules/popuppage/popuppage.js =================================================================== RCS file: modules/popuppage/popuppage.js diff -N modules/popuppage/popuppage.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/popuppage/popuppage.js 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,247 @@ +// $Id:$ + +/** + * This module uses the Popup API to build dialogs that show the content from a Drupal page. + */ + + +/** + * Attach the popup bevior to the all the requested links on the page. + * + * @param context: The jQuery object to apply the behaviors to. + */ +Drupal.behaviors.popups = function(context) { + var popups = new Drupal.popups(); + + // Add the popup-link-in-dialog behavior to links defined in Drupal.settings.popups.links array. + // TODO: how to handle popups-in-popups? + for (var link in Drupal.settings.popups.links) { + var options = Drupal.settings.popups.links[link]; + popups.attach(context, link, options); // Needs to be seperate function for closure. + } +}; + +/** + * Attach the popup behavior to a particular link. + * + * @param link - link that was clicked. + * @param options - options associated with the link. + */ +Drupal.popups.prototype.attach = function(context, link, options) { + var popups = this; + $(link, context).click( function(e){ + var a = this; + // If the option is distructive, check if the page is already modified, and offer to save. + var page_is_dirty = $('span.tabledrag-changed').size() > 0; + var will_modify_original = !options.noReload && !options.singleRow; + if( page_is_dirty && will_modify_original ) { + // The user will lose modifications, so popup dialog offering to save current state. + var body = Drupal.t("There are unsaved changes on this page, which you will lose if you continue."); + var buttons = { + 'popups_save': {title: Drupal.t('Save Changes'), func: function(){popups.save_page(a, options)}}, + 'popups_submit': {title: Drupal.t('Continue'), func: function(){Drupal.popups.close(); popups.open_path(a, options)}}, + 'popups_cancel': {title: Drupal.t('Cancel'), func: Drupal.popups.close} + }; + return popups.open( Drupal.t('Warning: Please Confirm'), body, buttons ); + } + else { + return popups.open_path(a, options); + } + }); +}; + + +/** + * Deal with the param string of a url to make the response popup friendly. + * Add 'page_override=popup' param. + * + * @param url + * String: The original url. + * @return url + * String: The url with the corrected parameters. + */ +Drupal.popups.prepUrl = function(url) { + var u = Drupal.popups.parseUrl(url); + // Add our param to the filtered array of existing params. + if (jQuery.inArray('page_override=popup', u.params) == -1) { + u.params.unshift('page_override=popup'); + } + // Rebuild the url with the new param, the old params and the old fragment. + return Drupal.popups.buildUrl(u); +} + +/** + * Use Ajax to open the link in a popup window. + * + * @param a - link that was clicked. + * @param options - options associated with the link. + */ +Drupal.popups.prototype.open_path = function( a, options ) { + var popup = this; + // let the user know something is happening + $('body').css("cursor", "wait"); + var $overlay = Drupal.popups.add_overlay(); + Drupal.popups.add_loading(); + + var url = Drupal.popups.prepUrl(a.href); + $.get(url, function(data) { + var $data = $(data); + var title = $data.find('#title').html(); + var messages = $data.find('#messages').html(); + var content = messages + $data.find('#content').html(); + popup.open_content(title, content, options, a); + $('body').css("cursor", "auto"); // Return the cursor to normal state. + }); + return false; +}; + + +Drupal.popups.prototype.open_content = function(title, content, options, a) { + this.open(title, content); + // Add behaviors to content in popup. + // TODO: d-n-d: need to click to let go of selection. + Drupal.attachBehaviors($('#popups-body')); + // Adding collapse moves focus. + this.refocus(); + + // If the popup contains a form, capture submits. + var $form = $('form', '#popups-body'); + $form.ajaxForm({ + beforeSubmit: Drupal.popups.beforeSubmit, + success: function(response, status) { Drupal.popups.formSuccess(response, options, a) }, + }); +} + +/** + * Do before the form in the popup is submitted. + * + */ +Drupal.popups.beforeSubmit = function(form_data, $form, options) { + Drupal.popups.remove_popup(); // Remove just the dialog, but not the overlay. + Drupal.popups.add_loading(); + // Send the original page back to Drupal with a flag to return the form results unthemed. + options.url = Drupal.popups.prepUrl(options.url); +}; + +/** + * The form in the popup was successfully submitted + * Update the originating page. + * Show any messages in a popup (TODO - make this a configurable option). + * + * @param response - specially formated page contents from server. + * @param options - hash of per link options. + * @param a - the link that was clicked. + */ +Drupal.popups.formSuccess = function (response, options, a) { + var $data = $(response); + if ($data.size() > 1) { // Bad html response, show an error message. + var $response = $('
'); + $response.html( response ); + var msg = 'Messages: ' + $('.messages', $response).text(); + Drupal.popups.message('Error: Bad response.', msg); + Drupal.popups.remove_loading(); + } + else { // Got a good response back from the server. + var messages = $data.find('#messages').html(); + + // Are we at an end point, or just moving from one popup to another? + var path = $data.find('#path').text(); + if (!location.href.match(path)) { // Not done yet, so show results in new popup. + var title = $data.find('#title').html(); + var content = $data.find('#content').html(); + Drupal.popups.remove_loading(); + var popups = new Drupal.popups(); + popups.open_content(title, messages + content, options, a); + } + else { // Done, so show messages in dialog and embed the results in the original page. + if (messages) { + Drupal.popups.message(messages); + // Also insert the message into the page above the content. + // Might not be the standard spot, but it is the easiest to find. + var $next = $(Drupal.settings.popups.defaultTargetSelector); + $next.parent().find('div.messages').remove(); // Remove the current messages. + $next.before(messages); + } + + // Just update a single row out of a table (still expiremental). + // Loop through, with special case for first element. + if (options.singleRow) { + var href = $(a).attr('href'); + var selector = 'table a[href=' + href + ']'; + var $new_row = $data.find(selector).parents('tr'); // new tr + var $target_row = $(selector).parents('tr'); // target tr. + for (var i in options.singleRow) { + var col = options.singleRow[i]; + $new_row.find(col).contents().not('div.indentation').wrapAll('
'); + $target_row.find(col).contents() + .not('a.tabledrag-handle').not('span.warning').not('div.indentation') + .wrapAll('
'); + $('#killme').replaceWith( $new_row.find('#newvalue').html() ); + } + } + // Update the entire content area (defined by 'target selector'). + else if (!options.noReload) { + var target = options.targetSelector; + if (!target) { + target = Drupal.settings.popups.defaultTargetSelector; + } + + // Remove page_override=popup param from form's action. + var action = $data.find('form').attr('action'); + if (action) { + action = Drupal.popups.parseUrl(action); + action.params = jQuery.grep(action.params, function(n, i){ + return n != 'page_override=popup'; + }); + $data.find('form').attr( 'action', Drupal.popups.buildUrl(action) ); + } + + // Update the original page. + var content = $data.find('#content').html(); + var $c = $(target).html(content); // Inject the new content into the page. + Drupal.attachBehaviors($c); + } + + // Update the title of the page. + if (options.updateTitle) { + var title = $data.find('#title').html(); + $(Drupal.settings.popups.defaultTitleSelector).html(title); + document.title = title; // Also update the browser page title (TODO: include site name?). + } + + // Done with changes to the original page, remove effects. + Drupal.popups.remove_loading(); + if (!messages) { + // If there is not a messages popup remove the overlay. + Drupal.popups.remove_overlay(); + } + } // End of updating original page. + } // End of good response. +}; + +/** + * Submit the page and reload the results, before popping up the real dialog. + * + * @param a - link that was clicked. + * @param options - options associated with the link. + */ +Drupal.popups.prototype.save_page = function(a, options) { + var popups = this; + // TODO - what if clicking on link with option['targetSelector']? + var target = Drupal.settings.popups.defaultTargetSelector; + var $form = $('form', target); + var ajaxOptions = { + beforeSubmit: Drupal.popups.beforeSubmit, + success: function(response, status) { // Sync up the current page contents with the submit. + var $data = $(response); + var content = $data.find('#content').html(); + var $c = $(target).html(content); // Inject the new content into the page. + Drupal.attachBehaviors($c); + Drupal.popups.close(); + // The form has been saved, the page reloaded, now safe to show the link in a popup. + popups.open_path(a, options); + } + }; + $form.ajaxSubmit( ajaxOptions ); // Submit the form. +}; +