--- a/includes/admin.inc +++ b/includes/admin.inc @@ -1307,6 +1307,23 @@ ); } + if ($view->current_display != 'default') { + // Buttons for copying to a new display. + foreach (views_fetch_plugin_names('display', NULL, array($view->base_table)) as $type => $label) { + $element['copy_display'][$type] = array( + '#type' => 'submit', + '#value' => t('Copy to !display', array('!display' => $label)), + '#limit_validation_errors' => array(), + '#submit' => array('views_ui_edit_form_submit_copy_display', 'views_ui_edit_form_submit_delay_destination'), + '#attributes' => array('class' => array('copy-display')), + // Allow JavaScript to remove the 'Copy ' prefix from the button label when + // placing the button in a "Copy" dropdown menu. + '#process' => array_merge(array('views_ui_form_button_was_clicked'), element_info_property('submit', '#process', array())), + '#values' => array(t('Copy to !display', array('!display' => $label)), 'to ' . $label), + ); + } + } + return $element; } @@ -1328,6 +1345,33 @@ // Redirect to the new display's edit page. $form_state['redirect'] = 'admin/structure/views/view/' . $view->name . '/edit/' . $display_id; +} + +/** + * Submit handler to copy a display in a view. + */ +function views_ui_edit_form_submit_copy_display($form, &$form_state) { + $view = $form_state['view']; + $old_display_id = $form_state['display_id']; + $old_display = $view->display[$old_display_id]; + + // Create the new display. + $parents = $form_state['triggering_element']['#parents']; + $new_display_type = array_pop($parents); + $new_display_id = $view->add_display($new_display_type); + $new_display = $view->display[$new_display_id]; + + $old_type = $old_display->display_plugin; + $options = $old_display->handler->option_definition(); + $default_options = (new views_plugin_display())->option_definition(); + $old_type_options = array_diff_key($options, $default_options); + $new_display->display_options = array_diff_key($old_display->display_options, $old_type_options); + + $view->current_display = $new_display_id; + views_ui_cache_set($view); + + // Redirect to the new display's edit page. + $form_state['redirect'] = 'admin/structure/views/view/' . $view->name . '/edit/' . $new_display_id; } /** @@ -2241,6 +2285,12 @@ 'title' => t('Add'), 'href' => "admin/structure/views/nojs/add-item/$view->name/$display->id/$type", 'attributes'=> array('class' => array('icon compact add', 'views-ajax-link'), 'title' => t('Add'), 'id' => 'views-add-' . $type), + 'html' => TRUE, + ); + $actions['copy'] = array( + 'title' => t('Copy to'), + 'href' => "admin/structure/views/nojs/copy-item/$view->name/$display->id/$type", + 'attributes'=> array('class' => array('icon compact add', 'views-ajax-link'), 'title' => t('Copy to'), 'id' => 'views-copy-' . $type), 'html' => TRUE, ); if ($count_handlers > 0) { --- a/js/views-admin.js +++ b/js/views-admin.js @@ -233,7 +233,7 @@ var $displayButtons = $menu.nextAll('input.add-display').detach(); $displayButtons.appendTo($addDisplayDropdown.find('.action-list')).wrap('
  • ') .parent().first().addClass('first').end().last().addClass('last'); - // Remove the 'Add ' prefix from the button labels since they're being palced + // Remove the 'Add ' prefix from the button labels since they're being placed // in an 'Add' dropdown. // @todo This assumes English, but so does $addDisplayDropdown above. Add // support for translation. @@ -273,6 +273,73 @@ * not written specifically for this UI, but I'm not sure where to put it. */ Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu = function ($trigger) { + $trigger.parent().toggleClass('open'); + $trigger.next().slideToggle('fast'); +} + +/** + * The input field items that copy-to displays must be rendered as elements. + * The following behavior detaches the elements from the DOM, wraps them + * in an unordered list, then appends them to the list of tabs. + */ +Drupal.behaviors.viewsUiRenderCopyViewButton = {}; + +Drupal.behaviors.viewsUiRenderCopyViewButton.attach = function (context, settings) { + var $ = jQuery; + // Build the copy display menu and pull the display input buttons into it. + var $menu = $('#views-display-menu-tabs', context).once('views-ui-render-copy-view-button-processed'); + + if (!$menu.length) { + return; + } + var $displayButtons = $menu.nextAll('input.copy-display').detach(); + if (!$displayButtons.length) { + return; + } + + var $addDisplayDropdown = $('
  • ' + Drupal.t('Copy') + '
  • '); + $displayButtons.appendTo($addDisplayDropdown.find('.action-list')).wrap('
  • ') + .parent().first().addClass('first').end().last().addClass('last'); + // Remove the 'copy to ' prefix from the button labels since they're being placed + // in a 'Copy to' dropdown. + // @todo This assumes English, but so does $addDisplayDropdown above. Add + // support for translation. + $displayButtons.each(function () { + var label = $(this).val(); + if (label.substr(0, 5) == 'Copy ') { + $(this).val(label.substr(5)); + } + }); + $addDisplayDropdown.appendTo($menu); + + // Add the click handler for the copy display button + $('li.copy > a', $menu).bind('click', function (event) { + event.preventDefault(); + var $trigger = $(this); + Drupal.behaviors.viewsUiRenderCopyViewButton.toggleMenu($trigger); + }); + // Add a mouseleave handler to close the dropdown when the user mouses + // away from the item. We use mouseleave instead of mouseout because + // the user is going to trigger mouseout when she moves from the trigger + // link to the sub menu items. + // + // We use the 'li.copy' selector because the open class on this item will be + // toggled on and off and we want the handler to take effect in the cases + // that the class is present, but not when it isn't. + $menu.delegate('li.copy', 'mouseleave', function (event) { + var $this = $(this); + var $trigger = $this.children('a[href="#"]'); + if ($this.children('.action-list').is(':visible')) { + Drupal.behaviors.viewsUiRenderCopyViewButton.toggleMenu($trigger); + } + }); +}; + +/** + * @note [@jessebeach] I feel like the following should be a more generic function and + * not written specifically for this UI, but I'm not sure where to put it. + */ +Drupal.behaviors.viewsUiRenderCopyViewButton.toggleMenu = function ($trigger) { $trigger.parent().toggleClass('open'); $trigger.next().slideToggle('fast'); } --- a/css/views-admin.css +++ b/css/views-admin.css @@ -159,6 +159,7 @@ /* @group Attachment details new section button */ +.views-displays .secondary li.copy, .views-displays .secondary li.add { position: relative; }