diff --git a/core/modules/edit/js/edit.js b/core/modules/edit/js/edit.js index bf181ede15ea..61157c4df969 100644 --- a/core/modules/edit/js/edit.js +++ b/core/modules/edit/js/edit.js @@ -6,22 +6,22 @@ "use strict"; -Drupal.edit = Drupal.edit || {}; -Drupal.edit.metadataCache = Drupal.edit.metadataCache || {}; +Drupal.edit = { metadataCache: {}, contextualLinksQueue: [] }; /** * Attach toggling behavior and in-place editing. */ Drupal.behaviors.edit = { - attach: function(context) { + attach: function (context) { var $context = $(context); var $fields = $context.find('[data-edit-id]'); // Initialize the Edit app. $('body').once('edit-init', Drupal.edit.init); - var annotateField = function(field) { - if (_.has(Drupal.edit.metadataCache, field.editID)) { + function annotateField (field) { + var hasField = _.has(Drupal.edit.metadataCache, field.editID); + if (hasField) { var meta = Drupal.edit.metadataCache[field.editID]; field.$el.addClass((meta.access) ? 'edit-allowed' : 'edit-disallowed'); @@ -31,21 +31,19 @@ Drupal.behaviors.edit = { .attr('aria-label', meta.aria) .addClass('edit-field edit-type-' + ((meta.editor === 'form') ? 'form' : 'direct')); } - - return true; } - return false; - }; + return hasField; + } // Find all fields in the context without metadata. - var fieldsToAnnotate = _.map($fields.not('.edit-allowed, .edit-disallowed'), function(el) { + var fieldsToAnnotate = _.map($fields.not('.edit-allowed, .edit-disallowed'), function (el) { var $el = $(el); return { $el: $el, editID: $el.attr('data-edit-id') }; }); // Fields whose metadata is known (typically when they were just modified) // can be annotated immediately, those remaining must be requested. - var remainingFieldsToAnnotate = _.reduce(fieldsToAnnotate, function(result, field) { + var remainingFieldsToAnnotate = _.reduce(fieldsToAnnotate, function (result, field) { if (!annotateField(field)) { result.push(field); } @@ -56,7 +54,7 @@ Drupal.behaviors.edit = { Drupal.edit.app.findEditableProperties($context); if (remainingFieldsToAnnotate.length) { - $(window).ready(function() { + $(window).ready(function () { var id = 'edit-load-metadata'; // Create a temporary element to be able to use Drupal.ajax. var $el = jQuery('
').appendTo('body'); @@ -64,13 +62,14 @@ Drupal.behaviors.edit = { Drupal.ajax[id] = new Drupal.ajax(id, $el, { url: drupalSettings.edit.metadataURL, event: 'edit-internal.edit', - submit: { 'fields[]' : _.pluck(remainingFieldsToAnnotate, 'editID') }, - progress: { type : null } // No progress indicator. + submit: { 'fields[]': _.pluck(remainingFieldsToAnnotate, 'editID') }, + // No progress indicator. + progress: { type: null } }); // Implement a scoped editMetaData AJAX command: calls the callback. - Drupal.ajax[id].commands.editMetadata = function(ajax, response, status) { + Drupal.ajax[id].commands.editMetadata = function (ajax, response, status) { // Update the metadata cache. - _.each(response.data, function(metadata, editID) { + _.each(response.data, function (metadata, editID) { Drupal.edit.metadataCache[editID] = metadata; }); @@ -80,6 +79,12 @@ Drupal.behaviors.edit = { // Find editable fields, make them editable. Drupal.edit.app.findEditableProperties($context); + // Metadata cache has been updated, try to set up more contextual + // links now. + Drupal.edit.contextualLinksQueue = _.filter(Drupal.edit.contextualLinksQueue, function (data) { + return !Drupal.edit.setUpContextualLink(data); + }); + // Delete the Drupal.ajax instance that called this very function. delete Drupal.ajax[id]; @@ -93,34 +98,98 @@ Drupal.behaviors.edit = { } }; -Drupal.edit.init = function() { +/** + * Detect contextual links on entities annotated by Edit; queue these to be + * processed. + */ +$(document).on('drupalContextualLinkAdded', function (event, data) { + if (data.$region.is('[data-edit-entity]')) { + var contextualLink = { + entity: data.$region.attr('data-edit-entity'), + $el: data.$el, + $region: data.$region + }; + // Set up contextual links for this, otherwise queue it to be set up later. + if (!Drupal.edit.setUpContextualLink(contextualLink)) { + Drupal.edit.contextualLinksQueue.push(contextualLink); + } + } +}); + +/** + * Attempts to set up a "Quick edit" contextual link. + * + * @param Object contextualLink + * An object with the following properties: + * - entity: an Edit entity identifier, e.g. "node/1" or "custom_block/5". + * - $el: a jQuery element pointing to the contextual links for this entity. + * - $region: a jQuery element pointing to the contextual region for this + * entity. + * + * @return Boolean + * Returns true when a contextual the given contextual link metadata can be + * removed from the queue (either because the contextual link has been set up + * or because it is certain that in-place editing is not allowed for any of + * its fields). + * Returns false otherwise. + */ +Drupal.edit.setUpContextualLink = function (contextualLink) { + // Check if the user has permission to edit at least one of them. + function hasFieldWithPermission (editIDs) { + var i, meta = Drupal.edit.metadataCache; + for (i = 0; i < editIDs.length; i++) { + var editID = editIDs[i]; + if (_.has(meta, editID) && meta[editID].access === true) { + return true; + } + } + return false; + } + + // Checks if the metadata for all given editIDs exists. + function allMetadataExists (editIDs) { + var editIDsWithMetadata = _.intersection(editIDs, _.keys(Drupal.edit.metadataCache)); + return editIDs.length === editIDsWithMetadata.length; + } + + // Find the Edit IDs of all fields within this entity. + var editIDs = []; + contextualLink.$region + .find('[data-edit-id^="' + contextualLink.entity + '/"]') + .each(function () { + editIDs.push($(this).attr('data-edit-id')); + }); + + // The entity for the given contextual link contains at least one field that + // the current user may edit in-place; instantiate ContextualLinkView. + if (hasFieldWithPermission(editIDs)) { + new Drupal.edit.views.ContextualLinkView({ + el: $('