From 1d402fc314a27733440f6a441b07bb86ed717daf Mon Sep 17 00:00:00 2001 From: Salvador Molina <inthorinrain@gmail.com> Date: Mon, 15 Apr 2019 18:40:13 +0200 Subject: [PATCH] First D8 version. Support for standard entity fields via config --- README.txt | 60 +--- config/schema/soft_length_limit.schema.yml | 10 + css/soft_length_limit.css | 49 +++ .../ico_socpub_alert.png | Bin .../ico_socpub_confirm.png | Bin .../jquery.textchange.min.js | 0 js/soft_length_limit.js | 141 ++++++++ soft_length_limit.css | 32 -- soft_length_limit.info | 4 - soft_length_limit.info.yml | 5 + soft_length_limit.install | 17 - soft_length_limit.js | 159 --------- soft_length_limit.libraries.yml | 10 + soft_length_limit.module | 315 ++++++------------ soft_length_limit.test | 87 ----- .../SoftLengthLimitJavascriptTest.php | 132 ++++++++ 16 files changed, 464 insertions(+), 557 deletions(-) create mode 100644 config/schema/soft_length_limit.schema.yml create mode 100644 css/soft_length_limit.css rename ico_socpub_alert.png => img/ico_socpub_alert.png (100%) rename ico_socpub_confirm.png => img/ico_socpub_confirm.png (100%) rename jquery.textchange.min.js => js/jquery.textchange.min.js (100%) create mode 100644 js/soft_length_limit.js delete mode 100644 soft_length_limit.css delete mode 100644 soft_length_limit.info create mode 100644 soft_length_limit.info.yml delete mode 100644 soft_length_limit.install delete mode 100644 soft_length_limit.js create mode 100644 soft_length_limit.libraries.yml delete mode 100644 soft_length_limit.test create mode 100644 tests/src/FunctionalJavascript/SoftLengthLimitJavascriptTest.php diff --git a/README.txt b/README.txt index 9430667..0681f78 100644 --- a/README.txt +++ b/README.txt @@ -1,55 +1,13 @@ Soft Length Limit -This module provides a counter that indicates the max. limit of -characters in a certain text field. The soft limit means that the user -will be warned if the content length of the field is exceeded, but he -will still be able to save all data in the field. +This module provides a counter that indicates the maximum recommended +characters in a certain text field. This means the user will be warned if the +content length of the field is exceeded, but will still be able to pass content +validation and be persisted in the relevant storage. +This module adds the following features: -It basically does two things: - -* Lets the user configure any instance of a text field's widget, to - have a specific soft limit. This is done in the usual field settings - for entity types. This counter will always be shown on fields with a - soft limit, no matter the settings mentioned below. - -* Adds a character countdown to form elements that already have a - maxlength specified. This functionality can be customized through - the options specified below. - - - --- Configuration of behavior of elements with "maxlength" attribute -- - -The following Drupal variables can be set to change the criteria for -which elements should have the limit counter: - - - -* (bool) soft_length_limit_maxlength_counter_disabled: - -If set to TRUE, no fields with maxlength attribute will have a counter. - - -* (bool) soft_length_limit_maxlength_counter_admin_only: - -If set to TRUE, the counter will only be shown when the admin_theme is -active. - - -* (Array) soft_length_limit_maxlength_counter_themes: - -This as an array of theme names for which the counter should be -shown. If empty, all themes show the counter. - - -* (Array) soft_limit_length_maxlength_exclude_selectors: - -This is an array of jQuery selectors (using classes, IDs etc.) for -elements which should be excluded from having a counter, although they -have a defined maxlength, i.e. date fields, autocomplete fields -etc. could be added here. - -* The icons for this came from Creative Commons Attribution 3.0 -Unported License, and they came from http://icomoon.io/#icons free -package. +* Lets the user configure any instance of a text field's widget, to have a + specific soft limit. This is done in the "Manage Form Display" page of the + relevant entity type. This counter will always be shown on fields with a soft + limit. diff --git a/config/schema/soft_length_limit.schema.yml b/config/schema/soft_length_limit.schema.yml new file mode 100644 index 0000000..73b547f --- /dev/null +++ b/config/schema/soft_length_limit.schema.yml @@ -0,0 +1,10 @@ +field.widget.third_party.soft_length_limit: + type: mapping + label: 'soft_length_limit settings' + mapping: + max_limit: + type: integer + minimum_limit: + type: integer + style_select: + type: boolean diff --git a/css/soft_length_limit.css b/css/soft_length_limit.css new file mode 100644 index 0000000..42c031e --- /dev/null +++ b/css/soft_length_limit.css @@ -0,0 +1,49 @@ +div.soft-length-limit-tooltip.description { + display: inline-block; + font-style: italic; + position: relative; + margin-top: 1em; + z-index: 10; +} + +.form-item div.soft-length-limit-tooltip.exceeded, +.form-item div.soft-length-limit-tooltip.under-min { + color: #a44; +} + +/* Min/Enhanced tooltip */ +.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip, +.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip, +.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip, +.form-item.form-type-textarea .soft-length-limit-tooltip.min-style-tooltip { + background-color: transparent; + background-image: url(../img/ico_socpub_confirm.png); + background-position: left top; + background-repeat: no-repeat; + background-size: 15px 15px; + left: auto !important; + padding: 0 0 0 20px; +} + +.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip, +.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip { + right: 10px; +} + +.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip { + margin-left: 5px; +} + +.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.under-min, +.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip.under-min, +.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.under-min, +.form-item.form-type-textarea .soft-length-limit-tooltip.min-style-tooltip.under-min { + background-image: url(../img/ico_socpub_alert.png); +} + +.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded, +.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip.exceeded, +.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded, +.form-item.form-type-textarea .soft-length-limit-tooltip.min-style-tooltip.exceeded { + background-image: url(../img/ico_socpub_alert.png); +} diff --git a/ico_socpub_alert.png b/img/ico_socpub_alert.png similarity index 100% rename from ico_socpub_alert.png rename to img/ico_socpub_alert.png diff --git a/ico_socpub_confirm.png b/img/ico_socpub_confirm.png similarity index 100% rename from ico_socpub_confirm.png rename to img/ico_socpub_confirm.png diff --git a/jquery.textchange.min.js b/js/jquery.textchange.min.js similarity index 100% rename from jquery.textchange.min.js rename to js/jquery.textchange.min.js diff --git a/js/soft_length_limit.js b/js/soft_length_limit.js new file mode 100644 index 0000000..32fb557 --- /dev/null +++ b/js/soft_length_limit.js @@ -0,0 +1,141 @@ +(function ($) { + + var sll = sll || {}; + + Drupal.behaviors.softLengthLimit = { + attach: function (context, settings) { + // Preparing the input elements by adding a tooltip container. + $('.soft-length-limit:not(.soft-length-limit-processed)').each(function(index) { + $(this).addClass('soft-length-limit-processed'); + + var $parent = $(this).parent(); + $parent.css('position','relative'); + $parent.append('<div class="soft-length-limit-tooltip description"></div>'); + $element = $(this); + }); + + var $softLengthElement = $('.soft-length-limit'); + + // Calculates the correct position of the tooltip and shows it. + $softLengthElement.focus(function(event){ + var $tooltip = $(this).parent().find('.soft-length-limit-tooltip'); + $(this).trigger('textchange', $(this).val()); + + $tooltip.fadeIn('fast'); + }); + + // Hides the tooltip. + $softLengthElement.blur(function(event){ + var $tooltip = $(this).parent().find('.soft-length-limit-tooltip'); + $tooltip.fadeOut('fast'); + }); + + // Shows the relevant info to the user in the tooltip. + $softLengthElement.bind('textchange', sll.refreshTooltip); + } + }; + + sll.refreshTooltip = function(event, prevText) { + let limit = $(this).attr('data-soft-length-limit'); + let minimum = $(this).attr('data-soft-length-minimum'); + let val = $(this).val(); + let remaining = limit - val.length; + let $tooltip = $(this).parent().find('.soft-length-limit-tooltip'); + let styleSelect = $(this).attr('data-soft-length-style-select'); + + // Removes the "exceeded" class if length is not exceeded. + if (prevText.length > limit && val.length <= limit) { + $tooltip.removeClass('exceeded'); + $(this).removeClass('exceeded'); + } + + // Adds the "exceeded" class if length is exceeded. + if (val.length > limit && !$tooltip.hasClass('exceeded')) { + $tooltip.addClass('exceeded'); + $(this).addClass('exceeded'); + } + + // Adds the "exceeded" class if the length is less than the minimum, in the case where the minimum is set. + // Adds the "under-min" class, for under-character-minimum specific behavior. Used for min/enhanced character count. + if (minimum > 0) { + if (val.length < minimum) { + $tooltip.addClass('under-min'); + $(this).addClass('under-min'); + } + if (val.length >= minimum) { + $tooltip.removeClass('under-min'); + $(this).removeClass('under-min'); + } + if (val.length > limit) { + $tooltip.addClass('exceeded'); + $(this).addClass('exceeded'); + } + if (val.length <= limit) { + $tooltip.removeClass('exceeded'); + $(this).removeClass('exceeded'); + } + } + + // The minimal / enhanced version of character limits. + if (styleSelect === '1') { + // No minimum treatment. + $tooltip.html(Drupal.t('@val/@limit', { + '@val': val.length, + '@limit': limit + })); + // Add class to tooltip for different CSS treatment (icons, text alignment, etc). + $tooltip.parent().children('.soft-length-limit-tooltip').addClass('min-style-tooltip'); + } + + // Original character limit treatment. + if ((styleSelect === '0') || (styleSelect === undefined)) { + // No minimum value is set. + if (minimum < 1) { + if (val.length === 0) { + $tooltip.html(Drupal.t('Content limited to @limit characters',{ + '@limit': limit + })); + } + else if (remaining < 0) { + $tooltip.html(Drupal.t('@limit character limit exceeded by @exceed characters.',{ + '@limit': limit, + '@exceed': -remaining + })); + } + else { + $tooltip.html(Drupal.t('Content limited to @limit characters. Remaining: @remaining',{ + '@limit': limit, + '@remaining': remaining + })); + } + } + // There is a minimum length set. + else { + if (val.length === 0) { + $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val. Content limited to @limit characters. ',{ + '@limit': limit, + '@minimum': minimum, + '@val': val.length + })); + } + else if (remaining < 0) { + $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val. @limit character limit exceeded by @exceed characters.',{ + '@limit': limit, + '@exceed': -remaining, + '@minimum': minimum, + '@val': val.length + })); + } + else { + $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val. Content limited to @limit characters. Remaining: @remaining.',{ + '@limit': limit, + '@remaining': remaining, + '@minimum': minimum, + '@val': val.length + })); + } + } + } + }; + +})(jQuery); diff --git a/soft_length_limit.css b/soft_length_limit.css deleted file mode 100644 index 8ba6229..0000000 --- a/soft_length_limit.css +++ /dev/null @@ -1,32 +0,0 @@ -.soft-length-limit-tooltip { - display: inline-block; - font-style: italic; - position: absolute; - z-index: 10; -} - -.form-item div.soft-length-limit-tooltip.exceeded { - color: #a44; -} - -/* Min/Enhanced tooltip */ -.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip, -.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip { - background-color: transparent; - background-image: url(ico_socpub_confirm.png); - background-position: left top; - background-repeat: no-repeat; - background-size: 15px 15px; - left: auto !important; - padding: 0 0 0 20px; - right: 10px; -} -.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded.under-min, -.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded.under-min { - background-image: none; -} - -.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded, -.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded { - background-image: url(ico_socpub_alert.png); -} diff --git a/soft_length_limit.info b/soft_length_limit.info deleted file mode 100644 index 4a3f814..0000000 --- a/soft_length_limit.info +++ /dev/null @@ -1,4 +0,0 @@ -name = Soft length limit -description = Makes a soft limit on the number of chars in text fields, which warns the user of too long texts -core = 7.x -files[] = soft_length_limit.test diff --git a/soft_length_limit.info.yml b/soft_length_limit.info.yml new file mode 100644 index 0000000..084c162 --- /dev/null +++ b/soft_length_limit.info.yml @@ -0,0 +1,5 @@ +name: Soft Length Limit +type: module +description: Soft limits number of chars in text fields, warning the user of too long texts +package: Fields +core: 8.x diff --git a/soft_length_limit.install b/soft_length_limit.install deleted file mode 100644 index 2bfdf33..0000000 --- a/soft_length_limit.install +++ /dev/null @@ -1,17 +0,0 @@ -<?php -/** - * @file - * Install/uninstall hooks for soft_length_limits module. - */ - -/** - * Implements hook_uninstall(). - * - * Deletes variables defined by the module. - */ -function soft_length_limit_uninstall() { - db_delete('variable') - ->condition('name', "soft_length_limit_%", "LIKE") - ->execute(); - cache_clear_all('variables', 'cache_bootstrap'); -} diff --git a/soft_length_limit.js b/soft_length_limit.js deleted file mode 100644 index 04efe0f..0000000 --- a/soft_length_limit.js +++ /dev/null @@ -1,159 +0,0 @@ -(function ($) { - Drupal.behaviors.softLengthLimit = { - attach: function (context, settings) { - - // Preparing the input elements by adding a tooltip container. - $('.soft-length-limit:not(.soft-length-limit-processed)').each(function(index){ - $(this).addClass('soft-length-limit-processed'); - - var $parent = $(this).parent(); - $parent.css('position','relative'); - $parent.append('<div class="soft-length-limit-tooltip description"></div>'); - $element = $(this); - - // Used for automatically moving the tooltip when resizing the - // text area. - $parent.find('.grippie').mousedown({ - element: $(this) - }, function(event){ - var endDrag = function(event) { - event.data.element.focus(); - $(document).unbind('mouseup',endDrag); - }; - $(document).mouseup(event.data,endDrag); - }); - }); - - // Calculates the correct position of the tooltip and shows it. - $('.soft-length-limit').focus(function(event){ - var $tooltip = $(this).parent().find('.soft-length-limit-tooltip'); - var left = $(this).position().left; - var top = $(this).position().top; - var bottom = top + $(this).outerHeight(true); - var right = left + $(this).outerWidth(true); - $(this).trigger('textchange', $(this).val()); - - if ($(this).get(0).tagName == "TEXTAREA") { - $tooltip.css('left', 10).css('top', bottom + 5); - } - else { - $tooltip.css('left', 10).css('top', bottom - 4); - } - - $tooltip.fadeIn('fast'); - }); - - // Hides the tooltip. - $('.soft-length-limit').blur(function(event){ - var $tooltip = $(this).parent().find('.soft-length-limit-tooltip'); - $tooltip.fadeOut('fast'); - }); - - // Shows the relevant info to the user in the tooltip. - $('.soft-length-limit').bind('textchange', function(event, prevText){ - var limit = $(this).attr('data-soft-length-limit'); - var minimum = $(this).attr('data-soft-length-minimum'); - var val = $(this).val(); - var remaining = limit - val.length; - var $tooltip = $(this).parent().find('.soft-length-limit-tooltip'); - var styleSelect = $(this).attr('data-soft-length-style-select'); - - // Removes the "exceeded" class if length is not exceeded - // anymore. - if (prevText.length > limit && val.length <= limit) { - $tooltip.removeClass('exceeded'); - $(this).removeClass('exceeded'); - } - - // Adds the "exceeded" class if length is exceeded. - if (val.length > limit && !$tooltip.hasClass('exceeded')) { - $tooltip.addClass('exceeded'); - $(this).addClass('exceeded'); - } - - // Adds the "exceeded" class if the length is less than the minimum, in the case where the minimum is set. - // Adds the "under-min" class, for under-character-minimum specific behavior. Used for min/enhanced character count. - if (minimum > 0) { - if (val.length < minimum) { - $tooltip.addClass('under-min'); - $(this).addClass('under-min'); - } - if (val.length >= minimum) { - $tooltip.removeClass('under-min'); - $(this).removeClass('under-min'); - } - if (val.length > limit) { - $tooltip.addClass('exceeded'); - $(this).addClass('exceeded'); - } - if (val.length <= limit) { - $tooltip.removeClass('exceeded'); - $(this).removeClass('exceeded'); - } - } - - // The minimal/enhanced version of character limits - if (styleSelect == '1') { - // No minimum treatment - $tooltip.html(Drupal.t('@val/@limit', { - '@val': val.length, - '@limit': limit - })); - // Add class to tooltip for different CSS treatment (icons, text alignment, etc ) - $tooltip.parent().children('.soft-length-limit-tooltip').addClass('min-style-tooltip'); - } - - // Original character limit treatment - if ((styleSelect == '0') || (styleSelect == undefined)) { - // No minimum value is set. - if (minimum < 1) { - if (val.length === 0) { - $tooltip.html(Drupal.t('Content limited to @limit characters',{ - '@limit': limit - })); - } - else if (remaining < 0) { - $tooltip.html(Drupal.t('@limit character limit exceeded by @exceed characters.',{ - '@limit': limit, - '@exceed': -remaining - })); - } - else { - $tooltip.html(Drupal.t('Content limited to @limit characters. Remaining: @remaining',{ - '@limit': limit, - '@remaining': remaining - })); - } - } - // There is a minimum length set. - else { - if (val.length === 0) { - $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val. Content limited to @limit characters. ',{ - '@limit': limit, - '@minimum': minimum, - '@val': val.length - })); - } - else if (remaining < 0) { - $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val. @limit character limit exceeded by @exceed characters.',{ - '@limit': limit, - '@exceed': -remaining, - '@minimum': minimum, - '@val': val.length - })); - } - else { - $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val. Content limited to @limit characters. Remaining: @remaining.',{ - '@limit': limit, - '@remaining': remaining, - '@minimum': minimum, - '@val': val.length - })); - } - } - } - }); - } - }; - -})(jQuery); diff --git a/soft_length_limit.libraries.yml b/soft_length_limit.libraries.yml new file mode 100644 index 0000000..34bfb4d --- /dev/null +++ b/soft_length_limit.libraries.yml @@ -0,0 +1,10 @@ +soft_length_limit: + js: + js/soft_length_limit.js: {} + js/jquery.textchange.min.js: {} + css: + component: + css/soft_length_limit.css: {} + dependencies: + - core/jquery + - core/jquery.once diff --git a/soft_length_limit.module b/soft_length_limit.module index 3b94784..8504b24 100644 --- a/soft_length_limit.module +++ b/soft_length_limit.module @@ -2,257 +2,158 @@ /** * @file - * Soft Length Limit module + * Soft Length Limit module. */ -define('SOFT_LENGTH_LIMIT_TITLE_MAX', 'soft_length_limit_title_max'); -define('SOFT_LENGTH_LIMIT_TITLE_MIN', 'soft_length_limit_title_min'); -define('SOFT_LENGTH_STYLE_SELECT', 'soft_length_style_select'); +declare(strict_types = 1); + +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\WidgetInterface; +use Drupal\Core\Form\FormStateInterface; /** - * Returns the field widget or form element types that should be affected. - * - * @param string $usage - * The desired usage of the data, can be one of 'fields' or 'elements' + * Returns the widget settings that can be used for a soft_length widget. * * @return array - * An array field widget or form element type names + * An array of settings and default values for each textfield type. */ -function _soft_length_limit_types($usage) { - $return = array(); - switch ($usage) { - case 'fields': - $return = array( - 'text_textarea' => 'text_textarea', - 'text_textfield' => 'text_textfield', - 'text_textarea_with_summary' => 'text_textarea_with_summary', - ); - break; - - case 'elements': - $return = array( - 'textarea' => 'textarea', - 'textfield' => 'textfield', - 'text_format' => 'text_format', - ); - break; - - case 'entity_types': - $return = array( - 'node' => 'node', - ); - break; - } - - return $return; +function _soft_length_widget_settings(string $plugin_id): array { + $settings = [ + 'string_textfield' => [ + 'max_limit' => TRUE, + 'minimum_limit' => TRUE, + 'style_select' => TRUE, + ], + 'string_textarea' => [ + 'max_limit' => TRUE, + 'minimum_limit' => TRUE, + 'style_select' => TRUE, + ], + 'text_textfield' => [ + 'max_limit' => TRUE, + 'minimum_limit' => TRUE, + 'style_select' => TRUE, + ], + 'text_textarea' => [ + 'max_limit' => TRUE, + 'minimum_limit' => TRUE, + 'style_select' => TRUE, + ], + 'text_textarea_with_summary' => [ + 'max_limit' => TRUE, + 'minimum_limit' => TRUE, + 'style_select' => TRUE, + ], + ]; + + return $settings[$plugin_id] ?? []; } /** - * Implements hook_form_FORM_ID_alter(). - * - * Adds soft length limit fields when editing a content type. + * Implements hook_field_widget_third_party_settings_form(). */ -function soft_length_limit_form_node_type_form_alter(&$form, &$form_state, $form_id) { - $form['submission'][SOFT_LENGTH_LIMIT_TITLE_MAX] = array( - '#type' => 'textfield', - '#title' => t('Soft length limit'), - '#default_value' => variable_get(SOFT_LENGTH_LIMIT_TITLE_MAX . '_' . $form['#node_type']->type, NULL), - '#description' => t('If any value is given here, a counter will appear next to this field, informing the user of the chosen number of allowed characters. If the number is exceeded, a warning will be shown.'), - '#element_validate' => array('element_validate_integer_positive'), - '#weight' => -2, - ); - - $form['submission'][SOFT_LENGTH_LIMIT_TITLE_MIN] = array( - '#type' => 'textfield', - '#title' => t('Soft length minimum'), - '#default_value' => variable_get(SOFT_LENGTH_LIMIT_TITLE_MIN . '_' . $form['#node_type']->type, NULL), - '#description' => t('If any value is given here, the minimum number recommended characters will be displayed as the editor enters text in this field.'), - '#element_validate' => array('element_validate_integer_positive'), - '#weight' => -1, - ); - - $form['submission'][SOFT_LENGTH_STYLE_SELECT] = array( - '#type' => 'checkbox', - '#title' => t('Enable enhanced view'), - '#default_value' => variable_get(SOFT_LENGTH_STYLE_SELECT . '_' . $form['#node_type']->type, NULL), - '#description' => t('Check this to enable an enhanced view of soft length states.'), - '#weight' => -1, - ); - - $form['submission']['title_label']['#weight'] = -3; - - // Add a custom submit handler to validate that the editor didn't set the - // maximum length to greater than 255 characters. - $form['#validate'][] = 'soft_length_limit_validate_title_length'; - - // Add a custom submit handler to save the title maximum and minimum. - $form['#submit'][] = 'soft_length_limit_set_title_maxlength'; -} - -/** - * Form validate handler for title length fields. - */ -function soft_length_limit_validate_title_length($form, &$form_state) { - if (($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX] < 0) || ($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX] > 255)) { - form_set_error('title_length', t('The value for soft length limit must be a whole number greater than zero and less than 256'), NULL); - } - - if (($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN] < 0) || ($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN] > 255)) { - form_set_error('soft_length_minimum', t('The value for soft length minimum must be a whole number greater than zero and less than 256')); - } - - if ($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX] < $form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN]) { - form_set_error('soft_length_minimum', t('The value for soft length minimum must be less than or equal to the soft length limit')); +function soft_length_limit_field_widget_third_party_settings_form(WidgetInterface $plugin, FieldDefinitionInterface $field_definition, $form_mode, $form, FormStateInterface $form_state) { + $plugin_id = $plugin->getPluginId(); + if (!$allowed_settings = _soft_length_widget_settings($plugin_id)) { + return NULL; } -} - -/** - * Save the values of max and minimum for title. - * - * So they can be applied later as an override to #maxlength. - */ -function soft_length_limit_set_title_maxlength($form, &$form_state) { - variable_set(SOFT_LENGTH_LIMIT_TITLE_MAX . '_' . $form_state['values']['type'], $form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX]); - variable_set(SOFT_LENGTH_LIMIT_TITLE_MIN . '_' . $form_state['values']['type'], $form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN]); - variable_set(SOFT_LENGTH_STYLE_SELECT . '_' . $form_state['values']['type'], $form_state['values'][SOFT_LENGTH_STYLE_SELECT]); -} -/** - * Implements hook_form_FORM_ID_alter(). - * - * Adds soft length limit fields when a field form field is rendered. - */ -function soft_length_limit_form_field_ui_field_edit_form_alter(&$form, &$form_state) { - $types = _soft_length_limit_types('fields'); - - if (isset($types[$form['#instance']['widget']['type']])) { - - $form['instance']['widget']['settings']['soft_length_limit'] = array( - '#type' => 'textfield', + $element = []; + if ($allowed_settings['max_limit']) { + $element['max_limit'] = [ + '#type' => 'number', + '#min' => 0, '#title' => t('Soft length limit'), - '#default_value' => isset($form['#instance']['widget']['settings']['soft_length_limit']) ? $form['#instance']['widget']['settings']['soft_length_limit'] : NULL, + '#default_value' => $plugin->getThirdPartySetting('soft_length_limit', 'max_limit'), '#description' => t('If any value is given here, a counter will appear next to this field, informing the user of the chosen number of allowed characters. If the number is exceeded, a warning will be shown.'), - '#element_validate' => array('element_validate_integer_positive'), '#weight' => -3, - ); + ]; + } - $form['instance']['widget']['settings']['soft_length_minimum'] = array( - '#type' => 'textfield', + if ($allowed_settings['minimum_limit']) { + $element['minimum_limit'] = [ + '#type' => 'number', + '#min' => 0, '#title' => t('Soft length minimum'), - '#default_value' => isset($form['#instance']['widget']['settings']['soft_length_minimum']) ? $form['#instance']['widget']['settings']['soft_length_minimum'] : NULL, + '#default_value' => $plugin->getThirdPartySetting('soft_length_limit', 'minimum_limit'), '#description' => t('If any value is given here, the minimum number recommended characters will be displayed as the editor enters text in this field.'), - '#element_validate' => array('element_validate_integer_positive'), '#weight' => -2, - ); + ]; + } - $form['instance']['widget']['settings']['soft_length_style_select'] = array( + if ($allowed_settings['style_select']) { + $element['style_select'] = [ '#type' => 'checkbox', '#title' => t('Enable enhanced view'), - '#default_value' => isset($form['#instance']['widget']['settings']['soft_length_style_select']) ? $form['#instance']['widget']['settings']['soft_length_style_select'] : 0, + '#default_value' => $plugin->getThirdPartySetting('soft_length_limit', 'style_select'), '#description' => t('Check this to enable an enhanced view of soft length states.'), '#weight' => -1, - ); + ]; } + + return $element; } /** - * Implements hook_field_attach_form(). + * Implements hook_field_widget_settings_summary_alter(). */ -function soft_length_limit_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { - $entity_types = _soft_length_limit_types('entity_types'); +function soft_length_limit_field_widget_settings_summary_alter(&$summary, $context) { + /* @var \Drupal\Core\Field\WidgetInterface $widget */ + $widget = $context['widget']; + $plugin_id = $widget->getPluginId(); - if (!isset($entity_types[$entity_type])) { - return; + if (!$allowed_settings = _soft_length_widget_settings($plugin_id)) { + return NULL; } - $fields = field_info_instances($entity_type, $form['#bundle']); - $elements = array(); - - foreach ($fields as $key => $value) { - if (isset($value['widget']['settings']['soft_length_limit']) && $value['widget']['settings']['soft_length_limit'] > 0) { - $elements[$key] = $value; - } + $max_limit = $allowed_settings['max_limit'] + ? $widget->getThirdPartySetting('soft_length_limit', 'max_limit') + : FALSE; + $minimum_limit = $allowed_settings['minimum_limit'] + ? $widget->getThirdPartySetting('soft_length_limit', 'minimum_limit') + : FALSE; + $style_select = $allowed_settings['minimum_limit'] + ? $widget->getThirdPartySetting('soft_length_limit', 'style_select') + : FALSE; + + if ($max_limit) { + $summary[] = t('Maximum recommended length: @count', ['@count' => $max_limit]); } - - if (count($elements) || isset($form['title'])) { - soft_length_limit_set_attr($form, $elements); - // Adds the javascript and CSS files as form attachments, in case the init - // hook does not add them due to the context or settings. - $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/jquery.textchange.min.js'; - $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.js'; - $form['#attached']['css'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.css'; + if ($minimum_limit) { + $summary[] = t('Minimum recommended length: @count', ['@count' => $minimum_limit]); + } + if ($style_select) { + $summary[] = t('Style select: @style', ['@style' => $style_select ? 'Enabled' : 'Disabled']); } } /** - * Recurse through form and set variables. - * - * Recursive helper function that sets the correct attributes for the form - * elements with a specific soft limit specified, and continues through child - * elements. - * - * @param array $element - * The form element to iterate through - * - * @param array $sub_elements - * Array of the elements which should have a soft limit attribute + * Implements hook_field_widget_form_alter(). */ -function soft_length_limit_set_attr(&$element, $sub_elements) { - $children = element_get_visible_children($element); - $types = _soft_length_limit_types('elements'); - - foreach ($children as $value) { - if (isset($element[$value]['#type']) && isset($types[$element[$value]['#type']])) { - if (isset($element[$value]['#field_name']) && isset($sub_elements[$element[$value]['#field_name']])) { - - $widget_settings = isset($sub_elements[$element[$value]['#field_name']]['widget']['settings']) ? $sub_elements[$element[$value]['#field_name']]['widget']['settings'] : FALSE; +function soft_length_limit_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) { + $third_party_settings = $context['widget']->getThirdPartySettings(); - if ($widget_settings) { - // Soft limit length. - $soft_limit = $widget_settings['soft_length_limit']; - $element[$value]['#soft_length_limit'] = (isset($element[$value]['#maxlength']) && $soft_limit > $element[$value]['#maxlength']) ? $element[$value]['#maxlength'] : $soft_limit; - $element[$value]['#attributes']['data-soft-length-limit'] = $soft_limit; - $element[$value]['#attributes']['class'][] = 'soft-length-limit'; - - // Soft minimum. - $soft_min = isset($widget_settings['soft_length_minimum']) ? $widget_settings['soft_length_minimum'] : ''; - $element[$value]['#attributes']['data-soft-length-minimum'] = $soft_min; + if (empty($third_party_settings['soft_length_limit'])) { + return NULL; + } - // Length style select. - if (isset($widget_settings['soft_length_style_select'])) { - $element[$value]['#attributes']['data-soft-length-style-select'] = $widget_settings['soft_length_style_select']; - } - } - } - } + $sll_config = $third_party_settings['soft_length_limit']; - soft_length_limit_set_attr($element[$value], $sub_elements); + if (isset($sll_config['max_limit'])) { + $element['value']['#attributes']['data-soft-length-limit'] = $sll_config['max_limit']; + $element['value']['#attributes']['class'][] = 'soft-length-limit'; } -} - -/** - * Implements hook_form_FORM_ID_alter(). - * - * Adds CSS and JS to display soft_length_limits when they are set for the - * title field. - */ -function soft_length_limit_form_node_form_alter(&$form, $form_state) { - $type = $form['#node']->type; - $max_length = variable_get(SOFT_LENGTH_LIMIT_TITLE_MAX . '_' . $type, NULL); - $min_length = variable_get(SOFT_LENGTH_LIMIT_TITLE_MIN . '_' . $type, NULL); - if (!$min_length) { - $min_length = 0; + if (isset($sll_config['minimum_limit'])) { + $element['value']['#attributes']['data-soft-length-minimum'] = $sll_config['minimum_limit']; + } + // Length style select. + if (isset($sll_config['style_select']) && $sll_config['style_select']) { + $element['value']['#attributes']['data-soft-length-style-select'] = (int) $sll_config['style_select']; } - $style_select = variable_get(SOFT_LENGTH_STYLE_SELECT . '_' . $type, NULL); - // Play nice with title module ensuring that core's title is present. - if (isset($form['title']) && (!empty($max_length) || !empty($min_length))) { - $form['title']['#attributes']['class'][] = 'soft-length-limit'; - $form['title']['#attributes']['data-soft-length-limit'] = $max_length; - $form['title']['#attributes']['data-soft-length-minimum'] = $min_length; - $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/jquery.textchange.min.js'; - $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.js'; - $form['#attached']['css'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.css'; - $form['title']['#attributes']['data-soft-length-style-select'] = $style_select; + if (isset($element['#type']) && $element['#type'] === 'text_format') { + $element['#attributes'] = array_merge_recursive($element['#attributes'], $element['value']['#attributes']); } + + $element['#attached']['library'][] = 'soft_length_limit/soft_length_limit'; } diff --git a/soft_length_limit.test b/soft_length_limit.test deleted file mode 100644 index 43ae08a..0000000 --- a/soft_length_limit.test +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -/** - * @file - * Test case for soft_length_limit module - */ - -/** - * Tests the relevant functionality provided by the soft_length_limit module. - */ -class SoftLengthLimitWebTestCase extends DrupalWebTestCase { - - /** - * Info for simpletest.module. - */ - public static function getInfo() { - return array( - 'name' => 'Soft Length Limit', - 'description' => 'Ensure that the soft limit is applied to the chosen fields', - 'group' => 'Soft Length Limit', - ); - } - - /** - * Setup for individual tests. - */ - public function setUp() { - // Enable any modules required for the test. - parent::setUp(array('soft_length_limit')); - - // Creating the needed user. - $this->privileged_user = $this->drupalCreateUser( - array( - 'bypass node access', - 'administer nodes', - 'administer taxonomy', - 'edit terms in 1', - )); - $this->drupalLogin($this->privileged_user); - - // Creating the needed field on the article node type. - $field = array( - 'field_name' => 'field_test', - 'type' => 'text_long', - 'cardinality' => 1, - 'settings' => array(), - ); - field_create_field($field); - - $instance = array( - 'field_name' => 'field_test', - 'entity_type' => 'node', - 'label' => 'Test field', - 'required' => 0, - 'bundle' => 'article', - 'description' => '', - 'widget' => array( - 'type' => 'text_textarea', - 'weight' => -4, - 'settings' => array( - 'rows' => '5', - 'soft_length_limit' => '666', - ), - ), - 'display' => array( - 'default' => array( - 'type' => 'text_default', - 'weight' => 10, - ), - 'teaser' => array( - 'type' => 'text_default', - 'weight' => 10, - ), - ), - ); - field_create_instance($instance); - - $this->drupalGet('node/add/article'); - } - - /** - * Tests that input element has the attributes that the javascript should use. - */ - public function testAll() { - $this->assertPattern('/<textarea .*class="[^"]*soft-length-limit[^"]*".*data-soft-length-limit="666"/', 'HTML element should contain the necessary attributesfor the soft length limiter javascript'); - } -} diff --git a/tests/src/FunctionalJavascript/SoftLengthLimitJavascriptTest.php b/tests/src/FunctionalJavascript/SoftLengthLimitJavascriptTest.php new file mode 100644 index 0000000..2923576 --- /dev/null +++ b/tests/src/FunctionalJavascript/SoftLengthLimitJavascriptTest.php @@ -0,0 +1,132 @@ +<?php + +namespace Drupal\Tests\soft_length_limit\FunctionalJavascript; + +use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\Core\Session\AccountInterface; +use Drupal\entity_test\Entity\EntityTest; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\FunctionalJavascriptTests\WebDriverTestBase; + +/** + * Tests soft length limit JavaScript behavior for text fields. + */ +class SoftLengthLimitJavascriptTest extends WebDriverTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'node', + 'field', + 'entity_test', + 'soft_length_limit', + 'text', + ]; + + /** + * Tests that the soft length limit widget works. + */ + public function testSoftLengthLimitWithStyleSelect() { + $this->setupContentType(); + $entity = EntityTest::create(['type' => 'entity_test', 'name' => 'Test Soft Length Limit.']); + $entity->save(); + + $admin = $this->drupalCreateUser(['administer entity_test content']); + $this->drupalLogin($admin); + $this->drupalGet($entity->toUrl('edit-form')); + + $page = $this->getSession()->getPage(); + $foo_field = $page->findField('Foo'); + $foo_field->focus(); + $foo_field->setValue('text'); + $tooltip_text = $page->find('css', '.soft-length-limit-tooltip')->getText(); + + $foo_field->focus(); + $this->assertEquals('4/20', $tooltip_text); + } + + /** + * Tests that the soft length limit widget works. + */ + public function testSoftLengthLimitWithoutStyleSelect() { + $this->setupContentType(FALSE); + $entity = EntityTest::create(['type' => 'entity_test', 'name' => 'Test Soft Length Limit.']); + $entity->save(); + + $admin = $this->drupalCreateUser(['administer entity_test content']); + $this->drupalLogin($admin); + $this->drupalGet($entity->toUrl('edit-form')); + + $page = $this->getSession()->getPage(); + $foo_field = $page->findField('Foo'); + $foo_field->focus(); + $foo_field->setValue('text'); + $tooltip_text = $page->find('css', '.soft-length-limit-tooltip')->getText(); + + $foo_field->focus(); + $this->assertEquals('Suggested minimum number of characters is 10, current count is 4. Content limited to 20 characters. Remaining: 16.', + $tooltip_text); + } + + /** + * {@inheritdoc} + */ + protected function drupalLogin(AccountInterface $account) { + // This is a clone of the UiHelperTrait::drupalLogin() method, + // which seems to make the test fail no matter what, when asserting the + // user session. + if ($this->loggedInUser) { + $this->drupalLogout(); + } + + $this->drupalGet('user/login'); + $this->submitForm([ + 'name' => $account->getAccountName(), + 'pass' => $account->passRaw, + ], t('Log in')); + + // @see ::drupalUserIsLoggedIn() + $account->sessionId = $this->getSession()->getCookie(\Drupal::service('session_configuration')->getOptions(\Drupal::request())['name']); + $this->loggedInUser = $account; + $this->container->get('current_user')->setAccount($account); + } + + /** + * Sets up the entity_test entity type with a field using soft_length_limit. + * + * @param bool $style_select + * Whether to use style_select option of soft_length_limit. + * + * @throws \Drupal\Core\Entity\EntityStorageException + */ + private function setupContentType($style_select = TRUE): void { + FieldStorageConfig::create([ + 'type' => 'text_long', + 'entity_type' => 'entity_test', + 'field_name' => 'foo', + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + 'field_name' => 'foo', + 'label' => 'Foo', + 'description' => 'Description of a text field', + ])->save(); + $widget = [ + 'type' => 'text_textarea', + 'third_party_settings' => [ + 'soft_length_limit' => [ + 'max_limit' => 20, + 'minimum_limit' => 10, + 'style_select' => $style_select, + ], + ], + ]; + EntityFormDisplay::load('entity_test.entity_test.default') + ->setComponent('foo', $widget) + ->save(); + } + +} -- GitLab