css/edit.css | 21 ++- edit.info | 1 + edit.module | 198 ++++++++------------ edit_aloha/edit_aloha.module | 28 +-- edit_aloha/js/createjs/drupalalohawidget.js | 156 +++++++++++++++ .../Plugin/edit/processed_text_editor/Aloha.php | 43 +++++ generate-core-patch.sh | 7 +- grunt.js | 5 +- includes/pages.inc | 48 ++--- js/app.js | 2 +- js/backbone.drupalform.js | 14 +- js/createjs/editable.js | 12 +- js/createjs/editingWidgets/drupalalohawidget.js | 153 --------------- .../editingWidgets/drupalcontenteditablewidget.js | 4 +- js/createjs/editingWidgets/formwidget.js | 10 +- js/edit.js | 6 + js/util.js | 28 +-- js/viejs/EditService.js | 8 +- js/views/modal-view.js | 2 +- js/views/overlay-view.js | 2 +- js/views/toolbar-view.js | 2 +- lib/Drupal/edit/Ajax/BaseCommand.php | 52 +++++ lib/Drupal/edit/Ajax/FieldFormCommand.php | 27 +++ lib/Drupal/edit/Ajax/FieldFormSavedCommand.php | 28 +++ .../edit/Ajax/FieldFormValidationErrorsCommand.php | 28 +++ ...RenderedWithoutTransformationFiltersCommand.php | 28 +++ lib/Drupal/edit/EditBundle.php | 26 +++ lib/Drupal/edit/Plugin/ProcessedTextEditorBase.php | 35 ++++ .../Plugin/Type/ProcessedTextEditorManager.php | 31 +++ lib/Drupal/edit/Tests/FieldEditorTest.php | 149 +++++++++++++++ text.patch | 8 +- 31 files changed, 781 insertions(+), 381 deletions(-) diff --git a/css/edit.css b/css/edit.css index 69de4f0..b270165 100644 --- a/css/edit.css +++ b/css/edit.css @@ -77,6 +77,9 @@ .toolbar .tray.edit.active { z-index: 340; } +.toolbar .icon-edit.edit-nothing-editable-hidden { + display: none; +} @@ -109,7 +112,7 @@ } .edit-field.edit-editable, .edit-field.edit-type-direct .edit-editable { - box-shadow: 0 0 1px 1px #4D9DE9; + box-shadow: 0 0 1px 1px #4d9de9; } /* Highlighted (hovered) editable. */ @@ -119,7 +122,7 @@ .edit-field.edit-editable.edit-highlighted, .edit-form.edit-editable.edit-highlighted, .edit-field.edit-type-direct .edit-editable.edit-highlighted { - box-shadow: 0 0 1px 1px #0199FF, 0 0 3px 3px rgba(153, 153, 153, .5); + box-shadow: 0 0 1px 1px #0199ff, 0 0 3px 3px rgba(153, 153, 153, .5); } .edit-field.edit-editable.edit-highlighted.edit-validation-error, .edit-form.edit-editable.edit-highlighted.edit-validation-error, @@ -153,7 +156,7 @@ left: 40%; box-shadow: 3px 3px 5px #333; background-color: white; - border: 1px solid #0199FF; + border: 1px solid #0199ff; font-family: 'Droid sans', 'Lucida Grande', sans-serif; } @@ -198,7 +201,7 @@ left: -5px; margin: 0; border: none; - box-shadow: 0 0 1 1px red, 0 0 3px 3px rgba(153, 153, 153, .5); + box-shadow: 0 0 1px 1px red, 0 0 3px 3px rgba(153, 153, 153, .5); background-color: white; } @@ -215,7 +218,7 @@ .edit-form { position: absolute; z-index: 300; - box-shadow: 0 0 30px 4px #4F4F4F; + box-shadow: 0 0 30px 4px #4f4f4f; max-width: 35em; } @@ -249,6 +252,8 @@ margin: 0; vertical-align: baseline; z-index: 310; +} +.edit-toolbar-container { -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; @@ -261,8 +266,8 @@ height: auto; position: absolute; bottom: 1px; - box-shadow: 0 0 1px 1px #0199FF, 0 0 3px 3px rgba(153, 153, 153, .5); - background: #FFF; + box-shadow: 0 0 1px 1px #0199ff, 0 0 3px 3px rgba(153, 153, 153, .5); + background: #fff; } /* The toolbar; these are not necessarily visible. */ @@ -292,7 +297,7 @@ float: left; /* LTR */ font-weight: bolder; padding: 0 5px; - background: #FFF url('../images/throbber.gif') no-repeat -60px 60px; + background: #fff url('../images/throbber.gif') no-repeat -60px 60px; } .edit-toolgroup.info.loading { padding-right: 35px; diff --git a/edit.info b/edit.info index 5298534..3328601 100644 --- a/edit.info +++ b/edit.info @@ -4,3 +4,4 @@ package = Core core = 8.x dependencies[] = field +dependencies[] = toolbar diff --git a/edit.module b/edit.module index 5df4a07..8740388 100644 --- a/edit.module +++ b/edit.module @@ -13,6 +13,7 @@ use Drupal\Core\Template\Attribute; use Drupal\field\FieldInstance; +use Drupal\edit\Plugin\Type\ProcessedTextEditorManager; // @todo: POSTPONED_ON(Drupal core, http://drupal.org/node/1839516) // Remove this when Entity Access API is ready. @@ -40,9 +41,8 @@ function edit_menu() { 'access callback' => TRUE, 'page callback' => 'edit_field_edit', 'page arguments' => array(3, 4, 5, 6, 7), - 'file' => 'includes/pages.inc', - 'delivery callback'=> 'ajax_deliver', 'theme callback' => 'ajax_base_page_theme', + 'file' => 'includes/pages.inc', ); $items['admin/render-without-transformations/field/%/%/%/%/%'] = array( // Access is controlled after we have inspected the entity, which can't @@ -51,9 +51,8 @@ function edit_menu() { 'access callback' => TRUE, 'page callback' => 'edit_text_field_render_without_transformation_filters', 'page arguments' => array(3, 4, 5, 6, 7), - 'file' => 'includes/pages.inc', - 'delivery callback'=> 'ajax_deliver', 'theme callback' => 'ajax_base_page_theme', + 'file' => 'includes/pages.inc', ); return $items; @@ -73,29 +72,33 @@ function edit_toolbar() { 'href' => '', 'html' => FALSE, 'attributes' => array( - 'class' => array('icon', 'icon-edit'), - ), - // @todo: BLOCKED_ON(Drupal core, http://drupal.org/node/1847198) - // This #post_render callback does not yet get called, which means that - // even if zero fields on the current page are editable for the current - // user, the "Edit" tab will still show up. - '#post_render' => array( - 'edit_toolbar_post_render', + 'class' => array('icon', 'icon-edit', 'edit-nothing-editable-hidden'), ), ), 'tray' => array( + '#heading' => t('In-place editing operations'), 'view_edit_toggle' => array( - '#prefix' => '

' . t('In-place edit operations') . '

', - 'content' => array( - array( - '#theme' => 'menu_local_task', - '#link' => array('title' => t('View'), 'href' => current_path(), 'localized_options' => array('fragment' => 'view', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-view')))), - '#active' => TRUE, + '#theme' => 'links__toolbar_edit', + '#attributes' => array( + 'id' => 'edit_view-edit-toggles', + 'class' => 'menu', + ), + '#links' => array( + 'view' => array( + 'title' => t('View'), + 'href' => request_path(), + 'fragment' => 'view', + 'attributes' => array( + 'class' => array('edit_view-edit-toggle', 'edit-view'), + ), ), - array( - '#theme' => 'menu_local_task', - '#link' => array('title' => t('Quick edit'), 'href' => current_path(), 'localized_options' => array('fragment' => 'quick-edit', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-edit')))), + 'edit' => array( + 'title' => t('Quick edit'), + 'href' => request_path(), + 'fragment' => 'quick-edit', + 'attributes' => array( + 'class' => array('edit_view-edit-toggle', 'edit-edit'), + ), ), ), '#attached' => array( @@ -104,9 +107,6 @@ function edit_toolbar() { ), ), ), - '#post_render' => array( - 'edit_toolbar_post_render', - ), ), ); @@ -114,17 +114,6 @@ function edit_toolbar() { } /** - * Post-render function to only show the Edit tab in the toolbar if relevant. - * - * When the user cannot edit any of the fields on the page, he should not be - * given the possibility to toggle edit mode. - */ -function edit_toolbar_post_render($html) { - global $edit_toolbar; - return ($edit_toolbar !== TRUE) ? '' : $html; -} - -/** * Implements hook_library(). */ function edit_library_info() { @@ -156,7 +145,6 @@ function edit_library_info() { $path . '/js/createjs/storage.js' => array('defer' => TRUE), $path . '/js/createjs/editingWidgets/formwidget.js' => array('defer' => TRUE), $path . '/js/createjs/editingWidgets/drupalcontenteditablewidget.js' => array('defer' => TRUE), - $path . '/js/createjs/editingWidgets/drupalalohawidget.js' => array('defer' => TRUE), // Other. $path . '/js/util.js' => array('defer' => TRUE), $path . '/js/theme.js' => array('defer' => TRUE), @@ -182,14 +170,10 @@ function edit_library_info() { array('system', 'jquery.form'), array('system', 'drupal.form'), array('system', 'drupal.ajax'), + array('system', 'drupalSettings'), ), ); - // Only add dependencies on the WYSIWYG editor when it's actually available. - if (count(module_implements('edit_wysiwyg_info'))) { - $libraries['edit']['dependencies'][] = _edit_get_wysiwyg_info('javascript library'); - } - return $libraries; } @@ -225,18 +209,15 @@ function edit_preprocess_field(&$variables) { $entity_access = edit_entity_access('update', $entity->entityType(), $entity); $field_access = field_access('edit', $field_name, $entity->entityType(), $entity); - $editability = _edit_analyze_field_editability($items, $instance, $formatter_type); - if ($entity_access && $field_access && $editability != 'disabled') { - global $edit_toolbar; - $edit_toolbar = TRUE; - + $editor = _edit_get_field_editor($items, $instance, $formatter_type); + if ($entity_access && $field_access && $editor != 'disabled') { // Mark this field as editable and provide metadata through data- attributes. $variables['attributes']['data-edit-field-label'] = $instance->definition['label']; $variables['attributes']['data-edit-id'] = $entity->entityType() . ':' . $entity->id() . ':' . $field_name . ':' . $langcode . ':' . $view_mode; $variables['attributes']['class'][] = 'edit-field'; $variables['attributes']['class'][] = 'edit-allowed'; - $variables['attributes']['class'][] = 'edit-type-' . $editability; - if ($editability == 'direct-with-wysiwyg') { + $variables['attributes']['class'][] = 'edit-type-' . $editor; + if ($editor == 'direct-with-wysiwyg') { $variables['attributes']['class'][] = 'edit-type-direct'; $format_id = $entity->{$field_name}[$langcode][0]['format']; _edit_preprocess_field_wysiwyg($variables, $format_id); @@ -245,7 +226,7 @@ function edit_preprocess_field(&$variables) { } /** - * Sets attributes on a field that have 'direct-with-wysiwyg' editability. + * Sets attributes on a field that have 'direct-with-wysiwyg' editor. * * @param array $variables * An associative array containing: the key 'attributes'. See the @@ -259,11 +240,6 @@ function _edit_preprocess_field_wysiwyg(&$variables, $format_id) { // Let the WYSIWYG editor know the text format. $variables['attributes']['data-edit-text-format'] = $format_id; - // Ensure the WYSIWYG editor has the necessary text format related - // metadata. - $settings_callback = _edit_get_wysiwyg_info('javascript settings callback'); - $settings_callback(); - // Let the JavaScript logic know whether transformation filters are used // in this format, so it can decide whether to re-render the text or not. $filter_types = filter_get_filter_types_by_format($format_id); @@ -280,7 +256,7 @@ function _edit_preprocess_field_wysiwyg(&$variables, $format_id) { } /** - * Determines editability given a field, its instance info and its formatter. + * Determines editor given a field, its instance info and its formatter. * * @param array $field * The field's field array. @@ -290,57 +266,57 @@ function _edit_preprocess_field_wysiwyg(&$variables, $format_id) { * The field's formatter type name. * * @return string - * The editability: 'disabled', 'form', 'direct' or 'direct-with-wysiwyg'. + * The editor: 'disabled', 'form', 'direct' or 'direct-with-wysiwyg'. */ -function _edit_analyze_field_editability($items, FieldInstance $instance, $formatter_type) { +function _edit_get_field_editor($items, FieldInstance $instance, $formatter_type) { $field_name = $instance['field_name']; // If the formatter doesn't contain the edit property, default it to 'form' - // editability, which should always work. + // editor, which should always work. $formatter_info = field_info_formatter_types($formatter_type); - if (empty($formatter_info['edit']['editability'])) { - $formatter_info['edit']['editability'] = 'form'; + if (empty($formatter_info['edit']['editor'])) { + $formatter_info['edit']['editor'] = 'form'; } - $editability = $formatter_info['edit']['editability']; + $editor = $formatter_info['edit']['editor']; // If editing is explicitly disabled for this field, return early to avoid // any further processing. - if ($editability == 'disabled') { + if ($editor == 'disabled') { return; } // If directly editable, check the cardinality. If the cardinality is greater // than 1, use a form to edit the field. - if ($editability == 'direct') { + if ($editor == 'direct') { $field = field_info_field($field_name); if ($field['cardinality'] != 1) { - $editability = 'form'; + $editor = 'form'; } } // If still directly editable, check whether "regular" direct editing (almost // bare contentEditable) editing should be used or WYSIWYG-based direct // editing should be used. In the latter case - if ($editability == 'direct') { + if ($editor == 'direct') { // If this field is configured to not use text processing; it is plain text // "regular" direct editing should be used, which is already set. // On the other hand, if it is configured to use text processing; then we - // must check whether 'direct-with-wysiwyg' or 'form' editability should be + // must check whether 'direct-with-wysiwyg' or 'form' editor should be // used. if (!empty($instance['settings']['text_processing'])) { $format_id = $items[0]['format']; - $editability = _edit_wysiwyg_analyze_field_editability($format_id); + $editor = _edit_wysiwyg_get_field_editor($format_id); } } - return $editability; + return $editor; } /** - * Determines editability given a directly editable field with text processing. + * Determines editor given a directly editable field with text processing. * - * Given a text field (with cardinality 1) that defaults to 'direct' editability + * Given a text field (with cardinality 1) that defaults to 'direct' editor * and has text processing enabled, check whether the text format allows it to * use WYSIWYG-powered direct editing or whether 'form' based editing needs to * be used. @@ -349,65 +325,57 @@ function _edit_analyze_field_editability($items, FieldInstance $instance, $forma * The field's current text format. * * @return string - * The editability: 'direct-with-wysiwyg' or 'form'. + * The editor: 'direct-with-wysiwyg' or 'form'. */ -function _edit_wysiwyg_analyze_field_editability($format_id = NULL) { +function _edit_wysiwyg_get_field_editor($format_id = NULL) { + $wysiwyg_plugin = &drupal_static(__FUNCTION__); + // If no format is assigned yet, (e.g. when the field is still empty (NULL)), // then provide form-based editing, so that the user is able to select a text // format. (Direct editing doesn't allow the user to change the format.) if (empty($format_id)) { return 'form'; } + + // NOTE: this code will pick the first processed text PropertyEditor widget + // plug-in that is available and consider that the only available choice. + // @todo: make it possible to have multiple processed text PropertyEditor + // widgets. + if (!isset($wysiwyg_plugin) && isset($format_id)) { + $definitions = drupal_container()->get('plugin.manager.edit.processed_text_editor')->getDefinitions(); + if (count($definitions)) { + $plugin_ids = array_keys($definitions); + $plugin_id = $plugin_ids[0]; + $wysiwyg_plugin = drupal_container()->get('plugin.manager.edit.processed_text_editor')->createInstance($plugin_id); + $wysiwyg_plugin->settingsAdded = FALSE; + } + } + // If no WYSIWYG editor is available, then fall back to form-based editing. - elseif (count(_edit_get_wysiwyg_info()) == 0) { + if (!isset($wysiwyg_plugin)) { return 'form'; } // If the WYSIWYG editor is not compatible with the current format, then fall // back to form-based editing. else { - $compatibility_callback = _edit_get_wysiwyg_info('format compatibility callback'); - if (!$compatibility_callback($format_id)) { + $match = $wysiwyg_plugin->checkFormatCompatibility($format_id); + if (!$match) { return 'form'; } - } - - return 'direct-with-wysiwyg'; -} - -/** - * Retrieves a list of all available WYSIWYG integration for Edit. Only the - * first is actually used. - * - * @todo Convert to the plug-in system! - * - * @param string $key - * The key to get a value for. - * - * @see hook_edit_wysiwyg_info() - * @see hook_edit_wysiwyg_info_alter() - */ -function _edit_get_wysiwyg_info($key = NULL) { - $edit_wysiwyg_info = &drupal_static(__FUNCTION__, array()); - - if (empty($edit_wysiwyg_info)) { - $cache = cache()->get('edit_wysiwyg_info'); - if ($cache === FALSE) { - // Rebuild the cache and save it. - $edit_wysiwyg_info = module_invoke_all('edit_wysiwyg_info'); - drupal_alter('edit_wysiwyg_info', $edit_wysiwyg_info); - } - else { - $edit_wysiwyg_info = $cache->data; + else if ($match) { + // Only load the WYSIWYG editor's JavaScript if it hasn't been already. + if ($wysiwyg_plugin->settingsAdded === FALSE) { + $definition = $wysiwyg_plugin->getDefinition(); + drupal_add_library($definition['library']['module'], $definition['library']['name']); + $wysiwyg_plugin->addJsSettings(); + + // Let Create.js know which WYSIWYG editor widget it should use. + drupal_add_js(array('edit' => array( + 'wysiwygEditorWidgetName' => $definition['propertyEditorName'], + )), 'setting'); + $wysiwyg_plugin->settingsAdded = TRUE; + } + return 'direct-with-wysiwyg'; } } - - if (isset($key)) { - $modules = array_keys($edit_wysiwyg_info); - $first = $modules[0]; - - return $edit_wysiwyg_info[$first][$key]; - } - else { - return $edit_wysiwyg_info; - } } diff --git a/edit_aloha/edit_aloha.module b/edit_aloha/edit_aloha.module index f3b3c08..865bcb0 100644 --- a/edit_aloha/edit_aloha.module +++ b/edit_aloha/edit_aloha.module @@ -8,18 +8,6 @@ */ /** - * Implements hook_edit_wysiwyg_info(). - */ -function edit_aloha_edit_wysiwyg_info() { - $info['edit_aloha'] = array( - 'javascript library' => array('edit_aloha', 'aloha.edit'), - 'format compatibility callback' => 'aloha_check_format_compatibility', - 'javascript settings callback' => 'aloha_add_format_settings', - ); - return $info; -} - -/** * Implements hook_library_info(). */ function edit_aloha_library_info() { @@ -28,26 +16,14 @@ function edit_aloha_library_info() { 'title' => 'Integrate Aloha Editor with the Edit module.', 'version' => ALOHA_VERSION, 'js' => array( - // Configure Edit's JS to use aloha as the WYSIWYG. - array( - 'data' => array( - 'edit' => array('wysiwyg' => 'aloha'), - 'aloha' => array('settings' => array( - // Ensure the Edit module can embed the rendered toolbar within - // its own DOM infrastructure. - 'DrupalUI' => array( - 'renderOwnToolbarContainer' => FALSE, - ) - )) - ), - 'type' => 'setting', - ), + $module_path . '/js/createjs/drupalalohawidget.js' => array('defer' => TRUE), ), 'css' => array( $module_path . '/css/drupal.aloha.edit.css', ), 'dependencies' => array( array('aloha', 'aloha'), + array('edit', 'edit') ) ); return $libraries; diff --git a/edit_aloha/js/createjs/drupalalohawidget.js b/edit_aloha/js/createjs/drupalalohawidget.js new file mode 100644 index 0000000..d020e8c --- /dev/null +++ b/edit_aloha/js/createjs/drupalalohawidget.js @@ -0,0 +1,156 @@ +/** + * @file + * Override of Create.js' default Aloha Editor widget. + * + * NOTE: This does in fact use zero code of jQuery.create.alohaWidget. + */ +(function (jQuery, Drupal, drupalSettings) { + +"use strict"; + + jQuery.widget('Drupal.drupalAlohaWidget', jQuery.Create.alohaWidget, { + + // @todo BLOCKED_ON(Create.js/VIE.js, how to restore original content when canceling editing) + // Actually use this when restoring original content, but for that we + // first need to know how to restore content in a Create.js context + originalTransformedContent: null, + + /** + * Implements jQuery UI widget factory's _init() method. + * + * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142) + * Get rid of this once that issue is solved. + */ + _init: function() {}, + + /** + * Implements Create's _initialize() method. + */ + _initialize: function() { + this._bindEvents(); + + // Immediately initialize Aloha, this can take some time. By doing it now + // already, it will most likely already be ready when the user actually + // wants to use Aloha Editor. + Drupal.aloha.init(); + }, + + /** + * Binds to events. + * + * @todo: POSTPONED_ON(Aloha Editor, https://github.com/alohaeditor/Aloha-Editor/issues/693) + * Get rid of this helper function and move it into _initialize() once that + * issue is solved. Also see http://drupal.org/node/1725032. + */ + _bindEvents: function() { + var that = this; + + // Sets the state to 'activated' upon clicking the element. + this.element.on("click.edit", function(event) { + event.stopPropagation(); + event.preventDefault(); + that.options.activating(); + }); + + // Sets the state to 'changed' whenever the content has changed. + this.element.on('aloha-content-changed', function(event, $alohaEditable, data) { + if (!data.editable.isModified()) { + return true; + } + that.options.changed(data.editable.getContents()); + data.editable.setUnmodified(); + }); + }, + + /** + * Makes this PropertyEditor widget react to state changes. + */ + stateChange: function(from, to) { + switch (to) { + case 'inactive': + break; + case 'candidate': + if (from !== 'inactive') { + Drupal.aloha.detach(this.element); + this._removeValidationErrors(); + this._cleanUp(); + this._bindEvents(); + } + break; + case 'highlighted': + break; + case 'activating': + // When transformation filters have been been applied to the processed + // text of this field, then we'll need to load a re-rendered version of + // it without the transformation filters. + if (this.options.widget.element.hasClass('edit-text-with-transformation-filters')) { + this.originalTransformedContent = this.element.html(); + + var that = this; + Drupal.edit.util.loadRerenderedProcessedText({ + $editorElement: this.element, + propertyID: Drupal.edit.util.calcPropertyID(this.options.entity, this.options.property), + callback: function (rerendered) { + that.element.html(rerendered); + that.options.activated(); + } + }); + } + // When no transformation filters have been applied: start WYSIWYG + // editing immediately! + else { + this.options.activated(); + } + break; + case 'active': + // Attach Aloha Editor with this field's text format. + var formatID = this.options.widget.element.attr('data-edit-text-format'); + var format = drupalSettings.aloha.formats[formatID]; + // Let the custom Aloha Editor UI for Drupal know that it should + // render the Aloha Editor toolbar into Edit's toolbar. + this.element.attr('data-edit-aloha-toolbar-custom-location', 'true'); + Drupal.aloha.attach(this.element, format); + Drupal.aloha.activate(this.element, format); + break; + case 'changed': + break; + case 'saving': + this._removeValidationErrors(); + break; + case 'saved': + break; + case 'invalid': + break; + } + }, + + /** + * Removes validation errors' markup changes, if any. + * + * Note: this only needs to happen for type=direct, because for type=direct, + * the property DOM element itself is modified; this is not the case for + * type=form. + */ + _removeValidationErrors: function() { + this.element + .removeClass('edit-validation-error') + .next('.edit-validation-errors').remove(); + }, + + /** + * Cleans up after the widget has been saved. + * + * Note: this is where the Create.Storage and accompanying Backbone.sync + * abstractions "leak" implementation details. That is only the case because + * we have to use Drupal's Form API as a transport mechanism. It is + * unfortunately a stateful transport mechanism, and that's why we have to + * clean it up here. This clean-up is only necessary when canceling the + * editing of a property after having attempted to save at least once. + */ + _cleanUp: function() { + Drupal.edit.util.form.unajaxifySaving(jQuery('#edit_backstage form .edit-form-submit')); + jQuery('#edit_backstage form').remove(); + } + }); + +})(jQuery, Drupal, drupalSettings); diff --git a/edit_aloha/lib/Drupal/edit_aloha/Plugin/edit/processed_text_editor/Aloha.php b/edit_aloha/lib/Drupal/edit_aloha/Plugin/edit/processed_text_editor/Aloha.php new file mode 100644 index 0000000..41474c1 --- /dev/null +++ b/edit_aloha/lib/Drupal/edit_aloha/Plugin/edit/processed_text_editor/Aloha.php @@ -0,0 +1,43 @@ + $EDIT_DIR/$FILENAME-$COMMENTNR.patch diff --git a/grunt.js b/grunt.js index f160df9..d4398dd 100644 --- a/grunt.js +++ b/grunt.js @@ -43,10 +43,11 @@ module.exports = function(grunt) { }, globals: { jQuery: true, + _: true, Backbone: true, - Drupal: true, VIE: true, - _: true + Drupal: true, + drupalSettings: true } }, csslint: { diff --git a/includes/pages.inc b/includes/pages.inc index d29e87b..10b682d 100644 --- a/includes/pages.inc +++ b/includes/pages.inc @@ -4,6 +4,11 @@ * AJAX endpoints to retrieve & save subforms for fields and re-render fields. */ +use Drupal\Core\Ajax\AjaxResponse; +use Drupal\edit\Ajax\FieldFormCommand; +use Drupal\edit\Ajax\FieldFormSavedCommand; +use Drupal\edit\Ajax\FieldFormValidationErrorsCommand; +use Drupal\edit\Ajax\FieldRenderedWithoutTransformationFiltersCommand; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -24,6 +29,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * A render array. */ function edit_field_edit($entity_type, $entity_id, $field_name, $langcode, $view_mode) { + $response = new AjaxResponse(); + // Ensure the entity type is valid. if (empty($entity_type)) { throw new NotFoundHttpException(); @@ -64,39 +71,23 @@ function edit_field_edit($entity_type, $entity_id, $field_name, $langcode, $view 'no_redirect' => TRUE, 'build_info' => array('args' => array()), ); - $commands = array(); form_load_include($form_state, 'inc', 'edit', 'includes/form'); $form = drupal_build_form('edit_field_form', $form_state); - $id = "$entity_type:$entity_id:$field_name:$langcode:$view_mode"; if (!empty($form_state['executed'])) { // Retrieve the updated entity, save it and render only the modified field. $entity = $form_state['entity']; $entity->save(); - $options = array('field_name' => $field_name); - field_attach_prepare_view($entity->entityType(), array($entity->id() => $entity), $view_mode, $langcode, $options); - $output = field_attach_view($entity->entityType(), $entity, $view_mode, $langcode, $options); - - $commands[] = array( - 'command' => 'edit_field_form_saved', - 'id' => $id, - 'data' => drupal_render($output), - ); + $output = field_view_field($entity->entityType(), $entity, $field_name, $view_mode, $langcode); + + $response->addCommand(new FieldFormSavedCommand(drupal_render($output))); } else { - $commands[] = array( - 'command' => 'edit_field_form', - 'id' => $id, - 'data' => drupal_render($form), - ); + $response->addCommand(new FieldFormCommand(drupal_render($form))); $errors = form_get_errors(); if (count($errors)) { - $commands[] = array( - 'command' => 'edit_field_form_validation_errors', - 'id' => $id, - 'data' => theme('status_messages'), - ); + $response->addCommand(new FieldFormValidationErrorsCommand(theme('status_messages'))); } } @@ -106,7 +97,7 @@ function edit_field_edit($entity_type, $entity_id, $field_name, $langcode, $view drupal_static_reset('drupal_add_js'); } - return array('#type' => 'ajax', '#commands' => $commands); + return $response; } /** @@ -129,6 +120,8 @@ function edit_field_edit($entity_type, $entity_id, $field_name, $langcode, $view * A render array. */ function edit_text_field_render_without_transformation_filters($entity_type, $entity_id, $field_name, $langcode, $view_mode) { + $response = new AjaxResponse(); + // Ensure the entity type is valid. if (empty($entity_type)) { throw new NotFoundHttpException(); @@ -162,18 +155,11 @@ function edit_text_field_render_without_transformation_filters($entity_type, $en throw new NotFoundHttpException(); } - $commands = array(); - // Render the field in our custom display mode; retrieve the re-rendered // markup, this is what we're after. $field_output = field_view_field($entity_type, $entity, $field_name, 'edit-render-without-transformation-filters'); $output = $field_output[0]['#markup']; + $response->addCommand(new FieldRenderedWithoutTransformationFiltersCommand($output)); - $commands[] = array( - 'command' => 'edit_field_rendered_without_transformation_filters', - 'id' => "$entity_type:$entity_id:$field_name:$langcode:$view_mode", - 'data' => $output, - ); - - return array('#type' => 'ajax', '#commands' => $commands); + return $response; } diff --git a/js/app.js b/js/app.js index 82d390f..c5d5be5 100644 --- a/js/app.js +++ b/js/app.js @@ -308,7 +308,7 @@ // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133) // Get rid of this once that issue is solved. - editor.options.widget.element.bind('createeditablestatechange', function(event, data) { + editor.options.widget.element.on('createeditablestatechange', function(event, data) { editor.decorationView.stateChange(data.previous, data.current); editor.toolbarView.stateChange(data.previous, data.current); }); diff --git a/js/backbone.drupalform.js b/js/backbone.drupalform.js index aee82c1..cc0e5c3 100644 --- a/js/backbone.drupalform.js +++ b/js/backbone.drupalform.js @@ -37,7 +37,7 @@ Backbone.syncDrupalFormWidget = function(method, model, options) { var base = $submit.attr('id'); // Successfully saved. - Drupal.ajax[base].commands.edit_field_form_saved = function(ajax, response, status) { + Drupal.ajax[base].commands.editFieldFormSaved = function(ajax, response, status) { Drupal.edit.util.form.unajaxifySaving(jQuery(ajax.element)); // Call Backbone.sync's success callback with the rerendered field. @@ -51,7 +51,7 @@ Backbone.syncDrupalFormWidget = function(method, model, options) { }; // Unsuccessfully saved; validation errors. - Drupal.ajax[base].commands.edit_field_form_validation_errors = function(ajax, response, status) { + Drupal.ajax[base].commands.editFieldFormValidationErrors = function(ajax, response, status) { // Call Backbone.sync's error callback with the validation error messages. options.error(response.data); }; @@ -61,7 +61,7 @@ Backbone.syncDrupalFormWidget = function(method, model, options) { // API then marks which form items have errors. Therefor, we have to replace // the existing form, unbind the existing Drupal.ajax instance and create a // new Drupal.ajax instance. - Drupal.ajax[base].commands.edit_field_form = function(ajax, response, status) { + Drupal.ajax[base].commands.editFieldForm = function(ajax, response, status) { Drupal.edit.util.form.unajaxifySaving(jQuery(ajax.element)); Drupal.ajax.prototype.commands.insert(ajax, { @@ -122,7 +122,7 @@ Backbone.syncDirect = function(method, model, options) { var base = Drupal.edit.util.form.ajaxifySaving(formOptions, $submit); // Successfully saved. - Drupal.ajax[base].commands.edit_field_form_saved = function (ajax, response, status) { + Drupal.ajax[base].commands.editFieldFormSaved = function (ajax, response, status) { Drupal.edit.util.form.unajaxifySaving(jQuery(ajax.element)); jQuery('#edit_backstage form').remove(); @@ -130,18 +130,18 @@ Backbone.syncDirect = function(method, model, options) { }; // Unsuccessfully saved; validation errors. - Drupal.ajax[base].commands.edit_field_form_validation_errors = function(ajax, response, status) { + Drupal.ajax[base].commands.editFieldFormValidationErrors = function(ajax, response, status) { // Call Backbone.sync's error callback with the validation error messages. options.error(response.data); }; - // The edit_field_form AJAX command is only called upon loading the form + // The editFieldForm AJAX command is only called upon loading the form // for the first time, and when there are validation errors in the form; // Form API then marks which form items have errors. This is useful for // "form" editors, but pointless for "direct" editors: the form itself // won't be visible at all anyway! Therefor, we ignore the new form and // we continue to use the existing form. - Drupal.ajax[base].commands.edit_field_form = function(ajax, response, status) { + Drupal.ajax[base].commands.editFieldForm = function(ajax, response, status) { // no-op }; diff --git a/js/createjs/editable.js b/js/createjs/editable.js index cc34498..aac1ed2 100644 --- a/js/createjs/editable.js +++ b/js/createjs/editable.js @@ -2,12 +2,12 @@ * @file * Determines which editor to use based on a class attribute. */ -(function (jQuery, Drupal) { +(function (jQuery, drupalSettings) { "use strict"; jQuery.widget('Drupal.createEditable', jQuery.Midgard.midgardEditable, { - _create: function () { + _create: function() { this.vie = this.options.vie; this.options.domService = 'edit'; @@ -18,7 +18,7 @@ options: {} }; this.options.editors['direct-with-wysiwyg'] = { - widget: 'drupalAlohaWidget', + widget: drupalSettings.edit.wysiwygEditorWidgetName, options: {} }; this.options.editors.form = { @@ -29,8 +29,8 @@ jQuery.Midgard.midgardEditable.prototype._create.call(this); }, - _propertyEditorName: function (data) { - if (Drupal.settings.edit.wysiwyg && jQuery(this.element).hasClass('edit-type-direct')) { + _propertyEditorName: function(data) { + if (jQuery(this.element).hasClass('edit-type-direct')) { if (jQuery(this.element).hasClass('edit-type-direct-with-wysiwyg')) { return 'direct-with-wysiwyg'; } @@ -40,4 +40,4 @@ } }); -})(jQuery, Drupal); +})(jQuery, drupalSettings); diff --git a/js/createjs/editingWidgets/drupalalohawidget.js b/js/createjs/editingWidgets/drupalalohawidget.js deleted file mode 100644 index 6ef3a93..0000000 --- a/js/createjs/editingWidgets/drupalalohawidget.js +++ /dev/null @@ -1,153 +0,0 @@ -/** - * @file - * Override of Create.js' default Aloha Editor widget. - * - * NOTE: This does in fact use zero code of jQuery.create.alohaWidget. - */ -(function (jQuery, Drupal) { - -"use strict"; - - jQuery.widget('Drupal.drupalAlohaWidget', jQuery.Create.alohaWidget, { - - // @todo BLOCKED_ON(Create.js/VIE.js, how to restore original content when canceling editing) - // Actually use this when restoring original content, but for that we - // first need to know how to restore content in a Create.js context - originalTransformedContent: null, - - /** - * Implements jQuery UI widget factory's _init() method. - * - * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142) - * Get rid of this once that issue is solved. - */ - _init: function() {}, - - /** - * Implements Create's _initialize() method. - */ - _initialize: function() { - this._bindEvents(); - - // Immediately initialize Aloha, this can take some time. By doing it now - // already, it will most likely already be ready when the user actually - // wants to use Aloha Editor. - Drupal.aloha.init(); - }, - - /** - * Binds to events. - * - * @todo: POSTPONED_ON(Aloha Editor, https://github.com/alohaeditor/Aloha-Editor/issues/693) - * Get rid of this helper function and move it into _initialize() once that - * issue is solved. Also see http://drupal.org/node/1725032. - */ - _bindEvents: function() { - var that = this; - - // Sets the state to 'activated' upon clicking the element. - this.element.bind("click.edit", function(event) { - event.stopPropagation(); - event.preventDefault(); - that.options.activating(); - }); - - // Sets the state to 'changed' whenever the content has changed. - this.element.bind('aloha-content-changed', function(event, $alohaEditable, data) { - if (!data.editable.isModified()) { - return true; - } - that.options.changed(data.editable.getContents()); - data.editable.setUnmodified(); - }); - }, - - /** - * Makes this PropertyEditor widget react to state changes. - */ - stateChange: function(from, to) { - switch (to) { - case 'inactive': - break; - case 'candidate': - if (from !== 'inactive') { - Drupal.aloha.detach(this.element); - this._removeValidationErrors(); - this._cleanUp(); - this._bindEvents(); - } - break; - case 'highlighted': - break; - case 'activating': - // When transformation filters have been been applied to the processed - // text of this field, then we'll need to load a re-rendered version of - // it without the transformation filters. - if (this.options.widget.element.hasClass('edit-text-with-transformation-filters')) { - this.originalTransformedContent = this.element.html(); - - var that = this; - Drupal.edit.util.loadRerenderedProcessedText({ - $editorElement: this.element, - propertyID: Drupal.edit.util.calcPropertyID(this.options.entity, this.options.property), - callback: function (rerendered) { - that.element.html(rerendered); - that.options.activated(); - } - }); - } - // When no transformation filters have been applied: start WYSIWYG - // editing immediately! - else { - this.options.activated(); - } - break; - case 'active': - // Attach Aloha Editor with this field's text format. - var formatID = this.options.widget.element.attr('data-edit-text-format'); - var format = Drupal.settings.aloha.formats[formatID]; - Drupal.aloha.attach(this.element, format); - Drupal.aloha.activate(this.element, format); - break; - case 'changed': - break; - case 'saving': - this._removeValidationErrors(); - break; - case 'saved': - break; - case 'invalid': - break; - } - }, - - /** - * Removes validation errors' markup changes, if any. - * - * Note: this only needs to happen for type=direct, because for type=direct, - * the property DOM element itself is modified; this is not the case for - * type=form. - */ - _removeValidationErrors: function() { - this.element - .removeClass('edit-validation-error') - .next('.edit-validation-errors').remove(); - }, - - /** - * Cleans up after the widget has been saved. - * - * Note: this is where the Create.Storage and accompanying Backbone.sync - * abstractions "leak" implementation details. That is only the case because - * we have to use Drupal's Form API as a transport mechanism. It is - * unfortunately a stateful transport mechanism, and that's why we have to - * clean it up here. This clean-up is only necessary when canceling the - * editing of a property after having attempted to save at least once. - */ - _cleanUp: function() { - Drupal.edit.util.form.unajaxifySaving(jQuery('#edit_backstage form .edit-form-submit')); - jQuery('#edit_backstage form').remove(); - } - }); - -})(jQuery, Drupal); diff --git a/js/createjs/editingWidgets/drupalcontenteditablewidget.js b/js/createjs/editingWidgets/drupalcontenteditablewidget.js index 65f5116..c773e6e 100644 --- a/js/createjs/editingWidgets/drupalcontenteditablewidget.js +++ b/js/createjs/editingWidgets/drupalcontenteditablewidget.js @@ -23,7 +23,7 @@ var that = this; // Sets the state to 'activated' upon clicking the element. - this.element.bind("click.edit", function(event) { + this.element.on("click.edit", function(event) { event.stopPropagation(); event.preventDefault(); that.options.activated(); @@ -31,7 +31,7 @@ // Sets the state to 'changed' whenever the content has changed. var before = jQuery.trim(this.element.text()); - this.element.bind('keyup paste', function (event) { + this.element.on('keyup paste', function (event) { if (that.options.disabled) { return; } diff --git a/js/createjs/editingWidgets/formwidget.js b/js/createjs/editingWidgets/formwidget.js index 889ee87..9a5153d 100644 --- a/js/createjs/editingWidgets/formwidget.js +++ b/js/createjs/editingWidgets/formwidget.js @@ -25,7 +25,7 @@ _initialize: function() { // Sets the state to 'activating' upon clicking the element. var that = this; - this.element.bind("click.edit", function(event) { + this.element.on("click.edit", function(event) { event.stopPropagation(); event.preventDefault(); that.options.activating(); @@ -114,11 +114,11 @@ var $submit = widget.$formContainer.find('.edit-form-submit'); Drupal.edit.util.form.ajaxifySaving(formOptions, $submit); widget.$formContainer - .delegate(':input', 'formUpdated.edit', function () { + .on('formUpdated.edit', ':input', function () { // Sets the state to 'changed'. widget.options.changed(); }) - .delegate('input', 'keypress.edit', function (event) { + .on('keypress.edit', 'input', function (event) { if (event.keyCode === 13) { return false; } @@ -139,8 +139,8 @@ Drupal.edit.util.form.unajaxifySaving(this.$formContainer.find('.edit-form-submit')); this.$formContainer - .undelegate(':input', 'change.edit') - .undelegate('input', 'keypress.edit') + .off('change.edit', ':input') + .off('keypress.edit', 'input') .remove(); this.$formContainer = null; } diff --git a/js/edit.js b/js/edit.js index 0c813fb..2c42068 100644 --- a/js/edit.js +++ b/js/edit.js @@ -25,6 +25,12 @@ Drupal.behaviors.editDiscoverEditables = { Drupal.behaviors.edit = { attach: function(context) { $('#edit_view-edit-toggles').once('edit-init', Drupal.edit.init); + + // As soon as there is at least one editable field, show the Edit tab in the + // toolbar. + if ($(context).find('.edit-field.edit-allowed').length) { + $('.toolbar .icon-edit.edit-nothing-editable-hidden').removeClass('edit-nothing-editable-hidden'); + } } }; diff --git a/js/util.js b/js/util.js index 6b0044b..8ed9a2b 100644 --- a/js/util.js +++ b/js/util.js @@ -2,7 +2,7 @@ * @file * Provides utility functions for Edit. */ -(function($, Drupal) { +(function($, Drupal, drupalSettings) { "use strict"; @@ -44,19 +44,21 @@ Drupal.edit.util.buildUrl = function(id, urlFormat) { Drupal.edit.util.loadRerenderedProcessedText = function(options) { // Create a Drupal.ajax instance to load the form. Drupal.ajax[options.propertyID] = new Drupal.ajax(options.propertyID, options.$editorElement, { - url: Drupal.edit.util.buildUrl(options.propertyID, Drupal.settings.edit.rerenderProcessedTextURL), + url: Drupal.edit.util.buildUrl(options.propertyID, drupalSettings.edit.rerenderProcessedTextURL), event: 'edit-internal.edit', submit: { nocssjs : true }, progress: { type : null } // No progress indicator. }); - // Implement a scoped edit_field_form AJAX command: calls the callback. - Drupal.ajax[options.propertyID].commands.edit_field_rendered_without_transformation_filters = function(ajax, response, status) { + // Implement a scoped editFieldRenderedWithoutTransformationFilters AJAX + // command: calls the callback. + Drupal.ajax[options.propertyID].commands.editFieldRenderedWithoutTransformationFilters = function(ajax, response, status) { options.callback(response.data); // Delete the Drupal.ajax instance that called this very function. delete Drupal.ajax[options.propertyID]; - options.$editorElement.unbind('edit-internal.edit'); + options.$editorElement.off('edit-internal.edit'); }; - // This will ensure our scoped edit_field_form AJAX command gets called. + // This will ensure our scoped editFieldRenderedWithoutTransformationFilters + // AJAX command gets called. options.$editorElement.trigger('edit-internal.edit'); }; @@ -82,19 +84,19 @@ Drupal.edit.util.form = { load: function(options, callback) { // Create a Drupal.ajax instance to load the form. Drupal.ajax[options.propertyID] = new Drupal.ajax(options.propertyID, options.$editorElement, { - url: Drupal.edit.util.buildUrl(options.propertyID, Drupal.settings.edit.fieldFormURL), + url: Drupal.edit.util.buildUrl(options.propertyID, drupalSettings.edit.fieldFormURL), event: 'edit-internal.edit', submit: { nocssjs : options.nocssjs }, progress: { type : null } // No progress indicator. }); - // Implement a scoped edit_field_form AJAX command: calls the callback. - Drupal.ajax[options.propertyID].commands.edit_field_form = function(ajax, response, status) { + // Implement a scoped editFieldForm AJAX command: calls the callback. + Drupal.ajax[options.propertyID].commands.editFieldForm = function(ajax, response, status) { callback(response.data, ajax); // Delete the Drupal.ajax instance that called this very function. delete Drupal.ajax[options.propertyID]; - options.$editorElement.unbind('edit-internal.edit'); + options.$editorElement.off('edit-internal.edit'); }; - // This will ensure our scoped edit_field_form AJAX command gets called. + // This will ensure our scoped editFieldForm AJAX command gets called. options.$editorElement.trigger('edit-internal.edit'); }, @@ -133,8 +135,8 @@ Drupal.edit.util.form = { */ unajaxifySaving: function($submit) { delete Drupal.ajax[$submit.attr('id')]; - $submit.unbind('click.edit'); + $submit.off('click.edit'); } }; -})(jQuery, Drupal); +})(jQuery, Drupal, drupalSettings); diff --git a/js/viejs/EditService.js b/js/viejs/EditService.js index cde0e2e..5545463 100644 --- a/js/viejs/EditService.js +++ b/js/viejs/EditService.js @@ -2,7 +2,7 @@ * @file * VIE DOM parsing service for Edit. */ -(function(jQuery, _, Drupal, VIE) { +(function(jQuery, _, VIE, Drupal, drupalSettings) { "use strict"; @@ -30,7 +30,7 @@ if (typeof document === 'undefined') { return loadable.resolve([]); } else { - element = Drupal.settings.edit.context; + element = drupalSettings.edit.context; } } else { element = loadable.options.element; @@ -170,7 +170,7 @@ // editable fields. findSubjectElements: function (element) { if (!element) { - element = Drupal.settings.edit.context; + element = drupalSettings.edit.context; } return jQuery(this.options.subjectSelector, element); }, @@ -198,4 +198,4 @@ } }; -})(jQuery, _, Drupal, VIE); +})(jQuery, _, VIE, Drupal, drupalSettings); diff --git a/js/views/modal-view.js b/js/views/modal-view.js index 0f3c0aa..c5d91ab 100644 --- a/js/views/modal-view.js +++ b/js/views/modal-view.js @@ -85,7 +85,7 @@ Drupal.edit.views.ModalView = Backbone.View.extend({ var that = this; this.$el .addClass('edit-animate-invisible') - .bind(Drupal.edit.util.constants.transitionEnd, function(e) { + .on(Drupal.edit.util.constants.transitionEnd, function(e) { that.remove(); }); diff --git a/js/views/overlay-view.js b/js/views/overlay-view.js index e545087..e357014 100644 --- a/js/views/overlay-view.js +++ b/js/views/overlay-view.js @@ -73,7 +73,7 @@ Drupal.edit.views.OverlayView = Backbone.View.extend({ var that = this; this.$el .addClass('edit-animate-invisible') - .bind(Drupal.edit.util.constants.transitionEnd, function (event) { + .on(Drupal.edit.util.constants.transitionEnd, function (event) { that.$el.remove(); }); } diff --git a/js/views/toolbar-view.js b/js/views/toolbar-view.js index c31791a..4b8235c 100644 --- a/js/views/toolbar-view.js +++ b/js/views/toolbar-view.js @@ -384,7 +384,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ .removeAttr('id') .find('.edit-toolbar .edit-toolgroup') .addClass('edit-animate-invisible') - .bind(Drupal.edit.util.constants.transitionEnd, function (e) { + .on(Drupal.edit.util.constants.transitionEnd, function (e) { $el.remove(); }); }, diff --git a/lib/Drupal/edit/Ajax/BaseCommand.php b/lib/Drupal/edit/Ajax/BaseCommand.php new file mode 100644 index 0000000..32d325d --- /dev/null +++ b/lib/Drupal/edit/Ajax/BaseCommand.php @@ -0,0 +1,52 @@ +command = $command; + $this->data = $data; + } + + /** + * Implements Drupal\Core\Ajax\CommandInterface:render(). + */ + public function render() { + return array( + 'command' => $this->command, + 'data' => $this->data, + ); + } + +} diff --git a/lib/Drupal/edit/Ajax/FieldFormCommand.php b/lib/Drupal/edit/Ajax/FieldFormCommand.php new file mode 100644 index 0000000..76b01c5 --- /dev/null +++ b/lib/Drupal/edit/Ajax/FieldFormCommand.php @@ -0,0 +1,27 @@ +register('plugin.manager.edit.processed_text_editor', 'Drupal\edit\Plugin\Type\ProcessedTextEditorManager'); + } + +} diff --git a/lib/Drupal/edit/Plugin/ProcessedTextEditorBase.php b/lib/Drupal/edit/Plugin/ProcessedTextEditorBase.php new file mode 100644 index 0000000..41c0141 --- /dev/null +++ b/lib/Drupal/edit/Plugin/ProcessedTextEditorBase.php @@ -0,0 +1,35 @@ +discovery = new AnnotatedClassDiscovery('edit', 'processed_text_editor'); + $this->discovery = new AlterDecorator($this->discovery, 'edit_wysiwyg'); + $this->discovery = new CacheDecorator($this->discovery, 'edit:wysiwyg'); + $this->factory = new DefaultFactory($this->discovery); + } + +} diff --git a/lib/Drupal/edit/Tests/FieldEditorTest.php b/lib/Drupal/edit/Tests/FieldEditorTest.php new file mode 100644 index 0000000..0763c68 --- /dev/null +++ b/lib/Drupal/edit/Tests/FieldEditorTest.php @@ -0,0 +1,149 @@ + 'Field editor selection', + 'description' => 'Tests field editor selection.', + 'group' => 'Edit', + ); + } + + function setUp() { + parent::setUp(); + + $filtered_html_format = filter_format_load('filtered_html'); + $this->admin_user = $this->drupalCreateUser(array( + 'administer filters', + 'administer nodes', + 'bypass node access', + filter_permission_name($filtered_html_format), + )); + $this->drupalLogin($this->admin_user); + + // Add test text field. + $field_name = 'field_test_edit_text_test'; + $field = array( + 'field_name' => $field_name, + 'type' => 'text', + 'cardinality' => 1, + ); + field_create_field($field); + $instance = array( + 'entity_type' => 'node', + 'field_name' => $field_name, + 'bundle' => 'article', + 'label' => 'Test text-field', + 'widget' => array( + 'type' => 'text_textfield', + 'weight' => 0, + ), + 'default_value' => array('Default text'), + ); + field_create_instance($instance); + + // Add test number field. + $field_name = 'field_test_edit_number_test'; + $field = array( + 'field_name' => $field_name, + 'type' => 'number_decimal', + 'cardinality' => 1, + 'settings' => array( + 'precision' => 8, 'scale' => 4, 'decimal_separator' => '.', + ) + ); + field_create_field($field); + $instance = array( + 'field_name' => $field_name, + 'entity_type' => 'node', + 'label' => 'Test number field', + 'bundle' => 'article', + 'widget' => array( + 'type' => 'number', + ), + 'display' => array( + 'default' => array( + 'type' => 'number_decimal', + ), + ), + 'default_value' => array('12.4'), + ); + field_create_instance($instance); + + // Create an article node. + $this->node = $this->drupalCreateNode(array('type' => 'article')); + } + + function testContent() { + $default_edit = 'edit-field edit-allowed edit-type-form'; + $fields = array( + 'body' => $default_edit, + 'field_test_edit_number_test' => $default_edit, + // Edit fields are directly editable, see edit.patch. + 'field_test_edit_text_test' => 'edit-field edit-allowed edit-type-direct', + ); + + // Test editability with fields on the node. + foreach ($fields as $field_name => $assert) { + $field_view = field_view_field('node', $this->node, $field_name); + $content = drupal_render($field_view); + $this->drupalSetContent($content); + $this->assertRaw($assert); + } + + // Turn off filters that are incompatible with in place editing. + // Add p and br as allowed tags. + $edit = array( + 'filters[filter_url][status]' => FALSE, + 'filters[filter_autop][status]' => FALSE, + 'filters[filter_html][settings][allowed_html]' => '

    1. ', + ); + $this->drupalPost('admin/config/content/formats/filtered_html', $edit, t('Save configuration')); + + // @todo: this should not pass yet. + + // Test editability with fields on the node. + foreach ($fields as $field_name => $assert) { + $field_view = field_view_field('node', $this->node, $field_name); + $content = drupal_render($field_view); + $this->drupalSetContent($content); + $this->assertRaw($assert); + } + + } +} diff --git a/text.patch b/text.patch index 83dcd75..d60f5df 100644 --- a/text.patch +++ b/text.patch @@ -14,7 +14,7 @@ index 6b34ba9..85d2878 100644 * "text_with_summary" + * }, + * edit = { -+ * "editability" = "direct" ++ * "editor" = "direct" * } * ) */ @@ -28,7 +28,7 @@ index 0f7b615..8dc4bf1 100644 * "text_with_summary" + * }, + * edit = { -+ * "editability" = "direct" ++ * "editor" = "direct" * } * ) */ @@ -42,7 +42,7 @@ index 11f0c14..c69e5e0 100644 * "trim_length" = "600" + * }, + * edit = { -+ * "editability" = "form" ++ * "editor" = "form" * } * ) */ @@ -56,7 +56,7 @@ index 349cf63..add5d55 100644 * "trim_length" = "600" + * }, + * edit = { -+ * "editability" = "form" ++ * "editor" = "form" * } * ) */