From 642656ad93efcb42086f576573c9b99f0e8b1c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Wed, 24 Feb 2016 08:46:44 +0100 Subject: [PATCH 001/222] Update FormHelper and its tests for CakePHP >= 3.2.3. --- composer.json | 2 +- src/View/Helper/BootstrapFormHelper.php | 19 --------- .../View/Helper/BootstrapFormHelperTest.php | 40 +++++++++++++++++-- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/composer.json b/composer.json index e674f01..b4f0cc2 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "license": "Apache", "type": "cakephp-plugin", "require": { - "cakephp/cakephp": "~3.0" + "cakephp/cakephp": ">=3.2.3" }, "require-dev": { "phpunit/phpunit": "*", diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index fc06e72..28d06a2 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -393,25 +393,6 @@ public function input($fieldName, array $options = array()) { return parent::input($fieldName, $options) ; } - /** - * Generates an group template element - * - * @param array $options The options for group template - * @return string The generated group template - */ - protected function _groupTemplate($options) { - $groupTemplate = $options['options']['type'] . 'FormGroup'; - if (!$this->templater()->get($groupTemplate)) { - $groupTemplate = 'formGroup'; - } - return $this->formatTemplate($groupTemplate, [ - 'input' => $options['input'], - 'label' => $options['label'], - 'error' => $options['error'], - 'templateVars' => isset($options['options']['templateVars']) ? $options['options']['templateVars'] : [] - ]); - } - /** * Generates an input element * diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php index c464551..2805a2c 100644 --- a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -402,7 +402,8 @@ public function testInputGroup () { '/div', '/div' ] ; - $this->_testInput ($expected, $fieldName, $options + ['prepend' => '$', 'append' => '.00']) ; + $this->_testInput ($expected, $fieldName, + $options + ['prepend' => '$', 'append' => '.00']) ; // Test with prepend button $expected = [ ['div' => [ @@ -431,7 +432,8 @@ public function testInputGroup () { '/div' ] ; - $this->_testInput ($expected, $fieldName, $options + ['prepend' => $this->Form->button('Go!')]) ; + $this->_testInput ($expected, $fieldName, + $options + ['prepend' => $this->Form->button('Go!')]) ; // Test with append button $expected = [ @@ -460,7 +462,8 @@ public function testInputGroup () { '/div', '/div' ] ; - $this->_testInput ($expected, $fieldName, $options + ['append' => $this->Form->button('Go!')]) ; + $this->_testInput ($expected, $fieldName, + $options + ['append' => $this->Form->button('Go!')]) ; // Test with append 2 button $expected = [ ['div' => [ @@ -557,4 +560,35 @@ public function testInputGroup () { ]); } + public function testInputTemplateVars () { + $fieldName = 'field' ; + // Add a template with the help placeholder. + $help = 'Some help text.'; + $this->Form->templates([ + 'inputContainer' => '
{{content}}{{help}}
' + ]); + // Standard form + $this->_testInput ([ + ['div' => [ + 'class' => 'form-group text' + ]], + ['label' => [ + 'class' => 'control-label', + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => true], + $help, + '/span', + '/div' + ], $fieldName, ['templateVars' => ['help' => $help]]) ; + } + } \ No newline at end of file From cec7fd7dffef7cdfa53d8049f89a983c3ba70584 Mon Sep 17 00:00:00 2001 From: ypnos-web Date: Wed, 24 Feb 2016 19:49:11 +0100 Subject: [PATCH 002/222] Improve ModalHelper::Create size flexibility I use a custom 'modal-xl' class to make modals even larger than lg. For this to work, the modal helper needs to accepts a size class string it doesn't understand. I don't see an adverse effect in letting the user choose their own size class(es). --- src/View/Helper/BootstrapModalHelper.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapModalHelper.php b/src/View/Helper/BootstrapModalHelper.php index cda5556..c1bd200 100644 --- a/src/View/Helper/BootstrapModalHelper.php +++ b/src/View/Helper/BootstrapModalHelper.php @@ -42,6 +42,7 @@ class BootstrapModalHelper extends Helper { * Extra options (useless if $title not specified) : * - close: Add close buttons to header (default true) * - no-body: Do not open the body after the create (default false) + * - size: Modal size (small, large or custom classes) **/ public function create($title = null, $options = array()) { @@ -72,7 +73,7 @@ public function create($title = null, $options = array()) { $size = 'modal-sm'; break; default: - $size = ''; + $size = $options['size']; break; } unset($options['size']); From 54c1a4013faaf28dcda80f0d6471bc3163f24d64 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Wed, 24 Feb 2016 22:54:40 +0100 Subject: [PATCH 003/222] Clean BootstrapModal code. --- src/View/Helper/BootstrapModalHelper.php | 213 ++++++++++++----------- 1 file changed, 114 insertions(+), 99 deletions(-) diff --git a/src/View/Helper/BootstrapModalHelper.php b/src/View/Helper/BootstrapModalHelper.php index c1bd200..52c644e 100644 --- a/src/View/Helper/BootstrapModalHelper.php +++ b/src/View/Helper/BootstrapModalHelper.php @@ -30,7 +30,9 @@ class BootstrapModalHelper extends Helper { public $helpers = ['Html']; - public $current = NULL ; + protected $_current = null; + + protected $_currentId = null; /** * @@ -44,47 +46,62 @@ class BootstrapModalHelper extends Helper { * - no-body: Do not open the body after the create (default false) * - size: Modal size (small, large or custom classes) **/ - public function create($title = null, $options = array()) { + public function create($title = null, $options = []) { if (is_array($title)) { $options = $title ; } - $close = $this->_extractOption('close', $options, true); - unset ($options['close']) ; - $nobody = $this->_extractOption('no-body', $options, false); - unset ($options['no-body']) ; - $options['tabindex'] = $this->_extractOption('tabindex', $options, -1); - $options['role'] = $this->_extractOption('role', $options, 'dialog'); - $options['aria-hidden'] = $this->_extractOption('aria-hidden', $options, 'true'); - if (isset($options['id'])) { - $this->currentId = $options['id'] ; - $options['aria-labelledby'] = $this->currentId.'Label' ; + + $this->_currentId = null; + $this->_current = null; + + $options += [ + 'id' => null, + 'close' => true, + 'body' => true, + 'tabindex' => -1, + 'role' => 'dialog', + 'aria-hidden' => 'true', + 'size' => false + ]; + + $close = $options['close']; + $body = $options['body']; + unset ($options['close'], $options['body']) ; + + + if ($options['id']) { + $this->_currentId = $options['id'] ; + $options['aria-labelledby'] = $this->_currentId.'Label' ; } - $options['size'] = $this->_extractOption('size', $options, ''); + switch($options['size']) { case 'lg': case 'large': case 'modal-lg': - $size = 'modal-lg'; + $size = ' modal-lg'; break; case 'sm': case 'small': case 'modal-sm': - $size = 'modal-sm'; + $size = ' modal-sm'; + break; + case false: + $size = ''; break; default: - $size = $options['size']; + $size = ' '.$options['size']; break; } unset($options['size']); - $res = $this->Html->div('modal fade '.$this->_extractOption('class', $options, ''), - null, $options) - .$this->Html->div('modal-dialog '.$size).$this->Html->div('modal-content'); + $options = $this->addClass($options, 'modal fade'); + $res = $this->Html->tag('div', null, $options) + .$this->Html->div('modal-dialog'.$size).$this->Html->div('modal-content'); if (is_string($title) && $title) { - $res .= $this->_createHeader($title, array('close' => $close)) ; - if (!$nobody) { - $res .= $this->_startPart('body'); + $res .= $this->_createHeader($title, ['close' => $close]) ; + if ($body) { + $res .= $this->_createBody(); } } return $res ; @@ -99,13 +116,9 @@ public function create($title = null, $options = array()) { * @param array $options * **/ - public function end ($buttons = NULL, $options = array()) { - $res = '' ; - if ($this->current != NULL) { - $this->current = NULL ; - $res .= $this->_endPart(); - } - if ($buttons !== NULL) { + public function end ($buttons = NULL, $options = []) { + $res = $this->_cleanCurrent(); + if ($buttons !== null) { $res .= $this->footer($buttons, $options) ; } $res .= '' ; @@ -113,71 +126,72 @@ public function end ($buttons = NULL, $options = array()) { } protected function _cleanCurrent () { - if ($this->current) { - $this->current = NULL ; - return $this->_endPart(); + if ($this->_current) { + $this->_current = null ; + return ''; } return '' ; } - protected function _createHeader ($title, $options = array()) { - $close = $this->_extractOption('close', $options, true); - unset($options['close']) ; - if ($close) { - $button = '' ; - } - else { - $button = '' ; - } - return $this->_cleanCurrent() - .$this->Html->div('modal-header '.$this->_extractOption('class', $options, ''), - $button.$this->Html->tag('h4', $title, [ - 'class' => 'modal-title', - 'id' => $this->currentId ? $this->currentId.'Label' : false - ]), - $options - ) ; + protected function _part ($part, $content = null, $options = []) { + $out = $this->_cleanCurrent().$this->Html->tag('div', $content, $options); + if (!$content) + $this->_current = $part; + return $out; } - protected function _createBody ($text, $options = array()) { - return $this->_cleanCurrent() - .$this->Html->div('modal-body '.$this->_extractOption('class', $options, ''), - $text, $options) ; - } + protected function _createHeader ($title = null, $options = []) { + $options += [ + 'close' => true + ]; + + $close = $options['close']; + unset($options['close']) ; + + $options = $this->addClass($options, 'modal-header'); - protected function _createFooter ($buttons = NULL, $options = array()) { - if ($buttons == NULL) { - $close = $this->_extractOption('close', $options, true); - unset($options['close']) ; + $out = null; + if ($title) { + $out = ''; if ($close) { - $buttons = '' ; - } - else { - $buttons = '' ; + $out .= $this->Html->tag('button', '×', [ + 'type' => 'button', + 'class' => 'close', + 'data-dismiss' => 'modal', + 'aria-hidden' => 'true' + ]); } + $out .= $this->Html->tag('h4', $title, [ + 'class' => 'modal-title', + 'id' => $this->_currentId ? $this->_currentId.'Label' : false + ]); } - return $this->_cleanCurrent() - .$this->Html->div('modal-footer '.$this->_extractOption('class', $options, ''), - $buttons, $options) ; + + return $this->_part('header', $out, $options); } - protected function _startPart ($part, $options = array()) { - $res = '' ; - if ($this->current != NULL) { - $res = $this->_endPart () ; - } - $this->current = $part ; - return $res - .$this->Html->div('modal-'.$part.' '.$this->_extractOption('class', $options, ''), - null, $options) ; + protected function _createBody ($text = null, $options = []) { + $options = $this->addClass($options, 'modal-body'); + return $this->_part('body', $text, $options); } - protected function _endPart () { - return '' ; + protected function _createFooter ($buttons = null, $options = []) { + $options += [ + 'close' => true + ]; + $close = $options['close']; + unset($options['close']); + + $content = ''; + if (!$buttons && $close) { + $content .= '' ; + } + $content .= $buttons; + + $options = $this->addClass($options, 'modal-footer'); + return $this->_part('footer', $buttons, $options); } /** @@ -192,11 +206,12 @@ protected function _endPart () { * - close: Add the 'close' button in the header (default true). * **/ - public function header ($info = NULL, $options = array()) { - if (is_string($info)) { - return $this->_createHeader($info, $options) ; + public function header ($info = null, $options = []) { + if (is_array($info)) { + $options = $info; + $info = null; } - return $this->_startPart('header', is_array($info) ? $info : $options) ; + return $this->_createHeader($info, $options) ; } /** @@ -209,14 +224,12 @@ public function header ($info = NULL, $options = array()) { * * **/ - public function body ($info = NULL, $options = array()) { - if (is_string($info)) { - if ($this->current != NULL) { - $this->_endPart() ; - } - return $this->_createBody($info, $options) ; + public function body ($info = null, $options = []) { + if (is_array($info)) { + $options = $info; + $info = null; } - return $this->_startPart('body', is_array($info) ? $info : $options) ; + return $this->_createBody($info, $options) ; } /** @@ -232,15 +245,17 @@ public function body ($info = NULL, $options = array()) { * - close: Add the 'close' button to the footer (default true). * **/ - public function footer ($buttons = [], $options = []) { - if ($buttons === NULL || (!empty($buttons) && $this->_isAssociativeArray($buttons))) { - return $this->_startPart('footer', $buttons === NULL ? $options : $buttons) ; - } - if (empty($buttons)) { - return $this->_createFooter(NULL, $options) ; + public function footer ($buttons = null, $options = []) { + if (is_array($buttons)) { + if (!empty($buttons) && $this->_isAssociativeArray($buttons)) { + $options = $buttons; + $buttons = null; + } + else { + $buttons = implode('', $buttons); + } } - return $this->_createFooter(is_string($buttons) ? $buttons : implode('', $buttons), - $options) ; + return $this->_createFooter($buttons, $options) ; } } From e27ae78e97fbf94d9b065db587cbe5b03c5646c5 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Wed, 24 Feb 2016 22:55:08 +0100 Subject: [PATCH 004/222] Add some tests for the BootstrapModalHelper class. --- .../View/Helper/BootstrapModalHelperTest.php | 370 ++++++++++++++++++ 1 file changed, 370 insertions(+) create mode 100644 tests/TestCase/View/Helper/BootstrapModalHelperTest.php diff --git a/tests/TestCase/View/Helper/BootstrapModalHelperTest.php b/tests/TestCase/View/Helper/BootstrapModalHelperTest.php new file mode 100644 index 0000000..aa5be17 --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapModalHelperTest.php @@ -0,0 +1,370 @@ +View = new View(); + $this->Modal = new BootstrapModalHelper ($this->View); + } + + public function testCreate () { + $title = "My Modal"; + $id = "myModalId"; + // Test standard create without ID + $result = $this->Modal->create($title); + $this->assertHtml([ + ['div' => [ + 'tabindex' => '-1', + 'role' => 'dialog', + 'aria-hidden' => 'true', + 'class' => 'modal fade' + ]], + ['div' => [ + 'class' => 'modal-dialog' + ]], + ['div' => [ + 'class' => 'modal-content' + ]], + ['div' => [ + 'class' => 'modal-header' + ]], + ['button' => [ + 'type' => 'button', + 'class' => 'close', + 'data-dismiss' => 'modal', + 'aria-hidden' => 'true' + ]], + '×', + '/button', + ['h4' => [ + 'class' => 'modal-title' + ]], + $title, + '/h4', + '/div', + ['div' => [ + 'class' => 'modal-body' + ]] + ], $result); + // Test standard create with ID + $result = $this->Modal->create($title, ['id' => $id]); + $this->assertHtml([ + ['div' => [ + 'id' => $id, + 'tabindex' => '-1', + 'role' => 'dialog', + 'aria-hidden' => 'true', + 'aria-labelledby' => $id.'Label', + 'class' => 'modal fade' + ]], + ['div' => [ + 'class' => 'modal-dialog' + ]], + ['div' => [ + 'class' => 'modal-content' + ]], + ['div' => [ + 'class' => 'modal-header' + ]], + ['button' => [ + 'type' => 'button', + 'class' => 'close', + 'data-dismiss' => 'modal', + 'aria-hidden' => 'true' + ]], + '×', + '/button', + ['h4' => [ + 'class' => 'modal-title', + 'id' => $id.'Label' + ]], + $title, + '/h4', + '/div', + ['div' => [ + 'class' => 'modal-body' + ]] + ], $result); + // Create without body + $result = $this->Modal->create($title, ['id' => $id, 'body' => false]); + $this->assertHtml([ + ['div' => [ + 'id' => $id, + 'tabindex' => '-1', + 'role' => 'dialog', + 'aria-hidden' => 'true', + 'aria-labelledby' => $id.'Label', + 'class' => 'modal fade' + ]], + ['div' => [ + 'class' => 'modal-dialog' + ]], + ['div' => [ + 'class' => 'modal-content' + ]], + ['div' => [ + 'class' => 'modal-header' + ]], + ['button' => [ + 'type' => 'button', + 'class' => 'close', + 'data-dismiss' => 'modal', + 'aria-hidden' => 'true' + ]], + '×', + '/button', + ['h4' => [ + 'class' => 'modal-title', + 'id' => $id.'Label' + ]], + $title, + '/h4', + '/div' + ], $result); + // Create without close + $result = $this->Modal->create($title, ['id' => $id, 'close' => false]); + $this->assertHtml([ + ['div' => [ + 'id' => $id, + 'tabindex' => '-1', + 'role' => 'dialog', + 'aria-hidden' => 'true', + 'aria-labelledby' => $id.'Label', + 'class' => 'modal fade' + ]], + ['div' => [ + 'class' => 'modal-dialog' + ]], + ['div' => [ + 'class' => 'modal-content' + ]], + ['div' => [ + 'class' => 'modal-header' + ]], + ['h4' => [ + 'class' => 'modal-title', + 'id' => $id.'Label' + ]], + $title, + '/h4', + '/div', + ['div' => [ + 'class' => 'modal-body' + ]] + ], $result); + // Create without title / no id + $result = $this->Modal->create(); + $this->assertHtml([ + ['div' => [ + 'tabindex' => '-1', + 'role' => 'dialog', + 'aria-hidden' => 'true', + 'class' => 'modal fade' + ]], + ['div' => [ + 'class' => 'modal-dialog' + ]], + ['div' => [ + 'class' => 'modal-content' + ]] + ], $result); + } + + public function testHeader () { + $content = 'Header'; + $extraclass = 'my-extra-class'; + // Test with HTML + $result = $this->Modal->header($content); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-header' + ]], + ['button' => [ + 'type' => 'button', + 'class' => 'close', + 'data-dismiss' => 'modal', + 'aria-hidden' => 'true' + ]], + '×', + '/button', + ['h4' => [ + 'class' => 'modal-title' + ]], + $content, + '/h4', + '/div' + ], $result); + // Test no close HTML + $result = $this->Modal->header($content, ['close' => false]); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-header' + ]], + ['h4' => [ + 'class' => 'modal-title' + ]], + $content, + '/h4', + '/div' + ], $result); + // Test option + $result = $this->Modal->header($content, ['close' => false, 'class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-header' + ]], + ['h4' => [ + 'class' => 'modal-title' + ]], + $content, + '/h4', + '/div' + ], $result); + // Test null first + $result = $this->Modal->header(null); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-header' + ]] + ], $result); + // Test option first + $this->Modal->create(); + $result = $this->Modal->header(['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-header' + ]] + ], $result); + // Test aut close + $this->Modal->create($content); + $result = $this->Modal->header(['class' => $extraclass]); + $this->assertHtml([ + '/div', + ['div' => [ + 'class' => $extraclass.' modal-header' + ]] + ], $result); + } + public function testBody () { + $content = 'Body'; + $extraclass = 'my-extra-class'; + // Test with HTML + $result = $this->Modal->body($content); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-body' + ]], + $content, + '/div' + ], $result); + // Test option + $result = $this->Modal->body($content, ['close' => false, 'class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-body' + ]], + $content, + '/div' + ], $result); + // Test null first + $result = $this->Modal->body(null); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-body' + ]] + ], $result); + // Test option first + $this->Modal->create(); + $result = $this->Modal->body(['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-body' + ]] + ], $result); + // Test aut close + $this->Modal->create(); + $this->Modal->header(); // Unclosed part + $result = $this->Modal->body(['class' => $extraclass]); + $this->assertHtml([ + '/div', + ['div' => [ + 'class' => $extraclass.' modal-body' + ]] + ], $result); + } + + public function testFooter () { + $content = 'Footer'; + $extraclass = 'my-extra-class'; + // Test with HTML + $result = $this->Modal->footer($content); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-footer' + ]], + $content, + '/div' + ], $result); + // Test with Array + $result = $this->Modal->footer([$content, $content], ['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-footer' + ]], + $content, + $content, + '/div' + ], $result); + // Test with null as first arg + $result = $this->Modal->footer(null, ['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-footer' + ]] + ], $result); + // Test with Options as first arg + $this->Modal->create(); + $result = $this->Modal->footer(['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-footer' + ]] + ], $result); + // Test with automatic close + $this->Modal->create($content); + $result = $this->Modal->footer(); + $this->assertHtml([ + '/div', + ['div' => [ + 'class' => 'modal-footer' + ]] + ], $result); + } + + public function testEnd() { + $result = $this->Modal->end(); + // Standard close + $this->assertHtml([ + '/div', '/div', '/div' + ], $result); + // Close open part + $this->Modal->create('Title'); // Create modal with open title + $result = $this->Modal->end(); + $this->assertHtml([ + '/div', '/div', '/div', '/div' + ], $result); + } + +} \ No newline at end of file From ec3ef6eba18227f6e56cc7727b593a5d101aa7cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 1 Mar 2016 11:22:42 +0100 Subject: [PATCH 005/222] Remove useless override of _getInput. --- src/View/Helper/BootstrapFormHelper.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 28d06a2..9838bf6 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -393,18 +393,6 @@ public function input($fieldName, array $options = array()) { return parent::input($fieldName, $options) ; } - /** - * Generates an input element - * - * @param string $fieldName the field name - * @param array $options The options for the input element - * @return string The generated input element - */ - protected function _getInput($fieldName, $options) { - unset($options['_data']); - return parent::_getInput($fieldName, $options); - } - protected function _getDatetimeTemplate ($fields, $options) { $inputs = [] ; foreach ($fields as $field => $in) { From f691db004840c4c050aca0f1a19e4dea98787d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 4 Mar 2016 08:31:05 +0100 Subject: [PATCH 006/222] Clean BootstrapNavbarHelper code. --- src/View/Helper/BootstrapNavbarHelper.php | 107 ++++++++++++---------- 1 file changed, 59 insertions(+), 48 deletions(-) diff --git a/src/View/Helper/BootstrapNavbarHelper.php b/src/View/Helper/BootstrapNavbarHelper.php index 2a00454..04c6ce6 100644 --- a/src/View/Helper/BootstrapNavbarHelper.php +++ b/src/View/Helper/BootstrapNavbarHelper.php @@ -42,20 +42,20 @@ class BootstrapNavbarHelper extends Helper { * @var bool */ public $autoActiveLink = true ; - + /** * Automatic button link when not in a menu. * * @var bool */ public $autoButtonLink = true ; - + protected $_fixed = false ; protected $_static = false ; protected $_responsive = false ; protected $_inverse = false ; protected $_fluid = false; - + /** * Menu level (0 = out of menu, 1 = main horizontal menu, 2 = dropdown menu). * @@ -91,19 +91,19 @@ public function addClass(array $options = [], $class = null, $key = 'class') { } /** - * + * * Create a new navbar. - * - * @param $brand + * + * @param $brand * @param options Options passed to tag method for outer navbar div - * + * * Extra options: * - fixed: false, 'top', 'bottom' * - static: false, true (useless if fixed != false) * - responsive: false, true (if true, a toggle button will be added) * - inverse: false, true * - fluid: false, true - * + * **/ public function create ($brand, $options = []) { $this->_fixed = $this->_extractOption('fixed', $options, false) ; @@ -116,7 +116,7 @@ public function create ($brand, $options = []) { unset($options['inverse']) ; $this->_fluid = $this->_extractOption('fluid', $options, false); unset($options['fluid']); - + /** Generate options for outer div. **/ $options = $this->addClass($options, 'navbar navbar-default') ; if ($this->_fixed !== false) { @@ -128,11 +128,11 @@ public function create ($brand, $options = []) { if ($this->_inverse !== false) { $options = $this->addClass($options , 'navbar-inverse') ; } - + $toggleButton = '' ; $rightOpen = '' ; if ($this->_responsive) { - $toggleButton = $this->Html->tag('button', + $toggleButton = $this->Html->tag('button', implode('', array( $this->Html->tag('span', __('Toggle navigation'), array('class' => 'sr-only')), $this->Html->tag('span', '', array('class' => 'icon-bar')), @@ -151,29 +151,36 @@ public function create ($brand, $options = []) { if ($brand) { if (is_string($brand)) { - $brand = $this->Html->link ($brand, '/', ['class' => 'navbar-brand', 'escape' => false]) ; + $brand = $this->Html->link ($brand, '/', [ + 'class' => 'navbar-brand', + 'escape' => false + ]) ; } else if (is_array($brand) && array_key_exists('url', $brand)) { $brandOptions = $this->_extractOption ('options', $brand, []) ; $brandOptions = $this->addClass ($brandOptions, 'navbar-brand') ; $brand = $this->Html->link ($brand['name'], $brand['url'], $brandOptions) ; } - $rightOpen = $this->Html->tag('div', $toggleButton.$brand, ['class' => 'navbar-header']).$rightOpen ; + $rightOpen = $this->Html->tag('div', $toggleButton.$brand, + ['class' => 'navbar-header']).$rightOpen ; } - + /** Add and return outer div openning. **/ - return $this->Html->tag('div', null, $options).$this->Html->tag('div', null, ['class' => $this->_fluid ? 'container-fluid' : 'container']).$rightOpen ; + return $this->Html->tag('div', null, $options) + .$this->Html->tag('div', null, [ + 'class' => $this->_fluid ? 'container-fluid' : 'container' + ]).$rightOpen ; } - + /** - * + * * Add a link to the navbar or to a menu. - * + * * @param name The link text * @param url The link URL * @param options Options passed to the tag method (for the li tag) * @param linkOptions Options passed to the link method - * + * **/ public function link ($name, $url = '', array $options = [], array $linkOptions = []) { if ($this->_level == 0 && $this->autoButtonLink) { @@ -187,24 +194,24 @@ public function link ($name, $url = '', array $options = [], array $linkOptions } /** - * + * * Add a button to the navbar. - * + * * @param name Text of the button. * @param options Options sent to the BootstrapFormHelper::button method. - * + * **/ public function button ($name, array $options = []) { $options = $this->addClass ($options, 'navbar-btn') ; return $this->Form->button ($name, $options) ; } - + /** - * + * * Add a divider to the navbar or to a menu. - * + * * @param options Options sent to the tag method. - * + * **/ public function divider (array $options = []) { $options = $this->addClass ($options, 'divider') ; @@ -213,12 +220,12 @@ public function divider (array $options = []) { } /** - * + * * Add a header to the navbar or to a menu, should not be used outside a submenu. - * + * * @param name Title of the header. * @param options Options sent to the tag method. - * + * **/ public function header ($name, array $options = []) { $options = $this->addClass ($options, 'dropdown-header') ; @@ -226,15 +233,15 @@ public function header ($name, array $options = []) { } /** - * + * * Add a text to the navbar. - * + * * @param text The text message. * @param options Options passed to the tag method (+ extra options, see above). - * + * * Extra options: * - tag The HTML tag to use (default 'p') - * + * **/ public function text ($text, $options = []) { $tag = $this->_extractOption ('tag', $options, 'p') ; @@ -251,8 +258,8 @@ public function text ($text, $options = []) { }, $text); return $this->Html->tag($tag, $text, $options) ; } - - + + /** * * Add a serach form to the navbar. @@ -267,18 +274,19 @@ public function searchForm ($model = null, $options = []) { $options = $this->addClass($options, ['navbar-form', 'navbar-'.$align]) ; return $this->Form->searchForm($model, $options) ; } - + /** - * + * * Start a new menu, 2 levels: If not in submenu, create a dropdown menu, * oterwize create hover menu. - * + * * @param name The name of the menu * @param url A URL for the menu (default null) * @param options Options passed to the tag method (+ extra options, see above) - * + * **/ - public function beginMenu ($name = null, $url = null, $options = [], $linkOptions = [], $listOptions = []) { + public function beginMenu ($name = null, $url = null, $options = [], + $linkOptions = [], $listOptions = []) { $res = ''; if ($this->_level == 0) { $options = is_array($name) ? $name : [] ; @@ -293,19 +301,22 @@ public function beginMenu ($name = null, $url = null, $options = [], $linkOption 'aria-expanded' => 'false', 'escape' => false ] ; - $link = $this->Html->link ($name.(array_key_exists ('caret', $linkOptions) ? $linkOptions['caret'] : ''), $url ? $url : '#', $linkOptions); + $caret = array_key_exists('caret', $linkOptions) ? + $linkOptions['caret'] : ''; + $link = $this->Html->link ($name.$caret, $url ? $url : '#', $linkOptions); $options = $this->addClass ($options, 'dropdown') ; $listOptions = $this->addClass ($listOptions, 'dropdown-menu') ; - $res = $this->Html->tag ('li', null, $options).$link.$this->Html->tag ('ul', null, $listOptions); + $res = $this->Html->tag ('li', null, $options) + .$link.$this->Html->tag ('ul', null, $listOptions); } $this->_level += 1 ; return $res ; } - + /** - * + * * End a menu. - * + * **/ public function endMenu () { $this->_level -= 1 ; @@ -313,9 +324,9 @@ public function endMenu () { } /** - * + * * End a navbar. - * + * **/ public function end () { $res = '' ; @@ -324,7 +335,7 @@ public function end () { } return $res ; } - + } ?> From 594e627a6f561cc6884653fdf2bf195a72390a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 4 Mar 2016 09:06:34 +0100 Subject: [PATCH 007/222] Add easyIcon to BootstrapPanelHelper::create and BootstrapPanelHelper::header. --- src/View/Helper/BootstrapPanelHelper.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index ac8e911..50dce41 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -26,9 +26,13 @@ class BootstrapPanelHelper extends Helper { - use BootstrapTrait ; + use BootstrapTrait; - public $helpers = ['Html']; + public $helpers = [ + 'Html' => [ + 'className' => 'Bootstrap.BootstrapHtml' + ] + ]; public $current = NULL ; @@ -96,6 +100,7 @@ public function endGroup() { public function create($title = null, $options = []) { if (is_array($title)) { + $title = null; $options = $title; } @@ -178,6 +183,7 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { $titleOptions = $options['title'] ; } unset ($options['title']); + $title = $this->_makeIcon($title); $options = $this->addClass($options, 'panel-heading'); if ($this->_collapsible) { $options += [ From 38e81d86e2cb4604ba3493495c3cd16846bb82d2 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 21 Mar 2016 18:46:40 +0100 Subject: [PATCH 008/222] Small bug correction. --- src/View/Helper/BootstrapPanelHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 50dce41..88eda37 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -100,8 +100,8 @@ public function endGroup() { public function create($title = null, $options = []) { if (is_array($title)) { - $title = null; $options = $title; + $title = null; } $options += [ From 8370b395023e3b3f6e3154582c4a4ddd6d2caef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 31 Mar 2016 12:53:43 +0200 Subject: [PATCH 009/222] Remove Windows endline of BootstrapTrait.php. --- src/View/Helper/BootstrapTrait.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 898c407..4d12933 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -178,13 +178,13 @@ protected function _makeIcon ($title, &$converted = false) { * **/ protected function _easyIcon ($callback, $title, $options) { - $title = $this->_makeIcon ($title, $converted); + $title = $this->_makeIcon ($title, $converted); if ($converted) { - $options += [ - 'escape' => false - ]; - } - return call_user_func ($callback, $title, $options) ; + $options += [ + 'escape' => false + ]; + } + return call_user_func ($callback, $title, $options) ; } } From 49aaf55be61f6f75ce4ed4e27c08504cb3558cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 31 Mar 2016 12:54:20 +0200 Subject: [PATCH 010/222] Escape HTML in link for collapsible panel when necessary. --- src/View/Helper/BootstrapPanelHelper.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 88eda37..bded8c8 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -183,7 +183,10 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { $titleOptions = $options['title'] ; } unset ($options['title']); - $title = $this->_makeIcon($title); + $title = $this->_makeIcon($title, $converted); + $options += [ + 'escape' => !$converted + ]; $options = $this->addClass($options, 'panel-heading'); if ($this->_collapsible) { $options += [ @@ -195,7 +198,8 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { 'data-toggle' => 'collapse', 'data-parent' => $this->_groupId ? '#'.$this->_groupId : false, 'aria-expanded' => true, - 'aria-controls' => '#'.$this->_bodyId + 'aria-controls' => '#'.$this->_bodyId, + 'escape' => $options['escape'] ]); } if ($titleOptions !== false) { From bdae603357a38456d0c81ed439cbeff5a0f7bc08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 31 Mar 2016 15:06:29 +0200 Subject: [PATCH 011/222] Remove useless conditional assignment. --- src/View/Helper/BootstrapPanelHelper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index bded8c8..6efca14 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -211,6 +211,7 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { $tag = $titleOptions['tag']; unset($titleOptions['tag']); $title = $titleOptions ? $this->Html->tag($tag, $title, $titleOptions) : $title; + $title = $this->Html->tag($tag, $title, $titleOptions); } return $this->_cleanCurrent().$this->Html->tag('div', $title, $options); } From b21bab345ad73281e3f5809aa2bc12eb5735d385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 31 Mar 2016 15:06:55 +0200 Subject: [PATCH 012/222] Correct bug with escaping in header. --- src/View/Helper/BootstrapPanelHelper.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 6efca14..f5db277 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -201,18 +201,22 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { 'aria-controls' => '#'.$this->_bodyId, 'escape' => $options['escape'] ]); + $options['escape'] = false; // Should not escape after } if ($titleOptions !== false) { if (!is_array($titleOptions)) { $titleOptions = []; } - $titleOptions += ['tag' => 'h4']; + $titleOptions += [ + 'tag' => 'h4', + 'escape' => $options['escape'] + ]; $titleOptions = $this->addClass($titleOptions, 'panel-title'); $tag = $titleOptions['tag']; unset($titleOptions['tag']); - $title = $titleOptions ? $this->Html->tag($tag, $title, $titleOptions) : $title; $title = $this->Html->tag($tag, $title, $titleOptions); } + unset($options['escape']); return $this->_cleanCurrent().$this->Html->tag('div', $title, $options); } From 0d78eddb5978dfd4530bf9e6fe6a96b639faba89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 31 Mar 2016 15:07:15 +0200 Subject: [PATCH 013/222] Add some test cases for the panel helper. --- .../View/Helper/BootstrapPanelHelperTest.php | 342 ++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 tests/TestCase/View/Helper/BootstrapPanelHelperTest.php diff --git a/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php new file mode 100644 index 0000000..9867590 --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php @@ -0,0 +1,342 @@ +View = new View(); + $this->View->Html = new BootstrapHtmlHelper($this->View); + $this->Panel = new BootstrapPanelHelper($this->View); + } + + protected function reset () { + $this->Panel->end(); + } + + public function testCreate () { + $title = "My Modal"; + $id = "myModalId"; + // Test standard create with title + $result = $this->Panel->create($title); + $this->assertHtml([ + ['div' => [ + 'class' => 'panel panel-default' + ]], + ['div' => [ + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + $title, + '/h4', + '/div', + ['div' => [ + 'class' => 'panel-body' + ]] + ], $result); + $this->reset(); + // Test standard create with title + $result = $this->Panel->create($title, ['no-body' => true]); + $this->assertHtml([ + ['div' => [ + 'class' => 'panel panel-default' + ]], + ['div' => [ + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + $title, + '/h4', + '/div' + ], $result); + $this->reset(); + // Test standard create without title + $result = $this->Panel->create(); + $this->assertHtml([ + ['div' => [ + 'class' => 'panel panel-default' + ]] + ], $result); + $this->reset(); + } + + public function testHeader () { + $content = 'Header'; + $htmlContent = ''.$content.''; + $extraclass = 'my-extra-class'; + + // Simple test + $result = $this->Panel->header($content); + $this->assertHtml([ + ['div' => [ + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + $content, + '/h4', + '/div' + ], $result); + $this->reset(); + + // Test with HTML content (should be escaped) + $result = $this->Panel->header($htmlContent); + $this->assertHtml([ + ['div' => [ + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + htmlspecialchars($htmlContent), + '/h4', + '/div' + ], $result); + $this->reset(); + + // Test with HTML content (should NOT be escaped) + $result = $this->Panel->header($htmlContent, ['escape' => false]); + $this->assertHtml([ + ['div' => [ + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + ['b' => true], $content, '/b', + '/h4', + '/div' + ], $result); + $this->reset(); + + // Test with icon + $iconContent = 'i:home Home'; + $result = $this->Panel->header($iconContent); + $this->assertHtml([ + ['div' => [ + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + ['i' => [ + 'class' => 'glyphicon glyphicon-home' + ]], '/i', ' Home', + '/h4', + '/div' + ], $result); + $this->reset(); + + // Test with collapsible (should NOT be escaped) + + // Test with HTML content (should be escaped) + $this->Panel->create(null, ['collapsible' => true]); + $result = $this->Panel->header($htmlContent); + $this->assertHtml([ + ['div' => [ + 'role' => 'tab', + 'id' => 'heading-0', + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + ['a' => [ + 'href' => '#collapse-0', + 'data-toggle' => 'collapse', + 'aria-expanded' => true, + 'aria-controls' => '#collapse-0' + ]], + htmlspecialchars($htmlContent), + '/a', + '/h4', + '/div' + ], $result); + $this->reset(); + + // Test with HTML content (should NOT be escaped) + $this->Panel->create(null, ['collapsible' => true]); + $result = $this->Panel->header($htmlContent, ['escape' => false]); + $this->assertHtml([ + ['div' => [ + 'role' => 'tab', + 'id' => 'heading-1', + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + ['a' => [ + 'href' => '#collapse-1', + 'data-toggle' => 'collapse', + 'aria-expanded' => true, + 'aria-controls' => '#collapse-1' + ]], + ['b' => true], $content, '/b', + '/a', + '/h4', + '/div' + ], $result); + $this->reset(); + + // Test with icon + $iconContent = 'i:home Home'; + $this->Panel->create(null, ['collapsible' => true]); + $result = $this->Panel->header($iconContent); + $this->assertHtml([ + ['div' => [ + 'role' => 'tab', + 'id' => 'heading-2', + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + ['a' => [ + 'href' => '#collapse-2', + 'data-toggle' => 'collapse', + 'aria-expanded' => true, + 'aria-controls' => '#collapse-2' + ]], + ['i' => [ + 'class' => 'glyphicon glyphicon-home' + ]], '/i', ' Home', + '/a', + '/h4', + '/div' + ], $result); + $this->reset(); + + + } + + /* + public function testBody () { + $content = 'Body'; + $extraclass = 'my-extra-class'; + // Test with HTML + $result = $this->Modal->body($content); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-body' + ]], + $content, + '/div' + ], $result); + // Test option + $result = $this->Modal->body($content, ['close' => false, 'class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-body' + ]], + $content, + '/div' + ], $result); + // Test null first + $result = $this->Modal->body(null); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-body' + ]] + ], $result); + // Test option first + $this->Modal->create(); + $result = $this->Modal->body(['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-body' + ]] + ], $result); + // Test aut close + $this->Modal->create(); + $this->Modal->header(); // Unclosed part + $result = $this->Modal->body(['class' => $extraclass]); + $this->assertHtml([ + '/div', + ['div' => [ + 'class' => $extraclass.' modal-body' + ]] + ], $result); + } + + public function testFooter () { + $content = 'Footer'; + $extraclass = 'my-extra-class'; + // Test with HTML + $result = $this->Modal->footer($content); + $this->assertHtml([ + ['div' => [ + 'class' => 'modal-footer' + ]], + $content, + '/div' + ], $result); + // Test with Array + $result = $this->Modal->footer([$content, $content], ['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-footer' + ]], + $content, + $content, + '/div' + ], $result); + // Test with null as first arg + $result = $this->Modal->footer(null, ['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-footer' + ]] + ], $result); + // Test with Options as first arg + $this->Modal->create(); + $result = $this->Modal->footer(['class' => $extraclass]); + $this->assertHtml([ + ['div' => [ + 'class' => $extraclass.' modal-footer' + ]] + ], $result); + // Test with automatic close + $this->Modal->create($content); + $result = $this->Modal->footer(); + $this->assertHtml([ + '/div', + ['div' => [ + 'class' => 'modal-footer' + ]] + ], $result); + } + + public function testEnd() { + $result = $this->Modal->end(); + // Standard close + $this->assertHtml([ + '/div', '/div', '/div' + ], $result); + // Close open part + $this->Modal->create('Title'); // Create modal with open title + $result = $this->Modal->end(); + $this->assertHtml([ + '/div', '/div', '/div', '/div' + ], $result); + } + */ + + +} \ No newline at end of file From b1cbd831237e4c9da73073c2759f80ffc6d9ecee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 11:14:55 +0200 Subject: [PATCH 014/222] Correct bug that made panels open by default. --- src/View/Helper/BootstrapPanelHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index f5db277..0d3b66c 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -225,8 +225,8 @@ protected function _createBody ($text = null, $options = []) { $body = $this->Html->tag('div', $text, $options); if ($this->_collapsible) { $open = ((is_int($this->_groupPanelOpen) - && $this->_groupPanelOpen == $this->_groupPanelCount) - || $this->_groupPanelOpen == $this->_bodyId) ? ' in' : ''; + && $this->_groupPanelOpen === $this->_groupPanelCount) + || $this->_groupPanelOpen === $this->_bodyId) ? ' in' : ''; $body = $this->Html->div('panel-collapse collapse'.$open, $text ? $body : null, [ 'role' => 'tabpanel', 'aria-labelledby' => $this->_headId, From 4deb42e9a55b7eba167ecb9778a79a34867f9c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 11:22:19 +0200 Subject: [PATCH 015/222] Add configuration option for collapsible. --- src/View/Helper/BootstrapPanelHelper.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 0d3b66c..422d356 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -34,6 +34,10 @@ class BootstrapPanelHelper extends Helper { ] ]; + public $_defaultConfig = [ + 'collapsible' => false + ]; + public $current = NULL ; /* Protected attributes used to generate ID for collapsible panels. */ @@ -41,9 +45,6 @@ class BootstrapPanelHelper extends Helper { protected $_bodyId = null; protected $_headId = null; - /* Default value for "collapsible" option. */ - protected $_defaultCollapsible = false; - /* Protected attribute used to generate group ID. */ protected $_groupCount = 0; protected $_groupId = false; @@ -65,7 +66,8 @@ public function startGroup($options = []) { 'collapsible' => true, 'open' => 0 ]; - $this->_defaultCollapsible = $options['collapsible']; + $this->config('saved.collapsible', $this->config('collapsible')); + $this->config('collapsible', $options['collapsible']); $this->_autoCloseOnCreate = true; $this->_lastPanelClosed = true; $this->_groupPanelCount = -1; @@ -77,7 +79,7 @@ public function startGroup($options = []) { } public function endGroup() { - $this->_defaultCollapsible = false; + $this->config('collapsible', $this->config('saved.collapsible')); $this->_autoCloseOnCreate = false; $this->_groupId = false; $out = ''; @@ -107,7 +109,7 @@ public function create($title = null, $options = []) { $options += [ 'no-body' => false, 'type' => 'default', - 'collapsible' => $this->_defaultCollapsible + 'collapsible' => $this->config('collapsible') ]; $nobody = $options['no-body']; From fbb81a491a3aee5b391edc2ac64d8e250dbb6c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 11:23:38 +0200 Subject: [PATCH 016/222] Remove useless comments and add test for group. --- .../View/Helper/BootstrapPanelHelperTest.php | 184 ++++++++---------- 1 file changed, 83 insertions(+), 101 deletions(-) diff --git a/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php index 9867590..0c99b61 100644 --- a/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php @@ -226,117 +226,99 @@ public function testHeader () { } - /* - public function testBody () { - $content = 'Body'; - $extraclass = 'my-extra-class'; - // Test with HTML - $result = $this->Modal->body($content); - $this->assertHtml([ - ['div' => [ - 'class' => 'modal-body' - ]], - $content, - '/div' - ], $result); - // Test option - $result = $this->Modal->body($content, ['close' => false, 'class' => $extraclass]); - $this->assertHtml([ + public function testGroup () { + + $panelHeading = 'This is a panel heading'; + $panelContent = 'A bit of HTML code inside!'; + + $result = ''; + $result .= $this->Panel->startGroup(); + $result .= $this->Panel->create($panelHeading); + $result .= $panelContent; + $result .= $this->Panel->create($panelHeading); + $result .= $panelContent; + $result .= $this->Panel->create($panelHeading); + $result .= $panelContent; + $result .= $this->Panel->endGroup(); + $result .= $this->Panel->create($panelHeading); + $result .= $panelContent; + $result .= $this->Panel->end(); + + $expected = [ ['div' => [ - 'class' => $extraclass.' modal-body' + 'id' => 'panelGroup-1', + 'role' => 'tablist', + 'aria-multiselectable' => true, + 'class' => 'panel-group' ]], - $content, - '/div' - ], $result); - // Test null first - $result = $this->Modal->body(null); - $this->assertHtml([ - ['div' => [ - 'class' => 'modal-body' - ]] - ], $result); - // Test option first - $this->Modal->create(); - $result = $this->Modal->body(['class' => $extraclass]); - $this->assertHtml([ - ['div' => [ - 'class' => $extraclass.' modal-body' - ]] - ], $result); - // Test aut close - $this->Modal->create(); - $this->Modal->header(); // Unclosed part - $result = $this->Modal->body(['class' => $extraclass]); - $this->assertHtml([ - '/div', - ['div' => [ - 'class' => $extraclass.' modal-body' - ]] - ], $result); - } + ]; - public function testFooter () { - $content = 'Footer'; - $extraclass = 'my-extra-class'; - // Test with HTML - $result = $this->Modal->footer($content); - $this->assertHtml([ + for ($i = 0; $i < 3; ++$i) { + $expected = array_merge($expected, [ + ['div' => [ + 'class' => 'panel panel-default' + ]], + ['div' => [ + 'role' => 'tab', + 'id' => 'heading-'.$i, + 'class' => 'panel-heading' + ]], + ['h4' => [ + 'class' => 'panel-title' + ]], + ['a' => [ + 'href' => '#collapse-'.$i, + 'data-toggle' => 'collapse', + 'data-parent' => '#panelGroup-1', + 'aria-expanded' => true, + 'aria-controls' => '#collapse-'.$i + ]], + $panelHeading, + '/a', + '/h4', + '/div', + ['div' => [ + 'id' => 'collapse-'.$i, + 'role' => 'tabpanel', + 'aria-labelledby' => 'heading-'.$i, + 'class' => 'panel-collapse collapse'.($i ? '' : ' in'), + + ]], + ['div' => [ + 'class' => 'panel-body' + ]], + $panelContent, + '/div', + '/div', + '/div' + ]); + } + + $expected = array_merge($expected, ['/div']); + + $expected = array_merge($expected, [ ['div' => [ - 'class' => 'modal-footer' + 'class' => 'panel panel-default' ]], - $content, - '/div' - ], $result); - // Test with Array - $result = $this->Modal->footer([$content, $content], ['class' => $extraclass]); - $this->assertHtml([ ['div' => [ - 'class' => $extraclass.' modal-footer' + 'class' => 'panel-heading' ]], - $content, - $content, - '/div' - ], $result); - // Test with null as first arg - $result = $this->Modal->footer(null, ['class' => $extraclass]); - $this->assertHtml([ - ['div' => [ - 'class' => $extraclass.' modal-footer' - ]] - ], $result); - // Test with Options as first arg - $this->Modal->create(); - $result = $this->Modal->footer(['class' => $extraclass]); - $this->assertHtml([ - ['div' => [ - 'class' => $extraclass.' modal-footer' - ]] - ], $result); - // Test with automatic close - $this->Modal->create($content); - $result = $this->Modal->footer(); - $this->assertHtml([ + ['h4' => [ + 'class' => 'panel-title' + ]], + $panelHeading, + '/h4', '/div', ['div' => [ - 'class' => 'modal-footer' - ]] - ], $result); - } + 'class' => 'panel-body' + ]], + $panelContent, + '/div', + '/div' + ]); - public function testEnd() { - $result = $this->Modal->end(); - // Standard close - $this->assertHtml([ - '/div', '/div', '/div' - ], $result); - // Close open part - $this->Modal->create('Title'); // Create modal with open title - $result = $this->Modal->end(); - $this->assertHtml([ - '/div', '/div', '/div', '/div' - ], $result); - } - */ + $this->assertHtml($expected, $result); + } } \ No newline at end of file From 1b2ce973e77e9405f9b84b7f74e1129cb66c1fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 11:56:37 +0200 Subject: [PATCH 017/222] Set lonely panel (outside group) open by default. --- src/View/Helper/BootstrapPanelHelper.php | 30 +++++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 422d356..da88a10 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -50,7 +50,10 @@ class BootstrapPanelHelper extends Helper { protected $_groupId = false; protected $_groupPanelCount = 0; - protected $_groupPanelOpen = 0; + protected $_groupPanelOpen = false; + + /* Attribute set to true when in group. */ + protected $_groupInGroup = false; protected $_lastPanelClosed = true; protected $_autoCloseOnCreate = false; @@ -59,12 +62,12 @@ class BootstrapPanelHelper extends Helper { public function startGroup($options = []) { $options += [ - 'class' => '', - 'role' => 'tablist', + 'class' => '', + 'role' => 'tablist', 'aria-multiselectable' => true, - 'id' => 'panelGroup-'.(++$this->_groupCount), - 'collapsible' => true, - 'open' => 0 + 'id' => 'panelGroup-'.(++$this->_groupCount), + 'collapsible' => true, + 'open' => 0 ]; $this->config('saved.collapsible', $this->config('collapsible')); $this->config('collapsible', $options['collapsible']); @@ -72,7 +75,8 @@ public function startGroup($options = []) { $this->_lastPanelClosed = true; $this->_groupPanelCount = -1; $this->_groupPanelOpen = $options['open']; - $this->_groupId = $options['id']; + $this->_groupId = $options['id']; + $this->_groupInGroup = true; $options = $this->addClass($options, 'panel-group'); unset($options['open'], $options['collapsible']); return $this->Html->tag('div', null, $options); @@ -82,6 +86,8 @@ public function endGroup() { $this->config('collapsible', $this->config('saved.collapsible')); $this->_autoCloseOnCreate = false; $this->_groupId = false; + $this->_groupPanelOpen = false; + $this->_groupInGroup = false; $out = ''; if (!$this->_lastPanelClosed) { $out = $this->end(); @@ -109,13 +115,16 @@ public function create($title = null, $options = []) { $options += [ 'no-body' => false, 'type' => 'default', - 'collapsible' => $this->config('collapsible') + 'collapsible' => $this->config('collapsible'), + 'open' => !$this->_groupInGroup ]; $nobody = $options['no-body']; $type = $options['type']; + $open = $options['open']; $this->_collapsible = $options['collapsible']; - unset ($options['no-body'], $options['collapsible'], $options['type']); + unset ($options['no-body'], $options['collapsible'], + $options['type'], $options['open']); $options = $this->addClass($options, ['panel', 'panel-'.$type]); @@ -123,6 +132,9 @@ public function create($title = null, $options = []) { $this->_headId = 'heading-'.($this->_panelCount); $this->_bodyId = 'collapse-'.($this->_panelCount); $this->_panelCount++; + if ($open) { + $this->_groupPanelOpen = $this->_bodyId; + } } $out = ''; From 93b91de5b4f4fa86062266c27458ba550b943e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 12:24:14 +0200 Subject: [PATCH 018/222] Adding gitattributes file for managing line ending conversions. --- .gitattributes | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..cc37cf1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Define the line ending behavior of the different file extensions +# Set default behaviour, in case users don't have core.autocrlf set. +* text=auto +* text eol=lf + +# Explicitly declare text files we want to always be normalized and converted +# to native line endings on checkout. +*.php text +*.default text +*.ctp text +*.md text +*.po text +*.js text +*.css text +*.ini text +*.txt text +*.xml text From bd2616b48f1d62233fb385f3356bc5ae7c9860ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 12:30:17 +0200 Subject: [PATCH 019/222] Normalizing line endings with git. --- src/View/Helper/BootstrapFlashHelper.php | 192 +-- src/View/Helper/BootstrapPanelHelper.php | 652 ++++----- .../View/Helper/BootstrapFormHelperTest.php | 1186 ++++++++--------- .../View/Helper/BootstrapHtmlHelperTest.php | 340 ++--- .../View/Helper/BootstrapTraitTest.php | 318 ++--- 5 files changed, 1344 insertions(+), 1344 deletions(-) diff --git a/src/View/Helper/BootstrapFlashHelper.php b/src/View/Helper/BootstrapFlashHelper.php index be7530f..c42d9b2 100644 --- a/src/View/Helper/BootstrapFlashHelper.php +++ b/src/View/Helper/BootstrapFlashHelper.php @@ -1,96 +1,96 @@ -Flash->render('somekey'); - * Will default to flash if no param is passed - * - * You can pass additional information into the flash message generation. This allows you - * to consolidate all the parameters for a given type of flash message into the view. - * - * ``` - * echo $this->Flash->render('flash', ['params' => ['name' => $user['User']['name']]]); - * ``` - * - * This would pass the current user's name into the flash message, so you could create personalized - * messages without the controller needing access to that data. - * - * Lastly you can choose the element that is used for rendering the flash message. Using - * custom elements allows you to fully customize how flash messages are generated. - * - * ``` - * echo $this->Flash->render('flash', ['element' => 'my_custom_element']); - * ``` - * - * If you want to use an element from a plugin for rendering your flash message - * you can use the dot notation for the plugin's element name: - * - * ``` - * echo $this->Flash->render('flash', [ - * 'element' => 'MyPlugin.my_custom_element', - * ]); - * ``` - * - * @param string $key The [Flash.]key you are rendering in the view. - * @param array $options Additional options to use for the creation of this flash message. - * Supports the 'params', and 'element' keys that are used in the helper. - * @return string|void Rendered flash message or null if flash key does not exist - * in session. - * @throws \UnexpectedValueException If value for flash settings key is not an array. - */ - public function render($key = 'flash', array $options = []) { - if (!$this->request->session()->check("Flash.$key")) { - return; - } - - $flash = $this->request->session()->read("Flash.$key"); - if (!is_array($flash)) { - throw new \UnexpectedValueException(sprintf( - 'Value for flash setting key "%s" must be an array.', - $key - )); - } - foreach ($flash as &$message) { - if (in_array(basename($message['element']), $this->_bootstrapTemplates)) { - $message['element'] = 'Bootstrap.'.$message['element']; - } - } - $this->request->session()->write("Flash.$key", $flash); - - return parent::render($key, $options); - } - -} - -?> +Flash->render('somekey'); + * Will default to flash if no param is passed + * + * You can pass additional information into the flash message generation. This allows you + * to consolidate all the parameters for a given type of flash message into the view. + * + * ``` + * echo $this->Flash->render('flash', ['params' => ['name' => $user['User']['name']]]); + * ``` + * + * This would pass the current user's name into the flash message, so you could create personalized + * messages without the controller needing access to that data. + * + * Lastly you can choose the element that is used for rendering the flash message. Using + * custom elements allows you to fully customize how flash messages are generated. + * + * ``` + * echo $this->Flash->render('flash', ['element' => 'my_custom_element']); + * ``` + * + * If you want to use an element from a plugin for rendering your flash message + * you can use the dot notation for the plugin's element name: + * + * ``` + * echo $this->Flash->render('flash', [ + * 'element' => 'MyPlugin.my_custom_element', + * ]); + * ``` + * + * @param string $key The [Flash.]key you are rendering in the view. + * @param array $options Additional options to use for the creation of this flash message. + * Supports the 'params', and 'element' keys that are used in the helper. + * @return string|void Rendered flash message or null if flash key does not exist + * in session. + * @throws \UnexpectedValueException If value for flash settings key is not an array. + */ + public function render($key = 'flash', array $options = []) { + if (!$this->request->session()->check("Flash.$key")) { + return; + } + + $flash = $this->request->session()->read("Flash.$key"); + if (!is_array($flash)) { + throw new \UnexpectedValueException(sprintf( + 'Value for flash setting key "%s" must be an array.', + $key + )); + } + foreach ($flash as &$message) { + if (in_array(basename($message['element']), $this->_bootstrapTemplates)) { + $message['element'] = 'Bootstrap.'.$message['element']; + } + } + $this->request->session()->write("Flash.$key", $flash); + + return parent::render($key, $options); + } + +} + +?> diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index da88a10..39c303e 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -1,326 +1,326 @@ - [ - 'className' => 'Bootstrap.BootstrapHtml' - ] - ]; - - public $_defaultConfig = [ - 'collapsible' => false - ]; - - public $current = NULL ; - - /* Protected attributes used to generate ID for collapsible panels. */ - protected $_panelCount = 0; - protected $_bodyId = null; - protected $_headId = null; - - /* Protected attribute used to generate group ID. */ - protected $_groupCount = 0; - protected $_groupId = false; - - protected $_groupPanelCount = 0; - protected $_groupPanelOpen = false; - - /* Attribute set to true when in group. */ - protected $_groupInGroup = false; - - protected $_lastPanelClosed = true; - protected $_autoCloseOnCreate = false; - - protected $_collapsible = false; - - public function startGroup($options = []) { - $options += [ - 'class' => '', - 'role' => 'tablist', - 'aria-multiselectable' => true, - 'id' => 'panelGroup-'.(++$this->_groupCount), - 'collapsible' => true, - 'open' => 0 - ]; - $this->config('saved.collapsible', $this->config('collapsible')); - $this->config('collapsible', $options['collapsible']); - $this->_autoCloseOnCreate = true; - $this->_lastPanelClosed = true; - $this->_groupPanelCount = -1; - $this->_groupPanelOpen = $options['open']; - $this->_groupId = $options['id']; - $this->_groupInGroup = true; - $options = $this->addClass($options, 'panel-group'); - unset($options['open'], $options['collapsible']); - return $this->Html->tag('div', null, $options); - } - - public function endGroup() { - $this->config('collapsible', $this->config('saved.collapsible')); - $this->_autoCloseOnCreate = false; - $this->_groupId = false; - $this->_groupPanelOpen = false; - $this->_groupInGroup = false; - $out = ''; - if (!$this->_lastPanelClosed) { - $out = $this->end(); - } - return $out.''; - } - - /** - * - * Create a Twitter Bootstrap like panel. - * - * @param array|string $title If array, works as $options, otherwize used as the panel title. - * @param array $options Options for the main div of the panel. - * - * Extra options (useless if $title not specified) : - * - no-body: Do not open the body after the create (default false) - **/ - public function create($title = null, $options = []) { - - if (is_array($title)) { - $options = $title; - $title = null; - } - - $options += [ - 'no-body' => false, - 'type' => 'default', - 'collapsible' => $this->config('collapsible'), - 'open' => !$this->_groupInGroup - ]; - - $nobody = $options['no-body']; - $type = $options['type']; - $open = $options['open']; - $this->_collapsible = $options['collapsible']; - unset ($options['no-body'], $options['collapsible'], - $options['type'], $options['open']); - - $options = $this->addClass($options, ['panel', 'panel-'.$type]); - - if ($this->_collapsible) { - $this->_headId = 'heading-'.($this->_panelCount); - $this->_bodyId = 'collapse-'.($this->_panelCount); - $this->_panelCount++; - if ($open) { - $this->_groupPanelOpen = $this->_bodyId; - } - } - - $out = ''; - - if ($this->_autoCloseOnCreate && !$this->_lastPanelClosed) { - $out .= $this->end(); - } - $this->_lastPanelClosed = false; - - /* Increment panel counter for the current group. */ - $this->_groupPanelCount++; - - $out .= $this->Html->tag('div', null, $options); - if (is_string($title) && $title) { - $out .= $this->_createHeader($title, [ - 'title' => isset($options['title']) ? $options['title'] : true - ]) ; - if (!$nobody) { - $out .= $this->_createBody(); - } - } - - return $out ; - } - - /** - * - * End a panel. If $title is not null, the PanelHelper::footer functions - * is called with $title and $options arguments. - * - * @param string|null $buttons - * @param array $options - * - **/ - public function end ($title = null, $options = []) { - $this->_lastPanelClosed = true; - $res = '' ; - $res .= $this->_cleanCurrent(); - if ($title !== null) { - $res .= $this->footer($title, $options) ; - } - $res .= '' ; - return $res ; - } - - protected function _cleanCurrent () { - $res = ''; - if ($this->current) { - $res .= ''; - if ($this->_collapsible && $this->current == 'body') { - $res .= ''; - } - $this->current = null ; - } - return $res; - } - - protected function _createHeader ($title, $options = [], $titleOptions = []) { - if (empty($titleOptions)) { - $titleOptions = $options['title'] ; - } - unset ($options['title']); - $title = $this->_makeIcon($title, $converted); - $options += [ - 'escape' => !$converted - ]; - $options = $this->addClass($options, 'panel-heading'); - if ($this->_collapsible) { - $options += [ - 'role' => 'tab', - 'id' => $this->_headId - ]; - $this->_headId = $options['id']; - $title = $this->Html->link($title, '#'.$this->_bodyId, [ - 'data-toggle' => 'collapse', - 'data-parent' => $this->_groupId ? '#'.$this->_groupId : false, - 'aria-expanded' => true, - 'aria-controls' => '#'.$this->_bodyId, - 'escape' => $options['escape'] - ]); - $options['escape'] = false; // Should not escape after - } - if ($titleOptions !== false) { - if (!is_array($titleOptions)) { - $titleOptions = []; - } - $titleOptions += [ - 'tag' => 'h4', - 'escape' => $options['escape'] - ]; - $titleOptions = $this->addClass($titleOptions, 'panel-title'); - $tag = $titleOptions['tag']; - unset($titleOptions['tag']); - $title = $this->Html->tag($tag, $title, $titleOptions); - } - unset($options['escape']); - return $this->_cleanCurrent().$this->Html->tag('div', $title, $options); - } - - protected function _createBody ($text = null, $options = []) { - $options = $this->addClass($options, 'panel-body'); - $body = $this->Html->tag('div', $text, $options); - if ($this->_collapsible) { - $open = ((is_int($this->_groupPanelOpen) - && $this->_groupPanelOpen === $this->_groupPanelCount) - || $this->_groupPanelOpen === $this->_bodyId) ? ' in' : ''; - $body = $this->Html->div('panel-collapse collapse'.$open, $text ? $body : null, [ - 'role' => 'tabpanel', - 'aria-labelledby' => $this->_headId, - 'id' => $this->_bodyId - ]).($text ? '' : $body); - } - $body = $this->_cleanCurrent().$body; - if (!$text) { - $this->current = 'body'; - } - return $body; - } - - protected function _createFooter ($text = null, $options = []) { - $options = $this->addClass($options, 'panel-footer'); - return $this->_cleanCurrent().$this->Html->tag('div', $text, $options) ; - } - - /** - * - * Create / Start the header. If $info is specified as a string, create and return the - * whole header, otherwize only open the header. - * - * @param array|string $info If string, use as the panel title, otherwize works as $options. - * @param array $options Options for the header div. - * - * Special option (if $info is string): - * - close: Add the 'close' button in the header (default true). - * - **/ - public function header ($info = null, $options = []) { - if (is_array($info)) { - $options = $info; - $info = null; - } - $options += [ - 'title' => true - ]; - return $this->_createHeader($info, $options) ; - } - - /** - * - * Create / Start the body. If $info is not null, it is used as the body content, otherwize - * start the body div. - * - * @param array|string $info If string, use as the body content, otherwize works as $options. - * @param array $options Options for the footer div. - * - * - **/ - public function body ($info = null, $options = []) { - if (is_array($info)) { - $options = $info; - $info = null; - } - return $this->_createBody($info, $options); - } - - protected function _isAssociativeArray ($array) { - return array_keys($array) !== range(0, count($array) - 1); - } - - /** - * - * Create / Start the footer. If $buttons is specified as an associative arrays or as null, - * start the footer, otherwize create the footer with the specified text. - * - * @param string $text Use as the footer content. - * @param array $options Options for the footer div. - * - **/ - public function footer ($text = null, $options = []) { - if (is_array($text)) { - $options = $text; - $text = null; - } - return $this->_createFooter($text, $options) ; - } - -} - -?> + [ + 'className' => 'Bootstrap.BootstrapHtml' + ] + ]; + + public $_defaultConfig = [ + 'collapsible' => false + ]; + + public $current = NULL ; + + /* Protected attributes used to generate ID for collapsible panels. */ + protected $_panelCount = 0; + protected $_bodyId = null; + protected $_headId = null; + + /* Protected attribute used to generate group ID. */ + protected $_groupCount = 0; + protected $_groupId = false; + + protected $_groupPanelCount = 0; + protected $_groupPanelOpen = false; + + /* Attribute set to true when in group. */ + protected $_groupInGroup = false; + + protected $_lastPanelClosed = true; + protected $_autoCloseOnCreate = false; + + protected $_collapsible = false; + + public function startGroup($options = []) { + $options += [ + 'class' => '', + 'role' => 'tablist', + 'aria-multiselectable' => true, + 'id' => 'panelGroup-'.(++$this->_groupCount), + 'collapsible' => true, + 'open' => 0 + ]; + $this->config('saved.collapsible', $this->config('collapsible')); + $this->config('collapsible', $options['collapsible']); + $this->_autoCloseOnCreate = true; + $this->_lastPanelClosed = true; + $this->_groupPanelCount = -1; + $this->_groupPanelOpen = $options['open']; + $this->_groupId = $options['id']; + $this->_groupInGroup = true; + $options = $this->addClass($options, 'panel-group'); + unset($options['open'], $options['collapsible']); + return $this->Html->tag('div', null, $options); + } + + public function endGroup() { + $this->config('collapsible', $this->config('saved.collapsible')); + $this->_autoCloseOnCreate = false; + $this->_groupId = false; + $this->_groupPanelOpen = false; + $this->_groupInGroup = false; + $out = ''; + if (!$this->_lastPanelClosed) { + $out = $this->end(); + } + return $out.''; + } + + /** + * + * Create a Twitter Bootstrap like panel. + * + * @param array|string $title If array, works as $options, otherwize used as the panel title. + * @param array $options Options for the main div of the panel. + * + * Extra options (useless if $title not specified) : + * - no-body: Do not open the body after the create (default false) + **/ + public function create($title = null, $options = []) { + + if (is_array($title)) { + $options = $title; + $title = null; + } + + $options += [ + 'no-body' => false, + 'type' => 'default', + 'collapsible' => $this->config('collapsible'), + 'open' => !$this->_groupInGroup + ]; + + $nobody = $options['no-body']; + $type = $options['type']; + $open = $options['open']; + $this->_collapsible = $options['collapsible']; + unset ($options['no-body'], $options['collapsible'], + $options['type'], $options['open']); + + $options = $this->addClass($options, ['panel', 'panel-'.$type]); + + if ($this->_collapsible) { + $this->_headId = 'heading-'.($this->_panelCount); + $this->_bodyId = 'collapse-'.($this->_panelCount); + $this->_panelCount++; + if ($open) { + $this->_groupPanelOpen = $this->_bodyId; + } + } + + $out = ''; + + if ($this->_autoCloseOnCreate && !$this->_lastPanelClosed) { + $out .= $this->end(); + } + $this->_lastPanelClosed = false; + + /* Increment panel counter for the current group. */ + $this->_groupPanelCount++; + + $out .= $this->Html->tag('div', null, $options); + if (is_string($title) && $title) { + $out .= $this->_createHeader($title, [ + 'title' => isset($options['title']) ? $options['title'] : true + ]) ; + if (!$nobody) { + $out .= $this->_createBody(); + } + } + + return $out ; + } + + /** + * + * End a panel. If $title is not null, the PanelHelper::footer functions + * is called with $title and $options arguments. + * + * @param string|null $buttons + * @param array $options + * + **/ + public function end ($title = null, $options = []) { + $this->_lastPanelClosed = true; + $res = '' ; + $res .= $this->_cleanCurrent(); + if ($title !== null) { + $res .= $this->footer($title, $options) ; + } + $res .= '' ; + return $res ; + } + + protected function _cleanCurrent () { + $res = ''; + if ($this->current) { + $res .= ''; + if ($this->_collapsible && $this->current == 'body') { + $res .= ''; + } + $this->current = null ; + } + return $res; + } + + protected function _createHeader ($title, $options = [], $titleOptions = []) { + if (empty($titleOptions)) { + $titleOptions = $options['title'] ; + } + unset ($options['title']); + $title = $this->_makeIcon($title, $converted); + $options += [ + 'escape' => !$converted + ]; + $options = $this->addClass($options, 'panel-heading'); + if ($this->_collapsible) { + $options += [ + 'role' => 'tab', + 'id' => $this->_headId + ]; + $this->_headId = $options['id']; + $title = $this->Html->link($title, '#'.$this->_bodyId, [ + 'data-toggle' => 'collapse', + 'data-parent' => $this->_groupId ? '#'.$this->_groupId : false, + 'aria-expanded' => true, + 'aria-controls' => '#'.$this->_bodyId, + 'escape' => $options['escape'] + ]); + $options['escape'] = false; // Should not escape after + } + if ($titleOptions !== false) { + if (!is_array($titleOptions)) { + $titleOptions = []; + } + $titleOptions += [ + 'tag' => 'h4', + 'escape' => $options['escape'] + ]; + $titleOptions = $this->addClass($titleOptions, 'panel-title'); + $tag = $titleOptions['tag']; + unset($titleOptions['tag']); + $title = $this->Html->tag($tag, $title, $titleOptions); + } + unset($options['escape']); + return $this->_cleanCurrent().$this->Html->tag('div', $title, $options); + } + + protected function _createBody ($text = null, $options = []) { + $options = $this->addClass($options, 'panel-body'); + $body = $this->Html->tag('div', $text, $options); + if ($this->_collapsible) { + $open = ((is_int($this->_groupPanelOpen) + && $this->_groupPanelOpen === $this->_groupPanelCount) + || $this->_groupPanelOpen === $this->_bodyId) ? ' in' : ''; + $body = $this->Html->div('panel-collapse collapse'.$open, $text ? $body : null, [ + 'role' => 'tabpanel', + 'aria-labelledby' => $this->_headId, + 'id' => $this->_bodyId + ]).($text ? '' : $body); + } + $body = $this->_cleanCurrent().$body; + if (!$text) { + $this->current = 'body'; + } + return $body; + } + + protected function _createFooter ($text = null, $options = []) { + $options = $this->addClass($options, 'panel-footer'); + return $this->_cleanCurrent().$this->Html->tag('div', $text, $options) ; + } + + /** + * + * Create / Start the header. If $info is specified as a string, create and return the + * whole header, otherwize only open the header. + * + * @param array|string $info If string, use as the panel title, otherwize works as $options. + * @param array $options Options for the header div. + * + * Special option (if $info is string): + * - close: Add the 'close' button in the header (default true). + * + **/ + public function header ($info = null, $options = []) { + if (is_array($info)) { + $options = $info; + $info = null; + } + $options += [ + 'title' => true + ]; + return $this->_createHeader($info, $options) ; + } + + /** + * + * Create / Start the body. If $info is not null, it is used as the body content, otherwize + * start the body div. + * + * @param array|string $info If string, use as the body content, otherwize works as $options. + * @param array $options Options for the footer div. + * + * + **/ + public function body ($info = null, $options = []) { + if (is_array($info)) { + $options = $info; + $info = null; + } + return $this->_createBody($info, $options); + } + + protected function _isAssociativeArray ($array) { + return array_keys($array) !== range(0, count($array) - 1); + } + + /** + * + * Create / Start the footer. If $buttons is specified as an associative arrays or as null, + * start the footer, otherwize create the footer with the specified text. + * + * @param string $text Use as the footer content. + * @param array $options Options for the footer div. + * + **/ + public function footer ($text = null, $options = []) { + if (is_array($text)) { + $options = $text; + $text = null; + } + return $this->_createFooter($text, $options) ; + } + +} + +?> diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php index 2805a2c..ed6d3e5 100644 --- a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -1,594 +1,594 @@ -View = new View(); - $this->Form = new BootstrapFormHelper ($this->View); - } - - /** - * Tear Down - * - * @return void - */ - public function tearDown() { - parent::tearDown(); - unset($this->Form); - unset($this->View); - } - - public function testCreate () { - // Standard form - $this->assertHtml([ - ['form' => [ - 'method', - 'accept-charset', - 'role' => 'form', - 'action' - ]] - ], $this->Form->create ()) ; - // Horizontal form - $result = $this->Form->create (null, ['horizontal' => true]) ; - $this->assertEquals($this->Form->horizontal, true) ; - // Automatically return to non horizonal form - $result = $this->Form->create () ; - $this->assertEquals($this->Form->horizontal, false) ; - // Inline form - $result = $this->Form->create (null, ['inline' => true]) ; - $this->assertEquals($this->Form->inline, true) ; - $this->assertHtml([ - ['form' => [ - 'method', - 'accept-charset', - 'role' => 'form', - 'action', - 'class' => 'form-inline' - ]] - ], $result) ; - // Automatically return to non horizonal form - $result = $this->Form->create () ; - $this->assertEquals($this->Form->inline, false) ; - } - - protected function _testInput ($expected, $fieldName, $options = []) { - $formOptions = [] ; - if (isset($options['_formOptions'])) { - $formOptions = $options['_formOptions'] ; - unset ($options['_formOptions']) ; - } - $this->Form->create (null, $formOptions) ; - return $this->assertHtml ($expected, $this->Form->input ($fieldName, $options)) ; - } - - public function testInput () { - $fieldName = 'field' ; - // Standard form - $this->_testInput ([ - ['div' => [ - 'class' => 'form-group text' - ]], - ['label' => [ - 'class' => 'control-label', - 'for' => $fieldName - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - '/div' - ], $fieldName) ; - // Horizontal form - $this->_testInput ([ - ['div' => [ - 'class' => 'form-group text' - ]], - ['label' => [ - 'class' => 'control-label col-md-2', - 'for' => $fieldName - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['div' => [ - 'class' => 'col-md-10' - ]], - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - '/div', - '/div' - ], $fieldName, [ - '_formOptions' => ['horizontal' => true] - ]) ; - } - - public function testInputText () { - $fieldName = 'field' ; - $this->_testInput ([ - ['div' => [ - 'class' => 'form-group text' - ]], - ['label' => [ - 'class' => 'control-label', - 'for' => $fieldName - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - '/div' - ], $fieldName, ['type' => 'text']) ; - } - - public function testInputSelect () { - - } - - public function testInputRadio () { - $fieldName = 'color' ; - $options = [ - 'type' => 'radio', - 'options' => [ - 'red' => 'Red', - 'blue' => 'Blue', - 'green' => 'Green' - ] - ] ; - // Default - $expected = [ - ['div' => [ - 'class' => 'form-group' - ]], - ['label' => [ - 'class' => 'control-label' - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['input' => [ - 'type' => 'hidden', - 'name' => $fieldName, - 'value' => '', - 'class' => 'form-control' - ]] - ] ; - foreach ($options['options'] as $key => $value) { - $expected = array_merge($expected, [ - ['div' => [ - 'class' => 'radio' - ]], - ['label' => [ - 'for' => $fieldName.'-'.$key - ]], - ['input' => [ - 'type' => 'radio', - 'name' => $fieldName, - 'value' => $key, - 'id' => $fieldName.'-'.$key - ]], - $value, - '/label', - '/div' - ]) ; - } - $expected = array_merge ($expected, ['/div']) ; - $this->_testInput ($expected, $fieldName, $options) ; - // Inline - $options += [ - 'inline' => true - ] ; - $expected = [ - ['div' => [ - 'class' => 'form-group' - ]], - ['label' => [ - 'class' => 'control-label' - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['input' => [ - 'type' => 'hidden', - 'name' => $fieldName, - 'value' => '', - 'class' => 'form-control' - ]] - ] ; - foreach ($options['options'] as $key => $value) { - $expected = array_merge($expected, [ - ['label' => [ - 'class' => 'radio-inline', - 'for' => $fieldName.'-'.$key - ]], - ['input' => [ - 'type' => 'radio', - 'name' => $fieldName, - 'value' => $key, - 'id' => $fieldName.'-'.$key - ]], - $value, - '/label' - ]) ; - } - $expected = array_merge ($expected, ['/div']) ; - $this->_testInput ($expected, $fieldName, $options) ; - // Horizontal - $options += [ - '_formOptions' => ['horizontal' => true] - ] ; - $options['inline'] = false ; - $expected = [ - ['div' => [ - 'class' => 'form-group' - ]], - ['label' => [ - 'class' => 'control-label col-md-2' - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['div' => [ - 'class' => 'col-md-10' - ]], - ['input' => [ - 'type' => 'hidden', - 'name' => $fieldName, - 'value' => '', - 'class' => 'form-control' - ]] - ] ; - foreach ($options['options'] as $key => $value) { - $expected = array_merge($expected, [ - ['div' => [ - 'class' => 'radio' - ]], - ['label' => [ - 'for' => $fieldName.'-'.$key - ]], - ['input' => [ - 'type' => 'radio', - 'name' => $fieldName, - 'value' => $key, - 'id' => $fieldName.'-'.$key - ]], - $value, - '/label', - '/div' - ]) ; - } - $expected = array_merge ($expected, ['/div', '/div']) ; - $this->_testInput ($expected, $fieldName, $options) ; - // Horizontal + Inline - $options['inline'] = true ; - $expected = [ - ['div' => [ - 'class' => 'form-group' - ]], - ['label' => [ - 'class' => 'control-label col-md-2' - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['div' => [ - 'class' => 'col-md-10' - ]], - ['input' => [ - 'type' => 'hidden', - 'name' => $fieldName, - 'value' => '', - 'class' => 'form-control' - ]] - ] ; - foreach ($options['options'] as $key => $value) { - $expected = array_merge($expected, [ - ['label' => [ - 'class' => 'radio-inline', - 'for' => $fieldName.'-'.$key - ]], - ['input' => [ - 'type' => 'radio', - 'name' => $fieldName, - 'value' => $key, - 'id' => $fieldName.'-'.$key - ]], - $value, - '/label' - ]) ; - } - $expected = array_merge ($expected, ['/div', '/div']) ; - $this->_testInput ($expected, $fieldName, $options) ; - } - - public function testInputCheckbox () { - - } - - public function testInputGroup () { - $fieldName = 'field' ; - $options = [ - 'type' => 'text', - 'label' => false - ] ; - // Test with prepend addon - $expected = [ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['span' => [ - 'class' => 'input-group-addon' - ]], - '@', - '/span', - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - '/div', - '/div' - ] ; - $this->_testInput ($expected, $fieldName, $options + ['prepend' => '@']) ; - // Test with append - $expected = [ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - ['span' => [ - 'class' => 'input-group-addon' - ]], - '.00', - '/span', - '/div', - '/div' - ] ; - $this->_testInput ($expected, $fieldName, $options + ['append' => '.00']) ; - // Test with append + prepend - $expected = [ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['span' => [ - 'class' => 'input-group-addon' - ]], - '$', - '/span', - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - ['span' => [ - 'class' => 'input-group-addon' - ]], - '.00', - '/span', - '/div', - '/div' - ] ; - $this->_testInput ($expected, $fieldName, - $options + ['prepend' => '$', 'append' => '.00']) ; - // Test with prepend button - $expected = [ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['span' => [ - 'class' => 'input-group-btn' - ]], - ['button' => [ - 'class' => 'btn btn-default', - 'type' => 'submit' - ]], - 'Go!', - '/button', - '/span', - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - '/div', - '/div' - ] ; - - $this->_testInput ($expected, $fieldName, - $options + ['prepend' => $this->Form->button('Go!')]) ; - - // Test with append button - $expected = [ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - ['span' => [ - 'class' => 'input-group-btn' - ]], - ['button' => [ - 'class' => 'btn btn-default', - 'type' => 'submit' - ]], - 'Go!', - '/button', - '/span', - '/div', - '/div' - ] ; - $this->_testInput ($expected, $fieldName, - $options + ['append' => $this->Form->button('Go!')]) ; - // Test with append 2 button - $expected = [ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - ['span' => [ - 'class' => 'input-group-btn' - ]], - ['button' => [ - 'class' => 'btn btn-default', - 'type' => 'submit' - ]], - 'Go!', - '/button', - ['button' => [ - 'class' => 'btn btn-default', - 'type' => 'submit' - ]], - 'GoGo!', - '/button', - '/span', - '/div', - '/div' - ] ; - $this->_testInput ($expected, $fieldName, $options + [ - 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] - ]) ; - // Test with append dropdown - $expected = [ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - ['span' => [ - 'class' => 'input-group-btn' - ]], - ['div' => [ - 'class' => 'btn-group' - ]], - ['button' => [ - 'data-toggle' => 'dropdown', - 'class' => 'dropdown-toggle btn btn-default' - ]], - 'Action', - ['span' => ['class' => 'caret']], '/span', - '/button', - ['ul' => [ - 'class' => 'dropdown-menu', - 'role' => 'menu' - ]], - ['li' => [ - 'role' => 'presentation' - ]], ['a' => ['href' => '#']], 'Link 1', '/a', '/li', - ['li' => [ - 'role' => 'presentation' - ]], ['a' => ['href' => '#']], 'Link 2', '/a', '/li', - ['li' => [ - 'role' => 'presentation', - 'class' => 'divider' - ]], '/li', - ['li' => [ - 'role' => 'presentation' - ]], ['a' => ['href' => '#']], 'Link 3', '/a', '/li', - '/ul', - '/div', - '/span', - '/div', - '/div' - ] ; - $this->_testInput ($expected, $fieldName, $options + [ - 'append' => $this->Form->dropdownButton('Action', [ - $this->Form->Html->link('Link 1', '#'), - $this->Form->Html->link('Link 2', '#'), - 'divider', - $this->Form->Html->link('Link 3', '#') - ]) - ]); - } - - public function testInputTemplateVars () { - $fieldName = 'field' ; - // Add a template with the help placeholder. - $help = 'Some help text.'; - $this->Form->templates([ - 'inputContainer' => '
{{content}}{{help}}
' - ]); - // Standard form - $this->_testInput ([ - ['div' => [ - 'class' => 'form-group text' - ]], - ['label' => [ - 'class' => 'control-label', - 'for' => $fieldName - ]], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => $fieldName, - 'id' => $fieldName - ]], - ['span' => true], - $help, - '/span', - '/div' - ], $fieldName, ['templateVars' => ['help' => $help]]) ; - } - +View = new View(); + $this->Form = new BootstrapFormHelper ($this->View); + } + + /** + * Tear Down + * + * @return void + */ + public function tearDown() { + parent::tearDown(); + unset($this->Form); + unset($this->View); + } + + public function testCreate () { + // Standard form + $this->assertHtml([ + ['form' => [ + 'method', + 'accept-charset', + 'role' => 'form', + 'action' + ]] + ], $this->Form->create ()) ; + // Horizontal form + $result = $this->Form->create (null, ['horizontal' => true]) ; + $this->assertEquals($this->Form->horizontal, true) ; + // Automatically return to non horizonal form + $result = $this->Form->create () ; + $this->assertEquals($this->Form->horizontal, false) ; + // Inline form + $result = $this->Form->create (null, ['inline' => true]) ; + $this->assertEquals($this->Form->inline, true) ; + $this->assertHtml([ + ['form' => [ + 'method', + 'accept-charset', + 'role' => 'form', + 'action', + 'class' => 'form-inline' + ]] + ], $result) ; + // Automatically return to non horizonal form + $result = $this->Form->create () ; + $this->assertEquals($this->Form->inline, false) ; + } + + protected function _testInput ($expected, $fieldName, $options = []) { + $formOptions = [] ; + if (isset($options['_formOptions'])) { + $formOptions = $options['_formOptions'] ; + unset ($options['_formOptions']) ; + } + $this->Form->create (null, $formOptions) ; + return $this->assertHtml ($expected, $this->Form->input ($fieldName, $options)) ; + } + + public function testInput () { + $fieldName = 'field' ; + // Standard form + $this->_testInput ([ + ['div' => [ + 'class' => 'form-group text' + ]], + ['label' => [ + 'class' => 'control-label', + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div' + ], $fieldName) ; + // Horizontal form + $this->_testInput ([ + ['div' => [ + 'class' => 'form-group text' + ]], + ['label' => [ + 'class' => 'control-label col-md-2', + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => 'col-md-10' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div', + '/div' + ], $fieldName, [ + '_formOptions' => ['horizontal' => true] + ]) ; + } + + public function testInputText () { + $fieldName = 'field' ; + $this->_testInput ([ + ['div' => [ + 'class' => 'form-group text' + ]], + ['label' => [ + 'class' => 'control-label', + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div' + ], $fieldName, ['type' => 'text']) ; + } + + public function testInputSelect () { + + } + + public function testInputRadio () { + $fieldName = 'color' ; + $options = [ + 'type' => 'radio', + 'options' => [ + 'red' => 'Red', + 'blue' => 'Blue', + 'green' => 'Green' + ] + ] ; + // Default + $expected = [ + ['div' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'class' => 'control-label' + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['div' => [ + 'class' => 'radio' + ]], + ['label' => [ + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + $value, + '/label', + '/div' + ]) ; + } + $expected = array_merge ($expected, ['/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Inline + $options += [ + 'inline' => true + ] ; + $expected = [ + ['div' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'class' => 'control-label' + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => 'radio-inline', + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Horizontal + $options += [ + '_formOptions' => ['horizontal' => true] + ] ; + $options['inline'] = false ; + $expected = [ + ['div' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'class' => 'control-label col-md-2' + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => 'col-md-10' + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['div' => [ + 'class' => 'radio' + ]], + ['label' => [ + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + $value, + '/label', + '/div' + ]) ; + } + $expected = array_merge ($expected, ['/div', '/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Horizontal + Inline + $options['inline'] = true ; + $expected = [ + ['div' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'class' => 'control-label col-md-2' + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => 'col-md-10' + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => 'radio-inline', + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div', '/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + } + + public function testInputCheckbox () { + + } + + public function testInputGroup () { + $fieldName = 'field' ; + $options = [ + 'type' => 'text', + 'label' => false + ] ; + // Test with prepend addon + $expected = [ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '@', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div', + '/div' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => '@']) ; + // Test with append + $expected = [ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '.00', + '/span', + '/div', + '/div' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['append' => '.00']) ; + // Test with append + prepend + $expected = [ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '$', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '.00', + '/span', + '/div', + '/div' + ] ; + $this->_testInput ($expected, $fieldName, + $options + ['prepend' => '$', 'append' => '.00']) ; + // Test with prepend button + $expected = [ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => 'btn btn-default', + 'type' => 'submit' + ]], + 'Go!', + '/button', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div', + '/div' + ] ; + + $this->_testInput ($expected, $fieldName, + $options + ['prepend' => $this->Form->button('Go!')]) ; + + // Test with append button + $expected = [ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => 'btn btn-default', + 'type' => 'submit' + ]], + 'Go!', + '/button', + '/span', + '/div', + '/div' + ] ; + $this->_testInput ($expected, $fieldName, + $options + ['append' => $this->Form->button('Go!')]) ; + // Test with append 2 button + $expected = [ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => 'btn btn-default', + 'type' => 'submit' + ]], + 'Go!', + '/button', + ['button' => [ + 'class' => 'btn btn-default', + 'type' => 'submit' + ]], + 'GoGo!', + '/button', + '/span', + '/div', + '/div' + ] ; + $this->_testInput ($expected, $fieldName, $options + [ + 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] + ]) ; + // Test with append dropdown + $expected = [ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['div' => [ + 'class' => 'btn-group' + ]], + ['button' => [ + 'data-toggle' => 'dropdown', + 'class' => 'dropdown-toggle btn btn-default' + ]], + 'Action', + ['span' => ['class' => 'caret']], '/span', + '/button', + ['ul' => [ + 'class' => 'dropdown-menu', + 'role' => 'menu' + ]], + ['li' => [ + 'role' => 'presentation' + ]], ['a' => ['href' => '#']], 'Link 1', '/a', '/li', + ['li' => [ + 'role' => 'presentation' + ]], ['a' => ['href' => '#']], 'Link 2', '/a', '/li', + ['li' => [ + 'role' => 'presentation', + 'class' => 'divider' + ]], '/li', + ['li' => [ + 'role' => 'presentation' + ]], ['a' => ['href' => '#']], 'Link 3', '/a', '/li', + '/ul', + '/div', + '/span', + '/div', + '/div' + ] ; + $this->_testInput ($expected, $fieldName, $options + [ + 'append' => $this->Form->dropdownButton('Action', [ + $this->Form->Html->link('Link 1', '#'), + $this->Form->Html->link('Link 2', '#'), + 'divider', + $this->Form->Html->link('Link 3', '#') + ]) + ]); + } + + public function testInputTemplateVars () { + $fieldName = 'field' ; + // Add a template with the help placeholder. + $help = 'Some help text.'; + $this->Form->templates([ + 'inputContainer' => '
{{content}}{{help}}
' + ]); + // Standard form + $this->_testInput ([ + ['div' => [ + 'class' => 'form-group text' + ]], + ['label' => [ + 'class' => 'control-label', + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => true], + $help, + '/span', + '/div' + ], $fieldName, ['templateVars' => ['help' => $help]]) ; + } + } \ No newline at end of file diff --git a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php index 857dfd6..1cad192 100644 --- a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php @@ -1,171 +1,171 @@ -View = new View(); - $this->Html = new BootstrapHtmlHelper ($this->View); - } - - /** - * Tear Down - * - * @return void - */ - public function tearDown() { - parent::tearDown(); - unset($this->Html); - unset($this->View); - } - - public function testInitialize () { - $oldHtml = $this->Html ; - // Test useGlyphicon - $type = 'home'; - $options = [ - 'id' => 'my-home', - 'class' => 'my-home-class' - ] ; - $this->Html = new BootstrapHtmlHelper ($this->View, [ - 'useGlyphicon' => true - ]) ; - $this->assertEquals ($this->Html->icon ($type, $options), $this->Html->glIcon($type, $options)) ; - unset ($this->Html) ; - $this->Html = $oldHtml ; - } - - public function testIcon () { - $type = 'home'; - $options = [ - 'id' => 'my-home', - 'class' => 'my-home-class' - ] ; - // Default icon (Glyphicon) - $this->assertHtml ([ - ['i' => [ - 'class' => 'glyphicon glyphicon-'.$type - ]], - '/i' - ], $this->Html->icon($type)); - $this->assertHtml ([ - ['i' => [ - 'class' => $options['class'].' glyphicon glyphicon-'.$type, - 'id' => $options['id'] - ]], - '/i' - ], $this->Html->icon($type, $options)); - // FontAwesome icon - $this->assertHtml ([ - ['i' => [ - 'class' => 'fa fa-'.$type - ]], - '/i' - ], $this->Html->faIcon($type)); - $this->assertHtml ([ - ['i' => [ - 'class' => $options['class'].' fa fa-'.$type, - 'id' => $options['id'] - ]], - '/i' - ], $this->Html->faIcon($type, $options)); - } - - public function testLabel () { - $content = 'My Label' ; - // Standard test - $this->assertHtml ([ - ['span' => [ - 'class' => 'label label-default' - ]], - 'My Label', - '/span' - ], $this->Html->label($content)) ; - // Type - $this->assertHtml ([ - ['span' => [ - 'class' => 'label label-primary' - ]], - 'My Label', - '/span' - ], $this->Html->label($content, 'primary')) ; - // Type + Options - $options = [ - 'class' => 'my-label-class', - 'id' => 'my-label-id' - ] ; - $this->assertHtml ([ - ['span' => [ - 'class' => $options['class'].' label label-primary', - 'id' => $options['id'] - ]], - 'My Label', - '/span' - ], $this->Html->label($content, 'primary', $options)) ; - // Only options - $options = [ - 'class' => 'my-label-class', - 'id' => 'my-label-id', - 'type' => 'primary' - ] ; - $this->assertHtml ([ - ['span' => [ - 'class' => $options['class'].' label label-primary', - 'id' => $options['id'] - ]], - 'My Label', - '/span' - ], $this->Html->label($content, $options)) ; - } - - public function testDropdown () { - /** - - **/ - $title = 'Action' ; - $menu = [ - $this->Html->link('Link 1', '#'), - $this->Html->link('Link 2', '#'), - 'divider', - $this->Html->link('Link 3', '#') - ] ; - $expected = [ - ['ul' => [ - 'role' => 'menu', - 'class' => 'dropdown-menu' - ]], - ['li' => [ - 'role' => 'presentation' - ]], ['a' => ['href' => '#']], 'Link 1', '/a', '/li', - ['li' => [ - 'role' => 'presentation' - ]], ['a' => ['href' => '#']], 'Link 2', '/a', '/li', - ['li' => [ - 'role' => 'presentation', - 'class' => 'divider' - ]], '/li', - ['li' => [ - 'role' => 'presentation' - ]], ['a' => ['href' => '#']], 'Link 3', '/a', '/li', - '/ul' - ] ; - - } - +View = new View(); + $this->Html = new BootstrapHtmlHelper ($this->View); + } + + /** + * Tear Down + * + * @return void + */ + public function tearDown() { + parent::tearDown(); + unset($this->Html); + unset($this->View); + } + + public function testInitialize () { + $oldHtml = $this->Html ; + // Test useGlyphicon + $type = 'home'; + $options = [ + 'id' => 'my-home', + 'class' => 'my-home-class' + ] ; + $this->Html = new BootstrapHtmlHelper ($this->View, [ + 'useGlyphicon' => true + ]) ; + $this->assertEquals ($this->Html->icon ($type, $options), $this->Html->glIcon($type, $options)) ; + unset ($this->Html) ; + $this->Html = $oldHtml ; + } + + public function testIcon () { + $type = 'home'; + $options = [ + 'id' => 'my-home', + 'class' => 'my-home-class' + ] ; + // Default icon (Glyphicon) + $this->assertHtml ([ + ['i' => [ + 'class' => 'glyphicon glyphicon-'.$type + ]], + '/i' + ], $this->Html->icon($type)); + $this->assertHtml ([ + ['i' => [ + 'class' => $options['class'].' glyphicon glyphicon-'.$type, + 'id' => $options['id'] + ]], + '/i' + ], $this->Html->icon($type, $options)); + // FontAwesome icon + $this->assertHtml ([ + ['i' => [ + 'class' => 'fa fa-'.$type + ]], + '/i' + ], $this->Html->faIcon($type)); + $this->assertHtml ([ + ['i' => [ + 'class' => $options['class'].' fa fa-'.$type, + 'id' => $options['id'] + ]], + '/i' + ], $this->Html->faIcon($type, $options)); + } + + public function testLabel () { + $content = 'My Label' ; + // Standard test + $this->assertHtml ([ + ['span' => [ + 'class' => 'label label-default' + ]], + 'My Label', + '/span' + ], $this->Html->label($content)) ; + // Type + $this->assertHtml ([ + ['span' => [ + 'class' => 'label label-primary' + ]], + 'My Label', + '/span' + ], $this->Html->label($content, 'primary')) ; + // Type + Options + $options = [ + 'class' => 'my-label-class', + 'id' => 'my-label-id' + ] ; + $this->assertHtml ([ + ['span' => [ + 'class' => $options['class'].' label label-primary', + 'id' => $options['id'] + ]], + 'My Label', + '/span' + ], $this->Html->label($content, 'primary', $options)) ; + // Only options + $options = [ + 'class' => 'my-label-class', + 'id' => 'my-label-id', + 'type' => 'primary' + ] ; + $this->assertHtml ([ + ['span' => [ + 'class' => $options['class'].' label label-primary', + 'id' => $options['id'] + ]], + 'My Label', + '/span' + ], $this->Html->label($content, $options)) ; + } + + public function testDropdown () { + /** + + **/ + $title = 'Action' ; + $menu = [ + $this->Html->link('Link 1', '#'), + $this->Html->link('Link 2', '#'), + 'divider', + $this->Html->link('Link 3', '#') + ] ; + $expected = [ + ['ul' => [ + 'role' => 'menu', + 'class' => 'dropdown-menu' + ]], + ['li' => [ + 'role' => 'presentation' + ]], ['a' => ['href' => '#']], 'Link 1', '/a', '/li', + ['li' => [ + 'role' => 'presentation' + ]], ['a' => ['href' => '#']], 'Link 2', '/a', '/li', + ['li' => [ + 'role' => 'presentation', + 'class' => 'divider' + ]], '/li', + ['li' => [ + 'role' => 'presentation' + ]], ['a' => ['href' => '#']], 'Link 3', '/a', '/li', + '/ul' + ] ; + + } + } \ No newline at end of file diff --git a/tests/TestCase/View/Helper/BootstrapTraitTest.php b/tests/TestCase/View/Helper/BootstrapTraitTest.php index c387ed5..9cc76a4 100644 --- a/tests/TestCase/View/Helper/BootstrapTraitTest.php +++ b/tests/TestCase/View/Helper/BootstrapTraitTest.php @@ -1,160 +1,160 @@ -_View = $View; - } - - public function publicEasyIcon ($callback, $title, $options) { - return $this->_easyIcon($callback, $title, $options); - } - -}; - -class BootstrapTraitTemplateTest extends TestCase { - - /** - * Setup - * - * @return void - */ - public function setUp () { - parent::setUp(); - $this->View = new View(); - $this->_Trait = new PublicBootstrapTrait($this->View); - $this->View->Html = new BootstrapHtmlHelper ($this->View); - $this->Form = new BootstrapFormHelper ($this->View); - $this->Paginator = new BootstrapPaginatorHelper ($this->View); - } - - - /** - * Tear Down - * - * @return void - */ - public function tearDown() { - parent::tearDown(); - unset($this->View->Html); - unset($this->View); - unset($this->Form); - unset($this->Paginator); - } - - public function testEasyIcon() { - - $that = $this; - $callback = function ($text, $options) use ($that) { - $that->assertEquals(isset($options['escape']) ? $options['escape'] : true, $options['expected']['escape']); - $that->assertHtml($options['expected']['result'], $text); - }; - - $this->_Trait->publicEasyIcon($callback, 'i:plus', [ - 'expected' => [ - 'escape' => false, - 'result' => [['i' => [ - 'class' => 'glyphicon glyphicon-plus' - ]], '/i'] - ] - ]); - - $this->_Trait->publicEasyIcon($callback, 'Click Me!', [ - 'expected' => [ - 'escape' => true, - 'result' => 'Click Me!' - ] - ]); - - $this->_Trait->publicEasyIcon($callback, 'i:plus Add', [ - 'expected' => [ - 'escape' => false, - 'result' => [['i' => [ - 'class' => 'glyphicon glyphicon-plus' - ]], '/i', ' Add'] - ] - ]); - - $this->_Trait->publicEasyIcon($callback, 'Add i:plus', [ - 'expected' => [ - 'escape' => false, - 'result' => ['Add ', ['i' => [ - 'class' => 'glyphicon glyphicon-plus' - ]], '/i'] - ] - ]); - - $this->_Trait->easyIcon = false; - $this->_Trait->publicEasyIcon($callback, 'i:plus', [ - 'expected' => [ - 'escape' => true, - 'result' => 'i:plus' - ] - ]); - - } - - public function testHelperMethods() { - - // BootstrapPaginatorHelper - TODO - // BootstrapPaginatorHelper::prev($title, array $options = []); - // BootstrapPaginatorHelper::next($title, array $options = []); - // BootstrapPaginatorHelper::numbers(array $options = []); // For `prev` and `next` options. - - // BootstrapFormatHelper - $result = $this->Form->button ('i:plus') ; - $this->assertHtml([ - ['button' => [ - 'class' => 'btn btn-default', - 'type' => 'submit' - ]], ['i' => [ - 'class' => 'glyphicon glyphicon-plus' - ]], '/i', '/button' - ], $result) ; - $result = $this->Form->input ('fieldname', [ - 'prepend' => 'i:home', - 'append' => 'i:plus', - 'label' => false - ]) ; - $this->assertHtml([ - ['div' => [ - 'class' => 'form-group text' - ]], - ['div' => [ - 'class' => 'input-group' - ]], - ['span' => [ - 'class' => 'input-group-addon' - ]], - ['i' => ['class' => 'glyphicon glyphicon-home']], '/i', - '/span', - ['input' => [ - 'type' => 'text', - 'class' => 'form-control', - 'name' => 'fieldname', - 'id' => 'fieldname' - ]], - ['span' => [ - 'class' => 'input-group-addon' - ]], - ['i' => ['class' => 'glyphicon glyphicon-plus']], '/i', - '/span', - '/div', - '/div' - ], $result) ; - //BootstrapFormHelper::prepend($input, $prepend); // For $prepend. - //BootstrapFormHelper::append($input, $append); // For $append. - } - +_View = $View; + } + + public function publicEasyIcon ($callback, $title, $options) { + return $this->_easyIcon($callback, $title, $options); + } + +}; + +class BootstrapTraitTemplateTest extends TestCase { + + /** + * Setup + * + * @return void + */ + public function setUp () { + parent::setUp(); + $this->View = new View(); + $this->_Trait = new PublicBootstrapTrait($this->View); + $this->View->Html = new BootstrapHtmlHelper ($this->View); + $this->Form = new BootstrapFormHelper ($this->View); + $this->Paginator = new BootstrapPaginatorHelper ($this->View); + } + + + /** + * Tear Down + * + * @return void + */ + public function tearDown() { + parent::tearDown(); + unset($this->View->Html); + unset($this->View); + unset($this->Form); + unset($this->Paginator); + } + + public function testEasyIcon() { + + $that = $this; + $callback = function ($text, $options) use ($that) { + $that->assertEquals(isset($options['escape']) ? $options['escape'] : true, $options['expected']['escape']); + $that->assertHtml($options['expected']['result'], $text); + }; + + $this->_Trait->publicEasyIcon($callback, 'i:plus', [ + 'expected' => [ + 'escape' => false, + 'result' => [['i' => [ + 'class' => 'glyphicon glyphicon-plus' + ]], '/i'] + ] + ]); + + $this->_Trait->publicEasyIcon($callback, 'Click Me!', [ + 'expected' => [ + 'escape' => true, + 'result' => 'Click Me!' + ] + ]); + + $this->_Trait->publicEasyIcon($callback, 'i:plus Add', [ + 'expected' => [ + 'escape' => false, + 'result' => [['i' => [ + 'class' => 'glyphicon glyphicon-plus' + ]], '/i', ' Add'] + ] + ]); + + $this->_Trait->publicEasyIcon($callback, 'Add i:plus', [ + 'expected' => [ + 'escape' => false, + 'result' => ['Add ', ['i' => [ + 'class' => 'glyphicon glyphicon-plus' + ]], '/i'] + ] + ]); + + $this->_Trait->easyIcon = false; + $this->_Trait->publicEasyIcon($callback, 'i:plus', [ + 'expected' => [ + 'escape' => true, + 'result' => 'i:plus' + ] + ]); + + } + + public function testHelperMethods() { + + // BootstrapPaginatorHelper - TODO + // BootstrapPaginatorHelper::prev($title, array $options = []); + // BootstrapPaginatorHelper::next($title, array $options = []); + // BootstrapPaginatorHelper::numbers(array $options = []); // For `prev` and `next` options. + + // BootstrapFormatHelper + $result = $this->Form->button ('i:plus') ; + $this->assertHtml([ + ['button' => [ + 'class' => 'btn btn-default', + 'type' => 'submit' + ]], ['i' => [ + 'class' => 'glyphicon glyphicon-plus' + ]], '/i', '/button' + ], $result) ; + $result = $this->Form->input ('fieldname', [ + 'prepend' => 'i:home', + 'append' => 'i:plus', + 'label' => false + ]) ; + $this->assertHtml([ + ['div' => [ + 'class' => 'form-group text' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + ['i' => ['class' => 'glyphicon glyphicon-home']], '/i', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => 'fieldname', + 'id' => 'fieldname' + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + ['i' => ['class' => 'glyphicon glyphicon-plus']], '/i', + '/span', + '/div', + '/div' + ], $result) ; + //BootstrapFormHelper::prepend($input, $prepend); // For $prepend. + //BootstrapFormHelper::append($input, $append); // For $append. + } + }; \ No newline at end of file From 999da75fc08687dbc3b3a36f048b2dd417f6db73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 14:48:42 +0200 Subject: [PATCH 020/222] Change config to new mode. --- src/View/Helper/BootstrapFormHelper.php | 59 ++++++------------------- src/View/Helper/BootstrapHtmlHelper.php | 50 ++++++++++++++------- src/View/Helper/BootstrapTrait.php | 4 +- 3 files changed, 50 insertions(+), 63 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 9838bf6..177d3b5 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -81,7 +81,17 @@ class BootstrapFormHelper extends FormHelper { 'radioContainer' => '{{h_radioContainer_start}}
{{content}}
{{h_radioContainer_end}}', 'textarea' => '', 'submitContainer' => '
{{h_submitContainer_start}}{{content}}{{h_submitContainer_end}}
', - ] + ], + 'templateClass' => 'Bootstrap\View\BootstrapStringTemplate', + 'buttons' => [ + 'type' => 'default' + ], + 'columns' => [ + 'label' => 2, + 'input' => 10, + 'error' => 0 + ], + 'useCustomFileInput' => false ]; /** @@ -105,53 +115,10 @@ class BootstrapFormHelper extends FormHelper { public $horizontal = false ; public $inline = false ; - public $colSize ; - - /** - * Use custom file inputs (bootstrap style, with javascript). - * - * @var boolean - */ - protected $_customFileInput = false ; - - /** - * Default type for buttons. - * - * @var string - */ - protected $_defaultButtonType = 'default' ; - - /** - * Default colums size. - * - * @var array - */ - protected $_defaultColumnSize = [ - 'label' => 2, - 'input' => 10, - 'error' => 0 - ]; private $buttonTypes = ['default', 'primary', 'info', 'success', 'warning', 'danger', 'link'] ; private $buttonSizes = ['xs', 'sm', 'lg'] ; - public function __construct (\Cake\View\View $view, array $config = []) { - if (isset($config['buttons'])) { - if (isset($config['buttons']['type'])) { - $this->_defaultButtonType = $config['buttons']['type'] ; - } - } - if (isset($config['columns'])) { - $this->_defaultColumnSize = $config['columns'] ; - } - if (isset($config['useCustomFileInput'])) { - $this->_customFileInput = $config['useCustomFileInput']; - } - $this->colSize = $this->_defaultColumnSize ; - $this->_defaultConfig['templateClass'] = 'Bootstrap\View\BootstrapStringTemplate' ; - parent::__construct($view, $config); - } - /** * * Replace the templates with the ones specified by newTemplates, call the specified function @@ -252,7 +219,7 @@ public function create($model = null, Array $options = array()) { unset($options['cols']) ; } else { - $this->colSize = $this->_defaultColumnSize ; + $this->colSize = $this->config('columns') ; } $this->horizontal = $this->_extractOption('horizontal', $options, false); unset($options['horizontal']); @@ -423,7 +390,7 @@ protected function _getDatetimeTemplate ($fields, $options) { * @link http://book.cakephp.org/3.0/en/views/helpers/form.html#creating-file-inputs */ public function file($fieldName, array $options = []) { - if (!$this->_customFileInput || (isset($options['default']) && $options['default'])) { + if (!$this->config('useCustomFileInput') || (isset($options['default']) && $options['default'])) { return parent::file($fieldName, $options); } if (!isset($options['id'])) { diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index c41e65b..586e582 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -28,19 +28,39 @@ class BootstrapHtmlHelper extends HtmlHelper { use BootstrapTrait ; - /** - * Use font awesome icons instead of glyphicons. - * - * @var boolean - */ - protected $_useFontAwesome = FALSE; - - public function __construct (\Cake\View\View $view, array $config = []) { - if (isset($config['useFontAwesome'])) { - $this->_useFontAwesome = $config['useFontAwesome']; - } - parent::__construct($view, $config); - } + protected $_defaultConfig = [ + 'templates' => [ + 'meta' => '', + 'metalink' => '', + 'link' => '{{content}}', + 'mailto' => '{{content}}', + 'image' => '', + 'tableheader' => '{{content}}', + 'tableheaderrow' => '{{content}}', + 'tablecell' => '{{content}}', + 'tablerow' => '{{content}}', + 'block' => '{{content}}', + 'blockstart' => '', + 'blockend' => '', + 'tag' => '<{{tag}}{{attrs}}>{{content}}', + 'tagstart' => '<{{tag}}{{attrs}}>', + 'tagend' => '', + 'tagselfclosing' => '<{{tag}}{{attrs}}/>', + 'para' => '{{content}}

', + 'parastart' => '', + 'css' => '', + 'style' => '{{content}}', + 'charset' => '', + 'ul' => '{{content}}', + 'ol' => '{{content}}', + 'li' => '{{content}}', + 'javascriptblock' => '{{content}}', + 'javascriptstart' => '', + 'javascriptend' => '' + ], + 'useFontAwesome' => false + ]; /** * @@ -48,9 +68,9 @@ public function __construct (\Cake\View\View $view, array $config = []) { * * @param $icon Name of the icon. * - **/ + **/ public function icon ($icon, $options = []) { - return $this->_useFontAwesome ? $this->faIcon($icon, $options) : $this->glIcon($icon, $options); + return $this->config('useFontAwesome') ? $this->faIcon($icon, $options) : $this->glIcon($icon, $options); } /** diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 4d12933..d6591e8 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -68,8 +68,8 @@ public function addClass(array $options = [], $class = null, $key = 'class') { * */ protected function _addButtonClasses ($options) { - $type = $this->_extractOption('bootstrap-type', $options, $this->_defaultButtonType); - $size = $this->_extractOption('bootstrap-size', $options, FALSE); + $type = $this->_extractOption('bootstrap-type', $options, $this->config('buttons.type')); + $size = $this->_extractOption('bootstrap-size', $options, false); unset($options['bootstrap-size']) ; unset($options['bootstrap-type']) ; $options = $this->addClass($options, 'btn') ; From 61407209b9fc2b07ae057fe5eaccf19b04d9e8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 14:48:57 +0200 Subject: [PATCH 021/222] Fix bad indentation. --- src/View/Helper/BootstrapFlashHelper.php | 46 +++++------ src/View/Helper/BootstrapFormHelper.php | 60 +++++++------- src/View/Helper/BootstrapHtmlHelper.php | 60 +++++++------- src/View/Helper/BootstrapModalHelper.php | 52 ++++++------- src/View/Helper/BootstrapNavbarHelper.php | 82 ++++++++++---------- src/View/Helper/BootstrapPaginatorHelper.php | 38 ++++----- src/View/Helper/BootstrapPanelHelper.php | 38 ++++----- 7 files changed, 188 insertions(+), 188 deletions(-) diff --git a/src/View/Helper/BootstrapFlashHelper.php b/src/View/Helper/BootstrapFlashHelper.php index c42d9b2..e43941e 100644 --- a/src/View/Helper/BootstrapFlashHelper.php +++ b/src/View/Helper/BootstrapFlashHelper.php @@ -1,24 +1,24 @@ request->session()->check("Flash.$key")) { + if (!$this->request->session()->check("Flash.$key")) { return; } @@ -82,9 +82,9 @@ public function render($key = 'flash', array $options = []) { )); } foreach ($flash as &$message) { - if (in_array(basename($message['element']), $this->_bootstrapTemplates)) { - $message['element'] = 'Bootstrap.'.$message['element']; - } + if (in_array(basename($message['element']), $this->_bootstrapTemplates)) { + $message['element'] = 'Bootstrap.'.$message['element']; + } } $this->request->session()->write("Flash.$key", $flash); diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 177d3b5..b738262 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -1,24 +1,24 @@ templates ($templates) ; @@ -147,7 +147,7 @@ protected function _wrapTemplates ($templates, $callback, $params) { * * @return true if the HTML code contains a button * - **/ + **/ protected function _matchButton ($html) { return strpos($html, ' '
', 'h_formGroup_end' => '
', 'h_checkboxContainer_start' => '
', + .' '.$this->_getColClass('input').'">', 'h_checkboxContainer_end' => '
', 'h_radioContainer_start' => '
', + .' '.$this->_getColClass('input').'">', 'h_radioContainer_end' => '
', 'h_submitContainer_start' => '
', 'h_submitContainer_end' => '
', @@ -212,7 +212,7 @@ protected function _inputContainerTemplate($options) { * * @return The HTML tags corresponding to the openning of the form * - **/ + **/ public function create($model = null, Array $options = array()) { if (isset($options['cols'])) { $this->colSize = $options['cols'] ; @@ -239,7 +239,7 @@ public function create($model = null, Array $options = array()) { * * Return the col size class for the specified column (label, input or error). * - **/ + **/ protected function _getColClass ($what, $offset = false) { if ($what === 'error' && isset($this->colSize['error']) && $this->colSize['error'] == 0) { return $this->_getColClass('label', true).' '.$this->_getColClass('input'); @@ -261,7 +261,7 @@ protected function _wrapInputGroup ($addonOrButtons) { if (is_string($addonOrButtons)) { $addonOrButtons = $this->_makeIcon($addonOrButtons); $addonOrButtons = ''.$addonOrButtons.'' ; + ($this->_matchButton($addonOrButtons) ? 'btn' : 'addon').'">'.$addonOrButtons.'' ; } else if ($addonOrButtons !== false) { $addonOrButtons = ''.implode('', $addonOrButtons).'' ; @@ -304,7 +304,7 @@ protected function _wrap ($input, $prepend, $append) { * -> array: Add elements in array before inputs * - append: Same as prepend except it add elements after input * - **/ + **/ public function input($fieldName, array $options = array()) { $options += [ @@ -547,7 +547,7 @@ public function button($title, array $options = []) { * Extra options: * - vertical true/false * - **/ + **/ public function buttonGroup ($buttons, array $options = array()) { $vertical = $this->_extractOption('vertical', $options, false) ; unset($options['vertical']) ; @@ -565,7 +565,7 @@ public function buttonGroup ($buttons, array $options = array()) { * @param $buttons The groups in the toolbar * @param $options Options for div method * - **/ + **/ public function buttonToolbar (array $buttonGroups, array $options = array()) { $options = $this->addClass($options, 'btn-toolbar') ; return $this->Html->tag('div', implode('', $buttonGroups), $options) ; @@ -609,7 +609,7 @@ public function dropdownButton ($title, array $menu = [], array $options = []) { * * Unusable options: div * - **/ + **/ public function submit($caption = null, array $options = array()) { return parent::submit($caption, $this->_createButtonOptions($options)) ; } @@ -633,7 +633,7 @@ public function submit($caption = null, array $options = array()) { * - _input Options for the input (overrided by $inpOpts) * - _button Options for the button (overrided by $btnOpts) * - **/ + **/ public function searchForm ($model = null, $options = [], $inpOpts = [], $btnOpts = []) { $options += [ diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index 586e582..8397039 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -1,24 +1,24 @@ _extractType($options, 'type', $default = 'default', - array('default', 'primary', 'success', 'warning', 'info', 'danger')) ; + array('default', 'primary', 'success', 'warning', 'info', 'danger')) ; unset ($options['type']) ; $options = $this->addClass($options, 'label') ; $options = $this->addClass($options, 'label-'.$type) ; @@ -135,7 +135,7 @@ public function label ($text, $type = 'default', $options = []) { * @param options Options for span * * - **/ + **/ public function badge ($text, $options = []) { $options = $this->addClass($options, 'badge') ; return $this->tag('span', $text, $options) ; @@ -149,8 +149,8 @@ public function badge ($text, $options = []) { * @param $startText Text to insert before list * * Unusable options: - * - Separator - **/ + * - Separator + **/ public function getCrumbList(array $options = [], $startText = false) { $options['separator'] = '' ; $options = $this->addClass($options, 'breadcrumb') ; @@ -169,10 +169,10 @@ public function getCrumbList(array $options = [], $startText = false) { * is useless, and the label type can be specified in the $options array). * * Available BootstrapHtml options: - * - type: string, type of alert (default, error, info, success ; useless if + * - type: string, type of alert (default, error, info, success ; useless if * $type is specified) * - **/ + **/ public function alert ($text, $type = 'warning', $options = []) { if (is_string($type)) { $options['type'] = $type ; @@ -197,8 +197,8 @@ public function alert ($text, $type = 'warning', $options = []) { * Create a Twitter Bootstrap style progress bar. * * @param $widths - * - The width (in %) of the bar (style primary, without display) - * - An array of bar, with (for each bar) : + * - The width (in %) of the bar (style primary, without display) + * - An array of bar, with (for each bar) : * - width (only field required) * - type (primary, info, danger, success, warning, default is primary) * - min (integer, default 0) @@ -210,10 +210,10 @@ public function alert ($text, $type = 'warning', $options = []) { * specified above. * * Available BootstrapHtml options: - * - striped: boolean, specify if progress bar should be striped - * - active: boolean, specify if progress bar should be active + * - striped: boolean, specify if progress bar should be striped + * - active: boolean, specify if progress bar should be active * - **/ + **/ public function progress ($widths, $options = []) { $striped = $this->_extractOption('striped', $options, false) || in_array('striped', $options); unset($options['striped']) ; diff --git a/src/View/Helper/BootstrapModalHelper.php b/src/View/Helper/BootstrapModalHelper.php index 52c644e..0d48519 100644 --- a/src/View/Helper/BootstrapModalHelper.php +++ b/src/View/Helper/BootstrapModalHelper.php @@ -1,24 +1,24 @@ _cleanCurrent(); if ($buttons !== null) { @@ -184,9 +184,9 @@ protected function _createFooter ($buttons = null, $options = []) { $content = ''; if (!$buttons && $close) { - $content .= '' ; + $content .= '' ; } $content .= $buttons; @@ -205,7 +205,7 @@ protected function _createFooter ($buttons = null, $options = []) { * Special option (if $info is string): * - close: Add the 'close' button in the header (default true). * - **/ + **/ public function header ($info = null, $options = []) { if (is_array($info)) { $options = $info; @@ -223,7 +223,7 @@ public function header ($info = null, $options = []) { * @param array $options Options for the footer div. * * - **/ + **/ public function body ($info = null, $options = []) { if (is_array($info)) { $options = $info; @@ -244,7 +244,7 @@ public function body ($info = null, $options = []) { * Special option (if $buttons is NOT NULL but empty): * - close: Add the 'close' button to the footer (default true). * - **/ + **/ public function footer ($buttons = null, $options = []) { if (is_array($buttons)) { if (!empty($buttons) && $this->_isAssociativeArray($buttons)) { diff --git a/src/View/Helper/BootstrapNavbarHelper.php b/src/View/Helper/BootstrapNavbarHelper.php index 04c6ce6..de03cb7 100644 --- a/src/View/Helper/BootstrapNavbarHelper.php +++ b/src/View/Helper/BootstrapNavbarHelper.php @@ -1,24 +1,24 @@ _fixed = $this->_extractOption('fixed', $options, false) ; unset($options['fixed']) ; @@ -133,18 +133,18 @@ public function create ($brand, $options = []) { $rightOpen = '' ; if ($this->_responsive) { $toggleButton = $this->Html->tag('button', - implode('', array( - $this->Html->tag('span', __('Toggle navigation'), array('class' => 'sr-only')), - $this->Html->tag('span', '', array('class' => 'icon-bar')), - $this->Html->tag('span', '', array('class' => 'icon-bar')), - $this->Html->tag('span', '', array('class' => 'icon-bar')) - )), - array( - 'type' => 'button', - 'class' => 'navbar-toggle collapsed', - 'data-toggle' => 'collapse', - 'data-target' => '.navbar-collapse' - ) + implode('', array( + $this->Html->tag('span', __('Toggle navigation'), array('class' => 'sr-only')), + $this->Html->tag('span', '', array('class' => 'icon-bar')), + $this->Html->tag('span', '', array('class' => 'icon-bar')), + $this->Html->tag('span', '', array('class' => 'icon-bar')) + )), + array( + 'type' => 'button', + 'class' => 'navbar-toggle collapsed', + 'data-toggle' => 'collapse', + 'data-target' => '.navbar-collapse' + ) ) ; $rightOpen = $this->Html->tag('div', null, ['class' => 'navbar-collapse collapse']) ; } @@ -181,7 +181,7 @@ public function create ($brand, $options = []) { * @param options Options passed to the tag method (for the li tag) * @param linkOptions Options passed to the link method * - **/ + **/ public function link ($name, $url = '', array $options = [], array $linkOptions = []) { if ($this->_level == 0 && $this->autoButtonLink) { $options = $this->addClass ($options, 'btn btn-default navbar-btn') ; @@ -200,7 +200,7 @@ public function link ($name, $url = '', array $options = [], array $linkOptions * @param name Text of the button. * @param options Options sent to the BootstrapFormHelper::button method. * - **/ + **/ public function button ($name, array $options = []) { $options = $this->addClass ($options, 'navbar-btn') ; return $this->Form->button ($name, $options) ; @@ -212,7 +212,7 @@ public function button ($name, array $options = []) { * * @param options Options sent to the tag method. * - **/ + **/ public function divider (array $options = []) { $options = $this->addClass ($options, 'divider') ; $options['role'] = 'separator' ; @@ -226,7 +226,7 @@ public function divider (array $options = []) { * @param name Title of the header. * @param options Options sent to the tag method. * - **/ + **/ public function header ($name, array $options = []) { $options = $this->addClass ($options, 'dropdown-header') ; return $this->Html->tag('li', $name, $options) ; @@ -242,7 +242,7 @@ public function header ($name, array $options = []) { * Extra options: * - tag The HTML tag to use (default 'p') * - **/ + **/ public function text ($text, $options = []) { $tag = $this->_extractOption ('tag', $options, 'p') ; $options = $this->addClass ($options, 'navbar-text') ; @@ -267,7 +267,7 @@ public function text ($text, $options = []) { * @param model Model for BootstrapFormHelper::searchForm method. * @param options Options for BootstrapFormHelper::searchForm method. * - **/ + **/ public function searchForm ($model = null, $options = []) { $align = $this->_extractOption ('align', $options, 'left') ; unset ($options['align']) ; @@ -284,7 +284,7 @@ public function searchForm ($model = null, $options = []) { * @param url A URL for the menu (default null) * @param options Options passed to the tag method (+ extra options, see above) * - **/ + **/ public function beginMenu ($name = null, $url = null, $options = [], $linkOptions = [], $listOptions = []) { $res = ''; @@ -317,7 +317,7 @@ public function beginMenu ($name = null, $url = null, $options = [], * * End a menu. * - **/ + **/ public function endMenu () { $this->_level -= 1 ; return ''.($this->_level == 1 ? '' : '') ; @@ -327,7 +327,7 @@ public function endMenu () { * * End a navbar. * - **/ + **/ public function end () { $res = '' ; if ($this->_responsive) { diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 275983e..aecf5b3 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -1,24 +1,24 @@ Html->tag('div', $text, $options); if ($this->_collapsible) { $open = ((is_int($this->_groupPanelOpen) - && $this->_groupPanelOpen === $this->_groupPanelCount) + && $this->_groupPanelOpen === $this->_groupPanelCount) || $this->_groupPanelOpen === $this->_bodyId) ? ' in' : ''; $body = $this->Html->div('panel-collapse collapse'.$open, $text ? $body : null, [ 'role' => 'tabpanel', From 460fbb899a2d2d3a7c7bd15747a4ff633e81d1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 1 Apr 2016 14:57:30 +0200 Subject: [PATCH 022/222] Add .gitignore file from cakephp/cakephp. --- .gitignore | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74b1d3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# User specific & automatically generated files # +################################################# +/build +/dist +/tags +/composer.lock +/phpunit.xml +/vendor +*.mo + +# IDE and editor specific files # +################################# + + +# OS generated files # +###################### +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Icon? +ehthumbs.db +Thumbs.db \ No newline at end of file From 95b2cfb9f7fb852d5766ad176ad44c67ea45fe0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 11 Apr 2016 15:55:41 +0200 Subject: [PATCH 023/222] Clean code. --- src/View/Helper/BootstrapHtmlHelper.php | 26 ++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index c41e65b..946840d 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -50,7 +50,8 @@ public function __construct (\Cake\View\View $view, array $config = []) { * **/ public function icon ($icon, $options = []) { - return $this->_useFontAwesome ? $this->faIcon($icon, $options) : $this->glIcon($icon, $options); + return $this->_useFontAwesome ? + $this->faIcon($icon, $options) : $this->glIcon($icon, $options); } /** @@ -129,7 +130,7 @@ public function badge ($text, $options = []) { * @param $startText Text to insert before list * * Unusable options: - * - Separator + * - Separator **/ public function getCrumbList(array $options = [], $startText = false) { $options['separator'] = '' ; @@ -149,7 +150,7 @@ public function getCrumbList(array $options = [], $startText = false) { * is useless, and the label type can be specified in the $options array). * * Available BootstrapHtml options: - * - type: string, type of alert (default, error, info, success ; useless if + * - type: string, type of alert (default, error, info, success ; useless if * $type is specified) * **/ @@ -160,8 +161,8 @@ public function alert ($text, $type = 'warning', $options = []) { else if (is_array($type)) { $options = $type ; } - $button = '' ; $type = $this->_extractType($options, 'type', 'warning', array('info', 'warning', 'success', 'danger')) ; + $button = '' ; + $type = $this->_extractOption('type', $options, 'warning'); unset($options['type']) ; $options = $this->addClass($options, 'alert') ; if ($type) { @@ -219,7 +219,7 @@ public function progress ($widths, $options = []) { } } else { - $type = $this->_extractType($options, 'type', 'primary', array('info', 'primary', 'success', 'warning', 'danger')) ; + $type = $this->_extractOption('type', $options, 'primary'); unset($options['type']) ; $class = 'progress-bar progress-bar-'.$type ; $min = $this->_extractOption('min', $options, 0); diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 4d12933..2b7f9cc 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -112,31 +112,6 @@ protected function _isAssociativeArray ($array) { return array_keys($array) !== range(0, count($array) - 1); } - /** - * Check type values in $options, returning null if no option is found or if - * option is not in $avail. - * If type == $default, $default is returned (even if it is not in $avail). - * - * @param $options The array from which to extract the type. - * @param $key The key of the value. - * @param $default The default value if the key is not present or if the value is not correct. - * @param $avail An array of possible values. - * - * @return mixed - * - **/ - protected function _extractType ($options, $key = 'type', $default = 'info', - $avail = array('info', 'success', 'warning', 'error')) { - $type = $this->_extractOption($key, $options, $default) ; - if ($default !== false && $type == $default) { - return $default ; - } - if (!in_array($type, $avail)) { - return null ; - } - return $type ; - } - /** * Try to convert the specified $text to a bootstrap icon. The $text is converted if it matches * a format "i:icon-name". From b55bdd85b307d0c7fd947db5f358620de9159dbc Mon Sep 17 00:00:00 2001 From: ypnos-web Date: Tue, 12 Apr 2016 17:15:21 +0200 Subject: [PATCH 025/222] Fix issues with custom file input Prevent '_input' / '_button' attributes in hidden file input element Avoid issue with SecurityComponent: > Missing field 'filename' in POST data Cake\Controller\Exception\SecurityException --- src/View/Helper/BootstrapFormHelper.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 9838bf6..532d1b3 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -426,6 +426,12 @@ public function file($fieldName, array $options = []) { if (!$this->_customFileInput || (isset($options['default']) && $options['default'])) { return parent::file($fieldName, $options); } + + $fakeInputCustomOptions = $this->_extractOption('_input', $options, []); + unset($options['_input']); + $fakeButtonCustomOptions = $this->_extractOption('_button', $options, []); + unset($options['_button']); + if (!isset($options['id'])) { $options['id'] = $fieldName; } @@ -440,12 +446,8 @@ public function file($fieldName, array $options = []) { 'onchange' => "document.getElementById('".$options['id']."-input').value = (this.files.length <= 1) ? this.files[0].name : this.files.length + ' ' + '" . $countLabel . "';" ])); - $fakeInputCustomOptions = $this->_extractOption('_input', $options, []); - unset($options['_input']); - $fakeButtonCustomOptions = $this->_extractOption('_button', $options, []); - unset($options['_button']); - $fakeInput = $this->text($fieldName, array_merge($options, $fakeInputCustomOptions, [ + 'name' => $fieldName.'-text', 'readonly' => 'readonly', 'id' => $options['id'].'-input', 'onclick' => "document.getElementById('".$options['id']."').click();" From 8c050e2f9e25caa4071208d824a3526cc579b591 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Tue, 12 Apr 2016 22:21:11 +0200 Subject: [PATCH 026/222] Add tooltip method to BootstrapHtmlHelper. --- src/View/Helper/BootstrapHtmlHelper.php | 33 ++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index b8054e8..b1bf4be 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -59,7 +59,10 @@ class BootstrapHtmlHelper extends HtmlHelper { 'javascriptlink' => '', 'javascriptend' => '' ], - 'useFontAwesome' => false + 'useFontAwesome' => false, + 'tooltip' => [ + 'placement' => 'right' + ] ]; /** @@ -192,6 +195,34 @@ public function alert ($text, $type = 'warning', $options = []) { return $this->div($class, $button.$text, $options) ; } + /** + * Create a Twitter Bootstrap style tooltip. + * + * @param $text The HTML tag inner text. + * @param $tooltip The tooltip text. + * @param $options + * + * @options tag The tag to use (default 'span'). + * @options data-toggle HTML attribute (default 'tooltip'). + * @options placement HTML attribute (default from config). + * @optioms title HTML attribute (default $tooltip). + * + * @return The text wrapped in the specified tag with a tooltip. + * + **/ + public function tooltip($text, $tooltip, $options = []) { + $options += [ + 'tag' => 'span', + 'data-toggle' => 'tooltip', + 'placement' => $this->config('tooltip.placement'), + 'title' => $tooltip + ]; + $options['data-placement'] = $options['placement']; + $tag = $options['tag']; + unset($options['placement'], $options['tag']); + return $this->tag($tag, $text, $options); + } + /** * * Create a Twitter Bootstrap style progress bar. From 9ebfb2460f06072fa785be16c6106c5cb8eeb016 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Tue, 12 Apr 2016 22:21:26 +0200 Subject: [PATCH 027/222] Remove _extractOption. --- src/View/Helper/BootstrapFormHelper.php | 60 +++++++++------- src/View/Helper/BootstrapHtmlHelper.php | 88 +++++++++++++---------- src/View/Helper/BootstrapNavbarHelper.php | 46 ++++++++---- src/View/Helper/BootstrapTrait.php | 28 ++------ 4 files changed, 125 insertions(+), 97 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 25baa12..4808339 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -214,17 +214,15 @@ protected function _inputContainerTemplate($options) { * **/ public function create($model = null, Array $options = array()) { - if (isset($options['cols'])) { - $this->colSize = $options['cols'] ; - unset($options['cols']) ; - } - else { - $this->colSize = $this->config('columns') ; - } - $this->horizontal = $this->_extractOption('horizontal', $options, false); - unset($options['horizontal']); - $this->inline = $this->_extractOption('inline', $options, false) ; - unset($options['inline']) ; + $options += [ + 'columns' => $this->config('columns'), + 'horizontal' => false, + 'inline' => false + ]; + $this->colSize = $options['columns']; + $this->horizontal = $options['horizontal']; + $this->inline = $options['inline']; + unset($options['columns'], $options['horizontal'], $options['inline']) ; if ($this->horizontal) { $options = $this->addClass($options, 'form-horizontal') ; } @@ -363,7 +361,8 @@ public function input($fieldName, array $options = array()) { protected function _getDatetimeTemplate ($fields, $options) { $inputs = [] ; foreach ($fields as $field => $in) { - if ($this->_extractOption($field, $options, $in)) { + $in = isset($options[$field]) ? $options[$field] : $in; + if ($in) { if ($field === 'timeFormat') $field = 'meridian' ; // Template uses "meridian" instead of timeFormat $inputs[$field] = '
{{'.$field.'}}
'; @@ -390,23 +389,28 @@ protected function _getDatetimeTemplate ($fields, $options) { * @link http://book.cakephp.org/3.0/en/views/helpers/form.html#creating-file-inputs */ public function file($fieldName, array $options = []) { + if (!$this->config('useCustomFileInput') || (isset($options['default']) && $options['default'])) { return parent::file($fieldName, $options); } - $fakeInputCustomOptions = $this->_extractOption('_input', $options, []); - unset($options['_input']); - $fakeButtonCustomOptions = $this->_extractOption('_button', $options, []); - unset($options['_button']); + $options += [ + '_input' => [], + '_button' => [], + 'id' => $fieldName, + 'secure' => true, + 'count-label' => __('files selected'), + 'button-label' => __('Choose File') + ]; - if (!isset($options['id'])) { - $options['id'] = $fieldName; - } + $fakeInputCustomOptions = $options['_input']; + $fakeButtonCustomOptions = $options['_button']; + unset($options['_input'], $options['_button']); $options += ['secure' => true]; $options = $this->_initInputField($fieldName, $options); unset($options['type']); - $countLabel = $this->_extractOption('count-label', $options, __('files selected')); + $countLabel = $options['count-label']; unset($options['count-label']); $fileInput = $this->widget('file', array_merge($options, [ 'style' => 'display: none;', @@ -419,7 +423,7 @@ public function file($fieldName, array $options = []) { 'id' => $options['id'].'-input', 'onclick' => "document.getElementById('".$options['id']."').click();" ])); - $buttonLabel = $this->_extractOption('button-label', $options, __('Choose File')); + $buttonLabel = $options['button-label']; unset($options['button-label']) ; $fakeButton = $this->button($buttonLabel, array_merge($fakeButtonCustomOptions, [ @@ -515,9 +519,12 @@ public function date($fieldName, array $options = []) { * Create & return a Cakephp options array from the $options specified. * */ - protected function _createButtonOptions (array $options = array()) { + protected function _createButtonOptions (array $options = []) { + $options += [ + 'bootstrap-block' => false + ]; $options = $this->_addButtonClasses($options); - $block = $this->_extractOption('bootstrap-block', $options, false) ; + $block = $options['bootstrap-block']; unset($options['bootstrap-block']); if ($block) { $options = $this->addClass($options, 'btn-block') ; @@ -550,8 +557,11 @@ public function button($title, array $options = []) { * - vertical true/false * **/ - public function buttonGroup ($buttons, array $options = array()) { - $vertical = $this->_extractOption('vertical', $options, false) ; + public function buttonGroup ($buttons, array $options = []) { + $options += [ + 'vertical' => false + ]; + $vertical = $options['vertical']; unset($options['vertical']) ; $options = $this->addClass($options, 'btn-group') ; if ($vertical) { diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index b1bf4be..d1455a4 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -122,7 +122,10 @@ public function label ($text, $type = 'default', $options = []) { else if (is_array($type)) { $options = $type ; } - $type = $this->_extractOption('type', $options, 'default'); + $options += [ + 'type' => 'default' + ]; + $type = $options['type']; unset ($options['type']) ; $options = $this->addClass($options, 'label') ; $options = $this->addClass($options, 'label-'.$type) ; @@ -182,9 +185,12 @@ public function alert ($text, $type = 'warning', $options = []) { else if (is_array($type)) { $options = $type ; } + $options += [ + 'type' => 'warning' + ]; $button = '' ; - $type = $this->_extractOption('type', $options, 'warning'); + $type = $options['type']; unset($options['type']) ; $options = $this->addClass($options, 'alert') ; if ($type) { @@ -246,45 +252,55 @@ public function tooltip($text, $tooltip, $options = []) { * **/ public function progress ($widths, $options = []) { - $striped = $this->_extractOption('striped', $options, false) - || in_array('striped', $options); - unset($options['striped']) ; - $active = $this->_extractOption('active', $options, false) || in_array('active', $options); - unset($options['active']) ; + $options += [ + 'striped' => false, + 'active' => false + ]; + $striped = $options['striped']; + $active = $options['active']; + unset($options['active'], $options['striped']) ; $bars = '' ; if (is_array($widths)) { - foreach ($widths as $w) { - $type = $this->_extractOption('type', $w, 'primary'); - $class = 'progress-bar progress-bar-'.$type ; - $min = $this->_extractOption('min', $w, 0); - $max = $this->_extractOption('max', $w, 100); - $display = $this->_extractOption('display', $w, false); - $bars .= $this->div($class, $display ? $w['width'].'%' : '', array( - 'aria-valuenow' => $w['width'], - 'aria-valuemin' => $min, - 'aria-valuemax' => $max, - 'role' => 'progressbar', - 'style' => 'width: '.$w['width'].'%;' - )) ; + foreach ($widths as $width) { + $width += [ + 'type' => 'primary', + 'min' => 0, + 'max' => 100, + 'display' => false + ]; + $class = 'progress-bar progress-bar-'.$width['type']; + $bars .= $this->div($class, + $width['display'] ? $width['width'].'%' : '', + [ + 'aria-valuenow' => $width['width'], + 'aria-valuemin' => $width['min'], + 'aria-valuemax' => $width['max'], + 'role' => 'progressbar', + 'style' => 'width: '.$width['width'].'%;' + ] + ); } } else { - $type = $this->_extractOption('type', $options, 'primary'); - unset($options['type']) ; - $class = 'progress-bar progress-bar-'.$type ; - $min = $this->_extractOption('min', $options, 0); - unset ($options['min']) ; - $max = $this->_extractOption('max', $options, 100); - unset ($options['max']) ; - $display = $this->_extractOption('display', $options, false); - unset ($options['display']) ; - $bars = $this->div($class, $display ? $widths.'%' : '', array( - 'aria-valuenow' => $widths, - 'aria-valuemin' => $min, - 'aria-valuemax' => $max, - 'role' => 'progressbar', - 'style' => 'width: '.$widths.'%;' - )) ; + $options += [ + 'type' => 'primary', + 'min' => 0, + 'max' => 100, + 'display' => false + ]; + $class = 'progress-bar progress-bar-'.$options['type']; + $bars = $this->div($class, + $options['display'] ? $widths.'%' : '', + [ + 'aria-valuenow' => $widths, + 'aria-valuemin' => $options['min'], + 'aria-valuemax' => $options['max'], + 'role' => 'progressbar', + 'style' => 'width: '.$widths.'%;' + ] + ); + unset($options['type'], $options['min'], + $options['max'], $options['display']); } $options = $this->addClass($options, 'progress') ; if ($active) { diff --git a/src/View/Helper/BootstrapNavbarHelper.php b/src/View/Helper/BootstrapNavbarHelper.php index de03cb7..13e4feb 100644 --- a/src/View/Helper/BootstrapNavbarHelper.php +++ b/src/View/Helper/BootstrapNavbarHelper.php @@ -106,16 +106,23 @@ public function addClass(array $options = [], $class = null, $key = 'class') { * **/ public function create ($brand, $options = []) { - $this->_fixed = $this->_extractOption('fixed', $options, false) ; - unset($options['fixed']) ; - $this->_responsive = $this->_extractOption('responsive', $options, false) ; - unset($options['responsive']) ; - $this->_static = $this->_extractOption('static', $options, false) ; - unset($options['static']) ; - $this->_inverse = $this->_extractOption('inverse', $options, false) ; - unset($options['inverse']) ; - $this->_fluid = $this->_extractOption('fluid', $options, false); - unset($options['fluid']); + + $options += [ + 'fixed' => false, + 'responsive' => false, + 'static' => false, + 'inverse' => false, + 'fluid' => false + ]; + + $this->_fixed = $options['fixed']; + $this->_responsive = $options['responsive']; + $this->_static = $options['static']; + $this->_inverse = $options['inverse']; + $this->_fluid = $options['fluid']; + unset($options['fixed'], $options['responsive'], + $options['fluid'], $options['static'], + $options['inverse']); /** Generate options for outer div. **/ $options = $this->addClass($options, 'navbar navbar-default') ; @@ -157,9 +164,11 @@ public function create ($brand, $options = []) { ]) ; } else if (is_array($brand) && array_key_exists('url', $brand)) { - $brandOptions = $this->_extractOption ('options', $brand, []) ; - $brandOptions = $this->addClass ($brandOptions, 'navbar-brand') ; - $brand = $this->Html->link ($brand['name'], $brand['url'], $brandOptions) ; + $brand += [ + 'options' => [] + ]; + $brand['options'] = $this->addClass ($brand['options'], 'navbar-brand') ; + $brand = $this->Html->link ($brand['name'], $brand['url'], $brand['options']) ; } $rightOpen = $this->Html->tag('div', $toggleButton.$brand, ['class' => 'navbar-header']).$rightOpen ; @@ -244,7 +253,11 @@ public function header ($name, array $options = []) { * **/ public function text ($text, $options = []) { - $tag = $this->_extractOption ('tag', $options, 'p') ; + $options += [ + 'tag' => 'p' + ]; + $tag = $options['tag']; + unset($options['tag']); $options = $this->addClass ($options, 'navbar-text') ; $text = preg_replace_callback ('/]*)?>([^<]*)?<\/a>/i', function ($matches) { $attrs = preg_replace_callback ('/class="(.*)?"/', function ($m) { @@ -269,7 +282,10 @@ public function text ($text, $options = []) { * **/ public function searchForm ($model = null, $options = []) { - $align = $this->_extractOption ('align', $options, 'left') ; + $options += [ + 'align' => false + ]; + $align = $options['align']; unset ($options['align']) ; $options = $this->addClass($options, ['navbar-form', 'navbar-'.$align]) ; return $this->Form->searchForm($model, $options) ; diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 0195934..9706be6 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -68,10 +68,13 @@ public function addClass(array $options = [], $class = null, $key = 'class') { * */ protected function _addButtonClasses ($options) { - $type = $this->_extractOption('bootstrap-type', $options, $this->config('buttons.type')); - $size = $this->_extractOption('bootstrap-size', $options, false); - unset($options['bootstrap-size']) ; - unset($options['bootstrap-type']) ; + $options += [ + 'bootstrap-type' => $this->config('buttons.type'), + 'bootstrap-size' => false + ]; + $type = $options['bootstrap-type']; + $size = $options['bootstrap-size']; + unset($options['bootstrap-type'], $options['bootstrap-size']) ; $options = $this->addClass($options, 'btn') ; if (in_array($type, $this->buttonTypes)) { $options = $this->addClass($options, 'btn-'.$type) ; @@ -82,23 +85,6 @@ protected function _addButtonClasses ($options) { return $options ; } - /** - * Extract options from $options, returning $default if $key is not found. - * - * @param $key The key to search for. - * @param $options The array from which to extract the value. - * @param $default The default value returned if the key is not found. - * - * @return mixed $options[$key] if $key is in $options, otherwize $default. - * - **/ - protected function _extractOption ($key, $options, $default = null) { - if (isset($options[$key])) { - return $options[$key] ; - } - return $default ; - } - /** * * Check weither the specified array is associative or not. From 6b97bf132a98bf2fb7f896200440129a717cab36 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Tue, 12 Apr 2016 22:39:17 +0200 Subject: [PATCH 028/222] Clean code. --- src/View/Helper/BootstrapHtmlHelper.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index d1455a4..f57408d 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -73,7 +73,8 @@ class BootstrapHtmlHelper extends HtmlHelper { * **/ public function icon ($icon, $options = []) { - return $this->config('useFontAwesome') ? $this->faIcon($icon, $options) : $this->glIcon($icon, $options); + return $this->config('useFontAwesome')? + $this->faIcon($icon, $options) : $this->glIcon($icon, $options); } /** @@ -188,8 +189,12 @@ public function alert ($text, $type = 'warning', $options = []) { $options += [ 'type' => 'warning' ]; - $button = '' ; + $button = $this->tag('button', '×', [ + 'type' => 'button', + 'class' => 'close', + 'data-dismiss' => 'alert', + 'aria-hidden' => true + ]); $type = $options['type']; unset($options['type']) ; $options = $this->addClass($options, 'alert') ; @@ -343,7 +348,8 @@ public function dropdown (array $menu = [], array $options = []) { $url = array_shift($action) ; $action['role'] = 'menuitem' ; $action['tabindex'] = -1 ; - $output .= '
  • '.$this->link($name, $url, $action).'
  • '; + $output .= '
  • ' + .$this->link($name, $url, $action).'
  • '; } } else { From 9f794056940d211c8b3dbe45e5ef4979ebbe5c57 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Tue, 12 Apr 2016 22:39:25 +0200 Subject: [PATCH 029/222] Add new options to BootstrapHtmlHelper to define default option for some methods. --- src/View/Helper/BootstrapHtmlHelper.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index f57408d..25872bc 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -62,6 +62,15 @@ class BootstrapHtmlHelper extends HtmlHelper { 'useFontAwesome' => false, 'tooltip' => [ 'placement' => 'right' + ], + 'label' => [ + 'type' => 'primary' + ], + 'alert' => [ + 'type' => 'warning' + ], + 'progress' => [ + 'type' => 'primary' ] ]; @@ -116,7 +125,7 @@ public function glIcon ($icon, $options = []) { * - type The type of the label (useless if $type specified) * **/ - public function label ($text, $type = 'default', $options = []) { + public function label ($text, $type = null, $options = []) { if (is_string($type)) { $options['type'] = $type ; } @@ -124,7 +133,7 @@ public function label ($text, $type = 'default', $options = []) { $options = $type ; } $options += [ - 'type' => 'default' + 'type' => $this->config('label.type') ]; $type = $options['type']; unset ($options['type']) ; @@ -179,7 +188,7 @@ public function getCrumbList(array $options = [], $startText = false) { * $type is specified) * **/ - public function alert ($text, $type = 'warning', $options = []) { + public function alert ($text, $type = null, $options = []) { if (is_string($type)) { $options['type'] = $type ; } @@ -187,7 +196,7 @@ public function alert ($text, $type = 'warning', $options = []) { $options = $type ; } $options += [ - 'type' => 'warning' + 'type' => $this->config('alert.type') ]; $button = $this->tag('button', '×', [ 'type' => 'button', @@ -268,7 +277,7 @@ public function progress ($widths, $options = []) { if (is_array($widths)) { foreach ($widths as $width) { $width += [ - 'type' => 'primary', + 'type' => $this->config('progress.type'), 'min' => 0, 'max' => 100, 'display' => false @@ -288,7 +297,7 @@ public function progress ($widths, $options = []) { } else { $options += [ - 'type' => 'primary', + 'type' => $this->config('progress.type'), 'min' => 0, 'max' => 100, 'display' => false From b00e3e7325d1d0d9342fdc322b06462ae277ccd3 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Tue, 12 Apr 2016 22:39:25 +0200 Subject: [PATCH 030/222] Add new options to BootstrapHtmlHelper to define default option for some methods. --- src/View/Helper/BootstrapHtmlHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index 25872bc..e194110 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -64,7 +64,7 @@ class BootstrapHtmlHelper extends HtmlHelper { 'placement' => 'right' ], 'label' => [ - 'type' => 'primary' + 'type' => 'default' ], 'alert' => [ 'type' => 'warning' From 159353ed2ca3b2e411e040fc7d5cbe804a192426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 19 Apr 2016 16:09:22 +0200 Subject: [PATCH 031/222] Set default alignment for `NavbarHelper::searchForm`. --- src/View/Helper/BootstrapNavbarHelper.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/View/Helper/BootstrapNavbarHelper.php b/src/View/Helper/BootstrapNavbarHelper.php index 13e4feb..fd96e7c 100644 --- a/src/View/Helper/BootstrapNavbarHelper.php +++ b/src/View/Helper/BootstrapNavbarHelper.php @@ -283,11 +283,10 @@ public function text ($text, $options = []) { **/ public function searchForm ($model = null, $options = []) { $options += [ - 'align' => false + 'align' => 'left' ]; - $align = $options['align']; + $options = $this->addClass($options, ['navbar-form', 'navbar-'.$options['align']]) ; unset ($options['align']) ; - $options = $this->addClass($options, ['navbar-form', 'navbar-'.$align]) ; return $this->Form->searchForm($model, $options) ; } From cba87cf7931d2f4f0359eede6946252b3b3104d0 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sat, 23 Apr 2016 23:51:45 +0200 Subject: [PATCH 032/222] Clean files. --- src/View/Helper/BootstrapTrait.php | 3 +- .../View/Helper/BootstrapTraitTest.php | 31 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 9706be6..15c2eb4 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -60,7 +60,8 @@ public function addClass(array $options = [], $class = null, $key = 'class') { } /** - * Add classes to options according to values of bootstrap-type and bootstrap-size for button. + * Add classes to options according to values of bootstrap-type and bootstrap-size + * for button. * * @param $options The initial options with bootstrap-type and/or bootstrat-size values * diff --git a/tests/TestCase/View/Helper/BootstrapTraitTest.php b/tests/TestCase/View/Helper/BootstrapTraitTest.php index 9cc76a4..57e4534 100644 --- a/tests/TestCase/View/Helper/BootstrapTraitTest.php +++ b/tests/TestCase/View/Helper/BootstrapTraitTest.php @@ -10,21 +10,21 @@ use Cake\View\View; class PublicBootstrapTrait { - + use BootstrapTrait ; - + public function __construct ($View) { $this->_View = $View; } - + public function publicEasyIcon ($callback, $title, $options) { return $this->_easyIcon($callback, $title, $options); } - + }; class BootstrapTraitTemplateTest extends TestCase { - + /** * Setup * @@ -54,13 +54,14 @@ public function tearDown() { } public function testEasyIcon() { - + $that = $this; $callback = function ($text, $options) use ($that) { - $that->assertEquals(isset($options['escape']) ? $options['escape'] : true, $options['expected']['escape']); + $that->assertEquals(isset($options['escape']) ? $options['escape'] : true, + $options['expected']['escape']); $that->assertHtml($options['expected']['result'], $text); }; - + $this->_Trait->publicEasyIcon($callback, 'i:plus', [ 'expected' => [ 'escape' => false, @@ -69,14 +70,14 @@ public function testEasyIcon() { ]], '/i'] ] ]); - + $this->_Trait->publicEasyIcon($callback, 'Click Me!', [ 'expected' => [ 'escape' => true, 'result' => 'Click Me!' ] ]); - + $this->_Trait->publicEasyIcon($callback, 'i:plus Add', [ 'expected' => [ 'escape' => false, @@ -85,7 +86,7 @@ public function testEasyIcon() { ]], '/i', ' Add'] ] ]); - + $this->_Trait->publicEasyIcon($callback, 'Add i:plus', [ 'expected' => [ 'escape' => false, @@ -94,7 +95,7 @@ public function testEasyIcon() { ]], '/i'] ] ]); - + $this->_Trait->easyIcon = false; $this->_Trait->publicEasyIcon($callback, 'i:plus', [ 'expected' => [ @@ -102,11 +103,11 @@ public function testEasyIcon() { 'result' => 'i:plus' ] ]); - + } - + public function testHelperMethods() { - + // BootstrapPaginatorHelper - TODO // BootstrapPaginatorHelper::prev($title, array $options = []); // BootstrapPaginatorHelper::next($title, array $options = []); From 0649df0c0a2865b65d16d80db349abfc1c1257e1 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sat, 23 Apr 2016 23:52:22 +0200 Subject: [PATCH 033/222] Update the addClass method to remove duplicate classes. --- src/View/Helper/BootstrapTrait.php | 20 +++++++++---------- .../View/Helper/BootstrapTraitTest.php | 18 ++++++++++++++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 15c2eb4..a474234 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -41,21 +41,21 @@ trait BootstrapTrait { * @return array Array of options with $key set. */ public function addClass(array $options = [], $class = null, $key = 'class') { - if (is_array($class)) { - $class = implode(' ', array_unique(array_map('trim', $class))) ; + if (!is_array($class)) { + $class = explode(' ', trim($class)); } + $optClass = []; if (isset($options[$key])) { $optClass = $options[$key]; - if (is_array($optClass)) { - $optClass = trim(implode(' ', array_unique(array_map('trim', $optClass)))); + if (!is_array($optClass)) { + $optClass = explode(' ', trim($optClass)); } } - if (isset($optClass) && $optClass) { - $options[$key] = $optClass.' '.$class ; - } - else { - $options[$key] = $class ; - } + $class = array_merge($optClass, $class); + $class = array_map('trim', $class); + $class = array_unique($class); + $class = array_filter($class); + $options[$key] = implode(' ', $class); return $options ; } diff --git a/tests/TestCase/View/Helper/BootstrapTraitTest.php b/tests/TestCase/View/Helper/BootstrapTraitTest.php index 57e4534..8c29980 100644 --- a/tests/TestCase/View/Helper/BootstrapTraitTest.php +++ b/tests/TestCase/View/Helper/BootstrapTraitTest.php @@ -52,7 +52,23 @@ public function tearDown() { unset($this->Form); unset($this->Paginator); } - + + public function testAddClass() { + // Test with a string + $opts = [ + 'class' => 'class-1' + ]; + $opts = $this->_Trait->addClass($opts, ' class-1 class-2 '); + $this->assertEquals($opts, [ + 'class' => 'class-1 class-2' + ]); + // Test with an array + $opts = $this->_Trait->addClass($opts, ['class-1', 'class-3']); + $this->assertEquals($opts, [ + 'class' => 'class-1 class-2 class-3' + ]); + } + public function testEasyIcon() { $that = $this; From 514266d8845f3338c4dbceb4ff20c74b076115fe Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sat, 23 Apr 2016 23:52:39 +0200 Subject: [PATCH 034/222] Update the _addButtonClasses method to not add duplicate btn-xxxx classes. --- src/View/Helper/BootstrapFormHelper.php | 3 -- src/View/Helper/BootstrapTrait.php | 12 +++---- .../View/Helper/BootstrapFormHelperTest.php | 32 +++++++++++++++++++ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 4808339..611d54f 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -116,9 +116,6 @@ class BootstrapFormHelper extends FormHelper { public $horizontal = false ; public $inline = false ; - private $buttonTypes = ['default', 'primary', 'info', 'success', 'warning', 'danger', 'link'] ; - private $buttonSizes = ['xs', 'sm', 'lg'] ; - /** * * Replace the templates with the ones specified by newTemplates, call the specified function diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index a474234..8a6e8e2 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -75,13 +75,13 @@ protected function _addButtonClasses ($options) { ]; $type = $options['bootstrap-type']; $size = $options['bootstrap-size']; - unset($options['bootstrap-type'], $options['bootstrap-size']) ; - $options = $this->addClass($options, 'btn') ; - if (in_array($type, $this->buttonTypes)) { - $options = $this->addClass($options, 'btn-'.$type) ; + unset($options['bootstrap-type'], $options['bootstrap-size']); + $options = $this->addClass($options, 'btn'); + if (!preg_match('#btn-[a-z]+#', $options['class'])) { + $options = $this->addClass($options, 'btn-'.$type); } - if (in_array($size, $this->buttonSizes)) { - $options = $this->addClass($options, 'btn-'.$size) ; + if ($size) { + $options = $this->addClass($options, 'btn-'.$size); } return $options ; } diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php index ed6d3e5..0a0eed3 100644 --- a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -63,6 +63,38 @@ public function testCreate () { $this->assertEquals($this->Form->inline, false) ; } + public function testButton () { + // default button + $button = $this->Form->button('Test'); + $this->assertHtml([ + ['button' => [ + 'class' => 'btn btn-default', + 'type' => 'submit' + ]], 'Test', '/button' + ], $button); + // button with bootstrap-type and bootstrap-size + $button = $this->Form->button('Test', [ + 'bootstrap-type' => 'success', + 'bootstrap-size' => 'sm' + ]); + $this->assertHtml([ + ['button' => [ + 'class' => 'btn btn-success btn-sm', + 'type' => 'submit' + ]], 'Test', '/button' + ], $button); + // button with class + $button = $this->Form->button('Test', [ + 'class' => 'btn btn-primary' + ]); + $this->assertHtml([ + ['button' => [ + 'class' => 'btn btn-primary', + 'type' => 'submit' + ]], 'Test', '/button' + ], $button); + } + protected function _testInput ($expected, $fieldName, $options = []) { $formOptions = [] ; if (isset($options['_formOptions'])) { From 1c7e8ba651bda8cc00554ba1242e71d026f1e3a0 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sat, 11 Jun 2016 12:03:51 +0200 Subject: [PATCH 035/222] Update license information in files. --- src/Template/Element/Flash/error.ctp | 19 ++++++++++-- src/Template/Element/Flash/info.ctp | 19 ++++++++++-- src/Template/Element/Flash/success.ctp | 19 ++++++++++-- src/Template/Element/Flash/warning.ctp | 19 ++++++++++-- src/View/BootstrapStringTemplate.php | 17 +++++++++-- src/View/Helper/BootstrapFlashHelper.php | 22 +++++--------- src/View/Helper/BootstrapFormHelper.php | 22 +++++--------- src/View/Helper/BootstrapHtmlHelper.php | 22 +++++--------- src/View/Helper/BootstrapModalHelper.php | 22 +++++--------- src/View/Helper/BootstrapNavbarHelper.php | 22 +++++--------- src/View/Helper/BootstrapPaginatorHelper.php | 22 +++++--------- src/View/Helper/BootstrapPanelHelper.php | 22 +++++--------- src/View/Helper/BootstrapTrait.php | 32 ++++++++------------ 13 files changed, 144 insertions(+), 135 deletions(-) diff --git a/src/Template/Element/Flash/error.ctp b/src/Template/Element/Flash/error.ctp index 39a8091..6cd83b5 100644 --- a/src/Template/Element/Flash/error.ctp +++ b/src/Template/Element/Flash/error.ctp @@ -1,4 +1,19 @@ alert (h($message), 'danger', $params) ; +/** + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE file + * Redistributions of files must retain the above copyright notice. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/mit-license.php + * + * + * @copyright Copyright (c) Mikaël Capelle (https://typename.fr) + * @license https://opensource.org/licenses/mit-license.php MIT License + */ + +$helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; +echo $helper->alert (h($message), 'danger', $params) ; + ?> diff --git a/src/Template/Element/Flash/info.ctp b/src/Template/Element/Flash/info.ctp index db09560..876da13 100644 --- a/src/Template/Element/Flash/info.ctp +++ b/src/Template/Element/Flash/info.ctp @@ -1,4 +1,19 @@ alert (h($message), 'info', $params) ; +/** + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE file + * Redistributions of files must retain the above copyright notice. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/mit-license.php + * + * + * @copyright Copyright (c) Mikaël Capelle (https://typename.fr) + * @license https://opensource.org/licenses/mit-license.php MIT License + */ + +$helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; +echo $helper->alert (h($message), 'info', $params) ; + ?> diff --git a/src/Template/Element/Flash/success.ctp b/src/Template/Element/Flash/success.ctp index ecd2b17..2b312c2 100644 --- a/src/Template/Element/Flash/success.ctp +++ b/src/Template/Element/Flash/success.ctp @@ -1,4 +1,19 @@ alert (h($message), 'success', $params) ; +/** + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE file + * Redistributions of files must retain the above copyright notice. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/mit-license.php + * + * + * @copyright Copyright (c) Mikaël Capelle (https://typename.fr) + * @license https://opensource.org/licenses/mit-license.php MIT License + */ + +$helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; +echo $helper->alert (h($message), 'success', $params) ; + ?> diff --git a/src/Template/Element/Flash/warning.ctp b/src/Template/Element/Flash/warning.ctp index 6f6a798..9b5d309 100644 --- a/src/Template/Element/Flash/warning.ctp +++ b/src/Template/Element/Flash/warning.ctp @@ -1,4 +1,19 @@ alert (h($message), 'warning', $params) ; +/** + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE file + * Redistributions of files must retain the above copyright notice. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/mit-license.php + * + * + * @copyright Copyright (c) Mikaël Capelle (https://typename.fr) + * @license https://opensource.org/licenses/mit-license.php MIT License + */ + +$helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; +echo $helper->alert (h($message), 'warning', $params) ; + ?> diff --git a/src/View/BootstrapStringTemplate.php b/src/View/BootstrapStringTemplate.php index f442a81..46e8c6a 100644 --- a/src/View/BootstrapStringTemplate.php +++ b/src/View/BootstrapStringTemplate.php @@ -1,4 +1,17 @@ Date: Tue, 12 Jul 2016 15:55:30 +0200 Subject: [PATCH 036/222] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f1d629..45435ee 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ composer require holt59/cakephp3-bootstrap-helpers:dev-master ``` ```php // in config/bootstrap.php -Plugin::load('Bootstrap') ; +Plugin::load('Bootstrap'); ``` ```php @@ -27,7 +27,7 @@ public $helpers = [ 'className' => 'Bootstrap.BootstrapForm' ], /* ... */ -] ; +]; ``` --- From bbcc55c9f4b0a64063c470d594661f0f9c24ae0e Mon Sep 17 00:00:00 2001 From: Holt59 Date: Fri, 15 Jul 2016 13:11:31 +0200 Subject: [PATCH 037/222] Fix column length in comments. --- src/View/Helper/BootstrapPanelHelper.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 4532847..7b2321a 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -91,7 +91,8 @@ public function endGroup() { * * Create a Twitter Bootstrap like panel. * - * @param array|string $title If array, works as $options, otherwize used as the panel title. + * @param array|string $title If array, works as $options, otherwize used as + * the panel title. * @param array $options Options for the main div of the panel. * * Extra options (useless if $title not specified) : @@ -256,7 +257,8 @@ protected function _createFooter ($text = null, $options = []) { * Create / Start the header. If $info is specified as a string, create and return the * whole header, otherwize only open the header. * - * @param array|string $info If string, use as the panel title, otherwize works as $options. + * @param array|string $info If string, use as the panel title, otherwize works as + * $options. * @param array $options Options for the header div. * * Special option (if $info is string): @@ -279,7 +281,8 @@ public function header ($info = null, $options = []) { * Create / Start the body. If $info is not null, it is used as the body content, otherwize * start the body div. * - * @param array|string $info If string, use as the body content, otherwize works as $options. + * @param array|string $info If string, use as the body content, otherwize works + * as $options. * @param array $options Options for the footer div. * * From 9714eafcca8087b46d53b253791cff30ed4e274f Mon Sep 17 00:00:00 2001 From: Holt59 Date: Fri, 15 Jul 2016 13:11:49 +0200 Subject: [PATCH 038/222] Fix aria-expanded for opened panels. --- src/View/Helper/BootstrapPanelHelper.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 7b2321a..6347df9 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -185,6 +185,19 @@ protected function _cleanCurrent () { return $res; } + /** + * + * Return true if the current panel should be open (only for collapsible). + * + * @return true if the current panel should be open, false otherwize. + * + **/ + protected function _isOpen () { + return (is_int($this->_groupPanelOpen) + && $this->_groupPanelOpen === $this->_groupPanelCount) + || $this->_groupPanelOpen === $this->_bodyId; + } + protected function _createHeader ($title, $options = [], $titleOptions = []) { if (empty($titleOptions)) { $titleOptions = $options['title'] ; @@ -198,13 +211,14 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { if ($this->_collapsible) { $options += [ 'role' => 'tab', - 'id' => $this->_headId + 'id' => $this->_headId, + 'open' => $this->_isOpen() ]; $this->_headId = $options['id']; $title = $this->Html->link($title, '#'.$this->_bodyId, [ 'data-toggle' => 'collapse', 'data-parent' => $this->_groupId ? '#'.$this->_groupId : false, - 'aria-expanded' => true, + 'aria-expanded' => json_encode($options['open']), 'aria-controls' => '#'.$this->_bodyId, 'escape' => $options['escape'] ]); @@ -231,9 +245,7 @@ protected function _createBody ($text = null, $options = []) { $options = $this->addClass($options, 'panel-body'); $body = $this->Html->tag('div', $text, $options); if ($this->_collapsible) { - $open = ((is_int($this->_groupPanelOpen) - && $this->_groupPanelOpen === $this->_groupPanelCount) - || $this->_groupPanelOpen === $this->_bodyId) ? ' in' : ''; + $open = $this->_isOpen() ? ' in' : ''; $body = $this->Html->div('panel-collapse collapse'.$open, $text ? $body : null, [ 'role' => 'tabpanel', 'aria-labelledby' => $this->_headId, From c33805b69f6df897162457ada76165ceacf2ad01 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 17 Jul 2016 12:52:51 +0200 Subject: [PATCH 039/222] Remove incorrect attribute remaining from options. --- src/View/Helper/BootstrapPanelHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 6347df9..5e5082e 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -237,7 +237,7 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { unset($titleOptions['tag']); $title = $this->Html->tag($tag, $title, $titleOptions); } - unset($options['escape']); + unset($options['escape'], $options['open']); return $this->_cleanCurrent().$this->Html->tag('div', $title, $options); } From 0fc33eb2fb397b8f3867a6eda5aebdfecc7bc325 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 17 Jul 2016 12:53:13 +0200 Subject: [PATCH 040/222] Fix test for Panel for previous commits. --- tests/TestCase/View/Helper/BootstrapPanelHelperTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php index 0c99b61..f04ec6b 100644 --- a/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php @@ -160,7 +160,7 @@ public function testHeader () { ['a' => [ 'href' => '#collapse-0', 'data-toggle' => 'collapse', - 'aria-expanded' => true, + 'aria-expanded' => 'true', 'aria-controls' => '#collapse-0' ]], htmlspecialchars($htmlContent), @@ -185,7 +185,7 @@ public function testHeader () { ['a' => [ 'href' => '#collapse-1', 'data-toggle' => 'collapse', - 'aria-expanded' => true, + 'aria-expanded' => 'true', 'aria-controls' => '#collapse-1' ]], ['b' => true], $content, '/b', @@ -211,7 +211,7 @@ public function testHeader () { ['a' => [ 'href' => '#collapse-2', 'data-toggle' => 'collapse', - 'aria-expanded' => true, + 'aria-expanded' => 'true', 'aria-controls' => '#collapse-2' ]], ['i' => [ @@ -270,7 +270,7 @@ public function testGroup () { 'href' => '#collapse-'.$i, 'data-toggle' => 'collapse', 'data-parent' => '#panelGroup-1', - 'aria-expanded' => true, + 'aria-expanded' => $i == 0 ? 'true' : 'false', 'aria-controls' => '#collapse-'.$i ]], $panelHeading, From 8a91dd8b6b56f2088e693529e30e0cd0053ed3aa Mon Sep 17 00:00:00 2001 From: mik-laj Date: Tue, 26 Jul 2016 18:11:09 +0200 Subject: [PATCH 041/222] Accessibility icons --- src/View/Helper/BootstrapHtmlHelper.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index 5d29241..8662894 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -86,6 +86,7 @@ public function icon ($icon, $options = []) { public function faIcon ($icon, $options = []) { $options = $this->addClass($options, 'fa'); $options = $this->addClass($options, 'fa-'.$icon); + $options['aria-hidden'] = 'true'; return $this->tag('i', '', $options); } @@ -98,6 +99,7 @@ public function faIcon ($icon, $options = []) { public function glIcon ($icon, $options = []) { $options = $this->addClass($options, 'glyphicon'); $options = $this->addClass($options, 'glyphicon-'.$icon); + $options['aria-hidden'] = 'true'; return $this->tag('i', '', $options); } From 2d8ad33a60442386c339c1a520feaa5a8b49d9f2 Mon Sep 17 00:00:00 2001 From: mik-laj Date: Thu, 28 Jul 2016 12:33:43 +0200 Subject: [PATCH 042/222] Fix test for aria-hidden in icon --- src/View/Helper/BootstrapHtmlHelper.php | 8 +++++-- .../View/Helper/BootstrapHtmlHelperTest.php | 14 +++++++----- .../Helper/BootstrapPaginatorHelperTest.php | 6 +++-- .../View/Helper/BootstrapPanelHelperTest.php | 6 +++-- .../View/Helper/BootstrapTraitTest.php | 22 ++++++++++++++----- 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index 8662894..8c542a9 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -86,7 +86,9 @@ public function icon ($icon, $options = []) { public function faIcon ($icon, $options = []) { $options = $this->addClass($options, 'fa'); $options = $this->addClass($options, 'fa-'.$icon); - $options['aria-hidden'] = 'true'; + $options += [ + 'aria-hidden' => 'true' + ]; return $this->tag('i', '', $options); } @@ -99,7 +101,9 @@ public function faIcon ($icon, $options = []) { public function glIcon ($icon, $options = []) { $options = $this->addClass($options, 'glyphicon'); $options = $this->addClass($options, 'glyphicon-'.$icon); - $options['aria-hidden'] = 'true'; + $options += [ + 'aria-hidden' => 'true' + ]; return $this->tag('i', '', $options); } diff --git a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php index 1cad192..6618827 100644 --- a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php @@ -55,31 +55,35 @@ public function testIcon () { // Default icon (Glyphicon) $this->assertHtml ([ ['i' => [ - 'class' => 'glyphicon glyphicon-'.$type + 'class' => 'glyphicon glyphicon-'.$type, + 'aria-hidden' => 'true' ]], '/i' ], $this->Html->icon($type)); $this->assertHtml ([ ['i' => [ 'class' => $options['class'].' glyphicon glyphicon-'.$type, - 'id' => $options['id'] + 'id' => $options['id'], + 'aria-hidden' => 'true' ]], '/i' ], $this->Html->icon($type, $options)); // FontAwesome icon $this->assertHtml ([ ['i' => [ - 'class' => 'fa fa-'.$type + 'class' => 'fa fa-'.$type, + 'aria-hidden' => 'true' ]], '/i' ], $this->Html->faIcon($type)); $this->assertHtml ([ ['i' => [ 'class' => $options['class'].' fa fa-'.$type, - 'id' => $options['id'] + 'id' => $options['id'], + 'aria-hidden' => 'true' ]], '/i' - ], $this->Html->faIcon($type, $options)); + ], $this->Html->faIcon($type, $options)); } public function testLabel () { diff --git a/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php b/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php index caf507e..8c925b5 100644 --- a/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php @@ -59,7 +59,8 @@ public function testPrev () { ]], ['a' => true], ['i' => [ - 'class' => 'glyphicon glyphicon-chevron-left' + 'class' => 'glyphicon glyphicon-chevron-left', + 'aria-hidden' => 'true' ]], '/i', '/a', '/li' ], $this->Paginator->prev('i:chevron-left')); @@ -79,7 +80,8 @@ public function testNext () { 'href' => '/index?page=2' ]], ['i' => [ - 'class' => 'glyphicon glyphicon-chevron-right' + 'class' => 'glyphicon glyphicon-chevron-right', + 'aria-hidden' => 'true' ]], '/i', '/a', '/li' ], $this->Paginator->next('i:chevron-right')); diff --git a/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php index f04ec6b..b1b98b1 100644 --- a/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapPanelHelperTest.php @@ -136,7 +136,8 @@ public function testHeader () { 'class' => 'panel-title' ]], ['i' => [ - 'class' => 'glyphicon glyphicon-home' + 'class' => 'glyphicon glyphicon-home', + 'aria-hidden' => 'true' ]], '/i', ' Home', '/h4', '/div' @@ -215,7 +216,8 @@ public function testHeader () { 'aria-controls' => '#collapse-2' ]], ['i' => [ - 'class' => 'glyphicon glyphicon-home' + 'class' => 'glyphicon glyphicon-home', + 'aria-hidden' => 'true' ]], '/i', ' Home', '/a', '/h4', diff --git a/tests/TestCase/View/Helper/BootstrapTraitTest.php b/tests/TestCase/View/Helper/BootstrapTraitTest.php index 8c29980..56896d3 100644 --- a/tests/TestCase/View/Helper/BootstrapTraitTest.php +++ b/tests/TestCase/View/Helper/BootstrapTraitTest.php @@ -82,7 +82,8 @@ public function testEasyIcon() { 'expected' => [ 'escape' => false, 'result' => [['i' => [ - 'class' => 'glyphicon glyphicon-plus' + 'class' => 'glyphicon glyphicon-plus', + 'aria-hidden' => 'true' ]], '/i'] ] ]); @@ -98,7 +99,8 @@ public function testEasyIcon() { 'expected' => [ 'escape' => false, 'result' => [['i' => [ - 'class' => 'glyphicon glyphicon-plus' + 'class' => 'glyphicon glyphicon-plus', + 'aria-hidden' => 'true' ]], '/i', ' Add'] ] ]); @@ -107,7 +109,8 @@ public function testEasyIcon() { 'expected' => [ 'escape' => false, 'result' => ['Add ', ['i' => [ - 'class' => 'glyphicon glyphicon-plus' + 'class' => 'glyphicon glyphicon-plus', + 'aria-hidden' => 'true' ]], '/i'] ] ]); @@ -136,7 +139,8 @@ public function testHelperMethods() { 'class' => 'btn btn-default', 'type' => 'submit' ]], ['i' => [ - 'class' => 'glyphicon glyphicon-plus' + 'class' => 'glyphicon glyphicon-plus', + 'aria-hidden' => 'true' ]], '/i', '/button' ], $result) ; $result = $this->Form->input ('fieldname', [ @@ -154,7 +158,10 @@ public function testHelperMethods() { ['span' => [ 'class' => 'input-group-addon' ]], - ['i' => ['class' => 'glyphicon glyphicon-home']], '/i', + ['i' => [ + 'class' => 'glyphicon glyphicon-home', + 'aria-hidden' => 'true' + ]], '/i', '/span', ['input' => [ 'type' => 'text', @@ -165,7 +172,10 @@ public function testHelperMethods() { ['span' => [ 'class' => 'input-group-addon' ]], - ['i' => ['class' => 'glyphicon glyphicon-plus']], '/i', + ['i' => [ + 'class' => 'glyphicon glyphicon-plus', + 'aria-hidden' => 'true' + ]], '/i', '/span', '/div', '/div' From e34e0d6db6336556669a20dc96a6744e4f1f20c2 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sat, 20 Aug 2016 19:06:26 +0200 Subject: [PATCH 043/222] Fix bug with progress bar not showing text for sr and add possibility to customize text. --- src/View/Helper/BootstrapHtmlHelper.php | 61 ++++++++++--------------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index 8c542a9..af88804 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -52,6 +52,7 @@ class BootstrapHtmlHelper extends HtmlHelper { 'javascriptend' => '' ], 'useFontAwesome' => false, + 'progressTextFormat' => '%d%% Complete', 'tooltip' => [ 'placement' => 'right' ], @@ -266,53 +267,38 @@ public function tooltip($text, $tooltip, $options = []) { public function progress ($widths, $options = []) { $options += [ 'striped' => false, - 'active' => false + 'active' => false, + 'format' => $this->config('progressTextFormat') ]; $striped = $options['striped']; $active = $options['active']; unset($options['active'], $options['striped']) ; $bars = '' ; - if (is_array($widths)) { - foreach ($widths as $width) { - $width += [ - 'type' => $this->config('progress.type'), - 'min' => 0, - 'max' => 100, - 'display' => false - ]; - $class = 'progress-bar progress-bar-'.$width['type']; - $bars .= $this->div($class, - $width['display'] ? $width['width'].'%' : '', - [ - 'aria-valuenow' => $width['width'], - 'aria-valuemin' => $width['min'], - 'aria-valuemax' => $width['max'], - 'role' => 'progressbar', - 'style' => 'width: '.$width['width'].'%;' - ] - ); - } + if (!is_array($widths)) { + $widths = [ + array_merge([ + 'width' => $widths + ], $options) + ]; } - else { - $options += [ + foreach ($widths as $width) { + $width += [ 'type' => $this->config('progress.type'), 'min' => 0, 'max' => 100, 'display' => false ]; - $class = 'progress-bar progress-bar-'.$options['type']; - $bars = $this->div($class, - $options['display'] ? $widths.'%' : '', - [ - 'aria-valuenow' => $widths, - 'aria-valuemin' => $options['min'], - 'aria-valuemax' => $options['max'], - 'role' => 'progressbar', - 'style' => 'width: '.$widths.'%;' - ] - ); - unset($options['type'], $options['min'], - $options['max'], $options['display']); + $class = 'progress-bar progress-bar-'.$width['type']; + $content = $this->tag('span', sprintf($options['format'], $width['width']), [ + 'class' => $width['display'] ? '': 'sr-only' + ]); + $bars .= $this->div($class, $content, [ + 'aria-valuenow' => $width['width'], + 'aria-valuemin' => $width['min'], + 'aria-valuemax' => $width['max'], + 'role' => 'progressbar', + 'style' => 'width: '.$width['width'].'%;' + ]); } $options = $this->addClass($options, 'progress') ; if ($active) { @@ -322,7 +308,8 @@ public function progress ($widths, $options = []) { $options = $this->addClass($options, 'progress-striped') ; } $classes = $options['class']; - unset($options['class']) ; + unset($options['class'], $options['active'], $options['type'], + $options['striped'], $options['format']) ; return $this->div($classes, $bars, $options) ; } From 07e00290af09a1a50ee8ba708357b9af56c56e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 19 Sep 2016 08:41:00 +0200 Subject: [PATCH 044/222] Add {{required}} to checkboxContainer. --- src/View/Helper/BootstrapFormHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 1dc8377..cd8c966 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -46,7 +46,7 @@ class BootstrapFormHelper extends FormHelper { 'checkbox' => '', 'checkboxFormGroup' => '{{label}}', 'checkboxWrapper' => '
    {{label}}
    ', - 'checkboxContainer' => '{{h_checkboxContainer_start}}
    {{content}}
    {{h_checkboxContainer_end}}', + 'checkboxContainer' => '{{h_checkboxContainer_start}}
    {{content}}
    {{h_checkboxContainer_end}}', 'dateWidget' => '{{year}}{{month}}{{day}}{{hour}}{{minute}}{{second}}{{meridian}}', 'error' => '{{content}}', 'errorList' => '
      {{content}}
    ', From de82953a12936a73354fe870832129100fef7950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Fri, 30 Sep 2016 14:05:27 +0200 Subject: [PATCH 045/222] Add possibility to specify panel count manually. --- src/View/Helper/BootstrapPanelHelper.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 5e5082e..b6dfa68 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -109,22 +109,24 @@ public function create($title = null, $options = []) { 'no-body' => false, 'type' => 'default', 'collapsible' => $this->config('collapsible'), - 'open' => !$this->_groupInGroup + 'open' => !$this->_groupInGroup, + 'panel-count' => $this->_panelCount ]; $nobody = $options['no-body']; $type = $options['type']; $open = $options['open']; $this->_collapsible = $options['collapsible']; + $panelCount = $options['panel-count']; unset ($options['no-body'], $options['collapsible'], - $options['type'], $options['open']); + $options['type'], $options['open'], $options['panel-count']); $options = $this->addClass($options, ['panel', 'panel-'.$type]); if ($this->_collapsible) { - $this->_headId = 'heading-'.($this->_panelCount); - $this->_bodyId = 'collapse-'.($this->_panelCount); - $this->_panelCount++; + $this->_headId = 'heading-'.$panelCount; + $this->_bodyId = 'collapse-'.$panelCount; + $this->_panelCount = intval($panelCount) + 1; if ($open) { $this->_groupPanelOpen = $this->_bodyId; } From fa2a71accffdb763d70a8dfbaa8e6ba6e6b03673 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Wed, 23 Nov 2016 19:46:26 +0100 Subject: [PATCH 046/222] Update flash elements to allow escaping. --- src/Template/Element/Flash/error.ctp | 5 ++++- src/Template/Element/Flash/info.ctp | 5 ++++- src/Template/Element/Flash/success.ctp | 5 ++++- src/Template/Element/Flash/warning.ctp | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Template/Element/Flash/error.ctp b/src/Template/Element/Flash/error.ctp index 6cd83b5..8b397d1 100644 --- a/src/Template/Element/Flash/error.ctp +++ b/src/Template/Element/Flash/error.ctp @@ -14,6 +14,9 @@ */ $helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; -echo $helper->alert (h($message), 'danger', $params) ; +if (!isset($params['escape']) || $params['escape'] !== false) { + $message = h($message); +} +echo $helper->alert($message, 'danger', $params) ; ?> diff --git a/src/Template/Element/Flash/info.ctp b/src/Template/Element/Flash/info.ctp index 876da13..57af315 100644 --- a/src/Template/Element/Flash/info.ctp +++ b/src/Template/Element/Flash/info.ctp @@ -14,6 +14,9 @@ */ $helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; -echo $helper->alert (h($message), 'info', $params) ; +if (!isset($params['escape']) || $params['escape'] !== false) { + $message = h($message); +} +echo $helper->alert($message, 'info', $params) ; ?> diff --git a/src/Template/Element/Flash/success.ctp b/src/Template/Element/Flash/success.ctp index 2b312c2..2b9dae3 100644 --- a/src/Template/Element/Flash/success.ctp +++ b/src/Template/Element/Flash/success.ctp @@ -14,6 +14,9 @@ */ $helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; -echo $helper->alert (h($message), 'success', $params) ; +if (!isset($params['escape']) || $params['escape'] !== false) { + $message = h($message); +} +echo $helper->alert($message, 'success', $params) ; ?> diff --git a/src/Template/Element/Flash/warning.ctp b/src/Template/Element/Flash/warning.ctp index 9b5d309..b13c65b 100644 --- a/src/Template/Element/Flash/warning.ctp +++ b/src/Template/Element/Flash/warning.ctp @@ -14,6 +14,9 @@ */ $helper = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this) ; -echo $helper->alert (h($message), 'warning', $params) ; +if (!isset($params['escape']) || $params['escape'] !== false) { + $message = h($message); +} +echo $helper->alert($message, 'warning', $params) ; ?> From 9aee906b27ea114c0836ac9f9e3ec9fa60ffb96b Mon Sep 17 00:00:00 2001 From: Holt59 Date: Wed, 23 Nov 2016 20:57:32 +0100 Subject: [PATCH 047/222] Remove caret attribute. --- src/View/Helper/BootstrapNavbarHelper.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/View/Helper/BootstrapNavbarHelper.php b/src/View/Helper/BootstrapNavbarHelper.php index 914aac4..97cd49d 100644 --- a/src/View/Helper/BootstrapNavbarHelper.php +++ b/src/View/Helper/BootstrapNavbarHelper.php @@ -310,6 +310,7 @@ public function beginMenu ($name = null, $url = null, $options = [], ] ; $caret = array_key_exists('caret', $linkOptions) ? $linkOptions['caret'] : ''; + unset($options['caret']); $link = $this->Html->link ($name.$caret, $url ? $url : '#', $linkOptions); $options = $this->addClass ($options, 'dropdown') ; $listOptions = $this->addClass ($listOptions, 'dropdown-menu') ; From 954fc0a5cc6bf9a512801b520ead344c69bb54c6 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Wed, 23 Nov 2016 21:01:56 +0100 Subject: [PATCH 048/222] Clean code. --- src/View/Helper/BootstrapFormHelper.php | 153 +++++++++++++----------- 1 file changed, 84 insertions(+), 69 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index cd8c966..447f74d 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -18,7 +18,7 @@ class BootstrapFormHelper extends FormHelper { - use BootstrapTrait ; + use BootstrapTrait; public $helpers = [ 'Html', @@ -26,7 +26,7 @@ class BootstrapFormHelper extends FormHelper { 'bHtml' => [ 'className' => 'Bootstrap.BootstrapHtml' ] - ] ; + ]; /** * Default config for the helper. @@ -105,13 +105,13 @@ class BootstrapFormHelper extends FormHelper { '_default' => ['Cake\View\Widget\BasicWidget'], ]; - public $horizontal = false ; - public $inline = false ; + public $horizontal = false; + public $inline = false; /** * - * Replace the templates with the ones specified by newTemplates, call the specified function - * with the specified parameters, and then restore the old templates. + * Replace the templates with the ones specified by newTemplates, call the + * specified function with the specified parameters, and then restore the old templates. * * @params $templates The new templates * @params $callback The function to call @@ -121,11 +121,13 @@ class BootstrapFormHelper extends FormHelper { * **/ protected function _wrapTemplates ($templates, $callback, $params) { - $oldTemplates = array_map ([$this, 'templates'], array_combine(array_keys($templates), array_keys($templates))) ; - $this->templates ($templates) ; - $result = call_user_func_array ($callback, $params) ; - $this->templates ($oldTemplates) ; - return $result ; + $oldTemplates = array_map ([$this, 'templates'], + array_combine(array_keys($templates), + array_keys($templates))); + $this->templates ($templates); + $result = call_user_func_array ($callback, $params); + $this->templates ($oldTemplates); + return $result; } /** @@ -138,7 +140,7 @@ protected function _wrapTemplates ($templates, $callback, $params) { * **/ protected function _matchButton ($html) { - return strpos($html, 'colSize = $options['columns']; $this->horizontal = $options['horizontal']; $this->inline = $options['inline']; - unset($options['columns'], $options['horizontal'], $options['inline']) ; + unset($options['columns'], $options['horizontal'], $options['inline']); if ($this->horizontal) { - $options = $this->addClass($options, 'form-horizontal') ; + $options = $this->addClass($options, 'form-horizontal'); } else if ($this->inline) { - $options = $this->addClass($options, 'form-inline') ; + $options = $this->addClass($options, 'form-inline'); } - $options['role'] = 'form' ; - return parent::create($model, $options) ; + $options['role'] = 'form'; + return parent::create($model, $options); } /** @@ -228,19 +230,20 @@ public function create($model = null, Array $options = array()) { * **/ protected function _getColClass ($what, $offset = false) { - if ($what === 'error' && isset($this->colSize['error']) && $this->colSize['error'] == 0) { + if ($what === 'error' + && isset($this->colSize['error']) && $this->colSize['error'] == 0) { return $this->_getColClass('label', true).' '.$this->_getColClass('input'); } if (isset($this->colSize[$what])) { - return 'col-md-'.($offset ? 'offset-' : '').$this->colSize[$what] ; + return 'col-md-'.($offset ? 'offset-' : '').$this->colSize[$what]; } - $classes = [] ; + $classes = []; foreach ($this->colSize as $cl => $arr) { if (isset($arr[$what])) { - $classes[] = 'col-'.$cl.'-'.($offset ? 'offset-' : '').$arr[$what] ; + $classes[] = 'col-'.$cl.'-'.($offset ? 'offset-' : '').$arr[$what]; } } - return implode(' ', $classes) ; + return implode(' ', $classes); } protected function _wrapInputGroup ($addonOrButtons) { @@ -248,19 +251,21 @@ protected function _wrapInputGroup ($addonOrButtons) { if (is_string($addonOrButtons)) { $addonOrButtons = $this->_makeIcon($addonOrButtons); $addonOrButtons = ''.$addonOrButtons.'' ; + ($this->_matchButton($addonOrButtons) ? + 'btn' : 'addon').'">'.$addonOrButtons.''; } else if ($addonOrButtons !== false) { - $addonOrButtons = ''.implode('', $addonOrButtons).'' ; + $addonOrButtons = '' + .implode('', $addonOrButtons).''; } } - return $addonOrButtons ; + return $addonOrButtons; } public function prepend ($input, $prepend) { $prepend = $this->_wrapInputGroup ($prepend); if ($input === null) { - return '
    '.$prepend ; + return '
    '.$prepend; } return $this->_wrap($input, $prepend, null); } @@ -268,7 +273,7 @@ public function prepend ($input, $prepend) { public function append ($input, $append) { $append = $this->_wrapInputGroup($append); if ($input === null) { - return $append.'
    ' ; + return $append.'
    '; } return $this->_wrap($input, null, $append); } @@ -278,7 +283,7 @@ public function wrap ($input, $prepend, $append) { } protected function _wrap ($input, $prepend, $append) { - return '
    '.$prepend.$input.$append.'
    ' ; + return '
    '.$prepend.$input.$append.'
    '; } /** @@ -316,20 +321,20 @@ public function input($fieldName, array $options = array()) { $help = $options['help']; unset($options['help']); if ($help) { - $append .= '

    '.$help.'

    ' ; + $append .= '

    '.$help.'

    '; } $inline = $options['inline']; - unset ($options['inline']) ; + unset ($options['inline']); if ($options['type'] === 'radio') { - $options['templates'] = [] ; + $options['templates'] = []; if ($inline) { $options['templates'] = [ 'label' => $this->templates('label'), 'radioWrapper' => '{{label}}', 'nestingLabel' => '{{hidden}}{{input}}{{text}}' - ] ; + ]; } if ($this->horizontal) { $options['templates']['radioContainer'] = '
    {{content}}
    '; @@ -344,29 +349,29 @@ public function input($fieldName, array $options = array()) { 'append' => $append ]; - return parent::input($fieldName, $options) ; + return parent::input($fieldName, $options); } protected function _getDatetimeTemplate ($fields, $options) { - $inputs = [] ; + $inputs = []; foreach ($fields as $field => $in) { $in = isset($options[$field]) ? $options[$field] : $in; if ($in) { if ($field === 'timeFormat') - $field = 'meridian' ; // Template uses "meridian" instead of timeFormat + $field = 'meridian'; // Template uses "meridian" instead of timeFormat $inputs[$field] = '
    {{'.$field.'}}
    '; } } $tplt = $this->templates('dateWidget'); $tplt = explode('}}{{', substr($tplt, 2, count($tplt) - 3)); - $html = '' ; + $html = ''; foreach ($tplt as $v) { if (isset($inputs[$v])) { - $html .= $inputs[$v] ; + $html .= $inputs[$v]; } } return str_replace('{{colsize}}', round(12 / count($inputs)), - '
    '.$html.'
    ') ; + '
    '.$html.'
    '); } /** @@ -379,7 +384,8 @@ protected function _getDatetimeTemplate ($fields, $options) { */ public function file($fieldName, array $options = []) { - if (!$this->config('useCustomFileInput') || (isset($options['default']) && $options['default'])) { + if (!$this->config('useCustomFileInput') + || (isset($options['default']) && $options['default'])) { return parent::file($fieldName, $options); } @@ -413,24 +419,28 @@ public function file($fieldName, array $options = []) { 'onclick' => "document.getElementById('".$options['id']."').click();" ])); $buttonLabel = $options['button-label']; - unset($options['button-label']) ; + unset($options['button-label']); $fakeButton = $this->button($buttonLabel, array_merge($fakeButtonCustomOptions, [ 'type' => 'button', 'onclick' => "document.getElementById('".$options['id']."').click();" ])); - return $fileInput.$this->Html->div('input-group', $this->Html->div('input-group-btn', $fakeButton).$fakeInput) ; + return $fileInput.$this->Html->div('input-group', + $this->Html->div('input-group-btn', + $fakeButton).$fakeInput); } /** - * Returns a set of SELECT elements for a full datetime setup: day, month and year, and then time. + * Returns a set of SELECT elements for a full datetime setup: day, month and year, and + * then time. * * ### Date Options: * * - `empty` - If true, the empty select option is shown. If a string, * that string is displayed as the empty element. - * - `value` | `default` The default value to be used by the input. A value in `$this->data` - * matching the field name will override this value. If no default is provided `time()` will be used. + * - `value` | `default` The default value to be used by the input. A value in + * `$this->data matching the field name will override this value. If no default is + * provided `time()` will be used. * - `monthNames` If false, 2 digit numbers will be used instead of text. * If an array, the given array will be used. * - `minYear` The lowest year to use in the year select @@ -441,11 +451,13 @@ public function file($fieldName, array $options = []) { * ### Time options: * * - `empty` - If true, the empty select option is shown. If a string, - * - `value` | `default` The default value to be used by the input. A value in `$this->data` - * matching the field name will override this value. If no default is provided `time()` will be used. + * - `value` | `default` The default value to be used by the input. A value in + * `$this->data` matching the field name will override this value. If no default + * is provided `time()` will be used. * - `timeFormat` The time format to use, either 12 or 24. * - `interval` The interval for the minutes select. Defaults to 1 - * - `round` - Set to `up` or `down` if you want to force rounding in either direction. Defaults to null. + * - `round` - Set to `up` or `down` if you want to force rounding in either direction. + * Defaults to null. * - `second` Set to true to enable seconds drop down. * * To control the order of inputs, and any elements/content between the inputs you @@ -459,7 +471,9 @@ public function file($fieldName, array $options = []) { * @link http://book.cakephp.org/3.0/en/views/helpers/form.html#creating-date-and-time-inputs */ public function dateTime($fieldName, array $options = []) { - $fields = ['year' => true, 'month' => true, 'day' => true, 'hour' => true, 'minute' => true, 'second' => false, 'timeFormat' => false]; + $fields = ['year' => true, 'month' => true, 'day' => true, + 'hour' => true, 'minute' => true, 'second' => false, + 'timeFormat' => false]; return $this->_wrapTemplates ([ 'dateWidget' => $this->_getDatetimeTemplate($fields, $options) ], 'parent::dateTime', [$fieldName, $options]); @@ -516,9 +530,9 @@ protected function _createButtonOptions (array $options = []) { $block = $options['bootstrap-block']; unset($options['bootstrap-block']); if ($block) { - $options = $this->addClass($options, 'btn-block') ; + $options = $this->addClass($options, 'btn-block'); } - return $options ; + return $options; } /** @@ -532,7 +546,8 @@ protected function _createButtonOptions (array $options = []) { * */ public function button($title, array $options = []) { - return $this->_easyIcon ('parent::button', $title, $this->_createButtonOptions($options)); + return $this->_easyIcon ('parent::button', $title, + $this->_createButtonOptions($options)); } /** @@ -551,12 +566,12 @@ public function buttonGroup ($buttons, array $options = []) { 'vertical' => false ]; $vertical = $options['vertical']; - unset($options['vertical']) ; - $options = $this->addClass($options, 'btn-group') ; + unset($options['vertical']); + $options = $this->addClass($options, 'btn-group'); if ($vertical) { - $options = $this->addClass($options, 'btn-group-vertical') ; + $options = $this->addClass($options, 'btn-group-vertical'); } - return $this->Html->tag('div', implode('', $buttons), $options) ; + return $this->Html->tag('div', implode('', $buttons), $options); } /** @@ -568,8 +583,8 @@ public function buttonGroup ($buttons, array $options = []) { * **/ public function buttonToolbar (array $buttonGroups, array $options = array()) { - $options = $this->addClass($options, 'btn-toolbar') ; - return $this->Html->tag('div', implode('', $buttonGroups), $options) ; + $options = $this->addClass($options, 'btn-toolbar'); + return $this->Html->tag('div', implode('', $buttonGroups), $options); } /** @@ -589,9 +604,9 @@ public function buttonToolbar (array $buttonGroups, array $options = array()) { */ public function dropdownButton ($title, array $menu = [], array $options = []) { - $options['type'] = false ; - $options['data-toggle'] = 'dropdown' ; - $options = $this->addClass($options, "dropdown-toggle") ; + $options['type'] = false; + $options['data-toggle'] = 'dropdown'; + $options = $this->addClass($options, "dropdown-toggle"); return $this->buttonGroup([ $this->button($title.' ', $options), @@ -612,7 +627,7 @@ public function dropdownButton ($title, array $menu = [], array $options = []) { * **/ public function submit($caption = null, array $options = array()) { - return parent::submit($caption, $this->_createButtonOptions($options)) ; + return parent::submit($caption, $this->_createButtonOptions($options)); } /** SPECIAL FORM **/ @@ -660,24 +675,24 @@ public function searchForm ($model = null, $options = [], $inpOpts = [], $btnOpt 'label' => $options['label'] ]; - unset($options['id']) ; - unset($options['label']) ; - unset($options['placeholder']) ; + unset($options['id']); + unset($options['label']); + unset($options['placeholder']); $btnName = $options['button']; - unset($options['button']) ; + unset($options['button']); $inpOpts['append'] = $this->button($btnName, $btnOpts); $options['inline'] = (bool)$inpOpts['label']; - $output = '' ; + $output = ''; - $output .= $this->create($model, $options) ; + $output .= $this->create($model, $options); $output .= $this->input($inpOpts['id'], $inpOpts); - $output .= $this->end() ; + $output .= $this->end(); - return $output ; + return $output; } } From 76a417e5e995fb38e8ce739d4cdf8e2cc2fdc68e Mon Sep 17 00:00:00 2001 From: Holt59 Date: Fri, 25 Nov 2016 21:57:01 +0100 Subject: [PATCH 049/222] Update custom file input. --- src/View/Helper/BootstrapFormHelper.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 447f74d..238a0a1 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -399,6 +399,9 @@ public function file($fieldName, array $options = []) { ]; $fakeInputCustomOptions = $options['_input']; + $fakeInputCustomOptions += [ + 'value' => '' + ]; $fakeButtonCustomOptions = $options['_button']; unset($options['_input'], $options['_button']); @@ -412,7 +415,7 @@ public function file($fieldName, array $options = []) { 'onchange' => "document.getElementById('".$options['id']."-input').value = (this.files.length <= 1) ? this.files[0].name : this.files.length + ' ' + '" . $countLabel . "';" ])); - $fakeInput = $this->text($fieldName, array_merge($options, $fakeInputCustomOptions, [ + $fakeInput = $this->text($fieldName, array_merge($fakeInputCustomOptions, [ 'name' => $fieldName.'-text', 'readonly' => 'readonly', 'id' => $options['id'].'-input', From ad90cb82156ef38f9527df5421c1fad9cec4eb8d Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 27 Nov 2016 14:07:03 +0100 Subject: [PATCH 050/222] Set value correctly in custom file input. --- src/View/Helper/BootstrapFormHelper.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 238a0a1..ecaffdc 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -399,13 +399,9 @@ public function file($fieldName, array $options = []) { ]; $fakeInputCustomOptions = $options['_input']; - $fakeInputCustomOptions += [ - 'value' => '' - ]; $fakeButtonCustomOptions = $options['_button']; unset($options['_input'], $options['_button']); - $options += ['secure' => true]; $options = $this->_initInputField($fieldName, $options); unset($options['type']); $countLabel = $options['count-label']; @@ -415,6 +411,9 @@ public function file($fieldName, array $options = []) { 'onchange' => "document.getElementById('".$options['id']."-input').value = (this.files.length <= 1) ? this.files[0].name : this.files.length + ' ' + '" . $countLabel . "';" ])); + $fakeInputCustomOptions += [ + 'value' => $options['val']['name'] + ]; $fakeInput = $this->text($fieldName, array_merge($fakeInputCustomOptions, [ 'name' => $fieldName.'-text', 'readonly' => 'readonly', From 522f2b48476cf3b42d20aab3762b16121b1ce685 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 28 Nov 2016 17:12:17 +0100 Subject: [PATCH 051/222] Disable bad escaping for custom file input. --- src/View/Helper/BootstrapFormHelper.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index ecaffdc..d4e4c5d 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -408,7 +408,8 @@ public function file($fieldName, array $options = []) { unset($options['count-label']); $fileInput = $this->widget('file', array_merge($options, [ 'style' => 'display: none;', - 'onchange' => "document.getElementById('".$options['id']."-input').value = (this.files.length <= 1) ? this.files[0].name : this.files.length + ' ' + '" . $countLabel . "';" + 'onchange' => "document.getElementById('".$options['id']."-input').value = (this.files.length <= 1) ? this.files[0].name : this.files.length + ' ' + '" . $countLabel . "';", + 'escape' => false ])); $fakeInputCustomOptions += [ @@ -418,7 +419,8 @@ public function file($fieldName, array $options = []) { 'name' => $fieldName.'-text', 'readonly' => 'readonly', 'id' => $options['id'].'-input', - 'onclick' => "document.getElementById('".$options['id']."').click();" + 'onclick' => "document.getElementById('".$options['id']."').click();", + 'escape' => false ])); $buttonLabel = $options['button-label']; unset($options['button-label']); From cb97cfda837465a13246267593427064f3fa7f6e Mon Sep 17 00:00:00 2001 From: ypnos-web Date: Fri, 2 Dec 2016 16:55:28 +0100 Subject: [PATCH 052/222] Extend and clarify documentation Make it more obvious how navbar menus and links interact with each other. --- src/View/Helper/BootstrapNavbarHelper.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapNavbarHelper.php b/src/View/Helper/BootstrapNavbarHelper.php index 97cd49d..204760a 100644 --- a/src/View/Helper/BootstrapNavbarHelper.php +++ b/src/View/Helper/BootstrapNavbarHelper.php @@ -176,6 +176,8 @@ public function create ($brand, $options = []) { /** * * Add a link to the navbar or to a menu. + * Links outside a menu are realized as buttons. Encapsulate links with + * beginMenu(), endMenu() to create a horizontal hover menu in the navbar. * * @param name The link text * @param url The link URL @@ -284,8 +286,12 @@ public function searchForm ($model = null, $options = []) { /** * - * Start a new menu, 2 levels: If not in submenu, create a dropdown menu, - * oterwize create hover menu. + * Start a new menu. Two types of menus exist: Horizontal hover menu in the + * navbar (level 0) and vertical dropdown menu (level 1). The menu level is + * determined automatically: A dropdown menu needs to be part of a hover menu. + * In the hover menu case, pass the options array as the first argument. + * Populate the menu with link(), divider(), and sub menus. + * Use 'class' => 'navbar-right' option for flush right. * * @param name The name of the menu * @param url A URL for the menu (default null) From 98cb4efc45d1974d2a5ffd75ceddf273e4c3abda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 13 Dec 2016 13:37:02 +0100 Subject: [PATCH 053/222] Update license. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b4f0cc2..78837c6 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "holt59/cakephp3-bootstrap-helpers", "description": "Bootstrap Helpers for CakePHP 3.0", "keywords": ["CakePHP", "Bootstrap"], - "license": "Apache", + "license": "MIT", "type": "cakephp-plugin", "require": { "cakephp/cakephp": ">=3.2.3" From ba6f8b4824790fd43e693c3cc55e4a60e4f50e2a Mon Sep 17 00:00:00 2001 From: Lyziane Date: Thu, 5 Jan 2017 16:02:25 -0500 Subject: [PATCH 054/222] Still trying to make it "multiple-files-friendly" If you pass the option 'multiple' to upload many files, the default button label becomes plural. Also, it fixes the displayed value after submit: the name of the file if only one or a count of files if many --- src/View/Helper/BootstrapFormHelper.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index d4e4c5d..a1a0fa5 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -395,7 +395,7 @@ public function file($fieldName, array $options = []) { 'id' => $fieldName, 'secure' => true, 'count-label' => __('files selected'), - 'button-label' => __('Choose File') + 'button-label' => (isset($options['multiple']) && $options['multiple']) ? __('Choose Files') : __('Choose File') ]; $fakeInputCustomOptions = $options['_input']; @@ -412,9 +412,19 @@ public function file($fieldName, array $options = []) { 'escape' => false ])); - $fakeInputCustomOptions += [ - 'value' => $options['val']['name'] - ]; + if (!empty($options['val']) && is_array($options['val'])) { + if (isset($options['val']['name']) || count($options['val']) == 1) { + $fakeInputCustomOptions += [ + 'value' => (isset($options['val']['name'])) ? $options['val']['name'] : $options['val'][0]['name'] + ]; + } + else { + $fakeInputCustomOptions += [ + 'value' => count($options['val']) . ' ' . $countLabel + ]; + } + } + $fakeInput = $this->text($fieldName, array_merge($fakeInputCustomOptions, [ 'name' => $fieldName.'-text', 'readonly' => 'readonly', From 0aa4fdbbb7eea7aa84e9f5dac009cb5fa8a745a1 Mon Sep 17 00:00:00 2001 From: Mikael Capelle Date: Tue, 31 Jan 2017 18:00:53 +0000 Subject: [PATCH 055/222] Start cleaning documentation. --- src/View/Helper/BootstrapFormHelper.php | 211 +++++++++++++------ src/View/Helper/BootstrapHtmlHelper.php | 130 +++++++----- src/View/Helper/BootstrapNavbarHelper.php | 36 ++-- src/View/Helper/BootstrapPaginatorHelper.php | 2 +- src/View/Helper/BootstrapTrait.php | 21 +- 5 files changed, 243 insertions(+), 157 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index a1a0fa5..cbdf7da 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -105,7 +105,18 @@ class BootstrapFormHelper extends FormHelper { '_default' => ['Cake\View\Widget\BasicWidget'], ]; + /** + * Horizontal mode enabled/disabled. + * + * @var bool + */ public $horizontal = false; + + /** + * Inline mode enabled/disabled. + * + * @var bool + */ public $inline = false; /** @@ -113,11 +124,11 @@ class BootstrapFormHelper extends FormHelper { * Replace the templates with the ones specified by newTemplates, call the * specified function with the specified parameters, and then restore the old templates. * - * @params $templates The new templates - * @params $callback The function to call - * @params $params The arguments for the $callback function + * @params array $templates The new templates + * @params callable $callback The function to call + * @params array $params The arguments for the $callback function * - * @return The return value of $callback + * @return mixed The return value of $callback * **/ protected function _wrapTemplates ($templates, $callback, $params) { @@ -134,9 +145,9 @@ protected function _wrapTemplates ($templates, $callback, $params) { * * Try to match the specified HTML code with a button or a input with submit type. * - * @param $html The HTML code to check + * @param string $html The HTML code to check * - * @return true if the HTML code contains a button + * @return bool true if the HTML code contains a button * **/ protected function _matchButton ($html) { @@ -155,10 +166,10 @@ protected function _getDefaultTemplateVars (&$options) { 'h_formGroup_start' => '
    ', 'h_formGroup_end' => '
    ', 'h_checkboxContainer_start' => '
    ', + .' '.$this->_getColClass('input').'">', 'h_checkboxContainer_end' => '
    ', 'h_radioContainer_start' => '
    ', + .' '.$this->_getColClass('input').'">', 'h_radioContainer_end' => '
    ', 'h_submitContainer_start' => '
    ', 'h_submitContainer_end' => '
    ', @@ -187,23 +198,39 @@ protected function _inputContainerTemplate($options) { } /** + * Returns an HTML form element. * - * Create a Twitter Bootstrap like form. - * - * New options available: - * - horizontal: boolean, specify if the form is horizontal - * - inline: boolean, specify if the form is inline - * - search: boolean, specify if the form is a search form - * - * Unusable options: - * - inputDefaults - * - * @param $model The model corresponding to the form - * @param $options Options to customize the form - * - * @return The HTML tags corresponding to the openning of the form + * ### Options: * - **/ + * - `type` Form method defaults to autodetecting based on the form context. If + * the form context's isCreate() method returns false, a PUT request will be done. + * - `method` Set the form's method attribute explicitly. + * - `action` The controller action the form submits to, (optional). Use this option if you + * don't need to change the controller from the current request's controller. Deprecated since 3.2, use `url`. + * - `url` The URL the form submits to. Can be a string or a URL array. If you use 'url' + * you should leave 'action' undefined. + * - `encoding` Set the accept-charset encoding for the form. Defaults to `Configure::read('App.encoding')` + * - `enctype` Set the form encoding explicitly. By default `type => file` will set `enctype` + * to `multipart/form-data`. + * - `templates` The templates you want to use for this form. Any templates will be merged on top of + * the already loaded templates. This option can either be a filename in /config that contains + * the templates you want to load, or an array of templates to use. + * - `context` Additional options for the context class. For example the EntityContext accepts a 'table' + * option that allows you to set the specific Table class the form should be based on. + * - `idPrefix` Prefix for generated ID attributes. + * - `templateVars` Provide template variables for the formStart template. + * + * - `horizontal` - Boolean specifying if the form should be horizontal. + * - `inline` - Boolean specifying if the form should be inlined. + * - `search` - Boolean specifying if the form is a search form. + * + * @param mixed $model The context for which the form is being defined. Can + * be an ORM entity, ORM resultset, or an array of meta data. You can use false or null + * to make a model-less form. + * @param array $options An array of html attributes and options. + * @return string An formatted opening FORM tag. + * @link http://book.cakephp.org/3.0/en/views/helpers/form.html#Cake\View\Helper\FormHelper::create + */ public function create($model = null, Array $options = array()) { $options += [ 'columns' => $this->config('columns'), @@ -228,6 +255,11 @@ public function create($model = null, Array $options = array()) { * * Return the col size class for the specified column (label, input or error). * + * @param string $what + * @param bool $offset Add the offset prefix. + * + * @return string + * **/ protected function _getColClass ($what, $offset = false) { if ($what === 'error' @@ -251,12 +283,12 @@ protected function _wrapInputGroup ($addonOrButtons) { if (is_string($addonOrButtons)) { $addonOrButtons = $this->_makeIcon($addonOrButtons); $addonOrButtons = ''.$addonOrButtons.''; + ($this->_matchButton($addonOrButtons) ? + 'btn' : 'addon').'">'.$addonOrButtons.''; } else if ($addonOrButtons !== false) { $addonOrButtons = '' - .implode('', $addonOrButtons).''; + .implode('', $addonOrButtons).''; } } return $addonOrButtons; @@ -287,16 +319,37 @@ protected function _wrap ($input, $prepend, $append) { } /** - * - * Create & return an input block (Twitter Boostrap Like). - * - * New options: - * - prepend: - * -> string: Add before the input - * -> array: Add elements in array before inputs - * - append: Same as prepend except it add elements after input - * - **/ + * Generates a form input element complete with label and wrapper div + * + * ### Options + * + * See each field type method for more information. Any options that are part of + * $attributes or $options for the different **type** methods can be included in `$options` for input(). + * Additionally, any unknown keys that are not in the list below, or part of the selected type's options + * will be treated as a regular HTML attribute for the generated input. + * + * - `type` - Force the type of widget you want. e.g. `type => 'select'` + * - `label` - Either a string label, or an array of options for the label. See FormHelper::label(). + * - `options` - For widgets that take options e.g. radio, select. + * - `error` - Control the error message that is produced. Set to `false` to disable any kind of error reporting (field + * error and error messages). + * - `empty` - String or boolean to enable empty select box options. + * - `nestedInput` - Used with checkbox and radio inputs. Set to false to render inputs outside of label + * elements. Can be set to true on any input to force the input inside the label. If you + * enable this option for radio buttons you will also need to modify the default `radioWrapper` template. + * - `templates` - The templates you want to use for this input. Any templates will be merged on top of + * the already loaded templates. This option can either be a filename in /config that contains + * the templates you want to load, or an array of templates to use. + * - `prepend` - String or array of elements to prepend. + * - `append` - String or array of elements to append. + * - `help` - String containing an help message for the input. + * - `inline` + * + * @param string $fieldName This should be "modelname.fieldname" + * @param array $options Each type of input takes different options. + * @return string Completed form widget. + * @link http://book.cakephp.org/3.0/en/views/helpers/form.html#creating-form-inputs + */ public function input($fieldName, array $options = array()) { $options += [ @@ -424,7 +477,7 @@ public function file($fieldName, array $options = []) { ]; } } - + $fakeInput = $this->text($fieldName, array_merge($fakeInputCustomOptions, [ 'name' => $fieldName.'-text', 'readonly' => 'readonly', @@ -550,14 +603,21 @@ protected function _createButtonOptions (array $options = []) { } /** + * Creates a `