From 0fb06bf0acec704ce75964267a7ae5330a99a17c Mon Sep 17 00:00:00 2001 From: zemiacsik Date: Wed, 24 Oct 2018 03:03:55 +0200 Subject: [PATCH] latest ss3 version with php 7.2 support (#98) --- .editorconfig | 17 ++ .gitignore | 1 + .scrutinizer.yml | 69 +++++++ README.md | 1 + _config.php | 6 + _config/display_logic.yml | 8 +- code-of-conduct.md | 1 + code/DisplayLogicCriteria.php | 91 +++++++-- code/DisplayLogicCriterion.php | 14 +- code/DisplayLogicFormField.php | 17 +- composer.json | 13 +- css/display_logic.css | 2 +- javascript/display_logic.js | 185 +++++++++++++----- templates/{ => forms}/CheckboxSetField.ss | 0 .../{ => forms}/DisplayLogicWrapper_holder.ss | 2 +- templates/forms/FieldGroup.ss | 7 + templates/forms/FieldGroup_holder.ss | 14 ++ templates/{ => forms}/OptionsetField.ss | 2 +- 18 files changed, 376 insertions(+), 74 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .scrutinizer.yml create mode 100644 code-of-conduct.md rename templates/{ => forms}/CheckboxSetField.ss (100%) rename templates/{ => forms}/DisplayLogicWrapper_holder.ss (77%) create mode 100644 templates/forms/FieldGroup.ss create mode 100644 templates/forms/FieldGroup_holder.ss rename templates/{ => forms}/OptionsetField.ss (62%) diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..47ae637 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# For more information about the properties used in this file, +# please see the EditorConfig documentation: +# http://editorconfig.org + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[{*.yml,package.json}] +indent_size = 2 + +# The indent size used in the package.json file cannot be changed: +# https://github.com/npm/npm/pull/3180#issuecomment-16336516 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..d1ebd80 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,69 @@ +inherit: true + +checks: + php: + verify_property_names: true + verify_argument_usable_as_reference: true + verify_access_scope_valid: true + useless_calls: true + use_statement_alias_conflict: true + variable_existence: true + unused_variables: true + unused_properties: true + unused_parameters: true + unused_methods: true + unreachable_code: true + too_many_arguments: true + sql_injection_vulnerabilities: true + simplify_boolean_return: true + side_effects_or_types: true + security_vulnerabilities: true + return_doc_comments: true + return_doc_comment_if_not_inferrable: true + require_scope_for_properties: true + require_scope_for_methods: true + require_php_tag_first: true + psr2_switch_declaration: true + psr2_class_declaration: true + property_assignments: true + prefer_while_loop_over_for_loop: true + precedence_mistakes: true + precedence_in_conditions: true + phpunit_assertions: true + php5_style_constructor: true + parse_doc_comments: true + parameter_non_unique: true + parameter_doc_comments: true + param_doc_comment_if_not_inferrable: true + optional_parameters_at_the_end: true + one_class_per_file: true + no_unnecessary_if: true + no_trailing_whitespace: true + no_property_on_interface: true + no_non_implemented_abstract_methods: true + no_error_suppression: true + no_duplicate_arguments: true + no_commented_out_code: true + newline_at_end_of_file: true + missing_arguments: true + method_calls_on_non_object: true + instanceof_class_exists: true + foreach_traversable: true + fix_line_ending: true + fix_doc_comments: true + duplication: true + deprecated_code_usage: true + deadlock_detection_in_loops: true + code_rating: true + closure_use_not_conflicting: true + catch_class_exists: true + blank_line_after_namespace_declaration: false + avoid_multiple_statements_on_same_line: true + avoid_duplicate_types: true + avoid_conflicting_incrementers: true + avoid_closing_tag: true + assignment_of_null_return: true + argument_type_checks: true + +filter: + paths: [code/*, tests/*] diff --git a/README.md b/README.md index eb3cbca..53bfeca 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ $shipping->displayIf("ProductType")->isEqualTo("furniture") - isNotEmpty - isBetween - isChecked + - isNotChecked() - hasCheckedOption - hasCheckedAtLeast - hasCheckedLessThan diff --git a/_config.php b/_config.php index cbd85c9..0b52a72 100644 --- a/_config.php +++ b/_config.php @@ -1,3 +1,9 @@ get(__CLASS__, 'animations'))) { + Config::inst()->update(__CLASS__, 'default_animation', $animation); + } + } + + /** * Constructor @@ -74,9 +93,9 @@ public function __construct(FormField $slave, $master, $parent = null) { * @param array $args The arguments * @return DisplayLogicCriteria */ - public function __call($method, $args) { - if(in_array($method, $this->config()->comparisons)) { - $val = isset($args[0]) ? $args[0] : null; + public function __call($method, $args) { + if(in_array($method, $this->config()->comparisons)) { + $val = isset($args[0]) ? $args[0] : null; if(substr($method, 0, 2) == "is") { $operator = substr($method, 2); } @@ -86,7 +105,7 @@ public function __call($method, $args) { $this->addCriterion(DisplayLogicCriterion::create($this->master, $operator, $val, $this)); return $this; - } + } return parent::__call($method, $args); } @@ -99,7 +118,7 @@ public function __call($method, $args) { * @param int $max The maxiumum value * @return DisplayLogicCriteria */ - public function isBetween($min, $max) { + public function isBetween($min, $max) { $this->addCriterion(DisplayLogicCriterion::create($this->master, "Between", "{$min}-{$max}", $this)); return $this; } @@ -142,9 +161,9 @@ public function orIf($master = null) { /** * Adds a new criterion - * @param DisplayLogicCriterion $c + * @param DisplayLogicCriterion|DisplayLogicCriteria $c */ - public function addCriterion(DisplayLogicCriterion $c) { + public function addCriterion($c) { $this->criteria[] = $c; } @@ -168,7 +187,27 @@ public function getLogicalOperator() { return $this->logicalOperator == "or" ? "||" : "&&"; } + /** + * Accessor for the master field + * @return string + */ + public function getMaster() { + return $this->master; + } + /** + * @return $this + */ + public function setMaster($fieldName) { + $this->master = $fieldName; + $criteria = $this->getCriteria(); + if ($criteria) { + foreach ($criteria as $criterion) { + $criterion->setMaster($fieldName); + } + } + return $this; + } /** * Creates a nested {@link DisplayLogicCriteria} @@ -180,6 +219,35 @@ public function group() { + /** + * + * Defines the animation method to use + * @param string $animation + * @return DisplayLogicCriteria + */ + public function useAnimation($animation) { + if(in_array($animation, $this->config()->animations)) { + $this->animation = $animation; + } + return $this; + } + + + + + /** + * Answers the animation method to use + * @return string + */ + public function getAnimation() { + if(!$this->animation) { + return $this->config()->default_animation; + } + return $this->animation; + } + + + /** * Ends the chaining and returns the parent object, either {@link DisplayLogicCriteria} or {@link FormField} @@ -188,6 +256,7 @@ public function group() { public function end() { if($this->parent) { $this->parent->addCriterion($this); + return $this->parent; } return $this->slave; } @@ -196,7 +265,7 @@ public function end() { /** - * Creates a JavaScript readable representation of the logic + * Creates a JavaScript readable representation of the logic * @return string */ public function toScript() { @@ -206,7 +275,7 @@ public function toScript() { $script .= $first ? "" : " {$this->getLogicalOperator()} "; $script .= $c->toScript(); $first = false; - } + } $script .= ")"; return $script; } @@ -222,7 +291,7 @@ public function getMasterList() { $list = array (); foreach($this->getCriteria() as $c) { if($c instanceof DisplayLogicCriteria) { - $list += $c->getMasterList(); + $list=array_merge($list, $c->getMasterList()); } else { $list[] = $c->getMaster(); @@ -236,4 +305,4 @@ public function getMasterList() { -} \ No newline at end of file +} diff --git a/code/DisplayLogicCriterion.php b/code/DisplayLogicCriterion.php index 426559f..da2be90 100644 --- a/code/DisplayLogicCriterion.php +++ b/code/DisplayLogicCriterion.php @@ -70,19 +70,25 @@ public function getMaster() { return $this->master; } - + /** + * @return $this + */ + public function setMaster($fieldName) { + $this->master = $fieldName; + return $this; + } /** * Creates a JavaScript-readable representation of this criterion * @return string */ - public function toScript() { + public function toScript() { return sprintf( - "this.closest('form').find(this.escapeSelector('#%s')).evaluate%s('%s')", + "this.findHolder('%s').evaluate%s('%s')", $this->master, $this->operator, addslashes($this->value) ); } -} \ No newline at end of file +} diff --git a/code/DisplayLogicFormField.php b/code/DisplayLogicFormField.php index 0f9f9fd..992c2fb 100644 --- a/code/DisplayLogicFormField.php +++ b/code/DisplayLogicFormField.php @@ -89,7 +89,9 @@ public function setDisplayLogicCriteria(DisplayLogicCriteria $c) { $this->displayLogicCriteria = $c; } - + public function getDisplayLogicCriteria() { + return $this->displayLogicCriteria; + } /** @@ -104,6 +106,18 @@ public function DisplayLogicMasters() { } + /** + * Answers the animation method to use from the criteria object + * + * @return string + */ + public function DisplayLogicAnimation() { + if($this->displayLogicCriteria) { + return $this->displayLogicCriteria->getAnimation(); + } + } + + /** * Loads the dependencies and renders the JavaScript-readable logic to the form HTML * @@ -126,6 +140,7 @@ public function onBeforeRender($field) { if($logic = $field->DisplayLogic()) { $field->setAttribute('data-display-logic-masters', $field->DisplayLogicMasters()); $field->setAttribute('data-display-logic-eval', $logic); + $field->setAttribute('data-display-logic-animation', $field->DisplayLogicAnimation()); } } diff --git a/composer.json b/composer.json index 4f3fb58..5c9110b 100644 --- a/composer.json +++ b/composer.json @@ -1,23 +1,22 @@ { "name": "unclecheese/display-logic", "replace": { - "silverstripe/display-logic": "*" + "silverstripe/display-logic": "*" }, "description": "Allows assignment of conditions for display and hide of specific form fields based on client side behavior.", "type": "silverstripe-module", "keywords": ["silverstripe", "display", "logic", "forms", "cms"], - "license": "BSD-3-Clause", + "license": "BSD-3-Clause", "authors": [ { "name": "Uncle Cheese", "email": "unclecheese@leftandmain.com" } ], - "require": - { - "silverstripe/framework": "~3.7" + "require": { + "silverstripe/framework": "^3.1" }, "extra": { - "installer-name": "display_logic" - } + "installer-name": "display_logic" + } } diff --git a/css/display_logic.css b/css/display_logic.css index bbf85d0..8ba75a7 100644 --- a/css/display_logic.css +++ b/css/display_logic.css @@ -1 +1 @@ -div.display-logic-hidden {display:none;} +div.field.display-logic-hidden, div.displaylogicwrapper.display-logic-hidden {display:none;} \ No newline at end of file diff --git a/javascript/display_logic.js b/javascript/display_logic.js index bcdeb04..ea28dae 100644 --- a/javascript/display_logic.js +++ b/javascript/display_logic.js @@ -1,10 +1,14 @@ (function($) { -//$.entwine('ss', function($) { - $('div.display-logic, div.display-logic-master').entwine({ escapeSelector: function(selector) { - return selector.replace(/(\[|])/g, '\\$1'); + return selector.replace(/(\[)/g, '_').replace(/(\])/g, ''); + }, + + findHolder: function(name) { + return this.closest('form').find( + this.escapeSelector('#'+this.nameToHolder(name)) + ); }, getFormField: function() { @@ -12,19 +16,48 @@ if (name) { name = this.escapeSelector(name); } - return this.find('[name='+name+']'); + + if(this.find('[name='+name+']').length) { + return this.find('[name='+name+']'); + } + + return this.find('#'+this.getFormID()+'_'+name); }, getFieldName: function() { - return this.attr('id'); + var fieldID = this.attr('id'); + + if(fieldID) { + return this.attr('id') + .replace(new RegExp('^'+this.getFormID()+'_'),'') + .replace(/_Holder$/,''); + } }, + nameToHolder: function (name) { + name = this.escapeSelector(name); + + // SS 3.2+, Convert::raw2htmlid() logic + name = name.replace(/[^a-zA-Z0-9\-_:.]+/g, '_').replace(/_+/g, '_'); + + // Hack! + // Remove this when OptionsetField_holder.ss uses $HolderID + // as its div ID instead of $ID + if(this.closest('form').find('ul.optionset li input[name='+name+']:first').length) { + return name; + } + return this.getFormID()+'_'+name+'_Holder'; + }, + + getFormID: function () { + return this.closest('form').attr('id'); + }, getFieldValue: function() { return this.getFormField().val(); }, - evaluateEqualTo: function(val) { + evaluateEqualTo: function(val) { return this.getFieldValue() === val; }, @@ -33,13 +66,14 @@ }, evaluateLessThan: function(val) { - num = parseFloat(val); + var num = parseFloat(val); + return this.getFieldValue() < num; }, evaluateGreaterThan: function(val) { - num = parseFloat(val); - + var num = parseFloat(val); + return parseFloat(this.getFieldValue()) > num; }, @@ -71,7 +105,7 @@ evaluateBetween: function(minmax) { v = parseFloat(this.getFieldValue()); parts = minmax.split("-"); - if(parts.length === 2) { + if(parts.length === 2) { return v > parseFloat(parts[0]) && v < parseFloat(parts[1]); } return false; @@ -81,21 +115,28 @@ return this.getFormField().is(":checked"); }, + evaluateNotChecked: function() { + return !this.getFormField().is(":checked"); + }, + onmatch: function () { - + var allReadonly = true; var masters = []; var field = this.getFormField(); if(field.data('display-logic-eval') && field.data('display-logic-masters')) { this.data('display-logic-eval', field.data('display-logic-eval')) - .data('display-logic-masters', field.data('display-logic-masters')); + .data('display-logic-masters', field.data('display-logic-masters')) + .data('display-logic-animation', field.data('display-logic-animation')); } + masters = this.getMasters(); + + for(var m in masters) { + var holderName = this.nameToHolder(this.escapeSelector(masters[m])); + var master = this.closest('form').find(this.escapeSelector('#'+holderName)); - masters = this.getMasters(); - for(m in masters) { - var master = this.closest('form').find(this.escapeSelector('#'+masters[m])); if(!master.is('.readonly')) allReadonly = false; master.addClass("display-logic-master"); @@ -108,7 +149,7 @@ } // If all the masters are readonly fields, the field has no way of displaying. - if(masters.length && allReadonly) { + if(masters.length && allReadonly) { this.show(); } }, @@ -118,15 +159,15 @@ }, parseLogic: function() { - var js = this.getLogic(); - var result = new Function("return " + js).bind(this)(); + var js = this.getLogic(); + var result = new Function("return " + js).bind(this)(); return result; }, getMasters: function() { - var masters = this.data('display-logic-masters'); + var masters = this.getFormField().data('display-logic-masters'); return (masters) ? masters.split(",") : []; } @@ -137,7 +178,7 @@ $('div.optionset').entwine({ getFormField: function() { - f = this._super().filter(":checked"); + f = this._super().filter(":checked"); return f; } @@ -148,19 +189,19 @@ evaluateHasCheckedOption: function(val) { var found = false; - this.find(':checkbox').filter(':checked').each(function() { + this.find(':checkbox').filter(':checked').each(function() { found = (found || ($(this).val() === val || $(this).getLabel().text() === val)); - }) + }); return found; }, - evaluateHasCheckedAtLeast: function(num) { + evaluateHasCheckedAtLeast: function(num) { return this.find(':checked').length >= num; }, evaluateHasCheckedLessThan: function(num) { - return this.find(':checked').length <= num; + return this.find(':checked').length <= num; } }); @@ -170,20 +211,68 @@ getLabel: function() { return this.closest('form').find('label[for='+this.getHolder().escapeSelector(this.attr('id'))+']'); } - }) + }); + var animation = { + + toggle: { + + show: function(el) { + el.show(); + }, + + hide: function(el) { + el.hide(); + } + + }, + + slide: { + + show: function(el) { + el.slideDown(); + }, + + hide: function(el) { + el.slideUp(); + } + + }, + + fade: { + + show: function(el) { + el.fadeIn(); + }, + + hide: function(el) { + el.fadeOut(); + } + + }, + + perform: function(el, result, method) { + if(typeof method == 'undefined') method = 'toggle'; + if(result) { + this[method].show(el); + } else { + this[method].hide(el); + } + } + + }; $('div.display-logic.display-logic-display').entwine({ - testLogic: function() { - this.toggle(this.parseLogic()); + testLogic: function() { + animation.perform(this, this.parseLogic(), this.data('display-logic-animation')); } }); $('div.display-logic.display-logic-hide').entwine({ testLogic: function() { - this.toggle(!this.parseLogic()); + animation.perform(this, !this.parseLogic(), this.data('display-logic-animation')); } }); @@ -193,18 +282,18 @@ this.closest(".display-logic-master").notify(); }, - onchange: function() { + onchange: function() { this.closest(".display-logic-master").notify(); } }); - + $('div.display-logic-master :checkbox, div.display-logic-master :radio').entwine({ - onmatch: function() { + onmatch: function() { this.closest(".display-logic-master").notify(); }, - onclick: function() { + onclick: function() { this.closest(".display-logic-master").notify(); } }); @@ -213,27 +302,28 @@ getFieldValue: function () { return this.find(':checked').val(); } - }); + }); $('div.display-logic-master').entwine({ Listeners: null, - notify: function() { - $.each(this.getListeners(), function() { + notify: function() { + $.each(this.getListeners(), function() { $(this).testLogic(); }); }, getListeners: function() { - if(l = this._super()) { + l = this._super(); + if(l) { return l; } var self = this; var listeners = []; this.closest("form").find('.display-logic').each(function() { masters = $(this).getMasters(); - for(m in masters) { - if(masters[m] == self.attr('id')) { + for(var m in masters) { + if(self.nameToHolder(masters[m]) == self.attr('id')) { listeners.push($(this)[0]); break; } @@ -244,19 +334,20 @@ } }); + $('div.display-logic.displaylogicwrapper.display-logic-display, div.display-logic.displaylogicwrapper.display-logic-hide').entwine({ + getFormField: function () { + return this; + }, - $('div.display-logic-master.checkboxset').entwine({ - - }) - - - + getFieldName: function () { + return ''; + } + }); - $('div.display-logic *').entwine({ + $('div.field *').entwine({ getHolder: function() { - return this.closest('.display-logic'); + return this.parents('.field'); } }); -//}) })(jQuery); diff --git a/templates/CheckboxSetField.ss b/templates/forms/CheckboxSetField.ss similarity index 100% rename from templates/CheckboxSetField.ss rename to templates/forms/CheckboxSetField.ss diff --git a/templates/DisplayLogicWrapper_holder.ss b/templates/forms/DisplayLogicWrapper_holder.ss similarity index 77% rename from templates/DisplayLogicWrapper_holder.ss rename to templates/forms/DisplayLogicWrapper_holder.ss index a6f2f73..5febb6a 100644 --- a/templates/DisplayLogicWrapper_holder.ss +++ b/templates/forms/DisplayLogicWrapper_holder.ss @@ -1,4 +1,4 @@ -
+
<% loop $FieldList %> $FieldHolder <% end_loop %> diff --git a/templates/forms/FieldGroup.ss b/templates/forms/FieldGroup.ss new file mode 100644 index 0000000..ef998be --- /dev/null +++ b/templates/forms/FieldGroup.ss @@ -0,0 +1,7 @@ +
id="$ID"<% end_if %>> + <% loop $FieldList %> +
+ $SmallFieldHolder +
+ <% end_loop %> +
diff --git a/templates/forms/FieldGroup_holder.ss b/templates/forms/FieldGroup_holder.ss new file mode 100644 index 0000000..9f70839 --- /dev/null +++ b/templates/forms/FieldGroup_holder.ss @@ -0,0 +1,14 @@ +
id="$Name"<% end_if %> class="field <% if $extraClass %>$extraClass<% end_if %>"> + <% if $Title %><% end_if %> + +
+ <% loop $FieldList %> +
+ $SmallFieldHolder +
+ <% end_loop %> +
+ <% if $RightTitle %><% end_if %> + <% if $Message %>$Message<% end_if %> + <% if $Description %>$Description<% end_if %> +
diff --git a/templates/OptionsetField.ss b/templates/forms/OptionsetField.ss similarity index 62% rename from templates/OptionsetField.ss rename to templates/forms/OptionsetField.ss index 66ba45b..f4cec4b 100644 --- a/templates/OptionsetField.ss +++ b/templates/forms/OptionsetField.ss @@ -1,4 +1,4 @@ -
    data-display-logic-masters="$DisplayLogicMasters" data-display-logic-eval="$DisplayLogic"<% end_if %>> +
      <% loop $Options %>
    • checked<% end_if %><% if $isDisabled %> disabled<% end_if %> />