From 0ba85bc1d00281c79f8623186b2e5ba6a3ec2929 Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Tue, 14 May 2024 12:43:59 +1200 Subject: [PATCH 1/7] API Deprecate passing non-arrays to FieldList methods --- src/Forms/FieldList.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Forms/FieldList.php b/src/Forms/FieldList.php index 7e8a80cd15e..85143cf1390 100644 --- a/src/Forms/FieldList.php +++ b/src/Forms/FieldList.php @@ -3,6 +3,7 @@ namespace SilverStripe\Forms; use SilverStripe\ORM\ArrayList; +use SilverStripe\Dev\Deprecation; /** * A list designed to hold form field instances. @@ -218,6 +219,10 @@ public function addFieldToTab($tabName, $field, $insertBefore = null) */ public function addFieldsToTab($tabName, $fields, $insertBefore = null) { + if (!is_array($fields)) { + Deprecation::notice('5.3.0', '$fields will need to be passed as an array in CMS 6', Deprecation::SCOPE_METHOD); + } + $this->flushFieldsCache(); // Find the tab @@ -270,6 +275,10 @@ public function removeFieldFromTab($tabName, $fieldName) */ public function removeFieldsFromTab($tabName, $fields) { + if (!is_array($fields)) { + Deprecation::notice('5.3.0', '$fields will need to be passed as an array in CMS 6', Deprecation::SCOPE_METHOD); + } + $this->flushFieldsCache(); // Find the tab From 264dfac902b17a00586f17c4a75b17ef2dc4b295 Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Thu, 16 May 2024 10:15:11 +1200 Subject: [PATCH 2/7] MNT Run module-standardiser --- .github/workflows/dispatch-ci.yml | 9 +++++-- .github/workflows/keepalive.yml | 8 +++++-- .github/workflows/merge-up.yml | 9 +++++-- CONTRIBUTING.md | 15 ------------ SUPPORT.md | 39 ------------------------------- 5 files changed, 20 insertions(+), 60 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 SUPPORT.md diff --git a/.github/workflows/dispatch-ci.yml b/.github/workflows/dispatch-ci.yml index d9091c496fb..f2597ae502d 100644 --- a/.github/workflows/dispatch-ci.yml +++ b/.github/workflows/dispatch-ci.yml @@ -1,9 +1,11 @@ name: Dispatch CI on: - # At 2:20 PM UTC, only on Tuesday and Wednesday + # At 4:20 PM UTC, only on Friday and Saturday schedule: - - cron: '20 14 * * 2,3' + - cron: '20 16 * * 5,6' + +permissions: {} jobs: dispatch-ci: @@ -11,6 +13,9 @@ jobs: # Only run cron on the silverstripe account if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule') runs-on: ubuntu-latest + permissions: + contents: read + actions: write steps: - name: Dispatch CI uses: silverstripe/gha-dispatch-ci@v1 diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml index 4b86440ba06..743269a2d8d 100644 --- a/.github/workflows/keepalive.yml +++ b/.github/workflows/keepalive.yml @@ -1,17 +1,21 @@ name: Keepalive on: - # At 1:05 PM UTC, on day 22 of the month + # At 4:20 PM UTC, on day 13 of the month schedule: - - cron: '5 13 22 * *' + - cron: '20 16 13 * *' workflow_dispatch: +permissions: {} + jobs: keepalive: name: Keepalive # Only run cron on the silverstripe account if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule') runs-on: ubuntu-latest + permissions: + actions: write steps: - name: Keepalive uses: silverstripe/gha-keepalive@v1 diff --git a/.github/workflows/merge-up.yml b/.github/workflows/merge-up.yml index 169bec61c56..8903a5845fb 100644 --- a/.github/workflows/merge-up.yml +++ b/.github/workflows/merge-up.yml @@ -1,17 +1,22 @@ name: Merge-up on: - # At 2:20 PM UTC, only on Saturday + # At 4:20 PM UTC, only on Tuesday schedule: - - cron: '20 14 * * 6' + - cron: '20 16 * * 2' workflow_dispatch: +permissions: {} + jobs: merge-up: name: Merge-up # Only run cron on the silverstripe account if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule') runs-on: ubuntu-latest + permissions: + contents: write + actions: write steps: - name: Merge-up uses: silverstripe/gha-merge-up@v1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 27a957c848e..00000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,15 +0,0 @@ -# Contributing - -Any open source product is only as good as the community behind it. You can participate by sharing code, ideas, or simply helping others. No matter what your skill level is, every contribution counts. - -See our [high level overview](http://silverstripe.org/contributing-to-silverstripe) on silverstripe.org on how you can help out. - -## Copyright - -**IMPORTANT: By supplying code to the SilverStripe core team in patches, tickets and pull requests, you agree to assign copyright of that code to SilverStripe Limited, on the condition that SilverStripe Limited releases that code under the BSD license.** - -We ask for this so that the ownership in the license is clear and unambiguous, and so that community involvement doesn't stop us from being able to continue supporting these projects. By releasing this code under a permissive license, this copyright assignment won't prevent you from using the code in any way you see fit. - -## Contributing code - -See [contributing code](https://docs.silverstripe.org/en/4/contributing/code/) diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index afd42e89c15..00000000000 --- a/SUPPORT.md +++ /dev/null @@ -1,39 +0,0 @@ -# Support - -The issue tracker is reserved for reporting bugs and we don't provide support through it. Support tickets opened via the issue tracker will be closed. - -If you require support there are two types of support available: community support and commercial support. - -## Community support - -Community support is provided by members of our open source community and is provided out of their good will. Please be kind, polite and corteous to those that try to help you; you may wish to refer to our [code of conduct](https://docs.silverstripe.org/en/4/contributing/code_of_conduct/). - -You can find support via: - -- [Stack Overflow (remember to tag as silverstripe)](http://stackoverflow.com/questions/tagged/silverstripe) -- [Slack](https://silverstripe.org/slack) -- [Forum](https://forum.silverstripe.org) - -We recommend choosing the support forum based on the nature of your question: - -### Do you have some code you've tried that isn't working? - -Your question will likely be best on Stack Overflow. You can add some detail about what you're trying to achieve and include some code that you have tried. Stack Overflow is a good forum for answers to be detailed and become available in the future for other developers in your position. Remember to [follow the guidance on Stack Overflow](https://stackoverflow.com/help/how-to-ask) for asking good questions! - -### Are you looking for an answer to a quick question or a recommendation for an approach or suitable SilverStripe module? - -The SilverStripe community Slack has a wealth of active SilverStripe developers active daily. Remember that those who contribute usually do so in their free time so be considerate when messaging people directly. Consider if your issue might benefit another developer before choosing Slack. An alternate forum like StackOverflow may help other developers that are experiencing the same issue find a resolution. - -### Are you looking for detailed feedback on a suggestion or module you have developed? - -Try starting a discussion in the community forum. Developers and users alike use the forum to keep up to date with the SilverStripe community and contributions that people are making or discussing. - -### More information - -For more information please see our [community page](http://www.silverstripe.org/community/#introduction). - -## Commercial support - -If you require a higher level of support with an SLA, you may want to consider commercial support provided by SilverStripe Ltd, a Professional Partner or another SilverStripe agency. - -Please see the [SilverStripe Developer Directory](https://www.silverstripe.org/community/developer-and-partner-directory/) for more information. From 56625081b43d2d49530fae577cdaa3d8fa572c17 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli <36352093+GuySartorelli@users.noreply.github.com> Date: Fri, 17 May 2024 10:28:47 +1200 Subject: [PATCH 3/7] ENH Use allowed view button for readonly GridField (#11228) --- src/Forms/GridField/GridField.php | 13 ++++++-- .../Forms/GridField/GridFieldReadonlyTest.php | 33 ++++++++++++++++++- .../GridFieldViewButtonReplacement.php | 11 +++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/php/Forms/GridField/GridFieldReadonlyTest/GridFieldViewButtonReplacement.php diff --git a/src/Forms/GridField/GridField.php b/src/Forms/GridField/GridField.php index d42fb7b34e9..1707fafdba3 100644 --- a/src/Forms/GridField/GridField.php +++ b/src/Forms/GridField/GridField.php @@ -276,9 +276,18 @@ public function performReadonlyTransformation() } } - // If the edit button has been removed, replace it with a view button + // If the edit button has been removed, replace it with a view button if one is allowed if ($hadEditButton && !$copyConfig->getComponentByType(GridFieldViewButton::class)) { - $copyConfig->addComponent(GridFieldViewButton::create()); + $viewButtonClass = null; + foreach ($allowedComponents as $componentClass) { + if (is_a($componentClass, GridFieldViewButton::class, true)) { + $viewButtonClass = $componentClass; + break; + } + } + if ($viewButtonClass) { + $copyConfig->addComponent($viewButtonClass::create()); + } } $copy->extend('afterPerformReadonlyTransformation', $this); diff --git a/tests/php/Forms/GridField/GridFieldReadonlyTest.php b/tests/php/Forms/GridField/GridFieldReadonlyTest.php index 9a0742d89f7..27b51096f4c 100644 --- a/tests/php/Forms/GridField/GridFieldReadonlyTest.php +++ b/tests/php/Forms/GridField/GridFieldReadonlyTest.php @@ -20,6 +20,8 @@ use SilverStripe\Forms\Tests\GridField\GridFieldTest\Team; use SilverStripe\Dev\SapphireTest; use SilverStripe\Forms\GridField\GridField; +use SilverStripe\Forms\GridField\GridFieldViewButton; +use SilverStripe\Forms\Tests\GridField\GridFieldReadonlyTest\GridFieldViewButtonReplacement; use SilverStripe\Versioned\VersionedGridFieldState\VersionedGridFieldState; class GridFieldReadonlyTest extends SapphireTest @@ -31,11 +33,28 @@ class GridFieldReadonlyTest extends SapphireTest Cheerleader::class, ]; + public function provideReadOnlyTransformation(): array + { + return [ + [ + 'viewButtonClass' => null, + ], + [ + 'viewButtonClass' => GridFieldViewButton::class, + ], + [ + 'viewButtonClass' => GridFieldViewButtonReplacement::class, + ], + ]; + } + /** * The CMS can set the value of a GridField to be a hasMany relation, which needs a readonly state. * This test ensures GridField has a readonly transformation. + * + * @dataProvider provideReadOnlyTransformation */ - public function testReadOnlyTransformation() + public function testReadOnlyTransformation(?string $viewButtonClass) { // Build a hasMany Relation via getComponents like ModelAdmin does. $components = Team::get_one(Team::class) @@ -67,6 +86,18 @@ public function testReadOnlyTransformation() $gridConfig ); + if ($viewButtonClass !== GridFieldViewButton::class) { + $allowedComponents = $gridField->getReadonlyComponents(); + $viewButtonIndex = array_search(GridFieldViewButton::class, $allowedComponents); + if ($viewButtonIndex !== false) { + unset($allowedComponents[$viewButtonIndex]); + } + if ($viewButtonClass !== null) { + $allowedComponents[] = $viewButtonClass; + } + $gridField->setReadonlyComponents($allowedComponents); + } + // Model Admin sets the value of the GridField directly to the relation, which doesn't have a forTemplate() // function, if we rely on FormField to render into a ReadonlyField we'll get an error as HasManyRelation // doesn't have a forTemplate() function. diff --git a/tests/php/Forms/GridField/GridFieldReadonlyTest/GridFieldViewButtonReplacement.php b/tests/php/Forms/GridField/GridFieldReadonlyTest/GridFieldViewButtonReplacement.php new file mode 100644 index 00000000000..9f26df5aafa --- /dev/null +++ b/tests/php/Forms/GridField/GridFieldReadonlyTest/GridFieldViewButtonReplacement.php @@ -0,0 +1,11 @@ + Date: Fri, 17 May 2024 13:57:50 +1200 Subject: [PATCH 4/7] MNT Run module-standardiser --- .github/workflows/dispatch-ci.yml | 4 ++-- .github/workflows/keepalive.yml | 4 ++-- .github/workflows/merge-up.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dispatch-ci.yml b/.github/workflows/dispatch-ci.yml index f2597ae502d..a5a4db160df 100644 --- a/.github/workflows/dispatch-ci.yml +++ b/.github/workflows/dispatch-ci.yml @@ -1,9 +1,9 @@ name: Dispatch CI on: - # At 4:20 PM UTC, only on Friday and Saturday + # At 8:40 AM UTC, only on Wednesday and Thursday schedule: - - cron: '20 16 * * 5,6' + - cron: '40 8 * * 3,4' permissions: {} diff --git a/.github/workflows/keepalive.yml b/.github/workflows/keepalive.yml index 743269a2d8d..9151d81da74 100644 --- a/.github/workflows/keepalive.yml +++ b/.github/workflows/keepalive.yml @@ -1,9 +1,9 @@ name: Keepalive on: - # At 4:20 PM UTC, on day 13 of the month + # At 5:25 AM UTC, on day 18 of the month schedule: - - cron: '20 16 13 * *' + - cron: '25 5 18 * *' workflow_dispatch: permissions: {} diff --git a/.github/workflows/merge-up.yml b/.github/workflows/merge-up.yml index 8903a5845fb..cd3689c4865 100644 --- a/.github/workflows/merge-up.yml +++ b/.github/workflows/merge-up.yml @@ -1,9 +1,9 @@ name: Merge-up on: - # At 4:20 PM UTC, only on Tuesday + # At 8:40 AM UTC, only on Sunday schedule: - - cron: '20 16 * * 2' + - cron: '40 8 * * 0' workflow_dispatch: permissions: {} From 699a8bf2daf1b7f28c034c68d0b69d499ee53a0e Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Fri, 17 May 2024 15:17:02 +1200 Subject: [PATCH 5/7] FIX Loop over current scope when no argument passed to loop block --- src/View/SSTemplateParser.peg | 6 +++--- src/View/SSTemplateParser.php | 13 +++++++++---- tests/php/View/SSViewerTest.php | 9 +++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/View/SSTemplateParser.peg b/src/View/SSTemplateParser.peg index 2061c1879a5..0f15460f97b 100644 --- a/src/View/SSTemplateParser.peg +++ b/src/View/SSTemplateParser.peg @@ -1031,13 +1031,13 @@ class SSTemplateParser extends Parser implements TemplateParser function ClosedBlock_Handle_Loop(&$res) { if ($res['ArgumentCount'] > 1) { - throw new SSTemplateParseException('Either no or too many arguments in control block. Must be one ' . - 'argument only.', $this); + throw new SSTemplateParseException('Too many arguments in control block. Must be one or no' . + 'arguments only.', $this); } //loop without arguments loops on the current scope if ($res['ArgumentCount'] == 0) { - $on = '$scope->obj(\'Up\', null)->obj(\'Foo\', null)'; + $on = '$scope->locally()->obj(\'Me\', null, true)'; } else { //loop in the normal way $arg = $res['Arguments'][0]; if ($arg['ArgumentMode'] == 'string') { diff --git a/src/View/SSTemplateParser.php b/src/View/SSTemplateParser.php index db9f0a6f342..9ca62eaa54b 100644 --- a/src/View/SSTemplateParser.php +++ b/src/View/SSTemplateParser.php @@ -1886,6 +1886,8 @@ function PresenceCheck_Argument(&$res, $sub) $res['php'] .= '((bool)'.$sub['php'].')'; } else { $php = ($sub['ArgumentMode'] == 'default' ? $sub['lookup_php'] : $sub['php']); + // TODO: kinda hacky - maybe we need a way to pass state down the parse chain so + // Lookup_LastLookupStep and Argument_BareWord can produce hasValue instead of XML_val $res['php'] .= str_replace('$$FINAL', 'hasValue', $php ?? ''); } } @@ -4257,13 +4259,13 @@ function ClosedBlock__finalise(&$res) function ClosedBlock_Handle_Loop(&$res) { if ($res['ArgumentCount'] > 1) { - throw new SSTemplateParseException('Either no or too many arguments in control block. Must be one ' . - 'argument only.', $this); + throw new SSTemplateParseException('Too many arguments in control block. Must be one or no' . + 'arguments only.', $this); } //loop without arguments loops on the current scope if ($res['ArgumentCount'] == 0) { - $on = '$scope->obj(\'Up\', null)->obj(\'Foo\', null)'; + $on = '$scope->locally()->obj(\'Me\', null, true)'; } else { //loop in the normal way $arg = $res['Arguments'][0]; if ($arg['ArgumentMode'] == 'string') { @@ -5290,6 +5292,8 @@ function Text__finalise(&$res) $text = stripslashes($text ?? ''); $text = addcslashes($text ?? '', '\'\\'); + // TODO: This is pretty ugly & gets applied on all files not just html. I wonder if we can make this + // non-dynamically calculated $code = <<<'EOC' (\SilverStripe\View\SSViewer::getRewriteHashLinksDefault() ? \SilverStripe\Core\Convert::raw2att( preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI'] ) ) @@ -5328,7 +5332,8 @@ public function compileString($string, $templateName = "", $includeDebuggingComm $this->includeDebuggingComments = $includeDebuggingComments; - // Ignore UTF8 BOM at beginning of string. + // Ignore UTF8 BOM at beginning of string. TODO: Confirm this is needed, make sure SSViewer handles UTF + // (and other encodings) properly if (substr($string ?? '', 0, 3) == pack("CCC", 0xef, 0xbb, 0xbf)) { $this->pos = 3; } diff --git a/tests/php/View/SSViewerTest.php b/tests/php/View/SSViewerTest.php index e5307a7c13e..b80b1c80949 100644 --- a/tests/php/View/SSViewerTest.php +++ b/tests/php/View/SSViewerTest.php @@ -500,6 +500,15 @@ public function testLocalFunctionsTakePriorityOverGlobals() ); } + public function testCurrentScopeLoop(): void + { + $data = new ArrayList([['Val' => 'one'], ['Val' => 'two'], ['Val' => 'three']]); + $this->assertEqualIgnoringWhitespace( + 'one two three', + $this->render('<% loop %>$Val<% end_loop %>', $data) + ); + } + public function testCurrentScopeLoopWith() { // Data to run the loop tests on - one sequence of three items, each with a subitem From 470293a6d215c446d66806990def0631fd99f923 Mon Sep 17 00:00:00 2001 From: Hernold Koch Date: Mon, 20 May 2024 01:04:44 +0200 Subject: [PATCH 6/7] FIX 'which' check in 'sake' now works for aliases (#11232) --- sake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sake b/sake index 3e1274d8cfb..65d9af3394a 100755 --- a/sake +++ b/sake @@ -9,7 +9,8 @@ Executes a SilverStripe command" exit 1 fi -if ! [ -x "$(command -v which)" ]; then +command -v which >/dev/null 2>&1 +if [ $? -ne 0 ]; then echo "Error: sake requires the 'which' command to operate." >&2 exit 1 fi From f0aaba5504aa070519792db8d63445f73b26362a Mon Sep 17 00:00:00 2001 From: Sabina Talipova <87288324+sabina-talipova@users.noreply.github.com> Date: Wed, 22 May 2024 09:39:36 +1200 Subject: [PATCH 7/7] FIX HTMLEditorField is not able to show html or xml code examples (#11243) --- src/Forms/HTMLEditor/HTMLEditorField.php | 14 ++++++- .../Shortcodes/EmbedShortcodeProvider.php | 4 ++ .../EmbedShortcodeProvider_video.ss | 2 +- .../Forms/HTMLEditor/HTMLEditorFieldTest.php | 37 ++++++++++++------- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/Forms/HTMLEditor/HTMLEditorField.php b/src/Forms/HTMLEditor/HTMLEditorField.php index db8d08a05ae..b8a1302d09f 100644 --- a/src/Forms/HTMLEditor/HTMLEditorField.php +++ b/src/Forms/HTMLEditor/HTMLEditorField.php @@ -191,7 +191,19 @@ public function getSchemaStateDefaults() */ public function ValueEntities() { - return htmlentities($this->Value() ?? '', ENT_COMPAT, 'UTF-8', false); + $entities = get_html_translation_table(HTML_ENTITIES); + + foreach ($entities as $key => $value) { + $entities[$key] = "/" . $value . "/"; + } + + $value = preg_replace_callback($entities, function ($matches) { + // Don't apply double encoding to ampersand + $doubleEncoding = $matches[0] != '&'; + return htmlentities($matches[0], ENT_COMPAT, 'UTF-8', $doubleEncoding); + }, $this->Value() ?? ''); + + return $value; } /** diff --git a/src/View/Shortcodes/EmbedShortcodeProvider.php b/src/View/Shortcodes/EmbedShortcodeProvider.php index 58aefc2c3b9..9a8c524506f 100644 --- a/src/View/Shortcodes/EmbedShortcodeProvider.php +++ b/src/View/Shortcodes/EmbedShortcodeProvider.php @@ -171,6 +171,10 @@ protected static function videoEmbed($arguments, $content) $arguments['style'] = 'width: ' . intval($arguments['width']) . 'px;'; } + if (!empty($arguments['caption'])) { + $arguments['caption'] = htmlentities($arguments['caption'], ENT_QUOTES, 'UTF-8', false); + } + // override iframe dimension attributes provided by webservice with ones specified in shortcode arguments foreach (['width', 'height'] as $attr) { if (!($value = $arguments[$attr] ?? false)) { diff --git a/templates/SilverStripe/View/Shortcodes/EmbedShortcodeProvider_video.ss b/templates/SilverStripe/View/Shortcodes/EmbedShortcodeProvider_video.ss index c526f16404d..be1a2a0a48d 100644 --- a/templates/SilverStripe/View/Shortcodes/EmbedShortcodeProvider_video.ss +++ b/templates/SilverStripe/View/Shortcodes/EmbedShortcodeProvider_video.ss @@ -3,6 +3,6 @@ > {$Content} <% if $Arguments.caption %> -

{$Arguments.caption}

+

{$Arguments.caption.RAW}

<% end_if %> diff --git a/tests/php/Forms/HTMLEditor/HTMLEditorFieldTest.php b/tests/php/Forms/HTMLEditor/HTMLEditorFieldTest.php index 2abe550aa5b..ce6c18d5540 100644 --- a/tests/php/Forms/HTMLEditor/HTMLEditorFieldTest.php +++ b/tests/php/Forms/HTMLEditor/HTMLEditorFieldTest.php @@ -74,7 +74,7 @@ public function testCasting() $inputText = "These are some unicodes: ä, ö, & ü"; $field = new HTMLEditorField("Test", "Test"); $field->setValue($inputText); - $this->assertStringContainsString('These are some unicodes: ä, ö, & ü', $field->Field()); + $this->assertStringContainsString('These are some unicodes: ä, ö, & ü', $field->Field()); // Test shortcodes $inputText = "Shortcode: [file_link id=4]"; $field = new HTMLEditorField("Test", "Test"); @@ -210,23 +210,34 @@ public function testReadonlyField() ); } - public function testValueEntities() + public function provideTestValueEntities() { - $inputText = "The company & partners"; - $field = new HTMLEditorField("Content"); - $field->setValue($inputText); - - $this->assertEquals( - "The company & partners", - $field->obj('ValueEntities')->forTemplate() - ); + return [ + "ampersand" => [ + "The company & partners", + "The company & partners" + ], + "double ampersand" => [ + "The company &amp; partners", + "The company &amp; partners" + ], + "left arrow and right arrow" => [ + "

<strong>The company &amp; partners</strong>

", + "

&lt;strong&gt;The company &amp; partners&lt;/strong&gt;

" + ], + ]; + } - $inputText = "The company && partners"; + /** + * @dataProvider provideTestValueEntities + */ + public function testValueEntities(string $input, string $result) + { $field = new HTMLEditorField("Content"); - $field->setValue($inputText); + $field->setValue($input); $this->assertEquals( - "The company && partners", + $result, $field->obj('ValueEntities')->forTemplate() ); }