From 6d5c211392fe2c2ed0732462f30d638b497ba099 Mon Sep 17 00:00:00 2001 From: Dries Date: Tue, 12 Feb 2013 16:46:04 -0500 Subject: [PATCH] =?UTF-8?q?Issue=20#1874664=20by=20Wim=20Leers,=20jessebea?= =?UTF-8?q?ch,=20tkoleary,=20G=C3=A1bor=20Hojtsy,=20quicksketch,=20Bojhan:?= =?UTF-8?q?=20Reconcile=20'Edit'=20toolbar=20option=20with=20local=20tasks?= =?UTF-8?q?=20(tabs)=20and=20contextual=20links=20for=20editing.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/misc/edit-active.png | Bin 0 -> 226 bytes core/misc/edit.png | Bin 0 -> 365 bytes core/modules/contextual/contextual.js | 52 ++++- core/modules/contextual/contextual.module | 56 ++++- .../contextual/contextual.theme-rtl.css | 2 + core/modules/contextual/contextual.theme.css | 40 ++-- .../contextual/contextual.toolbar-rtl.css | 11 + .../modules/contextual/contextual.toolbar.css | 36 ++++ core/modules/contextual/contextual.toolbar.js | 132 ++++++++++++ core/modules/edit/css/edit.css | 50 +---- core/modules/edit/edit.info | 1 + core/modules/edit/edit.module | 54 ++--- core/modules/edit/images/icon-edit-active.png | Bin 358 -> 0 bytes core/modules/edit/images/icon-edit.png | Bin 498 -> 0 bytes core/modules/edit/js/app.js | 197 +++--------------- .../drupalcontenteditablewidget.js | 8 +- .../js/createjs/editingWidgets/formwidget.js | 14 +- core/modules/edit/js/edit.js | 55 ++--- core/modules/edit/js/models/edit-app-model.js | 3 +- core/modules/edit/js/routers/edit-router.js | 59 ------ core/modules/edit/js/theme.js | 25 --- .../edit/js/views/contextuallink-view.js | 109 ++++++++++ core/modules/edit/js/views/menu-view.js | 74 ------- core/modules/edit/js/views/modal-view.js | 26 +-- core/modules/edit/js/views/overlay-view.js | 86 -------- .../js/views/propertyeditordecoration-view.js | 20 ++ core/modules/edit/js/views/toolbar-view.js | 18 +- .../filter/Tests/FilterFormatAccessTest.php | 9 + .../lib/Drupal/node/NodeRenderController.php | 6 +- .../lib/Drupal/node/Tests/PageEditTest.php | 16 +- core/modules/node/node.module | 3 +- core/modules/system/system.module | 4 +- .../lib/Drupal/taxonomy/Tests/TermTest.php | 9 +- 33 files changed, 551 insertions(+), 624 deletions(-) create mode 100644 core/misc/edit-active.png create mode 100644 core/misc/edit.png create mode 100644 core/modules/contextual/contextual.toolbar-rtl.css create mode 100644 core/modules/contextual/contextual.toolbar.css create mode 100644 core/modules/contextual/contextual.toolbar.js delete mode 100644 core/modules/edit/images/icon-edit-active.png delete mode 100644 core/modules/edit/images/icon-edit.png delete mode 100644 core/modules/edit/js/routers/edit-router.js create mode 100644 core/modules/edit/js/views/contextuallink-view.js delete mode 100644 core/modules/edit/js/views/menu-view.js delete mode 100644 core/modules/edit/js/views/overlay-view.js diff --git a/core/misc/edit-active.png b/core/misc/edit-active.png new file mode 100644 index 0000000000000000000000000000000000000000..6dd158da6f953b80c44663cc1b97fbe2d27a8789 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fD?MEtLn>~)z2eB*V8FwA!D|J} z5+5$12?m^#8(35n7=cvtCe`^ljRz))S$v$_R(K;?lVy{Oi$&;!Nv2cPk8tT1^=)rR z-WT^^_14o*xwbDTYF~YkO|B$EI$gTz+=@3_?tbjA`@4wCW`ASZjVsT4%S0H(HkSD> z?5Tgb;r?PZQ?3k#Gwp}>NU;iStejtZz|qpN+lpb|FD<2cO`fx>`Xa22*pnT$%i7N1 Y4Y(q2{lBc!3Fv4BPgg&ebxsLQ01jMMzW@LL literal 0 HcmV?d00001 diff --git a/core/misc/edit.png b/core/misc/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..eb93320c0499f1dc5577ffa4346a5c21794068e9 GIT binary patch literal 365 zcmV-z0h0cSP)uoq45=;3e>C=3b#{LJ-=?)Q6v!d@uk z?JgeP?REl%eV`Dh$f4+VyA>#`g+iV>qg7Q26c$iFz8c!2o6Sa`umFX0hS%%0Kw$w2 z88EHkoU=e-0gB&fYk0L<2^1Ef@MueTxm*eq7NGEGM|iPV)UW`BM?1pv`CMQ~s9}Fl zcr*~6&1MOW?*jWk;n84tI-TB-eHT~5oWi5Qa9Nf^7z)3Co+lU-9__<8g+$3x<(EXLvLk-H=fP-i5<=qhVdxWFf}200000 LNkvXXu0mjfDa4-h literal 0 HcmV?d00001 diff --git a/core/modules/contextual/contextual.js b/core/modules/contextual/contextual.js index fae84889d15..00c5bdfcb93 100644 --- a/core/modules/contextual/contextual.js +++ b/core/modules/contextual/contextual.js @@ -7,6 +7,8 @@ "use strict"; +var contextuals = []; + /** * Attaches outline behavior for regions associated with contextual links. */ @@ -14,7 +16,14 @@ Drupal.behaviors.contextual = { attach: function (context) { $('ul.contextual-links', context).once('contextual', function () { var $this = $(this); - $this.data('drupal-contextual', new Drupal.contextual($this, $this.closest('.contextual-region'))); + var contextual = new Drupal.contextual($this, $this.closest('.contextual-region')); + contextuals.push(contextual); + $this.data('drupal-contextual', contextual); + }); + + // Bind to edit mode changes. + $('body').once('contextual', function () { + $(document).on('drupalEditModeChanged.contextual', toggleEditMode); }); } }; @@ -54,16 +63,33 @@ Drupal.contextual.prototype.init = function() { .attr('aria-pressed', false) .prependTo(this.$wrapper); + // The trigger behaviors are never detached or mutated. + this.$region + .on('click.contextual', '.contextual .trigger', $.proxy(this.triggerClickHandler, this)) + .on('mouseleave.contextual', '.contextual', {show: false}, $.proxy(this.triggerLeaveHandler, this)); + // Attach highlight behaviors. + this.attachHighlightBehaviors(); +}; + +/** + * Attaches highlight-on-mouseenter behaviors. + */ +Drupal.contextual.prototype.attachHighlightBehaviors = function () { // Bind behaviors through delegation. var highlightRegion = $.proxy(this.highlightRegion, this); this.$region - .on('click.contextual', '.contextual .trigger', $.proxy(this.triggerClickHandler, this)) - .on('mouseenter.contextual', {highlight: true}, highlightRegion) - .on('mouseleave.contextual', {highlight: false}, highlightRegion) - .on('mouseleave.contextual', '.contextual', {show: false}, $.proxy(this.triggerLeaveHandler, this)) - .on('click.contextual', '.contextual-links a', {highlight: false}, highlightRegion) - .on('focus.contextual', '.contextual-links a, .contextual .trigger', {highlight: true}, highlightRegion) - .on('blur.contextual', '.contextual-links a, .contextual .trigger', {highlight: false}, highlightRegion); + .on('mouseenter.contextual.highlight', {highlight: true}, highlightRegion) + .on('mouseleave.contextual.highlight', {highlight: false}, highlightRegion) + .on('click.contextual.highlight', '.contextual-links a', {highlight: false}, highlightRegion) + .on('focus.contextual.highlight', '.contextual-links a, .contextual .trigger', {highlight: true}, highlightRegion) + .on('blur.contextual.highlight', '.contextual-links a, .contextual .trigger', {highlight: false}, highlightRegion); +}; + +/** + * Detaches unhighlight-on-mouseleave behaviors. + */ +Drupal.contextual.prototype.detachHighlightBehaviors = function () { + this.$region.off('.contextual.highlight'); }; /** @@ -139,6 +165,16 @@ Drupal.contextual.prototype.showLinks = function(show) { }; +/** + * Shows or hides all pencil icons and corresponding contextual regions. + */ +function toggleEditMode (event, data) { + for (var i = contextuals.length - 1; i >= 0; i--) { + contextuals[i][(data.status) ? 'detachHighlightBehaviors' : 'attachHighlightBehaviors'](); + contextuals[i].$region.toggleClass('contextual-region-active', data.status); + } +} + /** * Wraps contextual links. * diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module index 30dcacbaf75..76e39bacf29 100644 --- a/core/modules/contextual/contextual.module +++ b/core/modules/contextual/contextual.module @@ -5,6 +5,41 @@ * Adds contextual links to perform actions related to elements on a page. */ +/** + * Implements hook_toolbar(). + */ +function contextual_toolbar() { + if (!user_access('access contextual links')) { + return; + } + + $tab['contextual'] = array( + '#type' => 'toolbar_item', + 'tab' => array( + '#type' => 'html_tag', + '#tag' => 'button', + '#value' => t('Edit'), + '#attributes' => array( + 'class' => array('icon', 'icon-edit'), + 'role' => 'button', + 'aria-pressed' => 'false', + ), + // @todo remove this once http://drupal.org/node/1908906 lands. + '#options' => array('attributes' => array()), + ), + '#wrapper_attributes' => array( + 'class' => array('element-hidden', 'contextual-toolbar-tab'), + ), + '#attached' => array( + 'library' => array( + array('contextual', 'drupal.contextual-toolbar'), + ), + ), + ); + + return $tab; +} + /** * Implements hook_help(). */ @@ -45,7 +80,9 @@ function contextual_library_info() { 'website' => 'http://drupal.org/node/473268', 'version' => VERSION, 'js' => array( - $path . '/contextual.js' => array(), + // Add the JavaScript, with a group and weight such that it will run + // before modules/contextual/contextual.toolbar.js. + $path . '/contextual.js' => array('group' => JS_LIBRARY, 'weight' => -2), ), 'css' => array( $path . '/contextual.base.css' => array(), @@ -57,6 +94,23 @@ function contextual_library_info() { array('system', 'jquery.once'), ), ); + $libraries['drupal.contextual-toolbar'] = array( + 'title' => 'Contextual Links Toolbar Tab', + 'version' => VERSION, + 'js' => array( + // Add the JavaScript, with a group and weight such that it will run + // before modules/overlay/overlay-parent.js. + $path . '/contextual.toolbar.js' => array('group' => JS_LIBRARY, 'weight' => -1), + ), + 'css' => array( + $path . '/contextual.toolbar.css' => array(), + ), + 'dependencies' => array( + array('system', 'jquery'), + array('system', 'jquery.once'), + array('system', 'backbone'), + ), + ); return $libraries; } diff --git a/core/modules/contextual/contextual.theme-rtl.css b/core/modules/contextual/contextual.theme-rtl.css index ed4836736cd..e04a21fa1e2 100644 --- a/core/modules/contextual/contextual.theme-rtl.css +++ b/core/modules/contextual/contextual.theme-rtl.css @@ -16,6 +16,8 @@ */ .contextual .trigger { float: left; + right: 0; + left: 2px; } /** diff --git a/core/modules/contextual/contextual.theme.css b/core/modules/contextual/contextual.theme.css index aca019ce464..7c5cde46cac 100644 --- a/core/modules/contextual/contextual.theme.css +++ b/core/modules/contextual/contextual.theme.css @@ -10,39 +10,39 @@ position: absolute; right: 0; /* LTR */ top: 2px; - z-index: 999; -} -.contextual-region-active { - outline: 1px dashed #d6d6d6; - outline-offset: 1px; + z-index: 500; } /** * Contextual trigger. */ .contextual .trigger { - background: transparent url("images/gear-select.png") no-repeat 2px 0; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; + background-attachment: scroll; + background-color: #fff; + background-image: url("../../misc/edit.png"); + background-position: center center; + background-repeat: no-repeat; + background-size: 16px 16px; + border: 1px solid #ddd; + border-radius: 13px; + box-shadow: 1px 1px 2px rgba(0,0,0,0.3); /* Override the .element-focusable height: auto */ - height: 18px !important; + height: 28px !important; float: right; /* LTR */ margin: 0; overflow: hidden; padding: 0 2px; position: relative; - width: 34px; + right: 2px; /* LTR */ + width: 28px; text-indent: -9999px; z-index: 2; -} -.no-touch .contextual .trigger:hover, -.contextual-links-active .trigger { - background-position: 2px -18px; + cursor: pointer; } .contextual-links-active .trigger { - background-color: #fff; - border-bottom: none; - border-color: #d6d6d6; + border-bottom-color: transparent; + border-radius: 13px 13px 0 0; + box-shadow: none; } /** @@ -52,7 +52,7 @@ */ .contextual-region .contextual .contextual-links { background-color: #fff; - border: 1px solid #d6d6d6; + border: 1px solid #ddd; border-radius: 4px 0 4px 4px; /* LTR */ clear: both; float: right; /* LTR */ @@ -90,5 +90,7 @@ text-decoration: none; } .no-touch .contextual-region .contextual .contextual-links li a:hover { - background-color: #bfdcee; + color: white; + background-image: -webkit-linear-gradient(rgb(78,159,234) 0%,rgb(65,126,210) 100%); + background-image: linear-gradient(rgb(78,159,234) 0%,rgb(65,126,210) 100%); } diff --git a/core/modules/contextual/contextual.toolbar-rtl.css b/core/modules/contextual/contextual.toolbar-rtl.css new file mode 100644 index 00000000000..226969fe0fd --- /dev/null +++ b/core/modules/contextual/contextual.toolbar-rtl.css @@ -0,0 +1,11 @@ +/** + * @file + * RTL styling for contextual module's toolbar tab. + */ + +.js .toolbar .bar .contextual-toolbar-tab.tab { + float: left; +} +.js .toolbar .bar .contextual-toolbar-tab button { + padding-right: 1.3333em; +} diff --git a/core/modules/contextual/contextual.toolbar.css b/core/modules/contextual/contextual.toolbar.css new file mode 100644 index 00000000000..f7611f38631 --- /dev/null +++ b/core/modules/contextual/contextual.toolbar.css @@ -0,0 +1,36 @@ +/** + * @file + * Styling for contextual module's toolbar tab. + */ + +/* Tab icon. */ +.icon-edit:before { + background-image: url("../../misc/edit.png"); +} +.icon-edit:active:before, +.active.icon-edit:before { + background-image: url("../../misc/edit-active.png"); +} + +/* Tab appearance. */ +.js .toolbar .bar .contextual-toolbar-tab.tab { + float: right; /* LTR */ +} +.js .toolbar .bar .contextual-toolbar-tab button { + padding-bottom: 1em; + padding-top: 1em; + /* Hide tab text. */ + padding-left: 1.3333em; /* LTR */ + text-indent: -9999px; +} +.js .toolbar .bar .contextual-toolbar-tab button.active { + background-image:-moz-linear-gradient(rgb(78,159,234) 0%,rgb(69,132,221) 100%); + background-image:-webkit-gradient(linear,color-stop(0, rgb(78,159,234)),color-stop(1, rgb(69,132,221))); + background-image: -webkit-linear-gradient(top, rgb(78,159,234) 0%, rgb(69,132,221) 100%); + background-image:linear-gradient(rgb(78,159,234) 0%,rgb(69,132,221) 100%); +} + +/* @todo get rid of this declaration by making toolbar.module's CSS less specific */ +.js .toolbar .bar .contextual-toolbar-tab.tab.element-hidden { + display: none; +} diff --git a/core/modules/contextual/contextual.toolbar.js b/core/modules/contextual/contextual.toolbar.js new file mode 100644 index 00000000000..45f9757aaa2 --- /dev/null +++ b/core/modules/contextual/contextual.toolbar.js @@ -0,0 +1,132 @@ +/** + * @file + * Attaches behaviors for the Contextual module's edit toolbar tab. + */ + +(function ($, Backbone, Drupal, document, localStorage) { + +"use strict"; + +/** + * Attaches contextual's edit toolbar tab behavior. + * + * Events + * Contextual triggers an event that can be used by other scripts. + * - drupalEditModeChanged: Triggered when the edit mode changes. + */ +Drupal.behaviors.contextualToolbar = { + attach: function (context) { + $('body').once('contextualToolbar-init', function () { + var $contextuals = $(context).find('.contextual-links'); + var $tab = $('.js .toolbar .bar .contextual-toolbar-tab'); + var model = new Drupal.contextualToolbar.models.EditToggleModel({ + isViewing: true + }); + var view = new Drupal.contextualToolbar.views.EditToggleView({ + el: $tab, + model: model + }); + + // Update the model based on overlay events. + $(document) + .on('drupalOverlayOpen.contextualToolbar', function () { + model.set('isVisible', false); + }) + .on('drupalOverlayClose.contextualToolbar', function () { + model.set('isVisible', true); + }); + + // Update the model to show the edit tab if there's >=1 contextual link. + if ($contextuals.length > 0) { + model.set('isVisible', true); + } + + // Allow other scripts to respond to edit mode changes. + model.on('change:isViewing', function (model, value) { + $(document).trigger('drupalEditModeChanged', { status: !value }); + }); + + // Checks whether localStorage indicates we should start in edit mode + // rather than view mode. + // @see Drupal.contextualToolbar.views.EditToggleView.persist() + if (localStorage.getItem('Drupal.contextualToolbar.isViewing') !== null) { + model.set('isViewing', false); + } + }); + } +}; + +Drupal.contextualToolbar = Drupal.contextualToolbar || { models: {}, views: {}}; + +/** + * Backbone Model for the edit toggle. + */ +Drupal.contextualToolbar.models.EditToggleModel = Backbone.Model.extend({ + defaults: { + // Indicates whether the toggle is currently in "view" or "edit" mode. + isViewing: true, + // Indicates whether the toggle should be visible or hidden. + isVisible: false + } +}); + +/** + * Handles edit mode toggle interactions. + */ +Drupal.contextualToolbar.views.EditToggleView = Backbone.View.extend({ + + events: { 'click': 'onClick' }, + + /** + * Implements Backbone Views' initialize(). + */ + initialize: function () { + this.model.on('change', this.render, this); + this.model.on('change:isViewing', this.persist, this); + }, + + /** + * Implements Backbone Views' render(). + */ + render: function () { + var args = arguments; + // Render the visibility. + this.$el.toggleClass('element-hidden', !this.model.get('isVisible')); + + // Render the state. + var isViewing = this.model.get('isViewing'); + this.$el.find('button') + .toggleClass('active', !isViewing) + .attr('aria-pressed', !isViewing); + + return this; + }, + + /** + * Model change handler; persists the isViewing value to localStorage. + * + * isViewing === true is the default, so only stores in localStorage when + * it's not the default value (i.e. false). + * + * @param Drupal.contextualToolbar.models.EditToggleModel model + * An EditToggleModel Backbone model. + * @param bool isViewing + * The value of the isViewing attribute in the model. + */ + persist: function (model, isViewing) { + if (!isViewing) { + localStorage.setItem('Drupal.contextualToolbar.isViewing', 'false'); + } + else { + localStorage.removeItem('Drupal.contextualToolbar.isViewing'); + } + }, + + onClick: function (event) { + this.model.set('isViewing', !this.model.get('isViewing')); + event.preventDefault(); + event.stopPropagation(); + } +}); + +})(jQuery, Backbone, Drupal, document, localStorage); diff --git a/core/modules/edit/css/edit.css b/core/modules/edit/css/edit.css index c255d346055..7b758b702cb 100644 --- a/core/modules/edit/css/edit.css +++ b/core/modules/edit/css/edit.css @@ -71,47 +71,14 @@ -/** - * Toolbar. - */ -.icon-edit:before { - background-image: url("../images/icon-edit.png"); -} -.icon-edit:active:before, -.active .icon-edit:before { - background-image: url("../images/icon-edit-active.png"); -} -.js .toolbar .bar .edit-toolbar-tab.tab { - float: right; -} -.toolbar .icon-edit.edit-nothing-editable-hidden { - display: none; -} -/* In-place editing doesn't work in the overlay, so always hide the tab. */ -.overlay-open .toolbar .icon-edit { - display: none; -} - - /** - * Edit mode: overlay + candidate editables + editables being edited. + * Candidate editables + editables being edited. * * Note: every class is prefixed with "edit-" to prevent collisions with modules * or themes. In IPE-specific DOM subtrees, this is not necessary. */ -#edit_overlay { - position: fixed; - z-index: 250; - width: 100%; - height: 100%; - background-color: #fff; - background-color: rgba(255,255,255,.5); - top: 0; - left: 0; -} - /* Editable. */ .edit-editable { z-index: 300; @@ -127,6 +94,7 @@ /* Highlighted (hovered) editable. */ .edit-editable.edit-highlighted { + z-index: 305; min-width: 200px; } .edit-field.edit-editable.edit-highlighted, @@ -184,16 +152,6 @@ background: #f5f5f5; } -/* Modal active: prevent user from interacting with toolbar & editables. */ -.edit-form-container.edit-belowoverlay, -.edit-toolbar-container.edit-belowoverlay, -.edit-validation-errors.edit-belowoverlay { - z-index: 210; -} -.edit-editable.edit-belowoverlay { - z-index: 200; -} - @@ -279,6 +237,10 @@ bottom: 1px; box-shadow: 0 0 1px 1px #0199ff, 0 0 3px 3px rgba(153, 153, 153, .5); background: #fff; + display: none; +} +.edit-highlighted .edit-toolbar-heightfaker { + display: block; } /* The toolbar; these are not necessarily visible. */ diff --git a/core/modules/edit/edit.info b/core/modules/edit/edit.info index 4074a7b8419..7c6d4b93327 100644 --- a/core/modules/edit/edit.info +++ b/core/modules/edit/edit.info @@ -3,4 +3,5 @@ description = In-place content editing. package = Core core = 8.x version = VERSION +dependencies[] = contextual dependencies[] = field diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module index dc1eec771d8..e4a3f8274dd 100644 --- a/core/modules/edit/edit.module +++ b/core/modules/edit/edit.module @@ -39,42 +39,23 @@ function edit_permission() { } /** - * Implements hook_toolbar(). + * Implements hook_contextual_links_view_alter(). + * + * In-place editing builds upon contextual.module, but doesn't actually add its + * "Quick edit" contextual link in PHP (i.e. here) because: + * - that would require to add a local task menu item in the menu system, which + * doesn't make any sense, since there is no corresponding page; + * - it should only work when JavaScript is enabled, because only then in-place + * editing is possible. */ -function edit_toolbar() { +function edit_contextual_links_view_alter(&$element, $items) { if (!user_access('access in-place editing')) { return; } - $tab['edit'] = array( - '#type' => 'toolbar_item', - 'tab' => array( - '#type' => 'link', - '#title' => t('Edit'), - '#href' => '', - '#options' => array( - 'html' => FALSE, - 'attributes' => array( - 'id' => 'toolbar-tab-edit', - 'class' => array('icon', 'icon-edit', 'edit-nothing-editable-hidden'), - ), - ), - ), - '#wrapper_attributes' => array( - 'class' => array('edit-toolbar-tab'), - ), - '#attached' => array( - 'library' => array( - array('edit', 'edit'), - ), - ), - ); - // Include the attachments and settings for all available editors. $attachments = drupal_container()->get('edit.editor.selector')->getAllEditorAttachments(); - $tab['edit']['#attached'] = NestedArray::mergeDeep($tab['edit']['#attached'], $attachments); - - return $tab; + $element['#attached'] = NestedArray::mergeDeep($element['#attached'], $attachments); } /** @@ -94,15 +75,12 @@ function edit_library_info() { // Core. $path . '/js/edit.js' => $options, $path . '/js/app.js' => $options, - // Routers. - $path . '/js/routers/edit-router.js' => $options, // Models. $path . '/js/models/edit-app-model.js' => $options, // Views. $path . '/js/views/propertyeditordecoration-view.js' => $options, - $path . '/js/views/menu-view.js' => $options, + $path . '/js/views/contextuallink-view.js' => $options, $path . '/js/views/modal-view.js' => $options, - $path . '/js/views/overlay-view.js' => $options, $path . '/js/views/toolbar-view.js' => $options, // Backbone.sync implementation on top of Drupal forms. $path . '/js/backbone.drupalform.js' => $options, @@ -173,6 +151,16 @@ function edit_preprocess_field(&$variables) { $variables['attributes']['data-edit-id'] = $entity->entityType() . '/' . $entity->id() . '/' . $element['#field_name'] . '/' . $element['#language'] . '/' . $element['#view_mode']; } +/** + * Implements hook_preprocess_HOOK() for node.tpl.php. + * + * @todo Move towards hook_preprocess_entity() once that's available. + */ +function edit_preprocess_node(&$variables) { + $node = $variables['elements']['#node']; + $variables['attributes']['data-edit-entity'] = 'node/' . $node->nid; +} + /** * Form constructor for the field editing form. * diff --git a/core/modules/edit/images/icon-edit-active.png b/core/modules/edit/images/icon-edit-active.png deleted file mode 100644 index ad847612de18d0d60e59026155697d0288313803..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)62?0JKuK)i1dkF^r|NnpW>ebt~ zZ(qND{pr)E7cX7_MSyIe90$^~C#CeJ?-ymO|4Q}4`AhmXFR@vPRq%sMP>xn9_D zmAzg~={t8u*naokcVk;u!A2Q_2)Tyt;HgTPQ-jtpmJ7u!vug3#_58Qb=C+vE%j#ag nYqb`S5;tWme^)wH;T5;3tLB#pJ|7!^E@kj^^>bP0l+XkKE~LX> diff --git a/core/modules/edit/images/icon-edit.png b/core/modules/edit/images/icon-edit.png deleted file mode 100644 index 4f0dcc2367e114713beec8b8357fd54b558a9e22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 498 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)62LgOTTn`*Luy*a*9XocMIdf+B z?%lIz&pvhP)T&jhfZT-(7XrySbLIdQtXZ>W#flY6mo7bh`t$-Y07~uMyBA0${oKmWXd0K=TW$(MoJwt2cZhE&{2JrT~-WFWwB@kVv{8V)XxO-e!=um8S(YO4<0 z%>9)-|6Yq!OL`Pkc7GMRb0&X-{kB|zGyxW!bwRV7W&|Cp*mjuBiKoox>S1fOBR&t` zeY_oTpno&JSK_>VL452') + .each(function() { + // Instantiate ContextualLinkView. + var $editContextualLink = $(this).prev(); + var editContextualLinkView = new Drupal.edit.views.ContextualLinkView({ + el: $editContextualLink.get(0), + model: appModel, + entity: $editContextualLink.parents('[data-edit-entity]').attr('data-edit-entity') + }); }); - // Start Backbone's history/route handling. - Backbone.history.start(); - // For now, we work with a singleton app, because for Drupal.behaviors to be // able to discover new editable properties that get AJAXed in, it must know // with which app instance they should be associated. Drupal.edit.app = app; }; -/** - * Places the message in the edit ARIA live message area. - * - * The message will be read by speaking User Agents. - * - * @param {String} message - * A string to be inserted into the message area. - */ -Drupal.edit.setMessage = function(message) { - var args = Array.prototype.slice.call(arguments); - args.unshift('editMessage'); - $messages.html(Drupal.theme.apply(this, args)); -}; - })(jQuery, _, Backbone, Drupal, drupalSettings); diff --git a/core/modules/edit/js/models/edit-app-model.js b/core/modules/edit/js/models/edit-app-model.js index b6ff36f3f91..0c90fd00519 100644 --- a/core/modules/edit/js/models/edit-app-model.js +++ b/core/modules/edit/js/models/edit-app-model.js @@ -10,8 +10,7 @@ Drupal.edit = Drupal.edit || {}; Drupal.edit.models = Drupal.edit.models || {}; Drupal.edit.models.EditAppModel = Backbone.Model.extend({ defaults: { - // We always begin in view mode. - isViewing: true, + activeEntity: null, highlightedEditor: null, activeEditor: null, // Reference to a ModalView-instance if a transition requires confirmation. diff --git a/core/modules/edit/js/routers/edit-router.js b/core/modules/edit/js/routers/edit-router.js deleted file mode 100644 index d160ad48ae9..00000000000 --- a/core/modules/edit/js/routers/edit-router.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * @file - * A Backbone Router enabling URLs to make the user enter edit mode directly. - */ -(function(Backbone, Drupal) { - -"use strict"; - -Drupal.edit = Drupal.edit || {}; -Drupal.edit.routers = {}; -Drupal.edit.routers.EditRouter = Backbone.Router.extend({ - - appModel: null, - - routes: { - "edit": "edit", - "view": "view", - "": "view" - }, - - initialize: function(options) { - this.appModel = options.appModel; - - var that = this; - this.appModel.on('change:isViewing', function() { - that.navigate(that.appModel.get('isViewing') ? '#view' : '#edit'); - }); - }, - - edit: function() { - this.appModel.set('isViewing', false); - }, - - view: function(query, page) { - var that = this; - - // If there's an active editor, attempt to set its state to 'candidate', and - // then act according to the user's choice. - var activeEditor = this.appModel.get('activeEditor'); - if (activeEditor) { - var editableEntity = activeEditor.options.widget; - var predicate = activeEditor.options.property; - editableEntity.setState('candidate', predicate, { reason: 'menu' }, function(accepted) { - if (accepted) { - that.appModel.set('isViewing', true); - } - else { - that.appModel.set('isViewing', false); - } - }); - } - // Otherwise, we can switch to view mode directly. - else { - that.appModel.set('isViewing', true); - } - } -}); - -})(Backbone, Drupal); diff --git a/core/modules/edit/js/theme.js b/core/modules/edit/js/theme.js index 42f7613e365..f2452bab2ec 100644 --- a/core/modules/edit/js/theme.js +++ b/core/modules/edit/js/theme.js @@ -152,29 +152,4 @@ Drupal.theme.editFormContainer = function(settings) { return html; }; -/** - * A region to post messages that a screen reading UA will announce. - * - * @return {String} - * A string representing a DOM fragment. - */ -Drupal.theme.editMessageBox = function() { - return '
'; -}; - -/** - * Wrap message strings in p tags. - * - * @return {String} - * A string representing a DOM fragment. - */ -Drupal.theme.editMessage = function() { - var messages = Array.prototype.slice.call(arguments); - var output = ''; - for (var i = 0; i < messages.length; i++) { - output += '

' + messages[i] + '

'; - } - return output; -}; - })(jQuery, Drupal); diff --git a/core/modules/edit/js/views/contextuallink-view.js b/core/modules/edit/js/views/contextuallink-view.js new file mode 100644 index 00000000000..efe8ddd3c5d --- /dev/null +++ b/core/modules/edit/js/views/contextuallink-view.js @@ -0,0 +1,109 @@ +/** + * @file + * A Backbone View that a dynamic contextual link. + */ +(function ($, _, Backbone, Drupal) { + +"use strict"; + +Drupal.edit = Drupal.edit || {}; +Drupal.edit.views = Drupal.edit.views || {}; +Drupal.edit.views.ContextualLinkView = Backbone.View.extend({ + + entity: null, + + events: { + 'click': 'onClick' + }, + + /** + * Implements Backbone Views' initialize() function. + * + * @param options + * An object with the following keys: + * - entity: the entity ID (e.g. node/1) of the entity + */ + initialize: function (options) { + this.entity = options.entity; + + // Initial render. + this.render(); + + // Re-render whenever the app state's active entity changes. + this.model.on('change:activeEntity', this.render, this); + + // Hide the contextual links whenever an in-place editor is active. + this.model.on('change:activeEditor', this.toggleContextualLinksVisibility, this); + }, + + /** + * Equates clicks anywhere on the overlay to clicking the active editor's (if + * any) "close" button. + * + * @param {Object} event + */ + onClick: function (event) { + event.preventDefault(); + + var that = this; + var updateActiveEntity = function() { + // The active entity is the current entity, i.e. stop editing the current + // entity. + if (that.model.get('activeEntity') === that.entity) { + that.model.set('activeEntity', null); + } + // The active entity is different from the current entity, i.e. start + // editing this entity instead of the previous one. + else { + that.model.set('activeEntity', that.entity); + } + }; + + // If there's an active editor, attempt to set its state to 'candidate', and + // only then do what the user asked. + // (Only when all PropertyEditor widgets of an entity are in the 'candidate' + // state, it is possible to stop editing it.) + var activeEditor = this.model.get('activeEditor'); + if (activeEditor) { + var editableEntity = activeEditor.options.widget; + var predicate = activeEditor.options.property; + editableEntity.setState('candidate', predicate, { reason: 'stop or switch' }, function(accepted) { + if (accepted) { + updateActiveEntity(); + } + else { + // No change. + } + }); + } + // Otherwise, we can immediately do what the user asked. + else { + updateActiveEntity(); + } + }, + + /** + * Render the "Quick edit" contextual link. + */ + render: function () { + var activeEntity = this.model.get('activeEntity'); + var string = (activeEntity !== this.entity) ? Drupal.t('Quick edit') : Drupal.t('Stop quick edit'); + this.$el.html('' + string + ''); + return this; + }, + + /** + * Model change handler; hides the contextual links if an editor is active. + * + * @param Drupal.edit.models.EditAppModel model + * An EditAppModel model. + * @param jQuery|null activeEditor + * The active in-place editor (jQuery object) or, if none, null. + */ + toggleContextualLinksVisibility: function (model, activeEditor) { + this.$el.parents('.contextual').toggle(activeEditor === null); + } + +}); + +})(jQuery, _, Backbone, Drupal); diff --git a/core/modules/edit/js/views/menu-view.js b/core/modules/edit/js/views/menu-view.js deleted file mode 100644 index 35cac3b6206..00000000000 --- a/core/modules/edit/js/views/menu-view.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * @file - * A Backbone View that provides the app-level interactive menu. - */ -(function($, _, Backbone, Drupal) { - -"use strict"; - -Drupal.edit = Drupal.edit || {}; -Drupal.edit.views = Drupal.edit.views || {}; -Drupal.edit.views.MenuView = Backbone.View.extend({ - - events: { - 'click #toolbar-tab-edit': 'editClickHandler' - }, - - /** - * Implements Backbone Views' initialize() function. - */ - initialize: function() { - _.bindAll(this, 'stateChange'); - this.model.on('change:isViewing', this.stateChange); - // Respond to clicks on other toolbar tabs. - // @todo This temporary pending improvements to the toolbar module. - // @see https://drupal.org/node/1860434 - $('#toolbar-administration').on('click.edit', '.bar a:not(#toolbar-tab-edit)', _.bind(function (event) { - this.model.set('isViewing', true); - }, this)); - // We have to call stateChange() here because URL fragments are not passed - // to the server, thus the wrong anchor may be marked as active. - this.stateChange(); - }, - - /** - * Listens to app state changes. - */ - stateChange: function() { - var isViewing = this.model.get('isViewing'); - // Toggle the state of the Toolbar Edit tab based on the isViewing state. - this.$el.find('#toolbar-tab-edit') - .toggleClass('active', !isViewing) - .attr('aria-pressed', !isViewing); - // Manage the toolbar state until - // https://drupal.org/node/1847198 is resolved - if (!isViewing) { - // Remove the 'toolbar-tray-open' class from the body element. - this.$el.removeClass('toolbar-tray-open'); - // Deactivate any other active tabs and trays. - this.$el - .find('.bar a', '#toolbar-administration') - .not('#toolbar-tab-edit') - .add('.tray', '#toolbar-administration') - .removeClass('active'); - // Set the height of the toolbar. - if ('toolbar' in Drupal) { - Drupal.toolbar.setHeight(); - } - } - }, - /** - * Handles clicks on the edit tab of the toolbar. - * - * @param {Object} event - */ - editClickHandler: function (event) { - var isViewing = this.model.get('isViewing'); - // Toggle the href of the Toolbar Edit tab based on the isViewing state. The - // href value should represent to state to be entered. - this.$el.find('#toolbar-tab-edit').attr('href', (isViewing) ? '#edit' : '#view'); - this.model.set('isViewing', !isViewing); - } -}); - -})(jQuery, _, Backbone, Drupal); diff --git a/core/modules/edit/js/views/modal-view.js b/core/modules/edit/js/views/modal-view.js index 2e3b49cf2d1..b98c8762d60 100644 --- a/core/modules/edit/js/views/modal-view.js +++ b/core/modules/edit/js/views/modal-view.js @@ -43,17 +43,6 @@ Drupal.edit.views.ModalView = Backbone.View.extend({ * Implements Backbone Views' render() function. */ render: function() { - // Step 1: move certain UI elements below the overlay. - var editor = this.model.get('activeEditor'); - this.$elementsToHide = $([]) - .add((editor.element.hasClass('edit-belowoverlay')) ? null : editor.element) - .add(editor.toolbarView.$el) - .add((editor.options.editorName === 'form') ? editor.$formContainer : editor.element.next('.edit-validation-errors')); - this.$elementsToHide.addClass('edit-belowoverlay'); - - // Step 2: the modal. When the user makes a choice, the UI elements that - // were moved below the overlay will be restored, and the callback will be - // called. this.setElement(Drupal.theme('editModal', {})); this.$el.appendTo('body'); // Template. @@ -61,13 +50,11 @@ Drupal.edit.views.ModalView = Backbone.View.extend({ var $actions = $(Drupal.theme('editButtons', { 'buttons' : this.buttons})); this.$('.actions').append($actions); - // Step 3; show the modal with an animation. + // Show the modal with an animation. var that = this; setTimeout(function() { that.$el.removeClass('edit-animate-invisible'); }, 0); - - Drupal.edit.setMessage(Drupal.t('Confirmation dialog open')); }, /** @@ -90,17 +77,6 @@ Drupal.edit.views.ModalView = Backbone.View.extend({ var action = $(event.target).attr('data-edit-modal-action'); return this.callback(action); - }, - - /** - * Overrides Backbone Views' remove() function. - */ - remove: function() { - // Move the moved UI elements on top of the overlay again. - this.$elementsToHide.removeClass('edit-belowoverlay'); - - // Remove the modal itself. - this.$el.remove(); } }); diff --git a/core/modules/edit/js/views/overlay-view.js b/core/modules/edit/js/views/overlay-view.js deleted file mode 100644 index 2113ab8e345..00000000000 --- a/core/modules/edit/js/views/overlay-view.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file - * A Backbone View that provides the app-level overlay. - * - * The overlay sits on top of the existing content, the properties that are - * candidates for editing sit on top of the overlay. - */ -(function ($, _, Backbone, Drupal) { - -"use strict"; - -Drupal.edit = Drupal.edit || {}; -Drupal.edit.views = Drupal.edit.views || {}; -Drupal.edit.views.OverlayView = Backbone.View.extend({ - - events: { - 'click': 'onClick' - }, - - /** - * Implements Backbone Views' initialize() function. - */ - initialize: function (options) { - _.bindAll(this, 'stateChange'); - this.model.on('change:isViewing', this.stateChange); - // Add the overlay to the page. - this.$el - .addClass('edit-animate-slow edit-animate-invisible') - .hide() - .appendTo('body'); - }, - - /** - * Listens to app state changes. - */ - stateChange: function () { - if (this.model.get('isViewing')) { - this.remove(); - return; - } - this.render(); - }, - - /** - * Equates clicks anywhere on the overlay to clicking the active editor's (if - * any) "close" button. - * - * @param {Object} event - */ - onClick: function (event) { - event.preventDefault(); - var activeEditor = this.model.get('activeEditor'); - if (activeEditor) { - var editableEntity = activeEditor.options.widget; - var predicate = activeEditor.options.property; - editableEntity.setState('candidate', predicate, { reason: 'overlay' }); - } - else { - this.model.set('isViewing', true); - } - }, - - /** - * Reveal the overlay element. - */ - render: function () { - this.$el - .show() - .css('top', $('#navbar').outerHeight()) - .removeClass('edit-animate-invisible'); - }, - - /** - * Hide the overlay element. - */ - remove: function () { - var that = this; - this.$el - .addClass('edit-animate-invisible') - .on(Drupal.edit.util.constants.transitionEnd, function (event) { - that.$el.hide(); - }); - } -}); - -})(jQuery, _, Backbone, Drupal); diff --git a/core/modules/edit/js/views/propertyeditordecoration-view.js b/core/modules/edit/js/views/propertyeditordecoration-view.js index 0eb4e4569c0..aad9832c554 100644 --- a/core/modules/edit/js/views/propertyeditordecoration-view.js +++ b/core/modules/edit/js/views/propertyeditordecoration-view.js @@ -19,6 +19,7 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({ events: { 'mouseenter.edit' : 'onMouseEnter', 'mouseleave.edit' : 'onMouseLeave', + 'click': 'onClick', 'tabIn.edit': 'onMouseEnter', 'tabOut.edit': 'onMouseLeave' }, @@ -38,7 +39,12 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({ this.editor = options.editor; this.toolbarId = options.toolbarId; + this.predicate = this.editor.options.property; + this.$el.css('background-color', this._getBgColor(this.$el)); + + // Only start listening to events as soon as we're no longer in the 'inactive' state. + this.undelegateEvents(); }, /** @@ -113,13 +119,27 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({ }); }, + /** + * Clicks: transition to 'activating' stage. + * + * @param event + */ + onClick: function(event) { + var editableEntity = this.editor.options.widget; + editableEntity.setState('activating', this.predicate); + event.preventDefault(); + event.stopPropagation(); + }, + decorate: function () { this.$el.addClass('edit-animate-fast edit-candidate edit-editable'); + this.delegateEvents(); }, undecorate: function () { this.$el .removeClass('edit-candidate edit-editable edit-highlighted edit-editing'); + this.undelegateEvents(); }, startHighlight: function () { diff --git a/core/modules/edit/js/views/toolbar-view.js b/core/modules/edit/js/views/toolbar-view.js index 90f5db74727..b052362ddf3 100644 --- a/core/modules/edit/js/views/toolbar-view.js +++ b/core/modules/edit/js/views/toolbar-view.js @@ -29,7 +29,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ 'click.edit button.label': 'onClickInfoLabel', 'mouseleave.edit': 'onMouseLeave', 'click.edit button.field-save': 'onClickSave', - 'click.edit button.field-close': 'onClickClose' + 'click.edit button.field-close': 'onClickClose', }, /** @@ -66,19 +66,26 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ stateChange: function(from, to) { switch (to) { case 'inactive': - // Nothing happens in this stage. + if (from) { + this.remove(); + } break; case 'candidate': - if (from !== 'inactive') { + if (from === 'inactive') { + this.render(); + } + else { + // Remove all toolgroups; they're no longer necessary. + this.$el + .removeClass('edit-highlighted edit-editing') + .find('.edit-toolbar .edit-toolgroup').remove(); if (from !== 'highlighted' && this.getEditUISetting('padding')) { this._unpad(); } - this.remove(); } break; case 'highlighted': // As soon as we highlight, make sure we have a toolbar in the DOM (with at least a title). - this.render(); this.startHighlight(); break; case 'activating': @@ -275,6 +282,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ } this.$el + .addClass('edit-highlighted') .find('.edit-toolbar') // Append the "info" toolgroup into the toolbar. .append(Drupal.theme('editToolgroup', { diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php index 48faf24bc82..65dd7afee5d 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php @@ -48,6 +48,13 @@ class FilterFormatAccessTest extends WebTestBase { */ protected $disallowed_format; + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('contextual'); + public static function getInfo() { return array( 'name' => 'Filter format access', @@ -88,6 +95,7 @@ class FilterFormatAccessTest extends WebTestBase { $this->web_user = $this->drupalCreateUser(array( 'create page content', 'edit any page content', + 'access contextual links', filter_permission_name($this->allowed_format), )); @@ -96,6 +104,7 @@ class FilterFormatAccessTest extends WebTestBase { 'administer filters', 'create page content', 'edit any page content', + 'access contextual links', filter_permission_name($this->allowed_format), filter_permission_name($this->disallowed_format), )); diff --git a/core/modules/node/lib/Drupal/node/NodeRenderController.php b/core/modules/node/lib/Drupal/node/NodeRenderController.php index e829e404b39..6a6608a23dc 100644 --- a/core/modules/node/lib/Drupal/node/NodeRenderController.php +++ b/core/modules/node/lib/Drupal/node/NodeRenderController.php @@ -88,11 +88,7 @@ class NodeRenderController extends EntityRenderController { */ protected function alterBuild(array &$build, EntityInterface $entity, EntityDisplay $display, $view_mode, $langcode = NULL) { parent::alterBuild($build, $entity, $display, $view_mode, $langcode); - // Add contextual links for this node, except when the node is already being - // displayed on its own page. Modules may alter this behavior (for example, - // to restrict contextual links to certain view modes) by implementing - // hook_node_view_alter(). - if (!empty($entity->nid) && !($view_mode == 'full' && node_is_page($entity))) { + if (!empty($entity->nid)) { $build['#contextual_links']['node'] = array('node', array($entity->nid)); } } diff --git a/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php b/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php index b461492b8ae..b650fea2499 100644 --- a/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php @@ -14,6 +14,13 @@ class PageEditTest extends NodeTestBase { protected $web_user; protected $admin_user; + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('node', 'contextual'); + public static function getInfo() { return array( 'name' => 'Node edit', @@ -25,8 +32,8 @@ class PageEditTest extends NodeTestBase { function setUp() { parent::setUp(); - $this->web_user = $this->drupalCreateUser(array('edit own page content', 'create page content')); - $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes')); + $this->web_user = $this->drupalCreateUser(array('edit own page content', 'create page content', 'access contextual links')); + $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes', 'access contextual links')); } /** @@ -50,14 +57,11 @@ class PageEditTest extends NodeTestBase { // Check that "edit" link points to correct page. $this->clickLink(t('Edit')); - $edit_url = url("node/$node->nid/edit", array('absolute' => TRUE)); + $edit_url = url("node/$node->nid/edit", array('absolute' => TRUE, 'query' => array('destination' => 'node/1'))); $actual_url = $this->getURL(); $this->assertEqual($edit_url, $actual_url, 'On edit page.'); // Check that the title and body fields are displayed with the correct values. - $active = '' . t('(active tab)') . ''; - $link_text = t('!local-task-title!active', array('!local-task-title' => t('Edit'), '!active' => $active)); - $this->assertText(strip_tags($link_text), 0, 'Edit tab found and marked active.'); $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.'); $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.'); diff --git a/core/modules/node/node.module b/core/modules/node/node.module index f2206a08856..ff073aeff58 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -1769,7 +1769,7 @@ function node_menu() { 'access arguments' => array('update', 1), 'weight' => 0, 'type' => MENU_LOCAL_TASK, - 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, + 'context' => MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', ); $items['node/%node/delete'] = array( @@ -1791,6 +1791,7 @@ function node_menu() { 'access arguments' => array(1), 'weight' => 2, 'type' => MENU_LOCAL_TASK, + 'context' => MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', ); $items['node/%node/revisions/%node_revision/view'] = array( diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 223af2ea2c2..72ceea59efa 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1999,7 +1999,7 @@ function system_library_info() { 'website' => 'http://underscorejs.org/', 'version' => '1.4.0', 'js' => array( - 'core/misc/underscore/underscore.js' => array('group' => JS_LIBRARY), + 'core/misc/underscore/underscore.js' => array('group' => JS_LIBRARY, 'weight' => -20), ), ); @@ -2009,7 +2009,7 @@ function system_library_info() { 'website' => 'http://backbonejs.org/', 'version' => '0.9.2', 'js' => array( - 'core/misc/backbone/backbone.js' => array('group' => JS_LIBRARY), + 'core/misc/backbone/backbone.js' => array('group' => JS_LIBRARY, 'weight' => -19), ), 'dependencies' => array( array('system', 'underscore'), diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php index 93cc8d4c8f6..9f65f1cf7db 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php @@ -12,6 +12,13 @@ namespace Drupal\taxonomy\Tests; */ class TermTest extends TaxonomyTestBase { + /** + * Modules to enable. + * + * @var array + */ + public static $modules = array('taxonomy', 'contextual'); + public static function getInfo() { return array( 'name' => 'Taxonomy term functions and forms', @@ -22,7 +29,7 @@ class TermTest extends TaxonomyTestBase { function setUp() { parent::setUp(); - $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access')); + $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access', 'access contextual links')); $this->drupalLogin($this->admin_user); $this->vocabulary = $this->createVocabulary();