From dc995044d54838dcb55d0d37fe14a9a0ef8aaf8c Mon Sep 17 00:00:00 2001 From: jmenges Date: Tue, 15 Sep 2015 15:25:21 +0200 Subject: [PATCH 001/312] Update README.md Added short info for users migrating from holt59/cakephp3-bootstrap3-helpers. --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 60beaaf..818e58a 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,25 @@ If you want the latest **Bootstrap 3** version of the plugin: composer require holt59/cakephp3-bootstrap-helpers:~3.0 ``` +When updating from holt59/cakephp3-bootstrap3-helpers it is necessary to change the $helpers variable. +If you are including the Formhelper the change would look like this. +Original: +``` + public $helpers = [ + 'Form' => [ + 'className' => 'Bootstrap3.BootstrapForm' + ] + ]; +``` +``` + public $helpers = [ + 'Form' => [ + 'className' => 'Bootstrap.BootstrapForm' + ] + ]; +``` + + If you want to test the **Bootstrap 4** version of the plugin (alpha): ``` composer require holt59/cakephp3-bootstrap-helpers:dev-v4.0.0-alpha From dbc155f4134e3848f4757f63f12436e97ff0112f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 15 Sep 2015 15:38:50 +0200 Subject: [PATCH 002/312] Update BootstrapFormHelper.php --- src/View/Helper/BootstrapFormHelper.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 8f10025..f096952 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -233,14 +233,14 @@ public function create($model = null, Array $options = array()) { $this->colSize = $this->_defaultColumnSize ; } $this->horizontal = $this->_extractOption('horizontal', $options, false); - unset($options['horizontal']); + unset($options['horizontal']); $this->search = $this->_extractOption('search', $options, false) ; unset($options['search']) ; $this->inline = $this->_extractOption('inline', $options, false) ; unset($options['inline']) ; - if ($this->horizontal) { - $options = $this->addClass($options, 'form-horizontal') ; - } + if ($this->horizontal) { + $options = $this->addClass($options, 'form-horizontal') ; + } else if ($this->inline) { $options = $this->addClass($options, 'form-inline') ; } @@ -249,8 +249,8 @@ public function create($model = null, Array $options = array()) { } $options['role'] = 'form' ; $this->_setDefaultTemplates () ; - return parent::create($model, $options) ; - } + return parent::create($model, $options) ; + } /** * From 1750c550ba318a73868b835222115cb88ea10033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 15 Sep 2015 15:54:09 +0200 Subject: [PATCH 003/312] Update README.md --- README.md | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 818e58a..e036a3f 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,22 @@ If you want the latest **Bootstrap 3** version of the plugin: composer require holt59/cakephp3-bootstrap-helpers:~3.0 ``` -When updating from holt59/cakephp3-bootstrap3-helpers it is necessary to change the $helpers variable. -If you are including the Formhelper the change would look like this. -Original: -``` - public $helpers = [ - 'Form' => [ - 'className' => 'Bootstrap3.BootstrapForm' - ] - ]; -``` +If you are updating from `holt59/cakephp3-bootstrap3-helpers`, do not forget to change the the following: + +```php +// in config/bootstrap.php +Plugin::load('Bootstrap') ; // instead of Plugin::load('Boostrap3') ; ``` - public $helpers = [ - 'Form' => [ - 'className' => 'Bootstrap.BootstrapForm' - ] - ]; + +```php +// in your AppController +public $helpers = [ + 'Form' => [ + 'className' => 'Bootstrap.BootstrapForm' // instead of 'Boostrap3.BootstrapForm' + ] +] ; ``` +--- If you want to test the **Bootstrap 4** version of the plugin (alpha): From 79d10e4916804326794880fc428fce898df8f820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 15 Sep 2015 15:54:34 +0200 Subject: [PATCH 004/312] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e036a3f..4d47ef8 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@ If you are updating from `holt59/cakephp3-bootstrap3-helpers`, do not forget to ```php // in config/bootstrap.php -Plugin::load('Bootstrap') ; // instead of Plugin::load('Boostrap3') ; +Plugin::load('Bootstrap') ; // instead of Plugin::load('Bootstrap3') ; ``` ```php // in your AppController public $helpers = [ 'Form' => [ - 'className' => 'Bootstrap.BootstrapForm' // instead of 'Boostrap3.BootstrapForm' + 'className' => 'Bootstrap.BootstrapForm' // instead of 'Bootstrap3.BootstrapForm' ] ] ; ``` From 390f4a07afa4de49d88691a778f5388dcf20085a Mon Sep 17 00:00:00 2001 From: Dave Baker Date: Thu, 8 Oct 2015 10:25:00 +0100 Subject: [PATCH 005/312] Alter the way default values are set in file() To prevent a chain of `if(!isset)` operations, I replaced the default options set up with an array merge --- src/View/Helper/BootstrapFormHelper.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index f096952..e336cc3 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -441,9 +441,13 @@ public function file($fieldName, array $options = []) { if (!$this->_customFileInput || (isset($options['default']) && $options['default'])) { return parent::file($fieldName, $options); } - if (!isset($options['id'])) { - $options['id'] = $fieldName ; - } + $defaultOptions = [ + 'id' => $fieldName, + 'button-class' => '', + 'input-class' => '' + ]; + $options = array_merge($options, $defaultOptions); + $options += ['secure' => true]; $options = $this->_initInputField($fieldName, $options); unset($options['type']); From 9c3a5bb410f8a7e7dfcba716c18c5a600a8a541a Mon Sep 17 00:00:00 2001 From: Dave Baker Date: Thu, 8 Oct 2015 10:25:38 +0100 Subject: [PATCH 006/312] Add support for passing class attr to custom File --- 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 e336cc3..eee84eb 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -460,13 +460,15 @@ public function file($fieldName, array $options = []) { $fakeInput = $this->text($fieldName, array_merge($options, [ 'readonly' => 'readonly', 'id' => $options['id'].'-input', - 'onclick' => "document.getElementById('".$options['id']."').click();" + 'onclick' => "document.getElementById('".$options['id']."').click();", + 'class' => $options['input-class'] ])); $buttonLabel = $this->_extractOption('button-label', $options, __('Choose File')); unset($options['button-label']) ; $fakeButton = $this->button($buttonLabel, [ 'type' => 'button', - 'onclick' => "document.getElementById('".$options['id']."').click();" + 'onclick' => "document.getElementById('".$options['id']."').click();", + 'class' => $options['button-class'] ]); return $fileInput.$this->Html->div('input-group', $this->Html->div('input-group-btn', $fakeButton).$fakeInput) ; } From 433766212f927f3fa366a29cf74317bee87b2700 Mon Sep 17 00:00:00 2001 From: Dave Baker Date: Thu, 8 Oct 2015 10:31:41 +0100 Subject: [PATCH 007/312] Reverse args order in file() array_merge for options --- 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 eee84eb..86cbc15 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -446,7 +446,7 @@ public function file($fieldName, array $options = []) { 'button-class' => '', 'input-class' => '' ]; - $options = array_merge($options, $defaultOptions); + $options = array_merge($defaultOptions, $options); $options += ['secure' => true]; $options = $this->_initInputField($fieldName, $options); From 3dbf1416d1648d11e151c59f9fadb17954339e0f Mon Sep 17 00:00:00 2001 From: Dave Baker Date: Thu, 8 Oct 2015 10:50:39 +0100 Subject: [PATCH 008/312] Revert change on default options, will use _extractOption instead --- src/View/Helper/BootstrapFormHelper.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 86cbc15..00d8e4e 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -441,12 +441,9 @@ public function file($fieldName, array $options = []) { if (!$this->_customFileInput || (isset($options['default']) && $options['default'])) { return parent::file($fieldName, $options); } - $defaultOptions = [ - 'id' => $fieldName, - 'button-class' => '', - 'input-class' => '' - ]; - $options = array_merge($defaultOptions, $options); + if (!isset($options['id'])) { + $options['id'] = $fieldName; + } $options += ['secure' => true]; $options = $this->_initInputField($fieldName, $options); From 9cb3d205b049c89c0bdf8483b28cc3e97d25ea47 Mon Sep 17 00:00:00 2001 From: Dave Baker Date: Thu, 8 Oct 2015 11:05:19 +0100 Subject: [PATCH 009/312] Replace button/input -class options with more general ones Rather than have an array key per option, pass in _button and/or _input with an array of the additional options you need to pass to the custom File input elements --- src/View/Helper/BootstrapFormHelper.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 00d8e4e..5198991 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -454,19 +454,24 @@ public function file($fieldName, array $options = []) { 'style' => 'display: none;', '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 = $this->_extractOption('_input', $options, []); + unset($options['_input']); + $fakeButtonCustomOptions = $this->_extractOption('_button', $options, []); + unset($options['_button']); + + $fakeInput = $this->text($fieldName, array_merge($options, $fakeInputCustomOptions, [ 'readonly' => 'readonly', 'id' => $options['id'].'-input', - 'onclick' => "document.getElementById('".$options['id']."').click();", - 'class' => $options['input-class'] + 'onclick' => "document.getElementById('".$options['id']."').click();" ])); $buttonLabel = $this->_extractOption('button-label', $options, __('Choose File')); unset($options['button-label']) ; - $fakeButton = $this->button($buttonLabel, [ + + $fakeButton = $this->button($buttonLabel, array_merge($fakeButtonCustomOptions, [ 'type' => 'button', - 'onclick' => "document.getElementById('".$options['id']."').click();", - 'class' => $options['button-class'] - ]); + 'onclick' => "document.getElementById('".$options['id']."').click();" + ])); return $fileInput.$this->Html->div('input-group', $this->Html->div('input-group-btn', $fakeButton).$fakeInput) ; } From 193b78482c67f05de93cd0da2af2a0a3ee9197e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 29 Oct 2015 10:05:39 +0100 Subject: [PATCH 010/312] Update BootstrapFlashHelper.php --- src/View/Helper/BootstrapFlashHelper.php | 176 +++++++++++------------ 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/src/View/Helper/BootstrapFlashHelper.php b/src/View/Helper/BootstrapFlashHelper.php index 0c60569..05cf690 100644 --- a/src/View/Helper/BootstrapFlashHelper.php +++ b/src/View/Helper/BootstrapFlashHelper.php @@ -1,97 +1,97 @@ 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 - )); - } - $flash = $options + $flash; - $this->request->session()->delete("Flash.$key"); - - $element = $flash['element'] ; - if (in_array(basename($element), $this->_bootstrapTemplates)) { - $flash['element'] = 'Bootstrap3.'.$element ; - } - - return $this->_View->element($flash['element'], $flash); + /** + * Used to render the message set in FlashComponent::set() + * + * In your view: $this->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 + )); + } + $flash = $options + $flash; + $this->request->session()->delete("Flash.$key"); + + $element = $flash['element'] ; + if (in_array(basename($element), $this->_bootstrapTemplates)) { + $flash['element'] = 'Bootstrap.'.$element ; + } + + return $this->_View->element($flash['element'], $flash); } } -?> \ No newline at end of file +?> From 372ef73e3e9827fcc1dc4466fd35cde5c67c3c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 29 Oct 2015 10:05:50 +0100 Subject: [PATCH 011/312] Update error.ctp --- src/Template/Element/Flash/error.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Template/Element/Flash/error.ctp b/src/Template/Element/Flash/error.ctp index 1f0cff9..39a8091 100644 --- a/src/Template/Element/Flash/error.ctp +++ b/src/Template/Element/Flash/error.ctp @@ -1,4 +1,4 @@ alert (h($message), 'danger', $params) ; -?> \ No newline at end of file +?> From 0a1d412919edb64aa1eb7b25e621b6b755275631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 29 Oct 2015 10:06:00 +0100 Subject: [PATCH 012/312] Update info.ctp --- src/Template/Element/Flash/info.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Template/Element/Flash/info.ctp b/src/Template/Element/Flash/info.ctp index 2139c57..db09560 100644 --- a/src/Template/Element/Flash/info.ctp +++ b/src/Template/Element/Flash/info.ctp @@ -1,4 +1,4 @@ alert (h($message), 'info', $params) ; -?> \ No newline at end of file +?> From 45d82bf900d3b54896feafd0d01c5c13e7e0aabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 29 Oct 2015 10:06:09 +0100 Subject: [PATCH 013/312] Update success.ctp --- src/Template/Element/Flash/success.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Template/Element/Flash/success.ctp b/src/Template/Element/Flash/success.ctp index ac49222..ecd2b17 100644 --- a/src/Template/Element/Flash/success.ctp +++ b/src/Template/Element/Flash/success.ctp @@ -1,4 +1,4 @@ alert (h($message), 'success', $params) ; -?> \ No newline at end of file +?> From 430e1da59f3b83f7c26c4a103dab205b2ea229cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 29 Oct 2015 10:06:17 +0100 Subject: [PATCH 014/312] Update warning.ctp --- src/Template/Element/Flash/warning.ctp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Template/Element/Flash/warning.ctp b/src/Template/Element/Flash/warning.ctp index d66bc0a..6f6a798 100644 --- a/src/Template/Element/Flash/warning.ctp +++ b/src/Template/Element/Flash/warning.ctp @@ -1,4 +1,4 @@ alert (h($message), 'warning', $params) ; -?> \ No newline at end of file +?> From 8083a65e3971350db5b793c5ff343e51b1598857 Mon Sep 17 00:00:00 2001 From: Brian Adams Date: Mon, 2 Nov 2015 00:34:16 -0500 Subject: [PATCH 015/312] Update FlashHelper to fix error in Cakephp 3.1.3 Update the flash element for each flash message and pass rendering off to the core flash helper. --- src/View/Helper/BootstrapFlashHelper.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/View/Helper/BootstrapFlashHelper.php b/src/View/Helper/BootstrapFlashHelper.php index 05cf690..be7530f 100644 --- a/src/View/Helper/BootstrapFlashHelper.php +++ b/src/View/Helper/BootstrapFlashHelper.php @@ -70,7 +70,7 @@ class BootstrapFlashHelper extends FlashHelper { * @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")) { + if (!$this->request->session()->check("Flash.$key")) { return; } @@ -81,15 +81,14 @@ public function render($key = 'flash', array $options = []) { $key )); } - $flash = $options + $flash; - $this->request->session()->delete("Flash.$key"); - - $element = $flash['element'] ; - if (in_array(basename($element), $this->_bootstrapTemplates)) { - $flash['element'] = 'Bootstrap.'.$element ; + 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 $this->_View->element($flash['element'], $flash); + return parent::render($key, $options); } } From 0ad9b083ffaa644b12d0c04353ef4f25d8da00ec Mon Sep 17 00:00:00 2001 From: ndm2 Date: Wed, 4 Nov 2015 04:18:42 +0100 Subject: [PATCH 016/312] Add support for template variables. refs cakephp/cakephp#6850 --- src/View/BootstrapStringTemplate.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/View/BootstrapStringTemplate.php b/src/View/BootstrapStringTemplate.php index 6360393..d48ea74 100644 --- a/src/View/BootstrapStringTemplate.php +++ b/src/View/BootstrapStringTemplate.php @@ -59,6 +59,10 @@ public function format($name, array $data) if ($template === null) { return ''; } + if (isset($data['templateVars'])) { + $data += $data['templateVars']; + unset($data['templateVars']); + } $replace = []; foreach ($placeholders as $placeholder) { $replace[] = isset($data[$placeholder]) ? $data[$placeholder] : null; From f1af86a26c49a98759bed287afe2bd7a52f85437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Wed, 4 Nov 2015 08:33:27 +0100 Subject: [PATCH 017/312] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d47ef8..c43120e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ How to... ? If you want the latest **Bootstrap 3** version of the plugin: ``` -composer require holt59/cakephp3-bootstrap-helpers:~3.0 +composer require holt59/cakephp3-bootstrap-helpers:dev-master ``` If you are updating from `holt59/cakephp3-bootstrap3-helpers`, do not forget to change the the following: From c1efda00bdd750cdc5b30db0a527d9b71bc7bdbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Wed, 11 Nov 2015 21:19:16 +0100 Subject: [PATCH 018/312] Replace code to reflect CakePHP change. Replace code to reflect CakePHP change in commit https://github.com/cakephp/cakephp/commit/5f5d1e19dbacdb95de523aade4835a6325b15e6c. --- src/View/BootstrapStringTemplate.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/View/BootstrapStringTemplate.php b/src/View/BootstrapStringTemplate.php index d48ea74..d023bed 100644 --- a/src/View/BootstrapStringTemplate.php +++ b/src/View/BootstrapStringTemplate.php @@ -65,7 +65,11 @@ public function format($name, array $data) } $replace = []; foreach ($placeholders as $placeholder) { - $replace[] = isset($data[$placeholder]) ? $data[$placeholder] : null; + $replacement = isset($data[$placeholder]) ? $data[$placeholder] : null; + if (is_array($replacement)) { + $replacement = implode('', $replacement); + } + $replace[] = $replacement; } return vsprintf($template, $replace); } From 99ec729085f5d753c77a177479e564cdf569da4f Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 28 Dec 2015 16:47:31 +0100 Subject: [PATCH 019/312] Add possibility to customize prev and next buttons in Paginator::numbers. --- src/View/Helper/BootstrapPaginatorHelper.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 2363296..5c260ce 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -83,11 +83,25 @@ public function numbers (array $options = array()) { } if (isset($options['prev'])) { - $options['before'] .= $this->prev($options['prev']) ; + $title = $options['prev'] ; + $opts = [] ; + if (is_array($title)) { + $title = $title['title'] ; + unset ($options['prev']['title']) ; + $opts = $options['prev'] ; + } + $options['before'] .= $this->prev($title, $opts) ; } if (isset($options['next'])) { - $options['after'] = $this->next($options['next']).$options['after'] ; + $title = $options['next'] ; + $opts = [] ; + if (is_array($title)) { + $title = $title['title']; + unset ($options['next']['title']); + $opts = $options['next']; + } + $options['after'] = $this->next($title, $opts).$options['after'] ; } return parent::numbers ($options) ; From d0912aada3544dc583b07373e3c13560de18a8d8 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 28 Dec 2015 17:23:08 +0100 Subject: [PATCH 020/312] Create BootstrapTrait::_easyIcon decorator. --- src/View/Helper/BootstrapHtmlHelper.php | 16 ++++++------- src/View/Helper/BootstrapPaginatorHelper.php | 4 ++++ src/View/Helper/BootstrapTrait.php | 24 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index afe1a6a..5165335 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -49,7 +49,7 @@ public function __construct (\Cake\View\View $view, array $config = []) { * @param $icon Name of the icon. * **/ - public function icon ($icon, $options = array()) { + public function icon ($icon, $options = []) { return $this->_useFontAwesome ? $this->faIcon($icon, $options) : $this->glIcon($icon, $options); } @@ -58,7 +58,7 @@ public function icon ($icon, $options = array()) { * * @param $icon Name of the icon. */ - public function faIcon ($icon, $options = array()) { + public function faIcon ($icon, $options = []) { $options = $this->addClass($options, 'fa'); $options = $this->addClass($options, 'fa-'.$icon); @@ -70,7 +70,7 @@ public function faIcon ($icon, $options = array()) { * * @param $icon Name of the icon. */ - public function glIcon ($icon, $options = array()) { + public function glIcon ($icon, $options = []) { $options = $this->addClass($options, 'glyphicon'); $options = $this->addClass($options, 'glyphicon-'.$icon); @@ -92,7 +92,7 @@ public function glIcon ($icon, $options = array()) { * - type The type of the label (useless if $type specified) * **/ - public function label ($text, $type = 'default', $options = array()) { + public function label ($text, $type = 'default', $options = []) { if (is_string($type)) { $options['type'] = $type ; } @@ -116,7 +116,7 @@ public function label ($text, $type = 'default', $options = array()) { * * **/ - public function badge ($text, $options = array()) { + public function badge ($text, $options = []) { $options = $this->addClass($options, 'badge') ; return $this->tag('span', $text, $options) ; } @@ -131,7 +131,7 @@ public function badge ($text, $options = array()) { * Unusable options: * - Separator **/ - public function getCrumbList(array $options = array(), $startText = false) { + public function getCrumbList(array $options = [], $startText = false) { $options['separator'] = '' ; $options = $this->addClass($options, 'breadcrumb') ; return parent::getCrumbList ($options, $startText) ; @@ -153,7 +153,7 @@ public function getCrumbList(array $options = array(), $startText = false) { * $type is specified) * **/ - public function alert ($text, $type = 'warning', $options = array()) { + public function alert ($text, $type = 'warning', $options = []) { if (is_string($type)) { $options['type'] = $type ; } @@ -194,7 +194,7 @@ public function alert ($text, $type = 'warning', $options = array()) { * - active: boolean, specify if progress bar should be active * **/ - public function progress ($widths, $options = array()) { + 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); diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 5c260ce..6f1f22d 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -107,6 +107,10 @@ public function numbers (array $options = array()) { return parent::numbers ($options) ; } + public function prev ($title = '<< Previous', array $options = []) { + return $this->_easyIcon ('parent::prev', $title, $options); + } + } diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index c6be438..53111a1 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -120,6 +120,30 @@ protected function _extractType ($options, $key = 'type', $default = 'info', return $type ; } + /** + * + * This method will the function $callback with the specified argument ($title and $options) + * after applying a filter on them. + * + * @param $callback The method to call. + * @param $title The first argument ($title). + * @param $options The second argument ($options). + * + * @return Whatever might be returned by $callback. + * + * Note: Currently this method only works for function that take two arguments ($title and $options). + * + **/ + protected function _easyIcon ($callback, $title, $options) { + if (preg_match('#i:([a-zA-Z0-9\\-_]+)#', $title, $matches)) { + $options += [ + 'escape' => false + ]; + $title = $this->_View->Html->icon($matches[1]); + } + return call_user_func ($callback, $title, $options) ; + } + } ?> \ No newline at end of file From 9967e8869ae35ccb91bf354661142abd9f299e1f Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 28 Dec 2015 17:24:08 +0100 Subject: [PATCH 021/312] Add easy icon to Paginator::next. --- src/View/Helper/BootstrapPaginatorHelper.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 6f1f22d..0d8e0b5 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -111,6 +111,10 @@ public function prev ($title = '<< Previous', array $options = []) { return $this->_easyIcon ('parent::prev', $title, $options); } + public function next ($title = 'Next >>', array $options = []) { + return $this->_easyIcon ('parent::next', $title, $options); + } + } From 5bebe50ae9e6493f99d167439f13b4a611d380d6 Mon Sep 17 00:00:00 2001 From: mcapelle Date: Tue, 5 Jan 2016 11:24:50 +0100 Subject: [PATCH 022/312] Add easyIcon to button method and prepend/append options of input method for BootstrapFormHelper. --- src/View/Helper/BootstrapFormHelper.php | 30 +++++++++++++------------ src/View/Helper/BootstrapTrait.php | 29 +++++++++++++++++++++--- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 5198991..0645ca3 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -280,16 +280,23 @@ protected function _getColClass ($what, $offset = false) { } return implode(' ', $classes) ; } - - public function prepend ($input, $prepend) { - if ($prepend) { - if (is_string($prepend)) { - $prepend = ''.$prepend.'' ; + + protected function _wrapInputGroup ($addonOrButtons) { + if ($addonOrButtons) { + if (is_string($addonOrButtons)) { + $addonOrButtons = $this->_makeIcon($addonOrButtons); + $addonOrButtons = ''.$addonOrButtons.'' ; } - else if ($prepend !== false) { - $prepend = ''.implode('', $prepend).'' ; + else if ($addonOrButtons !== false) { + $addonOrButtons = ''.implode('', $addonOrButtons).'' ; } } + return $addonOrButtons ; + } + + public function prepend ($input, $prepend) { + $prepend = $this->_wrapInputGroup ($prepend); if ($input === null) { return '
'.$prepend ; } @@ -297,12 +304,7 @@ public function prepend ($input, $prepend) { } public function append ($input, $append) { - if (is_string($append)) { - $append = ''.$append.'' ; - } - else if ($append !== false) { - $append = ''.implode('', $append).'' ; - } + $append = $this->_wrapInputGroup($append); if ($input === null) { return $append.'
' ; } @@ -582,7 +584,7 @@ protected function _createButtonOptions (array $options = array()) { * */ public function button($title, array $options = []) { - return parent::button($title, $this->_createButtonOptions($options)) ; + return $this->_easyIcon ('parent::button', $title, $this->_createButtonOptions($options)); } /** diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 53111a1..6c67989 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -119,6 +119,29 @@ protected function _extractType ($options, $key = 'type', $default = 'info', } return $type ; } + + /** + * + * Try to convert the specified $text to a bootstrap icon. The $text is converted if it matches + * a format "i:icon-name". + * + * @param $title The text to convert. + * @param $converted If specified, will contains true if the text was converted, false otherwize. + * + * @return The icon element if the conversion was successful, otherwize $text. + * + * Note: This function will currently fail if the Html helper associated with the view is not + * BootstrapHtmlHelper. + * + **/ + protected function _makeIcon ($title, &$converted = false) { + $conversion = false ; + if (preg_match('#i:([a-zA-Z0-9\\-_]+)#', $title, $matches)) { + $conversion = true ; + $title = $this->_View->Html->icon($matches[1]); + } + return $title ; + } /** * @@ -134,12 +157,12 @@ protected function _extractType ($options, $key = 'type', $default = 'info', * Note: Currently this method only works for function that take two arguments ($title and $options). * **/ - protected function _easyIcon ($callback, $title, $options) { - if (preg_match('#i:([a-zA-Z0-9\\-_]+)#', $title, $matches)) { + protected function _easyIcon ($callback, $title, $options) { + $title = $this->_makeIcon ($title, $converted); + if ($converted) { $options += [ 'escape' => false ]; - $title = $this->_View->Html->icon($matches[1]); } return call_user_func ($callback, $title, $options) ; } From 2f4e9049ee9070fb9c6f2e25efe8d8cc9a63ddb4 Mon Sep 17 00:00:00 2001 From: mcapelle Date: Tue, 5 Jan 2016 11:35:33 +0100 Subject: [PATCH 023/312] Stronger regexp match for easy icon. --- src/View/Helper/BootstrapTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 6c67989..6085def 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -136,7 +136,7 @@ protected function _extractType ($options, $key = 'type', $default = 'info', **/ protected function _makeIcon ($title, &$converted = false) { $conversion = false ; - if (preg_match('#i:([a-zA-Z0-9\\-_]+)#', $title, $matches)) { + if (preg_match('#^i:([a-zA-Z0-9\\-_]+)$#', $title, $matches)) { $conversion = true ; $title = $this->_View->Html->icon($matches[1]); } From 2e0e55ca952ce4e69d60df002d487ffa876c1ce3 Mon Sep 17 00:00:00 2001 From: mcapelle Date: Tue, 5 Jan 2016 11:37:28 +0100 Subject: [PATCH 024/312] Add possibility to disable easy icon ($this->easyIcon = false). --- src/View/Helper/BootstrapTrait.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 6085def..29a0554 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -24,6 +24,13 @@ trait BootstrapTrait { + /** + * Set to false to disable easy icon processing. + * + * @var boolean + */ + public $easyIcon = true ; + /** * Adds the given class to the element options * @@ -136,6 +143,9 @@ protected function _extractType ($options, $key = 'type', $default = 'info', **/ protected function _makeIcon ($title, &$converted = false) { $conversion = false ; + if (!$this->easyIcon) { + return $title ; + } if (preg_match('#^i:([a-zA-Z0-9\\-_]+)$#', $title, $matches)) { $conversion = true ; $title = $this->_View->Html->icon($matches[1]); From aeb9b732741927e02d220200a9cfd923f33552fa Mon Sep 17 00:00:00 2001 From: mcapelle Date: Tue, 5 Jan 2016 12:19:50 +0100 Subject: [PATCH 025/312] Correct typo in _makeIcon. --- src/View/Helper/BootstrapTrait.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 29a0554..4923dc2 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -142,12 +142,12 @@ protected function _extractType ($options, $key = 'type', $default = 'info', * **/ protected function _makeIcon ($title, &$converted = false) { - $conversion = false ; + $converted = false ; if (!$this->easyIcon) { return $title ; } if (preg_match('#^i:([a-zA-Z0-9\\-_]+)$#', $title, $matches)) { - $conversion = true ; + $converted = true ; $title = $this->_View->Html->icon($matches[1]); } return $title ; @@ -169,7 +169,7 @@ protected function _makeIcon ($title, &$converted = false) { **/ protected function _easyIcon ($callback, $title, $options) { $title = $this->_makeIcon ($title, $converted); - if ($converted) { + if ($converted) { $options += [ 'escape' => false ]; From 6046ff65388aa456dc91c75bc9b0ef245b2248b6 Mon Sep 17 00:00:00 2001 From: mcapelle Date: Tue, 5 Jan 2016 12:20:17 +0100 Subject: [PATCH 026/312] Remove constructor and add templates to config attribute. --- src/View/Helper/BootstrapPaginatorHelper.php | 41 ++++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 0d8e0b5..6a71b49 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -27,21 +27,46 @@ class BootstrapPaginatorHelper extends PaginatorHelper { use BootstrapTrait ; - - public function __construct ($view, $config = []) { - $this->templates([ + + /** + * Default config for this class + * + * Options: Holds the default options for pagination links + * + * The values that may be specified are: + * + * - `url` Url of the action. See Router::url() + * - `url['sort']` the key that the recordset is sorted. + * - `url['direction']` Direction of the sorting (default: 'asc'). + * - `url['page']` Page number to use in links. + * - `model` The name of the model. + * - `escape` Defines if the title field for the link should be escaped (default: true). + * + * Templates: the templates used by this class + * + * @var array + */ + protected $_defaultConfig = [ + 'options' => [], + 'templates' => [ 'nextActive' => '
  • {{text}}
  • ', 'nextDisabled' => '
  • {{text}}
  • ', 'prevActive' => '
  • {{text}}
  • ', 'prevDisabled' => '
  • {{text}}
  • ', + 'counterRange' => '{{start}} - {{end}} of {{count}}', + 'counterPages' => '{{page}} of {{pages}}', 'first' => '
  • {{text}}
  • ', 'last' => '
  • {{text}}
  • ', 'number' => '
  • {{text}}
  • ', - 'current ' => '
  • {{text}}
  • ' - ]); - - parent::__construct($view, $config); - } + 'current' => '
  • {{text}}
  • ', + 'ellipsis' => '
  • ...
  • ', + 'sort' => '{{text}}', + 'sortAsc' => '{{text}}', + 'sortDesc' => '{{text}}', + 'sortAscLocked' => '{{text}}', + 'sortDescLocked' => '{{text}}', + ] + ]; /** * From 343bea4549bdbc58a3c8130ebfe85037fc33b00f Mon Sep 17 00:00:00 2001 From: mcapelle Date: Thu, 21 Jan 2016 15:04:56 +0100 Subject: [PATCH 027/312] Create PanelHelper for non-collapsible panel. --- src/View/Helper/BootstrapPanelHelper.php | 198 +++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/View/Helper/BootstrapPanelHelper.php diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php new file mode 100644 index 0000000..97a08fb --- /dev/null +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -0,0 +1,198 @@ +_extractOption('no-body', $options, false); + unset ($options['no-body']); + $type = $this->_extractOption('type', $options, 'default'); + unset ($options['type']); + + $options = $this->addClass($options, ['panel', 'panel-'.$type]); + $class = $options['class']; + unset ($options['class']); + + $res = $this->Html->div($class, null, $options); + if (is_string($title) && $title) { + $res .= $this->_createHeader($title, []) ; + if (!$nobody) { + $res .= $this->_startPart('body'); + } + } + return $res ; + } + + /** + * + * End a panel. If $title is not null, the ModalHelper::footer functions is called with $title and $options arguments. + * + * @param string|null $buttons + * @param array $options + * + **/ + public function end ($title = null, $options = []) { + $res = '' ; + if ($this->current != null) { + $this->current = null ; + $res .= $this->_endPart(); + } + if ($title !== null) { + $res .= $this->footer($title, $options) ; + } + $res .= '' ; + return $res ; + } + + protected function _cleanCurrent () { + if ($this->current) { + $this->current = NULL ; + return $this->_endPart(); + } + return '' ; + } + + protected function _createHeader ($title, $options = [], $titleOptions = []) { + $options += [ + '_title' => [] + ]; + if (empty($titleOptions)) + $titleOptions = $options['_title']; + unset ($options['_title']); + $options = $this->addClass($options, 'panel-heading'); + $class = $options['class']; + unset ($options['class']); + $titleOptions = $this->addClass($titleOptions, 'panel-title'); + return $this->_cleanCurrent().$this->Html->div($class, + $this->Html->tag('h3', $title, $titleOptions), + $options + ) ; + } + + protected function _createBody ($text, $options = []) { + $options = $this->addClass($options, 'panel-body'); + $class = $options['class']; + unset ($options['class']); + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + } + + protected function _createFooter ($text = null, $options = []) { + $options = $this->addClass($options, 'panel-footer'); + $class = $options['class']; + unset ($options['class']); + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + } + + protected function _startPart ($part, $options = []) { + $res = '' ; + if ($this->current != null) { + $res = $this->_endPart () ; + } + $this->current = $part ; + return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', $options, ''), null, $options) ; + } + + protected function _endPart () { + return '' ; + } + + /** + * + * 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 modal 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_string($info)) { + return $this->_createHeader($info, $options) ; + } + return $this->_startPart('header', is_array($info) ? $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_string($info)) { + if ($this->current != null) { + $this->_endPart() ; + } + return $this->_createBody($info, $options) ; + } + return $this->_startPart('body', is_array($info) ? $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 array|string $buttons If string, use as the footer content, if list, concatenate values in the list as content (use for buttons purpose), otherwize works as $options. + * @param array $options Options for the footer div. + * + **/ + public function footer ($text = "", $options = []) { + return $this->_createFooter($text, $options) ; + } + +} + +?> From 3177b84d56df5a9ef87f427a098a83b2fc3b9ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 08:41:28 +0100 Subject: [PATCH 028/312] Correct duplicate attribute bug. See issue #69. --- src/View/BootstrapStringTemplate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/BootstrapStringTemplate.php b/src/View/BootstrapStringTemplate.php index d023bed..967fd41 100644 --- a/src/View/BootstrapStringTemplate.php +++ b/src/View/BootstrapStringTemplate.php @@ -51,7 +51,7 @@ public function format($name, array $data) if (substr($placeholder, 0, 6) == 'attrs.' && in_array('attrs.'.substr($placeholder, 6), $placeholders) && preg_match('#'.substr($placeholder, 6).'="([^"]+)"#', $data['attrs'], $matches) > 0) { - preg_replace('#'.substr($placeholder, 6).'="[^"]+"#', '', $data['attrs']); + $data['attrs'] = preg_replace('#'.substr($placeholder, 6).'="[^"]+"#', '', $data['attrs']); $data[$placeholder] = $matches[1]; } } From 45d698929c568af8b8a68054633cdb4b34f3d65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 10:28:13 +0100 Subject: [PATCH 029/312] Add test files. --- composer.json | 8 + .../View/BootstrapStringTemplateTest.php | 79 +++ .../View/Helper/BootstrapFormHelperTest.php | 590 ++++++++++++++++++ .../View/Helper/BootstrapHtmlHelperTest.php | 236 +++++++ 4 files changed, 913 insertions(+) create mode 100644 tests/TestCase/View/BootstrapStringTemplateTest.php create mode 100644 tests/TestCase/View/Helper/BootstrapFormHelperTest.php create mode 100644 tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php diff --git a/composer.json b/composer.json index ba54db1..be0e66d 100644 --- a/composer.json +++ b/composer.json @@ -7,11 +7,19 @@ "require": { "cakephp/cakephp": "~3.0" }, + "require-dev": { + "phpunit/phpunit": "*" + } "autoload": { "psr-4": { "Bootstrap\\": "src" } }, + "autoload-dev": { + "psr-4": { + "Bootstrap\\Test\\": "tests" + } + }, "extra": { "installer-name": "Bootstrap" } diff --git a/tests/TestCase/View/BootstrapStringTemplateTest.php b/tests/TestCase/View/BootstrapStringTemplateTest.php new file mode 100644 index 0000000..e83ced9 --- /dev/null +++ b/tests/TestCase/View/BootstrapStringTemplateTest.php @@ -0,0 +1,79 @@ +templater = new BootstrapStringTemplate () ; + } + + + /** + * Tear Down + * + * @return void + */ + public function tearDown() + { + parent::tearDown(); + unset($this->templater); + } + + public function test () { + $this->templater->add([ + 'test_default' => '{{content}}

    ', + 'test_attrs_class' => '

    {{content}}

    ' + ]) ; + // Standard test + $result = $this->templater->format ('test_default', [ + 'attrs' => ' id="test-id" class="test-class"', + 'content' => 'Hello World!' + ]); + $this->assertHtml([ + ['p' => [ + 'id' => 'test-id', + 'class' => 'test-class' + ]], + 'Hello World!', + '/p' + ], $result) ; + // Test with class test + $result = $this->templater->format ('test_attrs_class', [ + 'attrs' => ' id="test-id" class="test-class-2"', + 'content' => 'Hello World!' + ]); + $this->assertHtml([ + ['p' => [ + 'id' => 'test-id', + 'class' => 'test-class test-class-2' + ]], + 'Hello World!', + '/p' + ], $result) ; + // Test with class test + $result = $this->templater->format ('test_attrs_class', [ + 'attrs' => 'class="test-class-2" id="test-id"', + 'content' => 'Hello World!' + ]); + $this->assertHtml([ + ['p' => [ + 'id' => 'test-id', + 'class' => 'test-class test-class-2' + ]], + 'Hello World!', + '/p' + ], $result) ; + } + +} \ No newline at end of file diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php new file mode 100644 index 0000000..fb61a3c --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -0,0 +1,590 @@ +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) ; + // type options + $result = $this->Form->create (null, ['type' => 'horizontal']) ; + $this->assertEquals($this->Form->horizontal, true) ; + $this->assertEquals($this->Form->inline, false) ; + $result = $this->Form->create (null, ['type' => 'inline']) ; + $this->assertEquals($this->Form->horizontal, false) ; + $this->assertEquals($this->Form->inline, true) ; + } + + 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 ([ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/fieldset' + ], $fieldName) ; + // Horizontal form + $this->_testInput ([ + ['fieldset' => [ + 'class' => ['form-group', 'row'] + ]], + ['label' => [ + 'class' => ['form-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', + '/fieldset' + ], $fieldName, [ + '_formOptions' => ['horizontal' => true] + ]) ; + } + + public function testInputText () { + $fieldName = 'field' ; + $this->_testInput ([ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/fieldset' + ], $fieldName, ['type' => 'text']) ; + } + + public function testInputSelect () { + + } + + public function testInputRadio () { + $fieldName = 'color' ; + $options = [ + 'type' => 'radio', + 'options' => [ + 'red' => 'Red', + 'blue' => 'Blue', + 'green' => 'Green' + ] + ] ; + // Default + $expected = [ + ['label' => true], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => ['radio', 'c-inputs-stacked'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Inline + $options += [ + 'inline' => true + ] ; + $expected = [ + ['label' => true], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => ['radio'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Horizontal + Inline + $options += [ + '_formOptions' => ['horizontal' => true] + ] ; + $options['inline'] = false ; + $expected = [ + ['fieldset' => [ + 'class' => ['form-group', 'row'] + ]], + ['label' => [ + 'class' => ['form-control-label', 'col-md-2'] + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => 'col-md-10' + ]], + ['div' => [ + 'class' => ['radio', 'c-inputs-stacked'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Horizontal + Inline + $options['inline'] = true ; + $expected = [ + ['fieldset' => [ + 'class' => ['form-group', 'row'] + ]], + ['label' => [ + 'class' => ['form-control-label', 'col-md-2'] + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => 'col-md-10' + ]], + ['div' => [ + 'class' => ['radio'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $this->_testInput ($expected, $fieldName, $options) ; + } + + public function testInputCheckbox () { + + } + + public function testInputGroup () { + $fieldName = 'field' ; + $options = [ + 'type' => 'text', + 'label' => false + ] ; + // Test with prepend addon + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '@', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => '@']) ; + // Test with append + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '.00', + '/span', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['append' => '.00']) ; + // Test with append + prepend + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['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', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => '$', 'append' => '.00']) ; + // Test with prepend button + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'Go!', + '/button', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => $this->Form->button('Go!')]) ; + // Test with append button + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'Go!', + '/button', + '/span', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['append' => $this->Form->button('Go!')]) ; + // Test with append 2 button + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'Go!', + '/button', + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'GoGo!', + '/button', + '/span', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + [ + 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] + ]) ; + // Test with append dropdown + $html = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this->View); + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['div' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'data-toggle' => 'dropdown', + 'aria-haspopup' => 'true', + 'aria-expanded' => 'false', + 'id' => 'dropdownMenu1', + 'class' => ['dropdown-toggle', 'btn', 'btn-secondary'] + ]], + 'Action', + '/button', + ['div' => [ + 'class' => ['dropdown-menu'], + 'aria-labelledby' => 'dropdownMenu1' + ]], + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 1', '/a', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 2', '/a', + ['div' => [ + 'class' => 'dropdown-divider' + ]], '/div', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 3', '/a', + '/div', + '/div', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + [ + 'append' => $html->dropdown('Action', [ + $html->link('Link 1', '#'), + $html->link('Link 2', '#'), + 'divider', + $html->link('Link 3', '#') + ]) + ]); + } + +} \ No newline at end of file diff --git a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php new file mode 100644 index 0000000..0fd351e --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php @@ -0,0 +1,236 @@ +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 (FontAwesome) + $this->assertHtml ([ + ['i' => [ + 'class' => 'fa fa-'.$type + ]], + '/i' + ], $this->Html->icon($type)); + $this->assertHtml ([ + ['i' => [ + 'class' => $options['class'].' fa fa-'.$type, + 'id' => $options['id'] + ]], + '/i' + ], $this->Html->icon($type, $options)); + // Glyphicon icon + $this->assertHtml ([ + ['i' => [ + 'class' => 'glyphicon glyphicon-'.$type + ]], + '/i' + ], $this->Html->glIcon($type)); + $this->assertHtml ([ + ['i' => [ + 'class' => $options['class'].' glyphicon glyphicon-'.$type, + 'id' => $options['id'] + ]], + '/i' + ], $this->Html->glIcon($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 = [ + ['div' => [ + 'class' => 'dropdown' + ]], + ['button' => [ + 'data-toggle' => 'dropdown', + 'aria-haspopup' => 'true', + 'aria-expanded' => 'false', + 'id' => 'dropdownMenu1', + 'class' => 'dropdown-toggle btn btn-secondary' + ]], + 'Action', + '/button', + ['div' => [ + 'class' => 'dropdown-menu', + 'aria-labelledby' => 'dropdownMenu1' + ]], + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 1', '/a', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 2', '/a', + ['div' => [ + 'class' => 'dropdown-divider' + ]], '/div', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 3', '/a', + '/div', + '/div' + ] ; + // Standard test + $this->assertHtml ($expected, $this->Html->dropdown($title, $menu)) ; + $menu = [ + ['Link 1', '#'], + ['Link 2', '#', ['class' => 'my-item-class', 'id' => 'my-item-id']], + 'divider', + ['Link 3', '#'] + ] ; + $options = [ + 'class' => 'my-dropdown', + '_button' => [ + 'tag' => 'a', + 'id' => 'my-dropdown-id' + ], + '_menu' => [ + 'class' => 'my-dropdown-menu', + '_item' => [ + 'class' => 'my-dropdown-item' + ] + ] + ] ; + $expected = [ + ['div' => [ + 'class' => $options['class'].' dropdown' + ]], + [$options['_button']['tag'] => [ + 'data-toggle' => 'dropdown', + 'aria-haspopup' => 'true', + 'aria-expanded' => 'false', + 'id' => $options['_button']['id'], + 'class' => 'dropdown-toggle btn btn-secondary' + ]], + 'Action', + '/'.$options['_button']['tag'], + ['div' => [ + 'class' => $options['_menu']['class'].' dropdown-menu', + 'aria-labelledby' => $options['_button']['id'] + ]], + ['a' => [ + 'href' => '#', + 'class' => $options['_menu']['_item']['class'].' dropdown-item' + ]], 'Link 1', '/a', + ['a' => [ + 'href' => '#', + 'class' => $menu[1][2]['class'].' dropdown-item', + 'id' => $menu[1][2]['id'] + ]], 'Link 2', '/a', + ['div' => [ + 'class' => $options['_menu']['_item']['class'].' dropdown-divider' + ]], '/div', + ['a' => [ + 'href' => '#', + 'class' => $options['_menu']['_item']['class'].' dropdown-item' + ]], 'Link 3', '/a', + '/div', + '/div' + ] ; + $this->assertHtml ($expected, $this->Html->dropdown($title, $menu, $options)) ; + + } + +} \ No newline at end of file From 9e13397acc745195a29fff90c51b821edd5bf42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 10:28:54 +0100 Subject: [PATCH 030/312] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index be0e66d..0c0ae10 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ }, "require-dev": { "phpunit/phpunit": "*" - } + }, "autoload": { "psr-4": { "Bootstrap\\": "src" From aa5c5487074cc8a58d431c1b98931e88e0bae950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 12:40:56 +0100 Subject: [PATCH 031/312] Update FormHelper to use additional template variables for horizontal form instead of custom templates. Remove useless space from templates. --- src/View/BootstrapStringTemplate.php | 3 +- src/View/Helper/BootstrapFormHelper.php | 110 +++++++++++++----------- src/View/Helper/BootstrapHtmlHelper.php | 6 +- 3 files changed, 67 insertions(+), 52 deletions(-) diff --git a/src/View/BootstrapStringTemplate.php b/src/View/BootstrapStringTemplate.php index 967fd41..f442a81 100644 --- a/src/View/BootstrapStringTemplate.php +++ b/src/View/BootstrapStringTemplate.php @@ -52,9 +52,10 @@ public function format($name, array $data) && in_array('attrs.'.substr($placeholder, 6), $placeholders) && preg_match('#'.substr($placeholder, 6).'="([^"]+)"#', $data['attrs'], $matches) > 0) { $data['attrs'] = preg_replace('#'.substr($placeholder, 6).'="[^"]+"#', '', $data['attrs']); - $data[$placeholder] = $matches[1]; + $data[$placeholder] = ' '.trim($matches[1]); } } + $data['attrs'] = ' '.trim($data['attrs']); } if ($template === null) { return ''; diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 0645ca3..70ba64e 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -54,33 +54,33 @@ class BootstrapFormHelper extends FormHelper { 'checkbox' => '', 'checkboxFormGroup' => '{{label}}', 'checkboxWrapper' => '
    {{label}}
    ', - 'checkboxContainer' => '
    {{content}}
    ', + 'checkboxContainer' => '{{h_checkboxContainer_start}}
    {{content}}
    {{h_checkboxContainer_end}}', 'dateWidget' => '{{year}}{{month}}{{day}}{{hour}}{{minute}}{{second}}{{meridian}}', - 'error' => '{{content}}', + 'error' => '{{content}}', 'errorList' => '
      {{content}}
    ', 'errorItem' => '
  • {{text}}
  • ', 'file' => '', 'fieldset' => '{{content}}', 'formStart' => '', 'formEnd' => '', - 'formGroup' => '{{label}}{{prepend}}{{input}}{{append}}', + 'formGroup' => '{{label}}{{h_formGroup_start}}{{prepend}}{{input}}{{append}}{{h_formGroup_end}}', 'hiddenBlock' => '
    {{content}}
    ', - 'input' => '', + 'input' => '', 'inputSubmit' => '', 'inputContainer' => '
    {{content}}
    ', 'inputContainerError' => '
    {{content}}{{error}}
    ', - 'label' => '', + 'label' => '', 'nestingLabel' => '{{hidden}}{{input}}{{text}}', 'legend' => '{{text}}', 'option' => '', 'optgroup' => '{{content}}', - 'select' => '', - 'selectMultiple' => '', + 'select' => '', + 'selectMultiple' => '', 'radio' => '', 'radioWrapper' => '
    {{label}}
    ', - 'radioContainer' => '
    {{content}}
    ', - 'textarea' => '', - 'submitContainer' => '
    {{content}}
    ', + 'radioContainer' => '{{h_radioContainer_start}}
    {{content}}
    {{h_radioContainer_end}}', + 'textarea' => '', + 'submitContainer' => '
    {{h_submitContainer_start}}{{content}}{{h_submitContainer_end}}
    ', ] ]; @@ -129,8 +129,8 @@ class BootstrapFormHelper extends FormHelper { */ protected $_defaultColumnSize = [ 'label' => 2, - 'input' => 6, - 'error' => 4 + 'input' => 10, + 'error' => 0 ]; private $buttonTypes = ['default', 'primary', 'info', 'success', 'warning', 'danger', 'link'] ; @@ -185,27 +185,50 @@ protected function _wrapTemplates ($templates, $callback, $params) { protected function _matchButton ($html) { return strpos($html, 'horizontal and $this->inline). - * - **/ - protected function _setDefaultTemplates () { - $this->templates([ - 'formGroup' => '{{label}}'.($this->horizontal ? '
    ' : '').'{{prepend}}{{input}}{{append}}'.($this->horizontal ? '
    ' : ''), - 'checkboxContainer' => ($this->horizontal ? '
    ' : '') - .'
    {{content}}
    ' - .($this->horizontal ? '
    ' : ''), - 'radioContainer' => ($this->horizontal ? '
    ' : '') - .'{{content}}' - .($this->horizontal ? '
    ' : ''), - 'label' => '', - 'error' => '{{content}}', - 'submitContainer' => '
    '.($this->horizontal ? '
    ' : '').'{{content}}'.($this->horizontal ? '
    ' : '').'
    ', - ]) ; + + protected function _getDefaultTemplateVars (&$options) { + $options += [ + 'templateVars' => [] + ]; + $options['templateVars'] += [ + 's_labelClass' => 'control-label' + ]; + if ($this->horizontal) { + $options['templateVars'] += [ + 'h_formGroup_start' => '
    ', + 'h_formGroup_end' => '
    ', + 'h_checkboxContainer_start' => '
    ', + 'h_checkboxContainer_end' => '
    ', + 'h_radioContainer_start' => '
    ', + 'h_radioContainer_end' => '
    ', + 'h_submitContainer_start' => '
    ', + 'h_submitContainer_end' => '
    ', + 'h_labelClass' => ' '.$this->_getColClass('label'), + 'h_errorClass' => ' '.$this->_getColClass('error') + ]; + } + if ($this->inline) { + $options['templateVars']['s_labelClass'] = 'sr-only'; + } + return $options; } - + + public function formatTemplate($name, $data) { + return $this->templater()->format($name, $this->_getDefaultTemplateVars($data)); + } + + public function widget($name, array $data = []) { + return parent::widget($name, $this->_getDefaultTemplateVars($data)); + } + + protected function _inputContainerTemplate($options) { + return parent::_inputContainerTemplate(array_merge($options, [ + 'options' => $this->_getDefaultTemplateVars($options['options']) + ])); + } + /** * * Create a Twitter Bootstrap like form. @@ -248,27 +271,18 @@ public function create($model = null, Array $options = array()) { $options = $this->addClass($options, 'form-search') ; } $options['role'] = 'form' ; - $this->_setDefaultTemplates () ; return parent::create($model, $options) ; } - /** - * - * Switch horizontal mode on or off. - * - **/ - public function setHorizontal ($horizontal) { - $this->horizontal = $horizontal ; - $this->_setDefaultTemplates () ; - } - - /** * * 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'); + } if (isset($this->colSize[$what])) { return 'col-md-'.($offset ? 'offset-' : '').$this->colSize[$what] ; } @@ -331,7 +345,7 @@ protected function _wrap ($input, $prepend, $append) { * **/ public function input($fieldName, array $options = array()) { - + $options = $this->_parseOptions($fieldName, $options); $prepend = $this->_extractOption('prepend', $options, false) ; @@ -356,7 +370,7 @@ public function input($fieldName, array $options = array()) { $options['templates'] = [] ; if ($inline) { $options['templates'] = [ - 'label' => $this->templates('label').'
    ', + 'label' => $this->templates('label'), 'radioWrapper' => '{{label}}', 'nestingLabel' => '{{hidden}}{{input}}{{text}}' ] ; @@ -373,7 +387,7 @@ public function input($fieldName, array $options = array()) { 'prepend' => $prepend, 'append' => $append ]; - + return parent::input($fieldName, $options) ; } @@ -397,7 +411,7 @@ protected function _groupTemplate($options) { $data = array_merge($data, $options['options']['_data']); unset($options['options']['_data']); } - return $this->templater()->format($groupTemplate, $data); + return $this->formatTemplate($groupTemplate, $data); } /** diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index 5165335..c41e65b 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -251,7 +251,7 @@ public function progress ($widths, $options = []) { * Create & return a twitter bootstrap dropdown menu. * * @param $menu HTML tags corresponding to menu options (which will be wrapped - * into
  • tag). To add separator, pass 'divider'. + * into
  • tag). To add separator, pass 'divider'. * @param $options Attributes for the wrapper (change it with tag) * */ @@ -281,8 +281,8 @@ public function dropdown (array $menu = [], array $options = []) { } } $options = $this->addClass($options, 'dropdown-menu'); - $options['role'] = 'menu' ; - $options += ['tag' => 'div']; + $options['role'] = 'menu'; + $options += ['tag' => 'ul']; $tag = $options['tag']; unset($options['tag']); return $this->tag($tag, $output, $options) ; From e4fad5e81b64f4e6438ca5ef5a3245e77df1f77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 12:43:23 +0100 Subject: [PATCH 032/312] Update test from B4 to work with B3. --- .../View/BootstrapStringTemplateTest.php | 2 +- .../View/Helper/BootstrapFormHelperTest.php | 238 ++++++++---------- .../View/Helper/BootstrapHtmlHelperTest.php | 131 +++------- 3 files changed, 138 insertions(+), 233 deletions(-) diff --git a/tests/TestCase/View/BootstrapStringTemplateTest.php b/tests/TestCase/View/BootstrapStringTemplateTest.php index e83ced9..4ed5cee 100644 --- a/tests/TestCase/View/BootstrapStringTemplateTest.php +++ b/tests/TestCase/View/BootstrapStringTemplateTest.php @@ -33,7 +33,7 @@ public function tearDown() public function test () { $this->templater->add([ 'test_default' => '{{content}}

    ', - 'test_attrs_class' => '

    {{content}}

    ' + 'test_attrs_class' => '

    {{content}}

    ' ]) ; // Standard test $result = $this->templater->format ('test_default', [ diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php index fb61a3c..0fc0194 100644 --- a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -7,20 +7,6 @@ use Cake\View\View; class BootstrapFormHelperTest extends TestCase { - - public function assertHtml($expected, $string, $fullDebug = false) { - array_walk ($expected, function (&$value) { - if (!is_array($value)) - return ; - $tag = array_keys ($value)[0] ; - if (!isset($value[$tag]['class'])) - return ; - if (is_string($value[$tag]['class'])) - $value[$tag]['class'] = explode(' ', $value[$tag]['class']) ; - $value[$tag]['class'] = 'preg:/'.implode(' ', array_map('trim', $value[$tag]['class'])).'\s*/' ; - }); - return parent::assertHtml ($expected, $string, $fullDebug) ; - } /** * Setup @@ -75,13 +61,6 @@ public function testCreate () { // Automatically return to non horizonal form $result = $this->Form->create () ; $this->assertEquals($this->Form->inline, false) ; - // type options - $result = $this->Form->create (null, ['type' => 'horizontal']) ; - $this->assertEquals($this->Form->horizontal, true) ; - $this->assertEquals($this->Form->inline, false) ; - $result = $this->Form->create (null, ['type' => 'inline']) ; - $this->assertEquals($this->Form->horizontal, false) ; - $this->assertEquals($this->Form->inline, true) ; } protected function _testInput ($expected, $fieldName, $options = []) { @@ -98,11 +77,12 @@ public function testInput () { $fieldName = 'field' ; // Standard form $this->_testInput ([ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['label' => [ - 'for' => $fieldName + 'class' => 'control-label', + 'for' => $fieldName ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', @@ -112,15 +92,15 @@ public function testInput () { 'name' => $fieldName, 'id' => $fieldName ]], - '/fieldset' + '/div' ], $fieldName) ; // Horizontal form $this->_testInput ([ - ['fieldset' => [ - 'class' => ['form-group', 'row'] + ['div' => [ + 'class' => 'form-group text' ]], ['label' => [ - 'class' => ['form-control-label', 'col-md-2'], + 'class' => 'control-label col-md-2', 'for' => $fieldName ]], \Cake\Utility\Inflector::humanize($fieldName), @@ -135,7 +115,7 @@ public function testInput () { 'id' => $fieldName ]], '/div', - '/fieldset' + '/div' ], $fieldName, [ '_formOptions' => ['horizontal' => true] ]) ; @@ -144,11 +124,12 @@ public function testInput () { public function testInputText () { $fieldName = 'field' ; $this->_testInput ([ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['label' => [ - 'for' => $fieldName + 'class' => 'control-label', + 'for' => $fieldName ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', @@ -158,7 +139,7 @@ public function testInputText () { 'name' => $fieldName, 'id' => $fieldName ]], - '/fieldset' + '/div' ], $fieldName, ['type' => 'text']) ; } @@ -178,12 +159,14 @@ public function testInputRadio () { ] ; // Default $expected = [ - ['label' => true], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', ['div' => [ - 'class' => ['radio', 'c-inputs-stacked'] + 'class' => 'form-group' + ]], + ['label' => [ + 'class' => 'control-label' ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -193,8 +176,10 @@ public function testInputRadio () { ] ; foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ + ['div' => [ + 'class' => 'radio' + ]], ['label' => [ - 'class' => ['c-input', 'c-radio'], 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -203,12 +188,9 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, - '/label' + '/label', + '/div' ]) ; } $expected = array_merge ($expected, ['/div']) ; @@ -218,12 +200,14 @@ public function testInputRadio () { 'inline' => true ] ; $expected = [ - ['label' => true], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', ['div' => [ - 'class' => ['radio'] + 'class' => 'form-group' ]], + ['label' => [ + 'class' => 'control-label' + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -234,7 +218,7 @@ public function testInputRadio () { foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ ['label' => [ - 'class' => ['c-input', 'c-radio'], + 'class' => 'radio-inline', 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -243,36 +227,29 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, '/label' ]) ; } $expected = array_merge ($expected, ['/div']) ; $this->_testInput ($expected, $fieldName, $options) ; - // Horizontal + Inline + // Horizontal $options += [ '_formOptions' => ['horizontal' => true] ] ; $options['inline'] = false ; $expected = [ - ['fieldset' => [ - 'class' => ['form-group', 'row'] + ['div' => [ + 'class' => 'form-group' ]], ['label' => [ - 'class' => ['form-control-label', 'col-md-2'] + 'class' => 'control-label col-md-2' ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', ['div' => [ 'class' => 'col-md-10' ]], - ['div' => [ - 'class' => ['radio', 'c-inputs-stacked'] - ]], ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -282,8 +259,10 @@ public function testInputRadio () { ] ; foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ + ['div' => [ + 'class' => 'radio' + ]], ['label' => [ - 'class' => ['c-input', 'c-radio'], 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -292,33 +271,27 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, - '/label' + '/label', + '/div' ]) ; } - $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $expected = array_merge ($expected, ['/div', '/div']) ; $this->_testInput ($expected, $fieldName, $options) ; // Horizontal + Inline $options['inline'] = true ; $expected = [ - ['fieldset' => [ - 'class' => ['form-group', 'row'] + ['div' => [ + 'class' => 'form-group' ]], ['label' => [ - 'class' => ['form-control-label', 'col-md-2'] + 'class' => 'control-label col-md-2' ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', ['div' => [ 'class' => 'col-md-10' ]], - ['div' => [ - 'class' => ['radio'] - ]], ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -329,7 +302,7 @@ public function testInputRadio () { foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ ['label' => [ - 'class' => ['c-input', 'c-radio'], + 'class' => 'radio-inline', 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -338,15 +311,11 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, '/label' ]) ; } - $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $expected = array_merge ($expected, ['/div', '/div']) ; $this->_testInput ($expected, $fieldName, $options) ; } @@ -362,8 +331,8 @@ public function testInputGroup () { ] ; // Test with prepend addon $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -380,13 +349,13 @@ public function testInputGroup () { 'id' => $fieldName ]], '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['prepend' => '@']) ; // Test with append $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -403,13 +372,13 @@ public function testInputGroup () { '.00', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['append' => '.00']) ; // Test with append + prepend $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -431,13 +400,13 @@ public function testInputGroup () { '.00', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['prepend' => '$', 'append' => '.00']) ; // Test with prepend button $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -446,7 +415,7 @@ public function testInputGroup () { 'class' => 'input-group-btn' ]], ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'Go!', @@ -459,13 +428,15 @@ public function testInputGroup () { 'id' => $fieldName ]], '/div', - '/fieldset' + '/div' ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => $this->Form->button('Go!')]) ; + // Test with append button $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -480,20 +451,20 @@ public function testInputGroup () { 'class' => 'input-group-btn' ]], ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'Go!', '/button', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['append' => $this->Form->button('Go!')]) ; // Test with append 2 button $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -508,29 +479,28 @@ public function testInputGroup () { 'class' => 'input-group-btn' ]], ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'Go!', '/button', ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'GoGo!', '/button', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + [ 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] ]) ; // Test with append dropdown - $html = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this->View); $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -541,48 +511,48 @@ public function testInputGroup () { 'name' => $fieldName, 'id' => $fieldName ]], - ['div' => [ + ['span' => [ 'class' => 'input-group-btn' ]], + ['div' => [ + 'class' => 'btn-group' + ]], ['button' => [ - 'data-toggle' => 'dropdown', - 'aria-haspopup' => 'true', - 'aria-expanded' => 'false', - 'id' => 'dropdownMenu1', - 'class' => ['dropdown-toggle', 'btn', 'btn-secondary'] + 'data-toggle' => 'dropdown', + 'class' => 'dropdown-toggle btn btn-default' ]], 'Action', + ['span' => ['class' => 'caret']], '/span', '/button', - ['div' => [ - 'class' => ['dropdown-menu'], - 'aria-labelledby' => 'dropdownMenu1' - ]], - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 1', '/a', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 2', '/a', - ['div' => [ - 'class' => 'dropdown-divider' - ]], '/div', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 3', '/a', - '/div', + ['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', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + [ - 'append' => $html->dropdown('Action', [ - $html->link('Link 1', '#'), - $html->link('Link 2', '#'), + 'append' => $this->Form->dropdownButton('Action', [ + $this->Form->Html->link('Link 1', '#'), + $this->Form->Html->link('Link 2', '#'), 'divider', - $html->link('Link 3', '#') + $this->Form->Html->link('Link 3', '#') ]) ]); } diff --git a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php index 0fd351e..857dfd6 100644 --- a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php @@ -52,34 +52,34 @@ public function testIcon () { 'id' => 'my-home', 'class' => 'my-home-class' ] ; - // Default icon (FontAwesome) + // Default icon (Glyphicon) $this->assertHtml ([ ['i' => [ - 'class' => 'fa fa-'.$type + 'class' => 'glyphicon glyphicon-'.$type ]], '/i' ], $this->Html->icon($type)); $this->assertHtml ([ ['i' => [ - 'class' => $options['class'].' fa fa-'.$type, + 'class' => $options['class'].' glyphicon glyphicon-'.$type, 'id' => $options['id'] ]], '/i' ], $this->Html->icon($type, $options)); - // Glyphicon icon + // FontAwesome icon $this->assertHtml ([ ['i' => [ - 'class' => 'glyphicon glyphicon-'.$type + 'class' => 'fa fa-'.$type ]], '/i' - ], $this->Html->glIcon($type)); + ], $this->Html->faIcon($type)); $this->assertHtml ([ ['i' => [ - 'class' => $options['class'].' glyphicon glyphicon-'.$type, + 'class' => $options['class'].' fa fa-'.$type, 'id' => $options['id'] ]], '/i' - ], $this->Html->glIcon($type, $options)); + ], $this->Html->faIcon($type, $options)); } public function testLabel () { @@ -130,6 +130,14 @@ public function testLabel () { } public function testDropdown () { + /** + + **/ $title = 'Action' ; $menu = [ $this->Html->link('Link 1', '#'), @@ -138,98 +146,25 @@ public function testDropdown () { $this->Html->link('Link 3', '#') ] ; $expected = [ - ['div' => [ - 'class' => 'dropdown' - ]], - ['button' => [ - 'data-toggle' => 'dropdown', - 'aria-haspopup' => 'true', - 'aria-expanded' => 'false', - 'id' => 'dropdownMenu1', - 'class' => 'dropdown-toggle btn btn-secondary' - ]], - 'Action', - '/button', - ['div' => [ - 'class' => 'dropdown-menu', - 'aria-labelledby' => 'dropdownMenu1' - ]], - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 1', '/a', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 2', '/a', - ['div' => [ - 'class' => 'dropdown-divider' - ]], '/div', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 3', '/a', - '/div', - '/div' - ] ; - // Standard test - $this->assertHtml ($expected, $this->Html->dropdown($title, $menu)) ; - $menu = [ - ['Link 1', '#'], - ['Link 2', '#', ['class' => 'my-item-class', 'id' => 'my-item-id']], - 'divider', - ['Link 3', '#'] - ] ; - $options = [ - 'class' => 'my-dropdown', - '_button' => [ - 'tag' => 'a', - 'id' => 'my-dropdown-id' - ], - '_menu' => [ - 'class' => 'my-dropdown-menu', - '_item' => [ - 'class' => 'my-dropdown-item' - ] - ] - ] ; - $expected = [ - ['div' => [ - 'class' => $options['class'].' dropdown' - ]], - [$options['_button']['tag'] => [ - 'data-toggle' => 'dropdown', - 'aria-haspopup' => 'true', - 'aria-expanded' => 'false', - 'id' => $options['_button']['id'], - 'class' => 'dropdown-toggle btn btn-secondary' - ]], - 'Action', - '/'.$options['_button']['tag'], - ['div' => [ - 'class' => $options['_menu']['class'].' dropdown-menu', - 'aria-labelledby' => $options['_button']['id'] + ['ul' => [ + 'role' => 'menu', + 'class' => 'dropdown-menu' ]], - ['a' => [ - 'href' => '#', - 'class' => $options['_menu']['_item']['class'].' dropdown-item' - ]], 'Link 1', '/a', - ['a' => [ - 'href' => '#', - 'class' => $menu[1][2]['class'].' dropdown-item', - 'id' => $menu[1][2]['id'] - ]], 'Link 2', '/a', - ['div' => [ - 'class' => $options['_menu']['_item']['class'].' dropdown-divider' - ]], '/div', - ['a' => [ - 'href' => '#', - 'class' => $options['_menu']['_item']['class'].' dropdown-item' - ]], 'Link 3', '/a', - '/div', - '/div' + ['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' ] ; - $this->assertHtml ($expected, $this->Html->dropdown($title, $menu, $options)) ; } From 6e24f6bd9a0ce0696708b6937f1b092284e4eff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 28 Jan 2016 15:20:20 +0100 Subject: [PATCH 033/312] Add custom options to searchForm. --- src/View/Helper/BootstrapFormHelper.php | 63 ++++++++++++++++++------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 70ba64e..a3deafa 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -105,7 +105,6 @@ class BootstrapFormHelper extends FormHelper { public $horizontal = false ; public $inline = false ; - public $search = false ; public $colSize ; /** @@ -257,8 +256,6 @@ public function create($model = null, Array $options = array()) { } $this->horizontal = $this->_extractOption('horizontal', $options, false); unset($options['horizontal']); - $this->search = $this->_extractOption('search', $options, false) ; - unset($options['search']) ; $this->inline = $this->_extractOption('inline', $options, false) ; unset($options['inline']) ; if ($this->horizontal) { @@ -267,9 +264,6 @@ public function create($model = null, Array $options = array()) { else if ($this->inline) { $options = $this->addClass($options, 'form-inline') ; } - if ($this->search) { - $options = $this->addClass($options, 'form-search') ; - } $options['role'] = 'form' ; return parent::create($model, $options) ; } @@ -686,33 +680,68 @@ public function submit($caption = null, array $options = array()) { * * @param $model The model of the form * @param $options The options that will be pass to the BootstrapForm::create method + * @param $inpOpts The options that will be pass to the BootstrapForm::input method + * @param $btnOpts The options that will be pass to the BootstrapForm::button method * * Extra options: - * - label: The input label (default false) - * - placeholder: The input placeholder (default "Search... ") - * - button: The search button text (default: "Search") + * - id ID of the input (and fieldname) + * - label The input label (default false) + * - placeholder The input placeholder (default "Search... ") + * - button The search button text (default: "Search") + * - _input Options for the input (overrided by $inpOpts) + * - _button Options for the button (overrided by $btnOpts) * **/ - public function searchForm ($model = null, $options = array()) { + public function searchForm ($model = null, $options = [], $inpOpts = [], $btnOpts = []) { + + $options += [ + 'id' => 'search', + 'label' => false, + 'placeholder' => 'Search... ', + 'button' => 'Search', + '_input' => [], + '_button' => [] + ]; + + $options = $this->addClass($options, 'form-search'); + + $btnOpts += $options['_button']; + unset($options['_button']); + + $inpOpts += $options['_input']; + unset($options['_input']); - $label = $this->_extractOption('label', $options, false) ; + $inpOpts += [ + 'id' => $options['id'], + 'placeholder' => $options['placeholder'], + 'label' => $options['label'] + ]; + + unset($options['id']) ; unset($options['label']) ; - $placeholder = $this->_extractOption('placeholder', $options, 'Search... ') ; unset($options['placeholder']) ; - $button = $this->_extractOption('button', $options, 'Search') ; + + $btnName = $options['button']; unset($options['button']) ; + $inpOpts['append'] = $this->button($btnName, $btnOpts); + + $options['inline'] = (bool)$inpOpts['label']; + $output = '' ; - $output .= $this->create($model, array_merge(array('search' => true, 'inline' => (bool)$label), $options)) ; - $output .= $this->input('search', array( + $output .= $this->create($model, $options) ; + $output .= $this->input($inpOpts['id'], $inpOpts); + $output .= $this->end() ; + +/* array( 'label' => $label, 'placeholder' => $placeholder, 'append' => array( - $this->button($button, array('style' => 'vertical-align: middle')) + $this->button($button, ['style' => 'vertical-align: middle']) ) )) ; - $output .= $this->end() ; + $output .= $this->end() ; */ return $output ; } From 23e809bd7f00f72831111a8334c3414bafcff634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 28 Jan 2016 16:44:11 +0100 Subject: [PATCH 034/312] Add test for easyIcon. Add possibility to add text after or before easy icons. --- src/View/Helper/BootstrapTrait.php | 8 +- .../View/Helper/BootstrapTraitTest.php | 160 ++++++++++++++++++ 2 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 tests/TestCase/View/Helper/BootstrapTraitTest.php diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 4923dc2..5f398d4 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -146,10 +146,10 @@ protected function _makeIcon ($title, &$converted = false) { if (!$this->easyIcon) { return $title ; } - if (preg_match('#^i:([a-zA-Z0-9\\-_]+)$#', $title, $matches)) { - $converted = true ; - $title = $this->_View->Html->icon($matches[1]); - } + $title = preg_replace_callback('#(^|\s+)i:([a-zA-Z0-9\\-_]+)(\s+|$)#', function ($matches) { + return $matches[1].$this->_View->Html->icon($matches[2]).$matches[3]; + }, $title, -1, $count); + $converted = (bool)$count; return $title ; } diff --git a/tests/TestCase/View/Helper/BootstrapTraitTest.php b/tests/TestCase/View/Helper/BootstrapTraitTest.php new file mode 100644 index 0000000..c387ed5 --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapTraitTest.php @@ -0,0 +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. + } + +}; \ No newline at end of file From 950221c851dc22da89a8d85820194c89296f6670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 4 Feb 2016 12:36:52 +0100 Subject: [PATCH 035/312] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c43120e..6125a65 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,18 @@ If you want the latest **Bootstrap 3** version of the plugin: ``` composer require holt59/cakephp3-bootstrap-helpers:dev-master ``` - -If you are updating from `holt59/cakephp3-bootstrap3-helpers`, do not forget to change the the following: - ```php // in config/bootstrap.php -Plugin::load('Bootstrap') ; // instead of Plugin::load('Bootstrap3') ; +Plugin::load('Bootstrap') ; ``` ```php // in your AppController public $helpers = [ 'Form' => [ - 'className' => 'Bootstrap.BootstrapForm' // instead of 'Bootstrap3.BootstrapForm' - ] + 'className' => 'Bootstrap.BootstrapForm' + ], + /* ... */ ] ; ``` --- From 798b3b8f25b107c7d5f8bd1bf9af45c95408782f Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Feb 2016 21:25:47 +0100 Subject: [PATCH 036/312] Remove override of groupTemplate, see https://github.com/Holt59/cakephp3-bootstrap-helpers/issues/73 --- src/View/Helper/BootstrapFormHelper.php | 77 ++++++++++--------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index a3deafa..30a6821 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -29,7 +29,7 @@ class BootstrapFormHelper extends FormHelper { use BootstrapTrait ; public $helpers = [ - 'Html', + 'Html', 'Url', 'bHtml' => [ 'className' => 'Bootstrap.BootstrapHtml' @@ -102,7 +102,7 @@ class BootstrapFormHelper extends FormHelper { 'datetime' => ['Cake\View\Widget\DateTimeWidget', 'select'], '_default' => ['Cake\View\Widget\BasicWidget'], ]; - + public $horizontal = false ; public $inline = false ; public $colSize ; @@ -151,7 +151,7 @@ public function __construct (\Cake\View\View $view, array $config = []) { $this->_defaultConfig['templateClass'] = 'Bootstrap\View\BootstrapStringTemplate' ; parent::__construct($view, $config); } - + /** * * Replace the templates with the ones specified by newTemplates, call the specified function @@ -326,40 +326,48 @@ public function wrap ($input, $prepend, $append) { protected function _wrap ($input, $prepend, $append) { return '
    '.$prepend.$input.$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 - * + * - prepend: + * -> string: Add before the input + * -> 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 += [ + 'templateVars' => [], + 'prepend' => false, + 'append' => false, + 'help' => false, + 'inline' => false + ]; + $options = $this->_parseOptions($fieldName, $options); - $prepend = $this->_extractOption('prepend', $options, false) ; + $prepend = $options['prepend']; unset($options['prepend']); - $append = $this->_extractOption('append', $options, false) ; + $append = $options['append']; unset($options['append']); if ($prepend || $append) { $prepend = $this->prepend(null, $prepend); $append = $this->append(null, $append); } - $help = $this->_extractOption('help', $options, ''); + $help = $options['help']; unset($options['help']); if ($help) { $append .= '

    '.$help.'

    ' ; } - $inline = $this->_extractOption('inline', $options, '') ; + $inline = $options['inline']; unset ($options['inline']) ; - + if ($options['type'] === 'radio') { $options['templates'] = [] ; if ($inline) { @@ -377,35 +385,12 @@ public function input($fieldName, array $options = array()) { } } - $options['_data'] = [ + $options['templateVars'] += [ 'prepend' => $prepend, 'append' => $append ]; - - 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'; - } - $data = [ - 'input' => $options['input'], - 'label' => $options['label'], - 'error' => $options['error'] - ]; - if (isset($options['options']['_data'])) { - $data = array_merge($data, $options['options']['_data']); - unset($options['options']['_data']); - } - return $this->formatTemplate($groupTemplate, $data); + return parent::input($fieldName, $options) ; } /** @@ -424,7 +409,8 @@ protected function _getDatetimeTemplate ($fields, $options) { $inputs = [] ; foreach ($fields as $field => $in) { if ($this->_extractOption($field, $options, $in)) { - if ($field === 'timeFormat') $field = 'meridian' ; // Template uses "meridian" instead of timeFormat + if ($field === 'timeFormat') + $field = 'meridian' ; // Template uses "meridian" instead of timeFormat $inputs[$field] = '
    {{'.$field.'}}
    '; } } @@ -436,7 +422,8 @@ protected function _getDatetimeTemplate ($fields, $options) { $html .= $inputs[$v] ; } } - return str_replace('{{colsize}}', round(12 / count($inputs)), '
    '.$html.'
    ') ; + return str_replace('{{colsize}}', round(12 / count($inputs)), + '
    '.$html.'
    ') ; } /** From dbca1172382e0eab0eeee6ddadac9829ef26bb0e Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 19:17:52 +0100 Subject: [PATCH 037/312] Clean code and doc. --- src/View/Helper/BootstrapPanelHelper.php | 77 +++++++++++++----------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 97a08fb..7c0de48 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -31,17 +31,17 @@ class BootstrapPanelHelper extends Helper { public $helpers = ['Html']; public $current = NULL ; - + /** - * - * Create a Twitter Bootstrap like panel. + * + * 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. + * @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)) { @@ -52,12 +52,12 @@ public function create($title = null, $options = []) { unset ($options['no-body']); $type = $this->_extractOption('type', $options, 'default'); unset ($options['type']); - + $options = $this->addClass($options, ['panel', 'panel-'.$type]); $class = $options['class']; unset ($options['class']); - - $res = $this->Html->div($class, null, $options); + + $res = $this->Html->div($class, null, $options); if (is_string($title) && $title) { $res .= $this->_createHeader($title, []) ; if (!$nobody) { @@ -65,16 +65,17 @@ public function create($title = null, $options = []) { } } return $res ; - } - + } + /** - * - * End a panel. If $title is not null, the ModalHelper::footer functions is called with $title and $options arguments. + * + * End a panel. If $title is not null, the ModalHelper::footer functions + * is called with $title and $options arguments. * * @param string|null $buttons * @param array $options - * - **/ + * + **/ public function end ($title = null, $options = []) { $res = '' ; if ($this->current != null) { @@ -85,7 +86,7 @@ public function end ($title = null, $options = []) { $res .= $this->footer($title, $options) ; } $res .= '' ; - return $res ; + return $res ; } protected function _cleanCurrent () { @@ -108,32 +109,35 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { unset ($options['class']); $titleOptions = $this->addClass($titleOptions, 'panel-title'); return $this->_cleanCurrent().$this->Html->div($class, - $this->Html->tag('h3', $title, $titleOptions), - $options - ) ; + $this->Html->tag('h3', $title, + $titleOptions), + $options + ) ; } protected function _createBody ($text, $options = []) { $options = $this->addClass($options, 'panel-body'); $class = $options['class']; unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; } protected function _createFooter ($text = null, $options = []) { $options = $this->addClass($options, 'panel-footer'); $class = $options['class']; unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; } - + protected function _startPart ($part, $options = []) { $res = '' ; if ($this->current != null) { $res = $this->_endPart () ; } $this->current = $part ; - return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', $options, ''), null, $options) ; + return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', + $options, ''), + null, $options) ; } protected function _endPart () { @@ -142,15 +146,16 @@ protected function _endPart () { /** * - * Create / Start the header. If $info is specified as a string, create and return the whole header, otherwize only open the header. - * + * 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 modal 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_string($info)) { return $this->_createHeader($info, $options) ; @@ -160,13 +165,14 @@ 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. - * + * 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_string($info)) { if ($this->current != null) { @@ -180,15 +186,16 @@ public function body ($info = null, $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 array|string $buttons If string, use as the footer content, if list, concatenate values in the list as content (use for buttons purpose), otherwize works as $options. + * 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 = "", $options = []) { return $this->_createFooter($text, $options) ; } From 0fc4ba1c6d8366405b30ff1016bc31a52ccac396 Mon Sep 17 00:00:00 2001 From: mcapelle Date: Thu, 21 Jan 2016 15:04:56 +0100 Subject: [PATCH 038/312] Create PanelHelper for non-collapsible panel. --- src/View/Helper/BootstrapPanelHelper.php | 198 +++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 src/View/Helper/BootstrapPanelHelper.php diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php new file mode 100644 index 0000000..97a08fb --- /dev/null +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -0,0 +1,198 @@ +_extractOption('no-body', $options, false); + unset ($options['no-body']); + $type = $this->_extractOption('type', $options, 'default'); + unset ($options['type']); + + $options = $this->addClass($options, ['panel', 'panel-'.$type]); + $class = $options['class']; + unset ($options['class']); + + $res = $this->Html->div($class, null, $options); + if (is_string($title) && $title) { + $res .= $this->_createHeader($title, []) ; + if (!$nobody) { + $res .= $this->_startPart('body'); + } + } + return $res ; + } + + /** + * + * End a panel. If $title is not null, the ModalHelper::footer functions is called with $title and $options arguments. + * + * @param string|null $buttons + * @param array $options + * + **/ + public function end ($title = null, $options = []) { + $res = '' ; + if ($this->current != null) { + $this->current = null ; + $res .= $this->_endPart(); + } + if ($title !== null) { + $res .= $this->footer($title, $options) ; + } + $res .= '' ; + return $res ; + } + + protected function _cleanCurrent () { + if ($this->current) { + $this->current = NULL ; + return $this->_endPart(); + } + return '' ; + } + + protected function _createHeader ($title, $options = [], $titleOptions = []) { + $options += [ + '_title' => [] + ]; + if (empty($titleOptions)) + $titleOptions = $options['_title']; + unset ($options['_title']); + $options = $this->addClass($options, 'panel-heading'); + $class = $options['class']; + unset ($options['class']); + $titleOptions = $this->addClass($titleOptions, 'panel-title'); + return $this->_cleanCurrent().$this->Html->div($class, + $this->Html->tag('h3', $title, $titleOptions), + $options + ) ; + } + + protected function _createBody ($text, $options = []) { + $options = $this->addClass($options, 'panel-body'); + $class = $options['class']; + unset ($options['class']); + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + } + + protected function _createFooter ($text = null, $options = []) { + $options = $this->addClass($options, 'panel-footer'); + $class = $options['class']; + unset ($options['class']); + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + } + + protected function _startPart ($part, $options = []) { + $res = '' ; + if ($this->current != null) { + $res = $this->_endPart () ; + } + $this->current = $part ; + return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', $options, ''), null, $options) ; + } + + protected function _endPart () { + return '' ; + } + + /** + * + * 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 modal 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_string($info)) { + return $this->_createHeader($info, $options) ; + } + return $this->_startPart('header', is_array($info) ? $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_string($info)) { + if ($this->current != null) { + $this->_endPart() ; + } + return $this->_createBody($info, $options) ; + } + return $this->_startPart('body', is_array($info) ? $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 array|string $buttons If string, use as the footer content, if list, concatenate values in the list as content (use for buttons purpose), otherwize works as $options. + * @param array $options Options for the footer div. + * + **/ + public function footer ($text = "", $options = []) { + return $this->_createFooter($text, $options) ; + } + +} + +?> From e7ff4ab103462e808c3320b2b2e9dd30fb3f04fc Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 19:17:52 +0100 Subject: [PATCH 039/312] Clean code and doc. --- src/View/Helper/BootstrapPanelHelper.php | 77 +++++++++++++----------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 97a08fb..7c0de48 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -31,17 +31,17 @@ class BootstrapPanelHelper extends Helper { public $helpers = ['Html']; public $current = NULL ; - + /** - * - * Create a Twitter Bootstrap like panel. + * + * 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. + * @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)) { @@ -52,12 +52,12 @@ public function create($title = null, $options = []) { unset ($options['no-body']); $type = $this->_extractOption('type', $options, 'default'); unset ($options['type']); - + $options = $this->addClass($options, ['panel', 'panel-'.$type]); $class = $options['class']; unset ($options['class']); - - $res = $this->Html->div($class, null, $options); + + $res = $this->Html->div($class, null, $options); if (is_string($title) && $title) { $res .= $this->_createHeader($title, []) ; if (!$nobody) { @@ -65,16 +65,17 @@ public function create($title = null, $options = []) { } } return $res ; - } - + } + /** - * - * End a panel. If $title is not null, the ModalHelper::footer functions is called with $title and $options arguments. + * + * End a panel. If $title is not null, the ModalHelper::footer functions + * is called with $title and $options arguments. * * @param string|null $buttons * @param array $options - * - **/ + * + **/ public function end ($title = null, $options = []) { $res = '' ; if ($this->current != null) { @@ -85,7 +86,7 @@ public function end ($title = null, $options = []) { $res .= $this->footer($title, $options) ; } $res .= '' ; - return $res ; + return $res ; } protected function _cleanCurrent () { @@ -108,32 +109,35 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { unset ($options['class']); $titleOptions = $this->addClass($titleOptions, 'panel-title'); return $this->_cleanCurrent().$this->Html->div($class, - $this->Html->tag('h3', $title, $titleOptions), - $options - ) ; + $this->Html->tag('h3', $title, + $titleOptions), + $options + ) ; } protected function _createBody ($text, $options = []) { $options = $this->addClass($options, 'panel-body'); $class = $options['class']; unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; } protected function _createFooter ($text = null, $options = []) { $options = $this->addClass($options, 'panel-footer'); $class = $options['class']; unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; } - + protected function _startPart ($part, $options = []) { $res = '' ; if ($this->current != null) { $res = $this->_endPart () ; } $this->current = $part ; - return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', $options, ''), null, $options) ; + return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', + $options, ''), + null, $options) ; } protected function _endPart () { @@ -142,15 +146,16 @@ protected function _endPart () { /** * - * Create / Start the header. If $info is specified as a string, create and return the whole header, otherwize only open the header. - * + * 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 modal 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_string($info)) { return $this->_createHeader($info, $options) ; @@ -160,13 +165,14 @@ 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. - * + * 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_string($info)) { if ($this->current != null) { @@ -180,15 +186,16 @@ public function body ($info = null, $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 array|string $buttons If string, use as the footer content, if list, concatenate values in the list as content (use for buttons purpose), otherwize works as $options. + * 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 = "", $options = []) { return $this->_createFooter($text, $options) ; } From 8e966c9846910fb0b1ea7cc8eec72d01a621603a Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:28:46 +0100 Subject: [PATCH 040/312] Create travis file. --- .travis.yml | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dcca55b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,74 @@ +language: php + +php: + - 5.5 + - 5.6 + - 7.0 + +sudo: false + +env: + matrix: + - DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' + - DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test' + - DB=sqlite db_dsn='sqlite:///:memory:' + global: + - DEFAULT=1 + +services: + - memcached + - redis-server + +cache: + directories: + - vendor + - $HOME/.composer/cache + +matrix: + fast_finish: true + + include: + - php: 7.0 + env: CODECOVERAGE=1 DEFAULT=0 + + - php: 7.0 + env: PHPCS=1 DEFAULT=0 + + - php: hhvm + env: HHVM=1 DB=sqlite db_dsn='sqlite:///:memory:' + + - php: hhvm + env: HHVM=1 DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' + + allow_failures: + - env: CODECOVERAGE=1 DEFAULT=0 + + - php: hhvm + +before_script: + - sh -c "if [ '$HHVM' != '1' ]; then phpenv config-rm xdebug.ini; fi" + + - composer self-update + - composer install --prefer-dist --no-interaction + + - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" + + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi" + + - sh -c "if [ '$PHP' = '5.6' ]; then pecl install apc; fi" + - sh -c "if [ '$HHVM' = '1' ]; then composer require lorenzo/multiple-iterator=~1.0; fi" + + - phpenv rehash + - set +H + +script: + - sh -c "if [ '$DEFAULT' = '1' ]; then phpunit; fi" + + - sh -c "if [ '$PHPCS' = '1' ]; then vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests; fi" + + - sh -c "if [ '$CODECOVERAGE' = '1' ]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=clover.xml || true; fi" + - sh -c "if [ '$CODECOVERAGE' = '1' ]; then wget -O codecov.sh https://codecov.io/bash; fi" + - sh -c "if [ '$CODECOVERAGE' = '1' ]; then bash codecov.sh; fi" + +notifications: + email: true \ No newline at end of file From db138dc73409c5a04a977ee6ba4383187e8f314e Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:34:12 +0100 Subject: [PATCH 041/312] Create PHP unit file. --- phpunit.xml.dist | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 phpunit.xml.dist diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..485f3af --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + + ./tests/ + + + + + + ./src/ + + + \ No newline at end of file From cd92ae87f26bd73917b543e13630e104de3e8a02 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:39:41 +0100 Subject: [PATCH 042/312] Update travis. --- composer.json | 48 ++++++++++++++++++------------------ phpunit.xml.dist | 3 ++- tests/bootstrap.php | 60 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 tests/bootstrap.php diff --git a/composer.json b/composer.json index 0c0ae10..1cc720d 100644 --- a/composer.json +++ b/composer.json @@ -1,26 +1,26 @@ { - "name": "holt59/cakephp3-bootstrap-helpers", - "description": "Bootstrap Helpers for CakePHP 3.0", - "keywords": ["CakePHP", "Bootstrap"], - "license": "Apache", - "type": "cakephp-plugin", - "require": { - "cakephp/cakephp": "~3.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "autoload": { - "psr-4": { - "Bootstrap\\": "src" - } - }, - "autoload-dev": { - "psr-4": { - "Bootstrap\\Test\\": "tests" - } - }, - "extra": { - "installer-name": "Bootstrap" - } + "name": "holt59/cakephp3-bootstrap-helpers", + "description": "Bootstrap Helpers for CakePHP 3.0", + "keywords": ["CakePHP", "Bootstrap"], + "license": "Apache", + "type": "cakephp-plugin", + "require": { + "cakephp/cakephp": "~3.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "autoload": { + "psr-4": { + "Bootstrap\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Bootstrap\\Test\\": "tests" + } + }, + "extra": { + "installer-name": "Bootstrap" + } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 485f3af..d0bb488 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,6 +2,7 @@ @@ -15,4 +16,4 @@ ./src/ - \ No newline at end of file + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..4e5d05b --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,60 @@ + 'App', + 'encoding' => 'UTF-8', + 'base' => false, + 'baseUrl' => false, + 'dir' => 'src', + 'webroot' => WEBROOT_DIR, + 'www_root' => WWW_ROOT, + 'fullBaseUrl' => 'http://localhost', + 'imageBaseUrl' => 'img/', + 'jsBaseUrl' => 'js/', + 'cssBaseUrl' => 'css/', + 'paths' => [ + 'plugins' => [dirname(APP) . DS . 'plugins' . DS], + 'templates' => [APP . 'Template' . DS] + ] +]); +Cache::config([ + '_cake_core_' => [ + 'engine' => 'File', + 'prefix' => 'cake_core_', + 'serialize' => true + ], + '_cake_model_' => [ + 'engine' => 'File', + 'prefix' => 'cake_model_', + 'serialize' => true + ] +]); +Plugin::load('Bootstrap', ['path' => ROOT]); From 7b589a902efdcf4a815a40819d1e36adbc9be4a1 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:46:44 +0100 Subject: [PATCH 043/312] Update bootstrap.php --- tests/bootstrap.php | 83 ++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 58 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 4e5d05b..1001fee 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,60 +1,27 @@ 'App', - 'encoding' => 'UTF-8', - 'base' => false, - 'baseUrl' => false, - 'dir' => 'src', - 'webroot' => WEBROOT_DIR, - 'www_root' => WWW_ROOT, - 'fullBaseUrl' => 'http://localhost', - 'imageBaseUrl' => 'img/', - 'jsBaseUrl' => 'js/', - 'cssBaseUrl' => 'css/', - 'paths' => [ - 'plugins' => [dirname(APP) . DS . 'plugins' . DS], - 'templates' => [APP . 'Template' . DS] - ] -]); -Cache::config([ - '_cake_core_' => [ - 'engine' => 'File', - 'prefix' => 'cake_core_', - 'serialize' => true - ], - '_cake_model_' => [ - 'engine' => 'File', - 'prefix' => 'cake_model_', - 'serialize' => true - ] -]); -Plugin::load('Bootstrap', ['path' => ROOT]); +require $root . '/vendor/cakephp/cakephp/tests/bootstrap.php'; +\Cake\Core\Plugin::load('Bootstrap', ['path' => dirname(dirname(__FILE__)) . DS]); From 7e320e22b7131285b448ec1d21757a34fb403466 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:49:59 +0100 Subject: [PATCH 044/312] Update composer.json require-dev. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 1cc720d..81af14a 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,7 @@ "cakephp/cakephp": "~3.0" }, "require-dev": { + "cakephp/cakephp": "~3.0", "phpunit/phpunit": "*" }, "autoload": { From ced40b958a195d1c3259f9990e8900e144e85ae7 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:26:31 +0100 Subject: [PATCH 045/312] Clean travis config. --- composer.json | 1 - phpunit.xml.dist | 6 +-- tests/bootstrap.php | 78 ++++++++++++++++++++++---------- tests/test_app/config/routes.php | 21 +++++++++ 4 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 tests/test_app/config/routes.php diff --git a/composer.json b/composer.json index 81af14a..1cc720d 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,6 @@ "cakephp/cakephp": "~3.0" }, "require-dev": { - "cakephp/cakephp": "~3.0", "phpunit/phpunit": "*" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0bb488..dd7760b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,8 +1,8 @@ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1001fee..052ed34 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,27 +1,55 @@ dirname(dirname(__FILE__)) . DS]); + +define('ROOT', dirname(__DIR__) . DS); +define('CAKE_CORE_INCLUDE_PATH', ROOT . 'vendor' . DS . 'cakephp' . DS . 'cakephp'); +define('CORE_PATH', ROOT . 'vendor' . DS . 'cakephp' . DS . 'cakephp' . DS); +define('CAKE', CORE_PATH . 'src' . DS); +define('TESTS', ROOT . 'tests'); +define('APP', ROOT . 'tests' . DS . 'test_app' . DS); +define('APP_DIR', 'app'); +define('WEBROOT_DIR', 'webroot'); +define('WWW_ROOT', dirname(APP) . DS . 'webroot' . DS); +define('TMP', sys_get_temp_dir() . DS); +define('CONFIG', APP . 'config' . DS); +define('CACHE', TMP); +define('LOGS', TMP); + +//@codingStandardsIgnoreStart +@mkdir(LOGS); +@mkdir(SESSIONS); +@mkdir(CACHE); +@mkdir(CACHE . 'views'); +@mkdir(CACHE . 'models'); + +require_once CORE_PATH . 'config/bootstrap.php'; +date_default_timezone_set('UTC'); +mb_internal_encoding('UTF-8'); + +Cache::config([ + '_cake_core_' => [ + 'engine' => 'File', + 'prefix' => 'cake_core_', + 'serialize' => true + ], + '_cake_model_' => [ + 'engine' => 'File', + 'prefix' => 'cake_model_', + 'serialize' => true + ] +]); + + +Plugin::load('Search', ['path' => ROOT]); diff --git a/tests/test_app/config/routes.php b/tests/test_app/config/routes.php new file mode 100644 index 0000000..d864631 --- /dev/null +++ b/tests/test_app/config/routes.php @@ -0,0 +1,21 @@ +connect('/', ['controller' => 'pages', 'action' => 'display', 'home']); + $routes->connect('/some_alias', ['controller' => 'tests_apps', 'action' => 'some_method']); + $routes->fallbacks(); +}); From accc7e01674cda6b47dcf3d87b4e5905ac8dbba2 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:29:15 +0100 Subject: [PATCH 046/312] Set encoding in bootstrap.php (accept-charset test for BootstrapFormHelper). --- tests/bootstrap.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 052ed34..66491e2 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -38,6 +38,16 @@ date_default_timezone_set('UTC'); mb_internal_encoding('UTF-8'); +Configure::write('App', [ + 'namespace' => 'App', + 'encoding' => 'UTF-8', + 'base' => false, + 'baseUrl' => false, + 'dir' => APP_DIR, + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT +]); + Cache::config([ '_cake_core_' => [ 'engine' => 'File', @@ -51,5 +61,6 @@ ] ]); +ini_set('intl.default_locale', 'en_US'); Plugin::load('Search', ['path' => ROOT]); From 8df80e8f364e37662bf854359216039b13fae4c6 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:38:49 +0100 Subject: [PATCH 047/312] Add require for codesniffer. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1cc720d..e674f01 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "cakephp/cakephp": "~3.0" }, "require-dev": { - "phpunit/phpunit": "*" + "phpunit/phpunit": "*", + "cakephp/cakephp-codesniffer": "dev-master" }, "autoload": { "psr-4": { From a9e709efc200fdc084c2055022008933137baf1e Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:44:57 +0100 Subject: [PATCH 048/312] Remove PHPCS test. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dcca55b..86cfbba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ env: - DB=sqlite db_dsn='sqlite:///:memory:' global: - DEFAULT=1 + - PHPCS=0 services: - memcached From 0d57983d985070616f9e6333b41a17f9f22e7b74 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:46:10 +0100 Subject: [PATCH 049/312] Remove PHPCS test (bis). --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 86cfbba..9cf77e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ env: - DB=sqlite db_dsn='sqlite:///:memory:' global: - DEFAULT=1 - - PHPCS=0 services: - memcached @@ -33,7 +32,7 @@ matrix: env: CODECOVERAGE=1 DEFAULT=0 - php: 7.0 - env: PHPCS=1 DEFAULT=0 + env: PHPCS=0 DEFAULT=0 - php: hhvm env: HHVM=1 DB=sqlite db_dsn='sqlite:///:memory:' From 41c21471d479e06c7590d14fb3bbcff10fcaaf40 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:52:37 +0100 Subject: [PATCH 050/312] Add badge in README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6125a65..e6ed226 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ CakePHP 3.x Helpers for Bootstrap ================================= +[![GitHub license](https://img.shields.io/github/license/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](LICENSE) +[![Travis](https://img.shields.io/travis/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](https://travis-ci.org/Holt59/cakephp3-bootstrap-helpers) + CakePHP 3.0 Helpers to generate HTML with @Twitter Boostrap style: `Html`, `Form`, `Modal` and `Paginator` helpers available! How to... ? From 167385c0e085eab3f9ebf4e4d940dafe13191c56 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 12:46:22 +0100 Subject: [PATCH 051/312] Update PaginatorHelper to deal with first and last options. --- src/View/Helper/BootstrapPaginatorHelper.php | 106 +++++++++++++------ 1 file changed, 74 insertions(+), 32 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 6a71b49..21feb4b 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -27,7 +27,7 @@ class BootstrapPaginatorHelper extends PaginatorHelper { use BootstrapTrait ; - + /** * Default config for this class * @@ -59,7 +59,7 @@ class BootstrapPaginatorHelper extends PaginatorHelper { 'last' => '
  • {{text}}
  • ', 'number' => '
  • {{text}}
  • ', 'current' => '
  • {{text}}
  • ', - 'ellipsis' => '
  • ...
  • ', + 'ellipsis' => '
  • ...
  • ', 'sort' => '{{text}}', 'sortAsc' => '{{text}}', 'sortDesc' => '{{text}}', @@ -67,26 +67,26 @@ class BootstrapPaginatorHelper extends PaginatorHelper { 'sortDescLocked' => '{{text}}', ] ]; - + /** - * + * * Get pagination link list. - * + * * @param $options Options for link element * * Extra options: * - size small/normal/large (default normal) - * + * **/ - public function numbers (array $options = array()) { - - $class = 'pagination' ; + public function numbers (array $options = []) { + + $options += [ + 'class' => '' + ]; + + $class = 'pagination '.$options['class'] ; + unset($options['class']); - if (isset($options['class'])) { - $class .= ' '.$options['class'] ; - unset($options['class']) ; - } - if (isset($options['size'])) { switch ($options['size']) { case 'small': @@ -98,46 +98,88 @@ public function numbers (array $options = array()) { } unset($options['size']) ; } - + if (!isset($options['before'])) { $options['before'] = '
      ' ; } - + if (!isset($options['after'])) { $options['after'] = '
    ' ; } + return parent::numbers($options); + + } + + /** + * Generates the numbers for the paginator numbers() method. + * + * @param \Cake\View\StringTemplate $templater StringTemplate instance. + * @param array $params Params from the numbers() method. + * @param array $options Options from the numbers() method. + * @return string Markup output. + */ + protected function _modulusNumbers($templater, $params, $options) { + + $options += [ + 'before' => '', + 'after' => '' + ]; + + $first = $prev = $next = $last = ''; + + /* Previous and Next buttons (addition from standard PaginatorHelper). */ + if (isset($options['prev'])) { $title = $options['prev'] ; $opts = [] ; - if (is_array($title)) { - $title = $title['title'] ; - unset ($options['prev']['title']) ; - $opts = $options['prev'] ; + if (is_array($title)) { + $title = $title['title'] ; + unset ($options['prev']['title']) ; + $opts = $options['prev'] ; } - $options['before'] .= $this->prev($title, $opts) ; + $prev = $this->prev($title, $opts) ; + unset($options['prev']); } if (isset($options['next'])) { $title = $options['next'] ; $opts = [] ; - if (is_array($title)) { - $title = $title['title']; - unset ($options['next']['title']); - $opts = $options['next']; + if (is_array($title)) { + $title = $title['title']; + unset ($options['next']['title']); + $opts = $options['next']; } - $options['after'] = $this->next($title, $opts).$options['after'] ; + $next = $this->next($title, $opts); + unset($options['next']); + } + + /* Custom First and Last. */ + + $ellipsis = $templater->format('ellipsis', []); + list($start, $end) = $this->_getNumbersStartAndEnd($params, $options); + + if (isset($options['last'])) { + $last = $this->_lastNumber($ellipsis, $params, $end, $options); } - - return parent::numbers ($options) ; + + if (isset($options['last'])) { + $first = $this->_firstNumber($ellipsis, $params, $start, $options); + } + + $options['before'] = $options['before'].$first.$prev; + $options['after'] = $next.$last.$options['after']; + $options['first'] = $options['last'] = false; + + return parent::_modulusNumbers($templater, $params, $options) ; } - public function prev ($title = '<< Previous', array $options = []) { - return $this->_easyIcon ('parent::prev', $title, $options); + public function prev ($title = '<< Previous', array $options = []) { + return $this->_easyIcon ('parent::prev', $title, $options); } - public function next ($title = 'Next >>', array $options = []) { - return $this->_easyIcon ('parent::next', $title, $options); + public function next ($title = 'Next >>', array $options = []) { + return $this->_easyIcon ('parent::next', $title, $options); } From ccc6e081e73f929b0fb04adc121b7763eaa1c73e Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 13:07:51 +0100 Subject: [PATCH 052/312] Start adding test for BootstrapPaginatorHelper. --- .../View/Helper/BootstrapFormHelperTest.php | 16 ++-- .../Helper/BootstrapPaginatorHelperTest.php | 88 +++++++++++++++++++ 2 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php index 0fc0194..c464551 100644 --- a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -39,7 +39,7 @@ public function testCreate () { 'role' => 'form', 'action' ]] - ], $this->Form->create ()) ; + ], $this->Form->create ()) ; // Horizontal form $result = $this->Form->create (null, ['horizontal' => true]) ; $this->assertEquals($this->Form->horizontal, true) ; @@ -57,12 +57,12 @@ public function testCreate () { 'action', 'class' => 'form-inline' ]] - ], $result) ; + ], $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'])) { @@ -72,7 +72,7 @@ protected function _testInput ($expected, $fieldName, $options = []) { $this->Form->create (null, $formOptions) ; return $this->assertHtml ($expected, $this->Form->input ($fieldName, $options)) ; } - + public function testInput () { $fieldName = 'field' ; // Standard form @@ -142,7 +142,7 @@ public function testInputText () { '/div' ], $fieldName, ['type' => 'text']) ; } - + public function testInputSelect () { } @@ -320,9 +320,9 @@ public function testInputRadio () { } public function testInputCheckbox () { - + } - + public function testInputGroup () { $fieldName = 'field' ; $options = [ @@ -495,7 +495,7 @@ public function testInputGroup () { '/div' ] ; $this->_testInput ($expected, $fieldName, $options + [ - 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] + 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] ]) ; // Test with append dropdown $expected = [ diff --git a/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php b/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php new file mode 100644 index 0000000..caf507e --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php @@ -0,0 +1,88 @@ +View = new View(); + $this->View->Html = new BootstrapHtmlHelper($this->View); + $this->Paginator = new BootstrapPaginatorHelper($this->View); + $this->Paginator->request = new Request(); + $this->Paginator->request->addParams([ + 'paging' => [ + 'Article' => [ + 'page' => 1, + 'current' => 9, + 'count' => 62, + 'prevPage' => false, + 'nextPage' => true, + 'pageCount' => 7, + 'sort' => null, + 'direction' => null, + 'limit' => null, + ] + ] + ]); + Configure::write('Routing.prefixes', []); + Router::reload(); + Router::connect('/:controller/:action/*'); + Router::connect('/:plugin/:controller/:action/*'); + } + + public function testPrev () { + $this->assertHtml([ + ['li' => [ + 'class' => 'disabled' + ]], + ['a' => true], '<', '/a', + '/li' + ], $this->Paginator->prev('<')); + $this->assertHtml([ + ['li' => [ + 'class' => 'disabled' + ]], + ['a' => true], + ['i' => [ + 'class' => 'glyphicon glyphicon-chevron-left' + ]], + '/i', '/a', '/li' + ], $this->Paginator->prev('i:chevron-left')); + } + + public function testNext () { + $this->assertHtml([ + ['li' => true], + ['a' => [ + 'href' => '/index?page=2' + ]], '>', '/a', + '/li' + ], $this->Paginator->next('>')); + $this->assertHtml([ + ['li' => true], + ['a' => [ + 'href' => '/index?page=2' + ]], + ['i' => [ + 'class' => 'glyphicon glyphicon-chevron-right' + ]], + '/i', '/a', '/li' + ], $this->Paginator->next('i:chevron-right')); + } + +}; \ No newline at end of file From 498840707440aa2f696ec34ad0b8d2d5113883fd Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 13:12:52 +0100 Subject: [PATCH 053/312] Add _groupTemplate back to call $this->formatTemplate. --- src/View/Helper/BootstrapFormHelper.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 30a6821..ec2104a 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -393,6 +393,25 @@ 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 * From 892ea6ff8b9323893bc30da10219b504ffc85a06 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 13:16:08 +0100 Subject: [PATCH 054/312] Clean code. --- src/View/Helper/BootstrapFormHelper.php | 145 +++++++++++------------- 1 file changed, 68 insertions(+), 77 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index ec2104a..fc06e72 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -170,7 +170,7 @@ protected function _wrapTemplates ($templates, $callback, $params) { $result = call_user_func_array ($callback, $params) ; $this->templates ($oldTemplates) ; return $result ; - } + } /** * @@ -184,7 +184,7 @@ protected function _wrapTemplates ($templates, $callback, $params) { protected function _matchButton ($html) { return strpos($html, ' [] @@ -213,38 +213,38 @@ protected function _getDefaultTemplateVars (&$options) { } return $options; } - + public function formatTemplate($name, $data) { return $this->templater()->format($name, $this->_getDefaultTemplateVars($data)); } - + public function widget($name, array $data = []) { return parent::widget($name, $this->_getDefaultTemplateVars($data)); } - + protected function _inputContainerTemplate($options) { return parent::_inputContainerTemplate(array_merge($options, [ 'options' => $this->_getDefaultTemplateVars($options['options']) ])); } - + /** - * - * Create a Twitter Bootstrap like form. - * + * + * 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 - * + * - 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 - * + * - 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 - * + * **/ public function create($model = null, Array $options = array()) { if (isset($options['cols'])) { @@ -258,14 +258,14 @@ public function create($model = null, Array $options = array()) { unset($options['horizontal']); $this->inline = $this->_extractOption('inline', $options, false) ; unset($options['inline']) ; - if ($this->horizontal) { - $options = $this->addClass($options, 'form-horizontal') ; - } + if ($this->horizontal) { + $options = $this->addClass($options, 'form-horizontal') ; + } else if ($this->inline) { $options = $this->addClass($options, 'form-inline') ; } $options['role'] = 'form' ; - return parent::create($model, $options) ; + return parent::create($model, $options) ; } /** @@ -288,7 +288,7 @@ protected function _getColClass ($what, $offset = false) { } return implode(' ', $classes) ; } - + protected function _wrapInputGroup ($addonOrButtons) { if ($addonOrButtons) { if (is_string($addonOrButtons)) { @@ -586,31 +586,31 @@ protected function _createButtonOptions (array $options = array()) { } return $options ; } - + /** - * + * * Create & return a Twitter Like button. - * + * * ### New options: * * - bootstrap-type: Twitter bootstrap button type (primary, danger, info, etc.) * - bootstrap-size: Twitter bootstrap button size (mini, small, large) - * + * */ public function button($title, array $options = []) { return $this->_easyIcon ('parent::button', $title, $this->_createButtonOptions($options)); } - + /** - * + * * Create & return a Twitter Like button group. - * + * * @param $buttons The buttons in the group * @param $options Options for div method * * Extra options: * - vertical true/false - * + * **/ public function buttonGroup ($buttons, array $options = array()) { $vertical = $this->_extractOption('vertical', $options, false) ; @@ -621,74 +621,74 @@ public function buttonGroup ($buttons, array $options = array()) { } return $this->Html->tag('div', implode('', $buttons), $options) ; } - + /** - * + * * Create & return a Twitter Like button toolbar. - * + * * @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) ; } - + /** - * + * * Create & return a twitter bootstrap dropdown button. This function is a shortcut for: - * + * * $this->Form->$buttonGroup([ - * $this->Form->button($title, $options), + * $this->Form->button($title, $options), * $this->Html->dropdown($menu, []) * ]); - * + * * @param $title The text in the button * @param $menu HTML tags corresponding to menu options (which will be wrapped - * into
  • tag). To add separator, pass 'divider'. + * into
  • tag). To add separator, pass 'divider'. * @param $options Options for button - * + * */ public function dropdownButton ($title, array $menu = [], array $options = []) { - + $options['type'] = false ; $options['data-toggle'] = 'dropdown' ; $options = $this->addClass($options, "dropdown-toggle") ; - + return $this->buttonGroup([ $this->button($title.' ', $options), $this->bHtml->dropdown($menu) ]); } - + /** - * + * * Create & return a Twitter Like submit input. - * + * * New options: - * - bootstrap-type: Twitter bootstrap button type (primary, danger, info, etc.) - * - bootstrap-size: Twitter bootstrap button size (mini, small, large) - * + * - bootstrap-type: Twitter bootstrap button type (primary, danger, info, etc.) + * - bootstrap-size: Twitter bootstrap button size (mini, small, large) + * * Unusable options: div - * - **/ + * + **/ public function submit($caption = null, array $options = array()) { return parent::submit($caption, $this->_createButtonOptions($options)) ; } - + /** SPECIAL FORM **/ - + /** - * + * * Create a basic bootstrap search form. - * + * * @param $model The model of the form * @param $options The options that will be pass to the BootstrapForm::create method * @param $inpOpts The options that will be pass to the BootstrapForm::input method * @param $btnOpts The options that will be pass to the BootstrapForm::button method - * + * * Extra options: * - id ID of the input (and fieldname) * - label The input label (default false) @@ -696,10 +696,10 @@ public function submit($caption = null, array $options = array()) { * - button The search button text (default: "Search") * - _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 += [ 'id' => 'search', 'label' => false, @@ -708,47 +708,38 @@ public function searchForm ($model = null, $options = [], $inpOpts = [], $btnOpt '_input' => [], '_button' => [] ]; - + $options = $this->addClass($options, 'form-search'); - + $btnOpts += $options['_button']; unset($options['_button']); - + $inpOpts += $options['_input']; unset($options['_input']); - + $inpOpts += [ 'id' => $options['id'], 'placeholder' => $options['placeholder'], 'label' => $options['label'] ]; - + unset($options['id']) ; unset($options['label']) ; unset($options['placeholder']) ; - + $btnName = $options['button']; unset($options['button']) ; - + $inpOpts['append'] = $this->button($btnName, $btnOpts); - + $options['inline'] = (bool)$inpOpts['label']; - + $output = '' ; - + $output .= $this->create($model, $options) ; $output .= $this->input($inpOpts['id'], $inpOpts); $output .= $this->end() ; -/* array( - 'label' => $label, - 'placeholder' => $placeholder, - 'append' => array( - $this->button($button, ['style' => 'vertical-align: middle']) - ) - )) ; - $output .= $this->end() ; */ - return $output ; } From 9790ff760edff49d7f50ea8e554662c94273d92f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 08:41:28 +0100 Subject: [PATCH 055/312] Correct duplicate attribute bug. See issue #69. --- src/View/BootstrapStringTemplate.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/View/BootstrapStringTemplate.php b/src/View/BootstrapStringTemplate.php index d023bed..967fd41 100644 --- a/src/View/BootstrapStringTemplate.php +++ b/src/View/BootstrapStringTemplate.php @@ -51,7 +51,7 @@ public function format($name, array $data) if (substr($placeholder, 0, 6) == 'attrs.' && in_array('attrs.'.substr($placeholder, 6), $placeholders) && preg_match('#'.substr($placeholder, 6).'="([^"]+)"#', $data['attrs'], $matches) > 0) { - preg_replace('#'.substr($placeholder, 6).'="[^"]+"#', '', $data['attrs']); + $data['attrs'] = preg_replace('#'.substr($placeholder, 6).'="[^"]+"#', '', $data['attrs']); $data[$placeholder] = $matches[1]; } } From b4e82137a1117c5cc9eb7632bf241b36d8973f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 10:28:13 +0100 Subject: [PATCH 056/312] Add test files. --- composer.json | 8 + .../View/BootstrapStringTemplateTest.php | 79 +++ .../View/Helper/BootstrapFormHelperTest.php | 590 ++++++++++++++++++ .../View/Helper/BootstrapHtmlHelperTest.php | 236 +++++++ 4 files changed, 913 insertions(+) create mode 100644 tests/TestCase/View/BootstrapStringTemplateTest.php create mode 100644 tests/TestCase/View/Helper/BootstrapFormHelperTest.php create mode 100644 tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php diff --git a/composer.json b/composer.json index ba54db1..be0e66d 100644 --- a/composer.json +++ b/composer.json @@ -7,11 +7,19 @@ "require": { "cakephp/cakephp": "~3.0" }, + "require-dev": { + "phpunit/phpunit": "*" + } "autoload": { "psr-4": { "Bootstrap\\": "src" } }, + "autoload-dev": { + "psr-4": { + "Bootstrap\\Test\\": "tests" + } + }, "extra": { "installer-name": "Bootstrap" } diff --git a/tests/TestCase/View/BootstrapStringTemplateTest.php b/tests/TestCase/View/BootstrapStringTemplateTest.php new file mode 100644 index 0000000..e83ced9 --- /dev/null +++ b/tests/TestCase/View/BootstrapStringTemplateTest.php @@ -0,0 +1,79 @@ +templater = new BootstrapStringTemplate () ; + } + + + /** + * Tear Down + * + * @return void + */ + public function tearDown() + { + parent::tearDown(); + unset($this->templater); + } + + public function test () { + $this->templater->add([ + 'test_default' => '{{content}}

    ', + 'test_attrs_class' => '

    {{content}}

    ' + ]) ; + // Standard test + $result = $this->templater->format ('test_default', [ + 'attrs' => ' id="test-id" class="test-class"', + 'content' => 'Hello World!' + ]); + $this->assertHtml([ + ['p' => [ + 'id' => 'test-id', + 'class' => 'test-class' + ]], + 'Hello World!', + '/p' + ], $result) ; + // Test with class test + $result = $this->templater->format ('test_attrs_class', [ + 'attrs' => ' id="test-id" class="test-class-2"', + 'content' => 'Hello World!' + ]); + $this->assertHtml([ + ['p' => [ + 'id' => 'test-id', + 'class' => 'test-class test-class-2' + ]], + 'Hello World!', + '/p' + ], $result) ; + // Test with class test + $result = $this->templater->format ('test_attrs_class', [ + 'attrs' => 'class="test-class-2" id="test-id"', + 'content' => 'Hello World!' + ]); + $this->assertHtml([ + ['p' => [ + 'id' => 'test-id', + 'class' => 'test-class test-class-2' + ]], + 'Hello World!', + '/p' + ], $result) ; + } + +} \ No newline at end of file diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php new file mode 100644 index 0000000..fb61a3c --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -0,0 +1,590 @@ +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) ; + // type options + $result = $this->Form->create (null, ['type' => 'horizontal']) ; + $this->assertEquals($this->Form->horizontal, true) ; + $this->assertEquals($this->Form->inline, false) ; + $result = $this->Form->create (null, ['type' => 'inline']) ; + $this->assertEquals($this->Form->horizontal, false) ; + $this->assertEquals($this->Form->inline, true) ; + } + + 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 ([ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/fieldset' + ], $fieldName) ; + // Horizontal form + $this->_testInput ([ + ['fieldset' => [ + 'class' => ['form-group', 'row'] + ]], + ['label' => [ + 'class' => ['form-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', + '/fieldset' + ], $fieldName, [ + '_formOptions' => ['horizontal' => true] + ]) ; + } + + public function testInputText () { + $fieldName = 'field' ; + $this->_testInput ([ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['label' => [ + 'for' => $fieldName + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/fieldset' + ], $fieldName, ['type' => 'text']) ; + } + + public function testInputSelect () { + + } + + public function testInputRadio () { + $fieldName = 'color' ; + $options = [ + 'type' => 'radio', + 'options' => [ + 'red' => 'Red', + 'blue' => 'Blue', + 'green' => 'Green' + ] + ] ; + // Default + $expected = [ + ['label' => true], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => ['radio', 'c-inputs-stacked'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Inline + $options += [ + 'inline' => true + ] ; + $expected = [ + ['label' => true], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => ['radio'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Horizontal + Inline + $options += [ + '_formOptions' => ['horizontal' => true] + ] ; + $options['inline'] = false ; + $expected = [ + ['fieldset' => [ + 'class' => ['form-group', 'row'] + ]], + ['label' => [ + 'class' => ['form-control-label', 'col-md-2'] + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => 'col-md-10' + ]], + ['div' => [ + 'class' => ['radio', 'c-inputs-stacked'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $this->_testInput ($expected, $fieldName, $options) ; + // Horizontal + Inline + $options['inline'] = true ; + $expected = [ + ['fieldset' => [ + 'class' => ['form-group', 'row'] + ]], + ['label' => [ + 'class' => ['form-control-label', 'col-md-2'] + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', + ['div' => [ + 'class' => 'col-md-10' + ]], + ['div' => [ + 'class' => ['radio'] + ]], + ['input' => [ + 'type' => 'hidden', + 'name' => $fieldName, + 'value' => '', + 'class' => 'form-control' + ]] + ] ; + foreach ($options['options'] as $key => $value) { + $expected = array_merge($expected, [ + ['label' => [ + 'class' => ['c-input', 'c-radio'], + 'for' => $fieldName.'-'.$key + ]], + ['input' => [ + 'type' => 'radio', + 'name' => $fieldName, + 'value' => $key, + 'id' => $fieldName.'-'.$key + ]], + ['span' => [ + 'class' => 'c-indicator' + ]], + '/span', + $value, + '/label' + ]) ; + } + $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $this->_testInput ($expected, $fieldName, $options) ; + } + + public function testInputCheckbox () { + + } + + public function testInputGroup () { + $fieldName = 'field' ; + $options = [ + 'type' => 'text', + 'label' => false + ] ; + // Test with prepend addon + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '@', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => '@']) ; + // Test with append + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-addon' + ]], + '.00', + '/span', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['append' => '.00']) ; + // Test with append + prepend + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['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', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => '$', 'append' => '.00']) ; + // Test with prepend button + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'Go!', + '/button', + '/span', + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => $this->Form->button('Go!')]) ; + // Test with append button + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'Go!', + '/button', + '/span', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + ['append' => $this->Form->button('Go!')]) ; + // Test with append 2 button + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['span' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'Go!', + '/button', + ['button' => [ + 'class' => ['btn', 'btn-secondary'], + 'type' => 'submit' + ]], + 'GoGo!', + '/button', + '/span', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + [ + 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] + ]) ; + // Test with append dropdown + $html = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this->View); + $expected = [ + ['fieldset' => [ + 'class' => 'form-group' + ]], + ['div' => [ + 'class' => 'input-group' + ]], + ['input' => [ + 'type' => 'text', + 'class' => 'form-control', + 'name' => $fieldName, + 'id' => $fieldName + ]], + ['div' => [ + 'class' => 'input-group-btn' + ]], + ['button' => [ + 'data-toggle' => 'dropdown', + 'aria-haspopup' => 'true', + 'aria-expanded' => 'false', + 'id' => 'dropdownMenu1', + 'class' => ['dropdown-toggle', 'btn', 'btn-secondary'] + ]], + 'Action', + '/button', + ['div' => [ + 'class' => ['dropdown-menu'], + 'aria-labelledby' => 'dropdownMenu1' + ]], + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 1', '/a', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 2', '/a', + ['div' => [ + 'class' => 'dropdown-divider' + ]], '/div', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 3', '/a', + '/div', + '/div', + '/div', + '/fieldset' + ] ; + $this->_testInput ($expected, $fieldName, $options + [ + 'append' => $html->dropdown('Action', [ + $html->link('Link 1', '#'), + $html->link('Link 2', '#'), + 'divider', + $html->link('Link 3', '#') + ]) + ]); + } + +} \ No newline at end of file diff --git a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php new file mode 100644 index 0000000..0fd351e --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php @@ -0,0 +1,236 @@ +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 (FontAwesome) + $this->assertHtml ([ + ['i' => [ + 'class' => 'fa fa-'.$type + ]], + '/i' + ], $this->Html->icon($type)); + $this->assertHtml ([ + ['i' => [ + 'class' => $options['class'].' fa fa-'.$type, + 'id' => $options['id'] + ]], + '/i' + ], $this->Html->icon($type, $options)); + // Glyphicon icon + $this->assertHtml ([ + ['i' => [ + 'class' => 'glyphicon glyphicon-'.$type + ]], + '/i' + ], $this->Html->glIcon($type)); + $this->assertHtml ([ + ['i' => [ + 'class' => $options['class'].' glyphicon glyphicon-'.$type, + 'id' => $options['id'] + ]], + '/i' + ], $this->Html->glIcon($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 = [ + ['div' => [ + 'class' => 'dropdown' + ]], + ['button' => [ + 'data-toggle' => 'dropdown', + 'aria-haspopup' => 'true', + 'aria-expanded' => 'false', + 'id' => 'dropdownMenu1', + 'class' => 'dropdown-toggle btn btn-secondary' + ]], + 'Action', + '/button', + ['div' => [ + 'class' => 'dropdown-menu', + 'aria-labelledby' => 'dropdownMenu1' + ]], + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 1', '/a', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 2', '/a', + ['div' => [ + 'class' => 'dropdown-divider' + ]], '/div', + ['a' => [ + 'href' => '#', + 'class' => 'dropdown-item' + ]], 'Link 3', '/a', + '/div', + '/div' + ] ; + // Standard test + $this->assertHtml ($expected, $this->Html->dropdown($title, $menu)) ; + $menu = [ + ['Link 1', '#'], + ['Link 2', '#', ['class' => 'my-item-class', 'id' => 'my-item-id']], + 'divider', + ['Link 3', '#'] + ] ; + $options = [ + 'class' => 'my-dropdown', + '_button' => [ + 'tag' => 'a', + 'id' => 'my-dropdown-id' + ], + '_menu' => [ + 'class' => 'my-dropdown-menu', + '_item' => [ + 'class' => 'my-dropdown-item' + ] + ] + ] ; + $expected = [ + ['div' => [ + 'class' => $options['class'].' dropdown' + ]], + [$options['_button']['tag'] => [ + 'data-toggle' => 'dropdown', + 'aria-haspopup' => 'true', + 'aria-expanded' => 'false', + 'id' => $options['_button']['id'], + 'class' => 'dropdown-toggle btn btn-secondary' + ]], + 'Action', + '/'.$options['_button']['tag'], + ['div' => [ + 'class' => $options['_menu']['class'].' dropdown-menu', + 'aria-labelledby' => $options['_button']['id'] + ]], + ['a' => [ + 'href' => '#', + 'class' => $options['_menu']['_item']['class'].' dropdown-item' + ]], 'Link 1', '/a', + ['a' => [ + 'href' => '#', + 'class' => $menu[1][2]['class'].' dropdown-item', + 'id' => $menu[1][2]['id'] + ]], 'Link 2', '/a', + ['div' => [ + 'class' => $options['_menu']['_item']['class'].' dropdown-divider' + ]], '/div', + ['a' => [ + 'href' => '#', + 'class' => $options['_menu']['_item']['class'].' dropdown-item' + ]], 'Link 3', '/a', + '/div', + '/div' + ] ; + $this->assertHtml ($expected, $this->Html->dropdown($title, $menu, $options)) ; + + } + +} \ No newline at end of file From 66a3f8b84f565bd34f86d37530544f136dcce0d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 10:28:54 +0100 Subject: [PATCH 057/312] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index be0e66d..0c0ae10 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ }, "require-dev": { "phpunit/phpunit": "*" - } + }, "autoload": { "psr-4": { "Bootstrap\\": "src" From 09356d09224807bcefc020ea177cb40648ad411b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 12:40:56 +0100 Subject: [PATCH 058/312] Update FormHelper to use additional template variables for horizontal form instead of custom templates. Remove useless space from templates. --- src/View/BootstrapStringTemplate.php | 3 +- src/View/Helper/BootstrapFormHelper.php | 110 +++++++++++++----------- src/View/Helper/BootstrapHtmlHelper.php | 6 +- 3 files changed, 67 insertions(+), 52 deletions(-) diff --git a/src/View/BootstrapStringTemplate.php b/src/View/BootstrapStringTemplate.php index 967fd41..f442a81 100644 --- a/src/View/BootstrapStringTemplate.php +++ b/src/View/BootstrapStringTemplate.php @@ -52,9 +52,10 @@ public function format($name, array $data) && in_array('attrs.'.substr($placeholder, 6), $placeholders) && preg_match('#'.substr($placeholder, 6).'="([^"]+)"#', $data['attrs'], $matches) > 0) { $data['attrs'] = preg_replace('#'.substr($placeholder, 6).'="[^"]+"#', '', $data['attrs']); - $data[$placeholder] = $matches[1]; + $data[$placeholder] = ' '.trim($matches[1]); } } + $data['attrs'] = ' '.trim($data['attrs']); } if ($template === null) { return ''; diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 0645ca3..70ba64e 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -54,33 +54,33 @@ class BootstrapFormHelper extends FormHelper { 'checkbox' => '', 'checkboxFormGroup' => '{{label}}', 'checkboxWrapper' => '
    {{label}}
    ', - 'checkboxContainer' => '
    {{content}}
    ', + 'checkboxContainer' => '{{h_checkboxContainer_start}}
    {{content}}
    {{h_checkboxContainer_end}}', 'dateWidget' => '{{year}}{{month}}{{day}}{{hour}}{{minute}}{{second}}{{meridian}}', - 'error' => '{{content}}', + 'error' => '{{content}}', 'errorList' => '
      {{content}}
    ', 'errorItem' => '
  • {{text}}
  • ', 'file' => '', 'fieldset' => '{{content}}', 'formStart' => '', 'formEnd' => '', - 'formGroup' => '{{label}}{{prepend}}{{input}}{{append}}', + 'formGroup' => '{{label}}{{h_formGroup_start}}{{prepend}}{{input}}{{append}}{{h_formGroup_end}}', 'hiddenBlock' => '
    {{content}}
    ', - 'input' => '', + 'input' => '', 'inputSubmit' => '', 'inputContainer' => '
    {{content}}
    ', 'inputContainerError' => '
    {{content}}{{error}}
    ', - 'label' => '', + 'label' => '', 'nestingLabel' => '{{hidden}}{{input}}{{text}}', 'legend' => '{{text}}', 'option' => '', 'optgroup' => '{{content}}', - 'select' => '', - 'selectMultiple' => '', + 'select' => '', + 'selectMultiple' => '', 'radio' => '', 'radioWrapper' => '
    {{label}}
    ', - 'radioContainer' => '
    {{content}}
    ', - 'textarea' => '', - 'submitContainer' => '
    {{content}}
    ', + 'radioContainer' => '{{h_radioContainer_start}}
    {{content}}
    {{h_radioContainer_end}}', + 'textarea' => '', + 'submitContainer' => '
    {{h_submitContainer_start}}{{content}}{{h_submitContainer_end}}
    ', ] ]; @@ -129,8 +129,8 @@ class BootstrapFormHelper extends FormHelper { */ protected $_defaultColumnSize = [ 'label' => 2, - 'input' => 6, - 'error' => 4 + 'input' => 10, + 'error' => 0 ]; private $buttonTypes = ['default', 'primary', 'info', 'success', 'warning', 'danger', 'link'] ; @@ -185,27 +185,50 @@ protected function _wrapTemplates ($templates, $callback, $params) { protected function _matchButton ($html) { return strpos($html, 'horizontal and $this->inline). - * - **/ - protected function _setDefaultTemplates () { - $this->templates([ - 'formGroup' => '{{label}}'.($this->horizontal ? '
    ' : '').'{{prepend}}{{input}}{{append}}'.($this->horizontal ? '
    ' : ''), - 'checkboxContainer' => ($this->horizontal ? '
    ' : '') - .'
    {{content}}
    ' - .($this->horizontal ? '
    ' : ''), - 'radioContainer' => ($this->horizontal ? '
    ' : '') - .'{{content}}' - .($this->horizontal ? '
    ' : ''), - 'label' => '', - 'error' => '{{content}}', - 'submitContainer' => '
    '.($this->horizontal ? '
    ' : '').'{{content}}'.($this->horizontal ? '
    ' : '').'
    ', - ]) ; + + protected function _getDefaultTemplateVars (&$options) { + $options += [ + 'templateVars' => [] + ]; + $options['templateVars'] += [ + 's_labelClass' => 'control-label' + ]; + if ($this->horizontal) { + $options['templateVars'] += [ + 'h_formGroup_start' => '
    ', + 'h_formGroup_end' => '
    ', + 'h_checkboxContainer_start' => '
    ', + 'h_checkboxContainer_end' => '
    ', + 'h_radioContainer_start' => '
    ', + 'h_radioContainer_end' => '
    ', + 'h_submitContainer_start' => '
    ', + 'h_submitContainer_end' => '
    ', + 'h_labelClass' => ' '.$this->_getColClass('label'), + 'h_errorClass' => ' '.$this->_getColClass('error') + ]; + } + if ($this->inline) { + $options['templateVars']['s_labelClass'] = 'sr-only'; + } + return $options; } - + + public function formatTemplate($name, $data) { + return $this->templater()->format($name, $this->_getDefaultTemplateVars($data)); + } + + public function widget($name, array $data = []) { + return parent::widget($name, $this->_getDefaultTemplateVars($data)); + } + + protected function _inputContainerTemplate($options) { + return parent::_inputContainerTemplate(array_merge($options, [ + 'options' => $this->_getDefaultTemplateVars($options['options']) + ])); + } + /** * * Create a Twitter Bootstrap like form. @@ -248,27 +271,18 @@ public function create($model = null, Array $options = array()) { $options = $this->addClass($options, 'form-search') ; } $options['role'] = 'form' ; - $this->_setDefaultTemplates () ; return parent::create($model, $options) ; } - /** - * - * Switch horizontal mode on or off. - * - **/ - public function setHorizontal ($horizontal) { - $this->horizontal = $horizontal ; - $this->_setDefaultTemplates () ; - } - - /** * * 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'); + } if (isset($this->colSize[$what])) { return 'col-md-'.($offset ? 'offset-' : '').$this->colSize[$what] ; } @@ -331,7 +345,7 @@ protected function _wrap ($input, $prepend, $append) { * **/ public function input($fieldName, array $options = array()) { - + $options = $this->_parseOptions($fieldName, $options); $prepend = $this->_extractOption('prepend', $options, false) ; @@ -356,7 +370,7 @@ public function input($fieldName, array $options = array()) { $options['templates'] = [] ; if ($inline) { $options['templates'] = [ - 'label' => $this->templates('label').'
    ', + 'label' => $this->templates('label'), 'radioWrapper' => '{{label}}', 'nestingLabel' => '{{hidden}}{{input}}{{text}}' ] ; @@ -373,7 +387,7 @@ public function input($fieldName, array $options = array()) { 'prepend' => $prepend, 'append' => $append ]; - + return parent::input($fieldName, $options) ; } @@ -397,7 +411,7 @@ protected function _groupTemplate($options) { $data = array_merge($data, $options['options']['_data']); unset($options['options']['_data']); } - return $this->templater()->format($groupTemplate, $data); + return $this->formatTemplate($groupTemplate, $data); } /** diff --git a/src/View/Helper/BootstrapHtmlHelper.php b/src/View/Helper/BootstrapHtmlHelper.php index 5165335..c41e65b 100644 --- a/src/View/Helper/BootstrapHtmlHelper.php +++ b/src/View/Helper/BootstrapHtmlHelper.php @@ -251,7 +251,7 @@ public function progress ($widths, $options = []) { * Create & return a twitter bootstrap dropdown menu. * * @param $menu HTML tags corresponding to menu options (which will be wrapped - * into
  • tag). To add separator, pass 'divider'. + * into
  • tag). To add separator, pass 'divider'. * @param $options Attributes for the wrapper (change it with tag) * */ @@ -281,8 +281,8 @@ public function dropdown (array $menu = [], array $options = []) { } } $options = $this->addClass($options, 'dropdown-menu'); - $options['role'] = 'menu' ; - $options += ['tag' => 'div']; + $options['role'] = 'menu'; + $options += ['tag' => 'ul']; $tag = $options['tag']; unset($options['tag']); return $this->tag($tag, $output, $options) ; From f7073438e369e351bad5d6007dd52318af138260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 26 Jan 2016 12:43:23 +0100 Subject: [PATCH 059/312] Update test from B4 to work with B3. --- .../View/BootstrapStringTemplateTest.php | 2 +- .../View/Helper/BootstrapFormHelperTest.php | 238 ++++++++---------- .../View/Helper/BootstrapHtmlHelperTest.php | 131 +++------- 3 files changed, 138 insertions(+), 233 deletions(-) diff --git a/tests/TestCase/View/BootstrapStringTemplateTest.php b/tests/TestCase/View/BootstrapStringTemplateTest.php index e83ced9..4ed5cee 100644 --- a/tests/TestCase/View/BootstrapStringTemplateTest.php +++ b/tests/TestCase/View/BootstrapStringTemplateTest.php @@ -33,7 +33,7 @@ public function tearDown() public function test () { $this->templater->add([ 'test_default' => '{{content}}

    ', - 'test_attrs_class' => '

    {{content}}

    ' + 'test_attrs_class' => '

    {{content}}

    ' ]) ; // Standard test $result = $this->templater->format ('test_default', [ diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php index fb61a3c..0fc0194 100644 --- a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -7,20 +7,6 @@ use Cake\View\View; class BootstrapFormHelperTest extends TestCase { - - public function assertHtml($expected, $string, $fullDebug = false) { - array_walk ($expected, function (&$value) { - if (!is_array($value)) - return ; - $tag = array_keys ($value)[0] ; - if (!isset($value[$tag]['class'])) - return ; - if (is_string($value[$tag]['class'])) - $value[$tag]['class'] = explode(' ', $value[$tag]['class']) ; - $value[$tag]['class'] = 'preg:/'.implode(' ', array_map('trim', $value[$tag]['class'])).'\s*/' ; - }); - return parent::assertHtml ($expected, $string, $fullDebug) ; - } /** * Setup @@ -75,13 +61,6 @@ public function testCreate () { // Automatically return to non horizonal form $result = $this->Form->create () ; $this->assertEquals($this->Form->inline, false) ; - // type options - $result = $this->Form->create (null, ['type' => 'horizontal']) ; - $this->assertEquals($this->Form->horizontal, true) ; - $this->assertEquals($this->Form->inline, false) ; - $result = $this->Form->create (null, ['type' => 'inline']) ; - $this->assertEquals($this->Form->horizontal, false) ; - $this->assertEquals($this->Form->inline, true) ; } protected function _testInput ($expected, $fieldName, $options = []) { @@ -98,11 +77,12 @@ public function testInput () { $fieldName = 'field' ; // Standard form $this->_testInput ([ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['label' => [ - 'for' => $fieldName + 'class' => 'control-label', + 'for' => $fieldName ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', @@ -112,15 +92,15 @@ public function testInput () { 'name' => $fieldName, 'id' => $fieldName ]], - '/fieldset' + '/div' ], $fieldName) ; // Horizontal form $this->_testInput ([ - ['fieldset' => [ - 'class' => ['form-group', 'row'] + ['div' => [ + 'class' => 'form-group text' ]], ['label' => [ - 'class' => ['form-control-label', 'col-md-2'], + 'class' => 'control-label col-md-2', 'for' => $fieldName ]], \Cake\Utility\Inflector::humanize($fieldName), @@ -135,7 +115,7 @@ public function testInput () { 'id' => $fieldName ]], '/div', - '/fieldset' + '/div' ], $fieldName, [ '_formOptions' => ['horizontal' => true] ]) ; @@ -144,11 +124,12 @@ public function testInput () { public function testInputText () { $fieldName = 'field' ; $this->_testInput ([ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['label' => [ - 'for' => $fieldName + 'class' => 'control-label', + 'for' => $fieldName ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', @@ -158,7 +139,7 @@ public function testInputText () { 'name' => $fieldName, 'id' => $fieldName ]], - '/fieldset' + '/div' ], $fieldName, ['type' => 'text']) ; } @@ -178,12 +159,14 @@ public function testInputRadio () { ] ; // Default $expected = [ - ['label' => true], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', ['div' => [ - 'class' => ['radio', 'c-inputs-stacked'] + 'class' => 'form-group' + ]], + ['label' => [ + 'class' => 'control-label' ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -193,8 +176,10 @@ public function testInputRadio () { ] ; foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ + ['div' => [ + 'class' => 'radio' + ]], ['label' => [ - 'class' => ['c-input', 'c-radio'], 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -203,12 +188,9 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, - '/label' + '/label', + '/div' ]) ; } $expected = array_merge ($expected, ['/div']) ; @@ -218,12 +200,14 @@ public function testInputRadio () { 'inline' => true ] ; $expected = [ - ['label' => true], - \Cake\Utility\Inflector::humanize($fieldName), - '/label', ['div' => [ - 'class' => ['radio'] + 'class' => 'form-group' ]], + ['label' => [ + 'class' => 'control-label' + ]], + \Cake\Utility\Inflector::humanize($fieldName), + '/label', ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -234,7 +218,7 @@ public function testInputRadio () { foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ ['label' => [ - 'class' => ['c-input', 'c-radio'], + 'class' => 'radio-inline', 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -243,36 +227,29 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, '/label' ]) ; } $expected = array_merge ($expected, ['/div']) ; $this->_testInput ($expected, $fieldName, $options) ; - // Horizontal + Inline + // Horizontal $options += [ '_formOptions' => ['horizontal' => true] ] ; $options['inline'] = false ; $expected = [ - ['fieldset' => [ - 'class' => ['form-group', 'row'] + ['div' => [ + 'class' => 'form-group' ]], ['label' => [ - 'class' => ['form-control-label', 'col-md-2'] + 'class' => 'control-label col-md-2' ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', ['div' => [ 'class' => 'col-md-10' ]], - ['div' => [ - 'class' => ['radio', 'c-inputs-stacked'] - ]], ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -282,8 +259,10 @@ public function testInputRadio () { ] ; foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ + ['div' => [ + 'class' => 'radio' + ]], ['label' => [ - 'class' => ['c-input', 'c-radio'], 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -292,33 +271,27 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, - '/label' + '/label', + '/div' ]) ; } - $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $expected = array_merge ($expected, ['/div', '/div']) ; $this->_testInput ($expected, $fieldName, $options) ; // Horizontal + Inline $options['inline'] = true ; $expected = [ - ['fieldset' => [ - 'class' => ['form-group', 'row'] + ['div' => [ + 'class' => 'form-group' ]], ['label' => [ - 'class' => ['form-control-label', 'col-md-2'] + 'class' => 'control-label col-md-2' ]], \Cake\Utility\Inflector::humanize($fieldName), '/label', ['div' => [ 'class' => 'col-md-10' ]], - ['div' => [ - 'class' => ['radio'] - ]], ['input' => [ 'type' => 'hidden', 'name' => $fieldName, @@ -329,7 +302,7 @@ public function testInputRadio () { foreach ($options['options'] as $key => $value) { $expected = array_merge($expected, [ ['label' => [ - 'class' => ['c-input', 'c-radio'], + 'class' => 'radio-inline', 'for' => $fieldName.'-'.$key ]], ['input' => [ @@ -338,15 +311,11 @@ public function testInputRadio () { 'value' => $key, 'id' => $fieldName.'-'.$key ]], - ['span' => [ - 'class' => 'c-indicator' - ]], - '/span', $value, '/label' ]) ; } - $expected = array_merge ($expected, ['/div', '/div', '/fieldset']) ; + $expected = array_merge ($expected, ['/div', '/div']) ; $this->_testInput ($expected, $fieldName, $options) ; } @@ -362,8 +331,8 @@ public function testInputGroup () { ] ; // Test with prepend addon $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -380,13 +349,13 @@ public function testInputGroup () { 'id' => $fieldName ]], '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['prepend' => '@']) ; // Test with append $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -403,13 +372,13 @@ public function testInputGroup () { '.00', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['append' => '.00']) ; // Test with append + prepend $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -431,13 +400,13 @@ public function testInputGroup () { '.00', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['prepend' => '$', 'append' => '.00']) ; // Test with prepend button $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -446,7 +415,7 @@ public function testInputGroup () { 'class' => 'input-group-btn' ]], ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'Go!', @@ -459,13 +428,15 @@ public function testInputGroup () { 'id' => $fieldName ]], '/div', - '/fieldset' + '/div' ] ; + $this->_testInput ($expected, $fieldName, $options + ['prepend' => $this->Form->button('Go!')]) ; + // Test with append button $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -480,20 +451,20 @@ public function testInputGroup () { 'class' => 'input-group-btn' ]], ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'Go!', '/button', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + ['append' => $this->Form->button('Go!')]) ; // Test with append 2 button $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -508,29 +479,28 @@ public function testInputGroup () { 'class' => 'input-group-btn' ]], ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'Go!', '/button', ['button' => [ - 'class' => ['btn', 'btn-secondary'], + 'class' => 'btn btn-default', 'type' => 'submit' ]], 'GoGo!', '/button', '/span', '/div', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + [ 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] ]) ; // Test with append dropdown - $html = new \Bootstrap\View\Helper\BootstrapHtmlHelper ($this->View); $expected = [ - ['fieldset' => [ - 'class' => 'form-group' + ['div' => [ + 'class' => 'form-group text' ]], ['div' => [ 'class' => 'input-group' @@ -541,48 +511,48 @@ public function testInputGroup () { 'name' => $fieldName, 'id' => $fieldName ]], - ['div' => [ + ['span' => [ 'class' => 'input-group-btn' ]], + ['div' => [ + 'class' => 'btn-group' + ]], ['button' => [ - 'data-toggle' => 'dropdown', - 'aria-haspopup' => 'true', - 'aria-expanded' => 'false', - 'id' => 'dropdownMenu1', - 'class' => ['dropdown-toggle', 'btn', 'btn-secondary'] + 'data-toggle' => 'dropdown', + 'class' => 'dropdown-toggle btn btn-default' ]], 'Action', + ['span' => ['class' => 'caret']], '/span', '/button', - ['div' => [ - 'class' => ['dropdown-menu'], - 'aria-labelledby' => 'dropdownMenu1' - ]], - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 1', '/a', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 2', '/a', - ['div' => [ - 'class' => 'dropdown-divider' - ]], '/div', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 3', '/a', - '/div', + ['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', - '/fieldset' + '/div' ] ; $this->_testInput ($expected, $fieldName, $options + [ - 'append' => $html->dropdown('Action', [ - $html->link('Link 1', '#'), - $html->link('Link 2', '#'), + 'append' => $this->Form->dropdownButton('Action', [ + $this->Form->Html->link('Link 1', '#'), + $this->Form->Html->link('Link 2', '#'), 'divider', - $html->link('Link 3', '#') + $this->Form->Html->link('Link 3', '#') ]) ]); } diff --git a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php index 0fd351e..857dfd6 100644 --- a/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapHtmlHelperTest.php @@ -52,34 +52,34 @@ public function testIcon () { 'id' => 'my-home', 'class' => 'my-home-class' ] ; - // Default icon (FontAwesome) + // Default icon (Glyphicon) $this->assertHtml ([ ['i' => [ - 'class' => 'fa fa-'.$type + 'class' => 'glyphicon glyphicon-'.$type ]], '/i' ], $this->Html->icon($type)); $this->assertHtml ([ ['i' => [ - 'class' => $options['class'].' fa fa-'.$type, + 'class' => $options['class'].' glyphicon glyphicon-'.$type, 'id' => $options['id'] ]], '/i' ], $this->Html->icon($type, $options)); - // Glyphicon icon + // FontAwesome icon $this->assertHtml ([ ['i' => [ - 'class' => 'glyphicon glyphicon-'.$type + 'class' => 'fa fa-'.$type ]], '/i' - ], $this->Html->glIcon($type)); + ], $this->Html->faIcon($type)); $this->assertHtml ([ ['i' => [ - 'class' => $options['class'].' glyphicon glyphicon-'.$type, + 'class' => $options['class'].' fa fa-'.$type, 'id' => $options['id'] ]], '/i' - ], $this->Html->glIcon($type, $options)); + ], $this->Html->faIcon($type, $options)); } public function testLabel () { @@ -130,6 +130,14 @@ public function testLabel () { } public function testDropdown () { + /** + + **/ $title = 'Action' ; $menu = [ $this->Html->link('Link 1', '#'), @@ -138,98 +146,25 @@ public function testDropdown () { $this->Html->link('Link 3', '#') ] ; $expected = [ - ['div' => [ - 'class' => 'dropdown' - ]], - ['button' => [ - 'data-toggle' => 'dropdown', - 'aria-haspopup' => 'true', - 'aria-expanded' => 'false', - 'id' => 'dropdownMenu1', - 'class' => 'dropdown-toggle btn btn-secondary' - ]], - 'Action', - '/button', - ['div' => [ - 'class' => 'dropdown-menu', - 'aria-labelledby' => 'dropdownMenu1' - ]], - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 1', '/a', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 2', '/a', - ['div' => [ - 'class' => 'dropdown-divider' - ]], '/div', - ['a' => [ - 'href' => '#', - 'class' => 'dropdown-item' - ]], 'Link 3', '/a', - '/div', - '/div' - ] ; - // Standard test - $this->assertHtml ($expected, $this->Html->dropdown($title, $menu)) ; - $menu = [ - ['Link 1', '#'], - ['Link 2', '#', ['class' => 'my-item-class', 'id' => 'my-item-id']], - 'divider', - ['Link 3', '#'] - ] ; - $options = [ - 'class' => 'my-dropdown', - '_button' => [ - 'tag' => 'a', - 'id' => 'my-dropdown-id' - ], - '_menu' => [ - 'class' => 'my-dropdown-menu', - '_item' => [ - 'class' => 'my-dropdown-item' - ] - ] - ] ; - $expected = [ - ['div' => [ - 'class' => $options['class'].' dropdown' - ]], - [$options['_button']['tag'] => [ - 'data-toggle' => 'dropdown', - 'aria-haspopup' => 'true', - 'aria-expanded' => 'false', - 'id' => $options['_button']['id'], - 'class' => 'dropdown-toggle btn btn-secondary' - ]], - 'Action', - '/'.$options['_button']['tag'], - ['div' => [ - 'class' => $options['_menu']['class'].' dropdown-menu', - 'aria-labelledby' => $options['_button']['id'] + ['ul' => [ + 'role' => 'menu', + 'class' => 'dropdown-menu' ]], - ['a' => [ - 'href' => '#', - 'class' => $options['_menu']['_item']['class'].' dropdown-item' - ]], 'Link 1', '/a', - ['a' => [ - 'href' => '#', - 'class' => $menu[1][2]['class'].' dropdown-item', - 'id' => $menu[1][2]['id'] - ]], 'Link 2', '/a', - ['div' => [ - 'class' => $options['_menu']['_item']['class'].' dropdown-divider' - ]], '/div', - ['a' => [ - 'href' => '#', - 'class' => $options['_menu']['_item']['class'].' dropdown-item' - ]], 'Link 3', '/a', - '/div', - '/div' + ['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' ] ; - $this->assertHtml ($expected, $this->Html->dropdown($title, $menu, $options)) ; } From 69b977a05806aea2007930a111b778e1a8117398 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 28 Jan 2016 15:20:20 +0100 Subject: [PATCH 060/312] Add custom options to searchForm. --- src/View/Helper/BootstrapFormHelper.php | 63 ++++++++++++++++++------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 70ba64e..a3deafa 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -105,7 +105,6 @@ class BootstrapFormHelper extends FormHelper { public $horizontal = false ; public $inline = false ; - public $search = false ; public $colSize ; /** @@ -257,8 +256,6 @@ public function create($model = null, Array $options = array()) { } $this->horizontal = $this->_extractOption('horizontal', $options, false); unset($options['horizontal']); - $this->search = $this->_extractOption('search', $options, false) ; - unset($options['search']) ; $this->inline = $this->_extractOption('inline', $options, false) ; unset($options['inline']) ; if ($this->horizontal) { @@ -267,9 +264,6 @@ public function create($model = null, Array $options = array()) { else if ($this->inline) { $options = $this->addClass($options, 'form-inline') ; } - if ($this->search) { - $options = $this->addClass($options, 'form-search') ; - } $options['role'] = 'form' ; return parent::create($model, $options) ; } @@ -686,33 +680,68 @@ public function submit($caption = null, array $options = array()) { * * @param $model The model of the form * @param $options The options that will be pass to the BootstrapForm::create method + * @param $inpOpts The options that will be pass to the BootstrapForm::input method + * @param $btnOpts The options that will be pass to the BootstrapForm::button method * * Extra options: - * - label: The input label (default false) - * - placeholder: The input placeholder (default "Search... ") - * - button: The search button text (default: "Search") + * - id ID of the input (and fieldname) + * - label The input label (default false) + * - placeholder The input placeholder (default "Search... ") + * - button The search button text (default: "Search") + * - _input Options for the input (overrided by $inpOpts) + * - _button Options for the button (overrided by $btnOpts) * **/ - public function searchForm ($model = null, $options = array()) { + public function searchForm ($model = null, $options = [], $inpOpts = [], $btnOpts = []) { + + $options += [ + 'id' => 'search', + 'label' => false, + 'placeholder' => 'Search... ', + 'button' => 'Search', + '_input' => [], + '_button' => [] + ]; + + $options = $this->addClass($options, 'form-search'); + + $btnOpts += $options['_button']; + unset($options['_button']); + + $inpOpts += $options['_input']; + unset($options['_input']); - $label = $this->_extractOption('label', $options, false) ; + $inpOpts += [ + 'id' => $options['id'], + 'placeholder' => $options['placeholder'], + 'label' => $options['label'] + ]; + + unset($options['id']) ; unset($options['label']) ; - $placeholder = $this->_extractOption('placeholder', $options, 'Search... ') ; unset($options['placeholder']) ; - $button = $this->_extractOption('button', $options, 'Search') ; + + $btnName = $options['button']; unset($options['button']) ; + $inpOpts['append'] = $this->button($btnName, $btnOpts); + + $options['inline'] = (bool)$inpOpts['label']; + $output = '' ; - $output .= $this->create($model, array_merge(array('search' => true, 'inline' => (bool)$label), $options)) ; - $output .= $this->input('search', array( + $output .= $this->create($model, $options) ; + $output .= $this->input($inpOpts['id'], $inpOpts); + $output .= $this->end() ; + +/* array( 'label' => $label, 'placeholder' => $placeholder, 'append' => array( - $this->button($button, array('style' => 'vertical-align: middle')) + $this->button($button, ['style' => 'vertical-align: middle']) ) )) ; - $output .= $this->end() ; + $output .= $this->end() ; */ return $output ; } From 0376de594cf119b8975392183517ce1f1b391dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 28 Jan 2016 16:44:11 +0100 Subject: [PATCH 061/312] Add test for easyIcon. Add possibility to add text after or before easy icons. --- src/View/Helper/BootstrapTrait.php | 8 +- .../View/Helper/BootstrapTraitTest.php | 160 ++++++++++++++++++ 2 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 tests/TestCase/View/Helper/BootstrapTraitTest.php diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 4923dc2..5f398d4 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -146,10 +146,10 @@ protected function _makeIcon ($title, &$converted = false) { if (!$this->easyIcon) { return $title ; } - if (preg_match('#^i:([a-zA-Z0-9\\-_]+)$#', $title, $matches)) { - $converted = true ; - $title = $this->_View->Html->icon($matches[1]); - } + $title = preg_replace_callback('#(^|\s+)i:([a-zA-Z0-9\\-_]+)(\s+|$)#', function ($matches) { + return $matches[1].$this->_View->Html->icon($matches[2]).$matches[3]; + }, $title, -1, $count); + $converted = (bool)$count; return $title ; } diff --git a/tests/TestCase/View/Helper/BootstrapTraitTest.php b/tests/TestCase/View/Helper/BootstrapTraitTest.php new file mode 100644 index 0000000..c387ed5 --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapTraitTest.php @@ -0,0 +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. + } + +}; \ No newline at end of file From 416eb73c7a8a8d9b25333e609b38ed0b78946c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Thu, 4 Feb 2016 12:36:52 +0100 Subject: [PATCH 062/312] Update README.md --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c43120e..6125a65 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,18 @@ If you want the latest **Bootstrap 3** version of the plugin: ``` composer require holt59/cakephp3-bootstrap-helpers:dev-master ``` - -If you are updating from `holt59/cakephp3-bootstrap3-helpers`, do not forget to change the the following: - ```php // in config/bootstrap.php -Plugin::load('Bootstrap') ; // instead of Plugin::load('Bootstrap3') ; +Plugin::load('Bootstrap') ; ``` ```php // in your AppController public $helpers = [ 'Form' => [ - 'className' => 'Bootstrap.BootstrapForm' // instead of 'Bootstrap3.BootstrapForm' - ] + 'className' => 'Bootstrap.BootstrapForm' + ], + /* ... */ ] ; ``` --- From 1ed7e6d2d7a4a48303e8b68c2229722933aaf47b Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Feb 2016 21:25:47 +0100 Subject: [PATCH 063/312] Remove override of groupTemplate, see https://github.com/Holt59/cakephp3-bootstrap-helpers/issues/73 --- src/View/Helper/BootstrapFormHelper.php | 77 ++++++++++--------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index a3deafa..30a6821 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -29,7 +29,7 @@ class BootstrapFormHelper extends FormHelper { use BootstrapTrait ; public $helpers = [ - 'Html', + 'Html', 'Url', 'bHtml' => [ 'className' => 'Bootstrap.BootstrapHtml' @@ -102,7 +102,7 @@ class BootstrapFormHelper extends FormHelper { 'datetime' => ['Cake\View\Widget\DateTimeWidget', 'select'], '_default' => ['Cake\View\Widget\BasicWidget'], ]; - + public $horizontal = false ; public $inline = false ; public $colSize ; @@ -151,7 +151,7 @@ public function __construct (\Cake\View\View $view, array $config = []) { $this->_defaultConfig['templateClass'] = 'Bootstrap\View\BootstrapStringTemplate' ; parent::__construct($view, $config); } - + /** * * Replace the templates with the ones specified by newTemplates, call the specified function @@ -326,40 +326,48 @@ public function wrap ($input, $prepend, $append) { protected function _wrap ($input, $prepend, $append) { return '
    '.$prepend.$input.$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 - * + * - prepend: + * -> string: Add before the input + * -> 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 += [ + 'templateVars' => [], + 'prepend' => false, + 'append' => false, + 'help' => false, + 'inline' => false + ]; + $options = $this->_parseOptions($fieldName, $options); - $prepend = $this->_extractOption('prepend', $options, false) ; + $prepend = $options['prepend']; unset($options['prepend']); - $append = $this->_extractOption('append', $options, false) ; + $append = $options['append']; unset($options['append']); if ($prepend || $append) { $prepend = $this->prepend(null, $prepend); $append = $this->append(null, $append); } - $help = $this->_extractOption('help', $options, ''); + $help = $options['help']; unset($options['help']); if ($help) { $append .= '

    '.$help.'

    ' ; } - $inline = $this->_extractOption('inline', $options, '') ; + $inline = $options['inline']; unset ($options['inline']) ; - + if ($options['type'] === 'radio') { $options['templates'] = [] ; if ($inline) { @@ -377,35 +385,12 @@ public function input($fieldName, array $options = array()) { } } - $options['_data'] = [ + $options['templateVars'] += [ 'prepend' => $prepend, 'append' => $append ]; - - 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'; - } - $data = [ - 'input' => $options['input'], - 'label' => $options['label'], - 'error' => $options['error'] - ]; - if (isset($options['options']['_data'])) { - $data = array_merge($data, $options['options']['_data']); - unset($options['options']['_data']); - } - return $this->formatTemplate($groupTemplate, $data); + return parent::input($fieldName, $options) ; } /** @@ -424,7 +409,8 @@ protected function _getDatetimeTemplate ($fields, $options) { $inputs = [] ; foreach ($fields as $field => $in) { if ($this->_extractOption($field, $options, $in)) { - if ($field === 'timeFormat') $field = 'meridian' ; // Template uses "meridian" instead of timeFormat + if ($field === 'timeFormat') + $field = 'meridian' ; // Template uses "meridian" instead of timeFormat $inputs[$field] = '
    {{'.$field.'}}
    '; } } @@ -436,7 +422,8 @@ protected function _getDatetimeTemplate ($fields, $options) { $html .= $inputs[$v] ; } } - return str_replace('{{colsize}}', round(12 / count($inputs)), '
    '.$html.'
    ') ; + return str_replace('{{colsize}}', round(12 / count($inputs)), + '
    '.$html.'
    ') ; } /** From de9945ddd4b231fa35ba56f480edd1ae52bb793a Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:28:46 +0100 Subject: [PATCH 064/312] Create travis file. --- .travis.yml | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dcca55b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,74 @@ +language: php + +php: + - 5.5 + - 5.6 + - 7.0 + +sudo: false + +env: + matrix: + - DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' + - DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test' + - DB=sqlite db_dsn='sqlite:///:memory:' + global: + - DEFAULT=1 + +services: + - memcached + - redis-server + +cache: + directories: + - vendor + - $HOME/.composer/cache + +matrix: + fast_finish: true + + include: + - php: 7.0 + env: CODECOVERAGE=1 DEFAULT=0 + + - php: 7.0 + env: PHPCS=1 DEFAULT=0 + + - php: hhvm + env: HHVM=1 DB=sqlite db_dsn='sqlite:///:memory:' + + - php: hhvm + env: HHVM=1 DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' + + allow_failures: + - env: CODECOVERAGE=1 DEFAULT=0 + + - php: hhvm + +before_script: + - sh -c "if [ '$HHVM' != '1' ]; then phpenv config-rm xdebug.ini; fi" + + - composer self-update + - composer install --prefer-dist --no-interaction + + - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" + + - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi" + + - sh -c "if [ '$PHP' = '5.6' ]; then pecl install apc; fi" + - sh -c "if [ '$HHVM' = '1' ]; then composer require lorenzo/multiple-iterator=~1.0; fi" + + - phpenv rehash + - set +H + +script: + - sh -c "if [ '$DEFAULT' = '1' ]; then phpunit; fi" + + - sh -c "if [ '$PHPCS' = '1' ]; then vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests; fi" + + - sh -c "if [ '$CODECOVERAGE' = '1' ]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=clover.xml || true; fi" + - sh -c "if [ '$CODECOVERAGE' = '1' ]; then wget -O codecov.sh https://codecov.io/bash; fi" + - sh -c "if [ '$CODECOVERAGE' = '1' ]; then bash codecov.sh; fi" + +notifications: + email: true \ No newline at end of file From 6830af5d16aba65bc0d9e96bc4f4451694f9b477 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:34:12 +0100 Subject: [PATCH 065/312] Create PHP unit file. --- phpunit.xml.dist | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 phpunit.xml.dist diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..485f3af --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,18 @@ + + + + + + ./tests/ + + + + + + ./src/ + + + \ No newline at end of file From 45a763fd37ed22f26afc15a8ecf2a5c641ef5346 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:39:41 +0100 Subject: [PATCH 066/312] Update travis. --- composer.json | 48 ++++++++++++++++++------------------ phpunit.xml.dist | 3 ++- tests/bootstrap.php | 60 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 tests/bootstrap.php diff --git a/composer.json b/composer.json index 0c0ae10..1cc720d 100644 --- a/composer.json +++ b/composer.json @@ -1,26 +1,26 @@ { - "name": "holt59/cakephp3-bootstrap-helpers", - "description": "Bootstrap Helpers for CakePHP 3.0", - "keywords": ["CakePHP", "Bootstrap"], - "license": "Apache", - "type": "cakephp-plugin", - "require": { - "cakephp/cakephp": "~3.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "autoload": { - "psr-4": { - "Bootstrap\\": "src" - } - }, - "autoload-dev": { - "psr-4": { - "Bootstrap\\Test\\": "tests" - } - }, - "extra": { - "installer-name": "Bootstrap" - } + "name": "holt59/cakephp3-bootstrap-helpers", + "description": "Bootstrap Helpers for CakePHP 3.0", + "keywords": ["CakePHP", "Bootstrap"], + "license": "Apache", + "type": "cakephp-plugin", + "require": { + "cakephp/cakephp": "~3.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "autoload": { + "psr-4": { + "Bootstrap\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Bootstrap\\Test\\": "tests" + } + }, + "extra": { + "installer-name": "Bootstrap" + } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 485f3af..d0bb488 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,6 +2,7 @@ @@ -15,4 +16,4 @@ ./src/ - \ No newline at end of file + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..4e5d05b --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,60 @@ + 'App', + 'encoding' => 'UTF-8', + 'base' => false, + 'baseUrl' => false, + 'dir' => 'src', + 'webroot' => WEBROOT_DIR, + 'www_root' => WWW_ROOT, + 'fullBaseUrl' => 'http://localhost', + 'imageBaseUrl' => 'img/', + 'jsBaseUrl' => 'js/', + 'cssBaseUrl' => 'css/', + 'paths' => [ + 'plugins' => [dirname(APP) . DS . 'plugins' . DS], + 'templates' => [APP . 'Template' . DS] + ] +]); +Cache::config([ + '_cake_core_' => [ + 'engine' => 'File', + 'prefix' => 'cake_core_', + 'serialize' => true + ], + '_cake_model_' => [ + 'engine' => 'File', + 'prefix' => 'cake_model_', + 'serialize' => true + ] +]); +Plugin::load('Bootstrap', ['path' => ROOT]); From cdb58b2213811bd0874b6638ab04d9d7bac90d73 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:46:44 +0100 Subject: [PATCH 067/312] Update bootstrap.php --- tests/bootstrap.php | 83 ++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 58 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 4e5d05b..1001fee 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,60 +1,27 @@ 'App', - 'encoding' => 'UTF-8', - 'base' => false, - 'baseUrl' => false, - 'dir' => 'src', - 'webroot' => WEBROOT_DIR, - 'www_root' => WWW_ROOT, - 'fullBaseUrl' => 'http://localhost', - 'imageBaseUrl' => 'img/', - 'jsBaseUrl' => 'js/', - 'cssBaseUrl' => 'css/', - 'paths' => [ - 'plugins' => [dirname(APP) . DS . 'plugins' . DS], - 'templates' => [APP . 'Template' . DS] - ] -]); -Cache::config([ - '_cake_core_' => [ - 'engine' => 'File', - 'prefix' => 'cake_core_', - 'serialize' => true - ], - '_cake_model_' => [ - 'engine' => 'File', - 'prefix' => 'cake_model_', - 'serialize' => true - ] -]); -Plugin::load('Bootstrap', ['path' => ROOT]); +require $root . '/vendor/cakephp/cakephp/tests/bootstrap.php'; +\Cake\Core\Plugin::load('Bootstrap', ['path' => dirname(dirname(__FILE__)) . DS]); From 96da1fada0db3b1d4f0e9c5055666b43701e70bb Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 20:49:59 +0100 Subject: [PATCH 068/312] Update composer.json require-dev. --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 1cc720d..81af14a 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,7 @@ "cakephp/cakephp": "~3.0" }, "require-dev": { + "cakephp/cakephp": "~3.0", "phpunit/phpunit": "*" }, "autoload": { From 324b3682520f2b5a70455bf1d426758248a547d6 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:26:31 +0100 Subject: [PATCH 069/312] Clean travis config. --- composer.json | 1 - phpunit.xml.dist | 6 +-- tests/bootstrap.php | 78 ++++++++++++++++++++++---------- tests/test_app/config/routes.php | 21 +++++++++ 4 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 tests/test_app/config/routes.php diff --git a/composer.json b/composer.json index 81af14a..1cc720d 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,6 @@ "cakephp/cakephp": "~3.0" }, "require-dev": { - "cakephp/cakephp": "~3.0", "phpunit/phpunit": "*" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d0bb488..dd7760b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,8 +1,8 @@ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1001fee..052ed34 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,27 +1,55 @@ dirname(dirname(__FILE__)) . DS]); + +define('ROOT', dirname(__DIR__) . DS); +define('CAKE_CORE_INCLUDE_PATH', ROOT . 'vendor' . DS . 'cakephp' . DS . 'cakephp'); +define('CORE_PATH', ROOT . 'vendor' . DS . 'cakephp' . DS . 'cakephp' . DS); +define('CAKE', CORE_PATH . 'src' . DS); +define('TESTS', ROOT . 'tests'); +define('APP', ROOT . 'tests' . DS . 'test_app' . DS); +define('APP_DIR', 'app'); +define('WEBROOT_DIR', 'webroot'); +define('WWW_ROOT', dirname(APP) . DS . 'webroot' . DS); +define('TMP', sys_get_temp_dir() . DS); +define('CONFIG', APP . 'config' . DS); +define('CACHE', TMP); +define('LOGS', TMP); + +//@codingStandardsIgnoreStart +@mkdir(LOGS); +@mkdir(SESSIONS); +@mkdir(CACHE); +@mkdir(CACHE . 'views'); +@mkdir(CACHE . 'models'); + +require_once CORE_PATH . 'config/bootstrap.php'; +date_default_timezone_set('UTC'); +mb_internal_encoding('UTF-8'); + +Cache::config([ + '_cake_core_' => [ + 'engine' => 'File', + 'prefix' => 'cake_core_', + 'serialize' => true + ], + '_cake_model_' => [ + 'engine' => 'File', + 'prefix' => 'cake_model_', + 'serialize' => true + ] +]); + + +Plugin::load('Search', ['path' => ROOT]); diff --git a/tests/test_app/config/routes.php b/tests/test_app/config/routes.php new file mode 100644 index 0000000..d864631 --- /dev/null +++ b/tests/test_app/config/routes.php @@ -0,0 +1,21 @@ +connect('/', ['controller' => 'pages', 'action' => 'display', 'home']); + $routes->connect('/some_alias', ['controller' => 'tests_apps', 'action' => 'some_method']); + $routes->fallbacks(); +}); From 049ca1b6c0faa481e14153cbe49274d2522ff5a4 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:29:15 +0100 Subject: [PATCH 070/312] Set encoding in bootstrap.php (accept-charset test for BootstrapFormHelper). --- tests/bootstrap.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 052ed34..66491e2 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -38,6 +38,16 @@ date_default_timezone_set('UTC'); mb_internal_encoding('UTF-8'); +Configure::write('App', [ + 'namespace' => 'App', + 'encoding' => 'UTF-8', + 'base' => false, + 'baseUrl' => false, + 'dir' => APP_DIR, + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT +]); + Cache::config([ '_cake_core_' => [ 'engine' => 'File', @@ -51,5 +61,6 @@ ] ]); +ini_set('intl.default_locale', 'en_US'); Plugin::load('Search', ['path' => ROOT]); From d0a827c4495003b74821ca67a19693c595e2310c Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:38:49 +0100 Subject: [PATCH 071/312] Add require for codesniffer. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1cc720d..e674f01 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,8 @@ "cakephp/cakephp": "~3.0" }, "require-dev": { - "phpunit/phpunit": "*" + "phpunit/phpunit": "*", + "cakephp/cakephp-codesniffer": "dev-master" }, "autoload": { "psr-4": { From 9704a782f42b5e6ac60cd83613e5f5b3388385c2 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:44:57 +0100 Subject: [PATCH 072/312] Remove PHPCS test. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index dcca55b..86cfbba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ env: - DB=sqlite db_dsn='sqlite:///:memory:' global: - DEFAULT=1 + - PHPCS=0 services: - memcached From 9e7e34e432fb7aff25481ba07fed5566e25f9d0f Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:46:10 +0100 Subject: [PATCH 073/312] Remove PHPCS test (bis). --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 86cfbba..9cf77e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,6 @@ env: - DB=sqlite db_dsn='sqlite:///:memory:' global: - DEFAULT=1 - - PHPCS=0 services: - memcached @@ -33,7 +32,7 @@ matrix: env: CODECOVERAGE=1 DEFAULT=0 - php: 7.0 - env: PHPCS=1 DEFAULT=0 + env: PHPCS=0 DEFAULT=0 - php: hhvm env: HHVM=1 DB=sqlite db_dsn='sqlite:///:memory:' From 744aa7f0f820b49f9a6ae743199d34bf0416d443 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Thu, 18 Feb 2016 21:52:37 +0100 Subject: [PATCH 074/312] Add badge in README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6125a65..e6ed226 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ CakePHP 3.x Helpers for Bootstrap ================================= +[![GitHub license](https://img.shields.io/github/license/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](LICENSE) +[![Travis](https://img.shields.io/travis/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](https://travis-ci.org/Holt59/cakephp3-bootstrap-helpers) + CakePHP 3.0 Helpers to generate HTML with @Twitter Boostrap style: `Html`, `Form`, `Modal` and `Paginator` helpers available! How to... ? From 2d958c3ab20135df193860df89318b2310fa45ab Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 12:46:22 +0100 Subject: [PATCH 075/312] Update PaginatorHelper to deal with first and last options. --- src/View/Helper/BootstrapPaginatorHelper.php | 106 +++++++++++++------ 1 file changed, 74 insertions(+), 32 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 6a71b49..21feb4b 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -27,7 +27,7 @@ class BootstrapPaginatorHelper extends PaginatorHelper { use BootstrapTrait ; - + /** * Default config for this class * @@ -59,7 +59,7 @@ class BootstrapPaginatorHelper extends PaginatorHelper { 'last' => '
  • {{text}}
  • ', 'number' => '
  • {{text}}
  • ', 'current' => '
  • {{text}}
  • ', - 'ellipsis' => '
  • ...
  • ', + 'ellipsis' => '
  • ...
  • ', 'sort' => '{{text}}', 'sortAsc' => '{{text}}', 'sortDesc' => '{{text}}', @@ -67,26 +67,26 @@ class BootstrapPaginatorHelper extends PaginatorHelper { 'sortDescLocked' => '{{text}}', ] ]; - + /** - * + * * Get pagination link list. - * + * * @param $options Options for link element * * Extra options: * - size small/normal/large (default normal) - * + * **/ - public function numbers (array $options = array()) { - - $class = 'pagination' ; + public function numbers (array $options = []) { + + $options += [ + 'class' => '' + ]; + + $class = 'pagination '.$options['class'] ; + unset($options['class']); - if (isset($options['class'])) { - $class .= ' '.$options['class'] ; - unset($options['class']) ; - } - if (isset($options['size'])) { switch ($options['size']) { case 'small': @@ -98,46 +98,88 @@ public function numbers (array $options = array()) { } unset($options['size']) ; } - + if (!isset($options['before'])) { $options['before'] = '
      ' ; } - + if (!isset($options['after'])) { $options['after'] = '
    ' ; } + return parent::numbers($options); + + } + + /** + * Generates the numbers for the paginator numbers() method. + * + * @param \Cake\View\StringTemplate $templater StringTemplate instance. + * @param array $params Params from the numbers() method. + * @param array $options Options from the numbers() method. + * @return string Markup output. + */ + protected function _modulusNumbers($templater, $params, $options) { + + $options += [ + 'before' => '', + 'after' => '' + ]; + + $first = $prev = $next = $last = ''; + + /* Previous and Next buttons (addition from standard PaginatorHelper). */ + if (isset($options['prev'])) { $title = $options['prev'] ; $opts = [] ; - if (is_array($title)) { - $title = $title['title'] ; - unset ($options['prev']['title']) ; - $opts = $options['prev'] ; + if (is_array($title)) { + $title = $title['title'] ; + unset ($options['prev']['title']) ; + $opts = $options['prev'] ; } - $options['before'] .= $this->prev($title, $opts) ; + $prev = $this->prev($title, $opts) ; + unset($options['prev']); } if (isset($options['next'])) { $title = $options['next'] ; $opts = [] ; - if (is_array($title)) { - $title = $title['title']; - unset ($options['next']['title']); - $opts = $options['next']; + if (is_array($title)) { + $title = $title['title']; + unset ($options['next']['title']); + $opts = $options['next']; } - $options['after'] = $this->next($title, $opts).$options['after'] ; + $next = $this->next($title, $opts); + unset($options['next']); + } + + /* Custom First and Last. */ + + $ellipsis = $templater->format('ellipsis', []); + list($start, $end) = $this->_getNumbersStartAndEnd($params, $options); + + if (isset($options['last'])) { + $last = $this->_lastNumber($ellipsis, $params, $end, $options); } - - return parent::numbers ($options) ; + + if (isset($options['last'])) { + $first = $this->_firstNumber($ellipsis, $params, $start, $options); + } + + $options['before'] = $options['before'].$first.$prev; + $options['after'] = $next.$last.$options['after']; + $options['first'] = $options['last'] = false; + + return parent::_modulusNumbers($templater, $params, $options) ; } - public function prev ($title = '<< Previous', array $options = []) { - return $this->_easyIcon ('parent::prev', $title, $options); + public function prev ($title = '<< Previous', array $options = []) { + return $this->_easyIcon ('parent::prev', $title, $options); } - public function next ($title = 'Next >>', array $options = []) { - return $this->_easyIcon ('parent::next', $title, $options); + public function next ($title = 'Next >>', array $options = []) { + return $this->_easyIcon ('parent::next', $title, $options); } From 05ca25c16bb96b0ac197ab7ed98322d5e7b3d914 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 13:07:51 +0100 Subject: [PATCH 076/312] Start adding test for BootstrapPaginatorHelper. --- .../View/Helper/BootstrapFormHelperTest.php | 16 ++-- .../Helper/BootstrapPaginatorHelperTest.php | 88 +++++++++++++++++++ 2 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php diff --git a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php index 0fc0194..c464551 100644 --- a/tests/TestCase/View/Helper/BootstrapFormHelperTest.php +++ b/tests/TestCase/View/Helper/BootstrapFormHelperTest.php @@ -39,7 +39,7 @@ public function testCreate () { 'role' => 'form', 'action' ]] - ], $this->Form->create ()) ; + ], $this->Form->create ()) ; // Horizontal form $result = $this->Form->create (null, ['horizontal' => true]) ; $this->assertEquals($this->Form->horizontal, true) ; @@ -57,12 +57,12 @@ public function testCreate () { 'action', 'class' => 'form-inline' ]] - ], $result) ; + ], $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'])) { @@ -72,7 +72,7 @@ protected function _testInput ($expected, $fieldName, $options = []) { $this->Form->create (null, $formOptions) ; return $this->assertHtml ($expected, $this->Form->input ($fieldName, $options)) ; } - + public function testInput () { $fieldName = 'field' ; // Standard form @@ -142,7 +142,7 @@ public function testInputText () { '/div' ], $fieldName, ['type' => 'text']) ; } - + public function testInputSelect () { } @@ -320,9 +320,9 @@ public function testInputRadio () { } public function testInputCheckbox () { - + } - + public function testInputGroup () { $fieldName = 'field' ; $options = [ @@ -495,7 +495,7 @@ public function testInputGroup () { '/div' ] ; $this->_testInput ($expected, $fieldName, $options + [ - 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] + 'append' => [$this->Form->button('Go!'), $this->Form->button('GoGo!')] ]) ; // Test with append dropdown $expected = [ diff --git a/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php b/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php new file mode 100644 index 0000000..caf507e --- /dev/null +++ b/tests/TestCase/View/Helper/BootstrapPaginatorHelperTest.php @@ -0,0 +1,88 @@ +View = new View(); + $this->View->Html = new BootstrapHtmlHelper($this->View); + $this->Paginator = new BootstrapPaginatorHelper($this->View); + $this->Paginator->request = new Request(); + $this->Paginator->request->addParams([ + 'paging' => [ + 'Article' => [ + 'page' => 1, + 'current' => 9, + 'count' => 62, + 'prevPage' => false, + 'nextPage' => true, + 'pageCount' => 7, + 'sort' => null, + 'direction' => null, + 'limit' => null, + ] + ] + ]); + Configure::write('Routing.prefixes', []); + Router::reload(); + Router::connect('/:controller/:action/*'); + Router::connect('/:plugin/:controller/:action/*'); + } + + public function testPrev () { + $this->assertHtml([ + ['li' => [ + 'class' => 'disabled' + ]], + ['a' => true], '<', '/a', + '/li' + ], $this->Paginator->prev('<')); + $this->assertHtml([ + ['li' => [ + 'class' => 'disabled' + ]], + ['a' => true], + ['i' => [ + 'class' => 'glyphicon glyphicon-chevron-left' + ]], + '/i', '/a', '/li' + ], $this->Paginator->prev('i:chevron-left')); + } + + public function testNext () { + $this->assertHtml([ + ['li' => true], + ['a' => [ + 'href' => '/index?page=2' + ]], '>', '/a', + '/li' + ], $this->Paginator->next('>')); + $this->assertHtml([ + ['li' => true], + ['a' => [ + 'href' => '/index?page=2' + ]], + ['i' => [ + 'class' => 'glyphicon glyphicon-chevron-right' + ]], + '/i', '/a', '/li' + ], $this->Paginator->next('i:chevron-right')); + } + +}; \ No newline at end of file From 0ac00375adba125ef0c57fc4680a2fb9bb3abbd9 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 13:12:52 +0100 Subject: [PATCH 077/312] Add _groupTemplate back to call $this->formatTemplate. --- src/View/Helper/BootstrapFormHelper.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index 30a6821..ec2104a 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -393,6 +393,25 @@ 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 * From 6caf4b9e8b301cac5370f149401e34cd6d82af24 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 13:16:08 +0100 Subject: [PATCH 078/312] Clean code. --- src/View/Helper/BootstrapFormHelper.php | 145 +++++++++++------------- 1 file changed, 68 insertions(+), 77 deletions(-) diff --git a/src/View/Helper/BootstrapFormHelper.php b/src/View/Helper/BootstrapFormHelper.php index ec2104a..fc06e72 100644 --- a/src/View/Helper/BootstrapFormHelper.php +++ b/src/View/Helper/BootstrapFormHelper.php @@ -170,7 +170,7 @@ protected function _wrapTemplates ($templates, $callback, $params) { $result = call_user_func_array ($callback, $params) ; $this->templates ($oldTemplates) ; return $result ; - } + } /** * @@ -184,7 +184,7 @@ protected function _wrapTemplates ($templates, $callback, $params) { protected function _matchButton ($html) { return strpos($html, ' [] @@ -213,38 +213,38 @@ protected function _getDefaultTemplateVars (&$options) { } return $options; } - + public function formatTemplate($name, $data) { return $this->templater()->format($name, $this->_getDefaultTemplateVars($data)); } - + public function widget($name, array $data = []) { return parent::widget($name, $this->_getDefaultTemplateVars($data)); } - + protected function _inputContainerTemplate($options) { return parent::_inputContainerTemplate(array_merge($options, [ 'options' => $this->_getDefaultTemplateVars($options['options']) ])); } - + /** - * - * Create a Twitter Bootstrap like form. - * + * + * 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 - * + * - 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 - * + * - 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 - * + * **/ public function create($model = null, Array $options = array()) { if (isset($options['cols'])) { @@ -258,14 +258,14 @@ public function create($model = null, Array $options = array()) { unset($options['horizontal']); $this->inline = $this->_extractOption('inline', $options, false) ; unset($options['inline']) ; - if ($this->horizontal) { - $options = $this->addClass($options, 'form-horizontal') ; - } + if ($this->horizontal) { + $options = $this->addClass($options, 'form-horizontal') ; + } else if ($this->inline) { $options = $this->addClass($options, 'form-inline') ; } $options['role'] = 'form' ; - return parent::create($model, $options) ; + return parent::create($model, $options) ; } /** @@ -288,7 +288,7 @@ protected function _getColClass ($what, $offset = false) { } return implode(' ', $classes) ; } - + protected function _wrapInputGroup ($addonOrButtons) { if ($addonOrButtons) { if (is_string($addonOrButtons)) { @@ -586,31 +586,31 @@ protected function _createButtonOptions (array $options = array()) { } return $options ; } - + /** - * + * * Create & return a Twitter Like button. - * + * * ### New options: * * - bootstrap-type: Twitter bootstrap button type (primary, danger, info, etc.) * - bootstrap-size: Twitter bootstrap button size (mini, small, large) - * + * */ public function button($title, array $options = []) { return $this->_easyIcon ('parent::button', $title, $this->_createButtonOptions($options)); } - + /** - * + * * Create & return a Twitter Like button group. - * + * * @param $buttons The buttons in the group * @param $options Options for div method * * Extra options: * - vertical true/false - * + * **/ public function buttonGroup ($buttons, array $options = array()) { $vertical = $this->_extractOption('vertical', $options, false) ; @@ -621,74 +621,74 @@ public function buttonGroup ($buttons, array $options = array()) { } return $this->Html->tag('div', implode('', $buttons), $options) ; } - + /** - * + * * Create & return a Twitter Like button toolbar. - * + * * @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) ; } - + /** - * + * * Create & return a twitter bootstrap dropdown button. This function is a shortcut for: - * + * * $this->Form->$buttonGroup([ - * $this->Form->button($title, $options), + * $this->Form->button($title, $options), * $this->Html->dropdown($menu, []) * ]); - * + * * @param $title The text in the button * @param $menu HTML tags corresponding to menu options (which will be wrapped - * into
  • tag). To add separator, pass 'divider'. + * into
  • tag). To add separator, pass 'divider'. * @param $options Options for button - * + * */ public function dropdownButton ($title, array $menu = [], array $options = []) { - + $options['type'] = false ; $options['data-toggle'] = 'dropdown' ; $options = $this->addClass($options, "dropdown-toggle") ; - + return $this->buttonGroup([ $this->button($title.' ', $options), $this->bHtml->dropdown($menu) ]); } - + /** - * + * * Create & return a Twitter Like submit input. - * + * * New options: - * - bootstrap-type: Twitter bootstrap button type (primary, danger, info, etc.) - * - bootstrap-size: Twitter bootstrap button size (mini, small, large) - * + * - bootstrap-type: Twitter bootstrap button type (primary, danger, info, etc.) + * - bootstrap-size: Twitter bootstrap button size (mini, small, large) + * * Unusable options: div - * - **/ + * + **/ public function submit($caption = null, array $options = array()) { return parent::submit($caption, $this->_createButtonOptions($options)) ; } - + /** SPECIAL FORM **/ - + /** - * + * * Create a basic bootstrap search form. - * + * * @param $model The model of the form * @param $options The options that will be pass to the BootstrapForm::create method * @param $inpOpts The options that will be pass to the BootstrapForm::input method * @param $btnOpts The options that will be pass to the BootstrapForm::button method - * + * * Extra options: * - id ID of the input (and fieldname) * - label The input label (default false) @@ -696,10 +696,10 @@ public function submit($caption = null, array $options = array()) { * - button The search button text (default: "Search") * - _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 += [ 'id' => 'search', 'label' => false, @@ -708,47 +708,38 @@ public function searchForm ($model = null, $options = [], $inpOpts = [], $btnOpt '_input' => [], '_button' => [] ]; - + $options = $this->addClass($options, 'form-search'); - + $btnOpts += $options['_button']; unset($options['_button']); - + $inpOpts += $options['_input']; unset($options['_input']); - + $inpOpts += [ 'id' => $options['id'], 'placeholder' => $options['placeholder'], 'label' => $options['label'] ]; - + unset($options['id']) ; unset($options['label']) ; unset($options['placeholder']) ; - + $btnName = $options['button']; unset($options['button']) ; - + $inpOpts['append'] = $this->button($btnName, $btnOpts); - + $options['inline'] = (bool)$inpOpts['label']; - + $output = '' ; - + $output .= $this->create($model, $options) ; $output .= $this->input($inpOpts['id'], $inpOpts); $output .= $this->end() ; -/* array( - 'label' => $label, - 'placeholder' => $placeholder, - 'append' => array( - $this->button($button, ['style' => 'vertical-align: middle']) - ) - )) ; - $output .= $this->end() ; */ - return $output ; } From ea2361fee8b0a978aa0ea5b123cffe9a6e72b619 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 17:18:47 +0100 Subject: [PATCH 079/312] Allow panel with headings without title. --- src/View/Helper/BootstrapPanelHelper.php | 42 ++++++++++++++---------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 7c0de48..dfd77e4 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -59,7 +59,9 @@ public function create($title = null, $options = []) { $res = $this->Html->div($class, null, $options); if (is_string($title) && $title) { - $res .= $this->_createHeader($title, []) ; + $res .= $this->_createHeader($title, [ + 'title' => isset($options['title']) ? $options['title'] : true + ]) ; if (!$nobody) { $res .= $this->_startPart('body'); } @@ -69,7 +71,7 @@ public function create($title = null, $options = []) { /** * - * End a panel. If $title is not null, the ModalHelper::footer functions + * End a panel. If $title is not null, the PanelHelper::footer functions * is called with $title and $options arguments. * * @param string|null $buttons @@ -98,21 +100,21 @@ protected function _cleanCurrent () { } protected function _createHeader ($title, $options = [], $titleOptions = []) { - $options += [ - '_title' => [] - ]; - if (empty($titleOptions)) - $titleOptions = $options['_title']; - unset ($options['_title']); + if (empty($titleOptions)) { + $titleOptions = $options['title'] ; + } + unset ($options['title']); + if ($titleOptions !== false) { + if (!is_array($titleOptions)) { + $titleOptions = []; + } + $titleOptions = $this->addClass($titleOptions, 'panel-title'); + $title = $titleOptions ? $this->Html->tag('h3', $title, $titleOptions) : $title; + } $options = $this->addClass($options, 'panel-heading'); $class = $options['class']; unset ($options['class']); - $titleOptions = $this->addClass($titleOptions, 'panel-title'); - return $this->_cleanCurrent().$this->Html->div($class, - $this->Html->tag('h3', $title, - $titleOptions), - $options - ) ; + return $this->_cleanCurrent().$this->Html->div($class, $title, $options); } protected function _createBody ($text, $options = []) { @@ -149,7 +151,7 @@ protected function _endPart () { * 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 modal 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): @@ -157,6 +159,9 @@ protected function _endPart () { * **/ public function header ($info = null, $options = []) { + $options += [ + 'title' => true + ]; if (is_string($info)) { return $this->_createHeader($info, $options) ; } @@ -196,8 +201,11 @@ protected function _isAssociativeArray ($array) { * @param array $options Options for the footer div. * **/ - public function footer ($text = "", $options = []) { - return $this->_createFooter($text, $options) ; + public function footer ($text = null, $options = []) { + if (is_string($text)) { + return $this->_createFooter($text, $options) ; + } + return $this->_startPart('footer', is_array($text) ? $text : $options); } } From cada17834f30e6b824bfea20d76cbe2d22963667 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Sun, 21 Feb 2016 18:09:45 +0100 Subject: [PATCH 080/312] Allow panel with headings without title. --- src/View/Helper/BootstrapPanelHelper.php | 153 ++++++++++++++++++++--- 1 file changed, 133 insertions(+), 20 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index dfd77e4..109e5b1 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -32,6 +32,56 @@ class BootstrapPanelHelper extends Helper { public $current = NULL ; + /* Protected attributes used to generate ID for collapsible panels. */ + protected $_panelCount = 0; + 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; + + protected $_groupPanelCount = 0; + protected $_groupPanelOpen = 0; + + 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->_defaultCollapsible = $options['collapsible']; + $this->_autoCloseOnCreate = true; + $this->_lastPanelClosed = true; + $this->_groupPanelCount = -1; + $this->_groupPanelOpen = $options['open']; + $this->_groupId = $options['id']; + $options = $this->addClass($options, 'panel-group'); + $class = $options['class']; + unset($options['class'], $options['open'], $options['collapsible']); + return $this->Html->div($class, null, $options); + } + + public function endGroup() { + $this->_defaultCollapsible = false; + $out = ''; + if (!$this->_lastPanelClosed) { + $out = $this->end(); + } + return $out.''; + } + /** * * Create a Twitter Bootstrap like panel. @@ -45,28 +95,52 @@ class BootstrapPanelHelper extends Helper { public function create($title = null, $options = []) { if (is_array($title)) { - $options = $title ; + $options = $title; } - $nobody = $this->_extractOption('no-body', $options, false); - unset ($options['no-body']); - $type = $this->_extractOption('type', $options, 'default'); - unset ($options['type']); + $options += [ + 'no-body' => false, + 'type' => 'default', + 'collapsible' => $this->_defaultCollapsible + ]; + + $nobody = $options['no-body']; + $type = $options['type']; + $this->_collapsible = $options['collapsible']; + unset ($options['no-body'], $options['collapsible'], $options['type']); $options = $this->addClass($options, ['panel', 'panel-'.$type]); + + if ($this->_collapsible) { + $this->_headId = 'heading-'.($this->_panelCount); + $this->_bodyId = 'collapse-'.($this->_panelCount); + $this->_panelCount++; + } + $class = $options['class']; unset ($options['class']); - $res = $this->Html->div($class, null, $options); + $out = ''; + + if ($this->_autoCloseOnCreate && !$this->_lastPanelClosed) { + $out .= $this->end(); + } + $this->_lastPanelClosed = false; + + /* Increment panel counter for the current group. */ + $this->_groupPanelCount++; + + $out .= $this->Html->div($class, null, $options); if (is_string($title) && $title) { - $res .= $this->_createHeader($title, [ + $out .= $this->_createHeader($title, [ 'title' => isset($options['title']) ? $options['title'] : true ]) ; if (!$nobody) { - $res .= $this->_startPart('body'); + $out .= $this->_startPart('body'); } } - return $res ; + + return $out ; } /** @@ -79,11 +153,9 @@ public function create($title = null, $options = []) { * **/ public function end ($title = null, $options = []) { + $this->_lastPanelClosed = true; $res = '' ; - if ($this->current != null) { - $this->current = null ; - $res .= $this->_endPart(); - } + $res .= $this->_cleanCurrent(); if ($title !== null) { $res .= $this->footer($title, $options) ; } @@ -92,11 +164,12 @@ public function end ($title = null, $options = []) { } protected function _cleanCurrent () { + $res = ''; if ($this->current) { + $res = $this->_endPart(); $this->current = NULL ; - return $this->_endPart(); } - return '' ; + return $res; } protected function _createHeader ($title, $options = [], $titleOptions = []) { @@ -104,16 +177,32 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { $titleOptions = $options['title'] ; } unset ($options['title']); + $options = $this->addClass($options, 'panel-heading'); + $class = $options['class']; + unset ($options['class']); + 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 + ]); + } if ($titleOptions !== false) { if (!is_array($titleOptions)) { $titleOptions = []; } + $titleOptions += ['tag' => 'h4']; $titleOptions = $this->addClass($titleOptions, 'panel-title'); - $title = $titleOptions ? $this->Html->tag('h3', $title, $titleOptions) : $title; + $tag = $titleOptions['tag']; + unset($titleOptions['tag']); + $title = $titleOptions ? $this->Html->tag($tag, $title, $titleOptions) : $title; } - $options = $this->addClass($options, 'panel-heading'); - $class = $options['class']; - unset ($options['class']); return $this->_cleanCurrent().$this->Html->div($class, $title, $options); } @@ -121,7 +210,18 @@ protected function _createBody ($text, $options = []) { $options = $this->addClass($options, 'panel-body'); $class = $options['class']; unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; + $body = $this->Html->div($class, $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, $body, [ + 'role' => 'tabpanel', + 'aria-labelledby' => $this->_headId, + 'id' => $this->_bodyId + ]); + } + return $this->_cleanCurrent().$body ; } protected function _createFooter ($text = null, $options = []) { @@ -137,12 +237,25 @@ protected function _startPart ($part, $options = []) { $res = $this->_endPart () ; } $this->current = $part ; + if ($this->_collapsible && $this->current == 'body') { + $open = ((is_int($this->_groupPanelOpen) + && $this->_groupPanelOpen === $this->_groupPanelCount) + || $this->_groupPanelOpen === $this->_bodyId) ? ' in' : ''; + $res .= $this->Html->div('panel-collapse collapse'.$open, null, [ + 'role' => 'tabpanel', + 'aria-labelledby' => $this->_headId, + 'id' => $this->_bodyId + ]); + } return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', $options, ''), null, $options) ; } protected function _endPart () { + if ($this->_collapsible && $this->current == 'body') { + return ''; + } return '' ; } From 3eefbb45c3a4dc70e7f50a917eef8c3378878e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 22 Feb 2016 15:22:21 +0100 Subject: [PATCH 081/312] Correct changes for 'last' and 'first' options to Paginator::numbers. --- src/View/Helper/BootstrapPaginatorHelper.php | 75 ++++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 21feb4b..2254425 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -80,51 +80,40 @@ class BootstrapPaginatorHelper extends PaginatorHelper { **/ public function numbers (array $options = []) { - $options += [ - 'class' => '' + $defaults = [ + 'before' => null, 'after' => null, 'model' => $this->defaultModel(), + 'modulus' => 8, 'first' => null, 'last' => null, 'url' => [], + 'class' => '', 'size' => false ]; + $options += $defaults; - $class = 'pagination '.$options['class'] ; - unset($options['class']); + $options = $this->addClass($options, 'pagination'); - if (isset($options['size'])) { - switch ($options['size']) { - case 'small': - $class .= ' pagination-sm' ; - break ; - case 'large': - $class .= ' pagination-lg' ; - break ; - } - unset($options['size']) ; + switch ($options['size']) { + case 'small': + $options = $this->addClass($options, 'pagination-sm') ; + break ; + case 'large': + $options = $this->addClass($options, 'pagination-lg') ; + break ; } + unset($options['size']) ; - if (!isset($options['before'])) { - $options['before'] = '
      ' ; - } + $options['before'] .= $this->Html->tag('ul', null, ['class' => $options['class']]); + $options['after'] = '
    '.$options['after'] ; + unset($options['class']); - if (!isset($options['after'])) { - $options['after'] = '' ; + $params = (array)$this->params($options['model']) + ['page' => 1]; + if ($params['pageCount'] <= 1) { + return false; } - return parent::numbers($options); - - } - - /** - * Generates the numbers for the paginator numbers() method. - * - * @param \Cake\View\StringTemplate $templater StringTemplate instance. - * @param array $params Params from the numbers() method. - * @param array $options Options from the numbers() method. - * @return string Markup output. - */ - protected function _modulusNumbers($templater, $params, $options) { - - $options += [ - 'before' => '', - 'after' => '' - ]; + $templater = $this->templater(); + if (isset($options['templates'])) { + $templater->push(); + $method = is_string($options['templates']) ? 'load' : 'add'; + $templater->{$method}($options['templates']); + } $first = $prev = $next = $last = ''; @@ -171,7 +160,17 @@ protected function _modulusNumbers($templater, $params, $options) { $options['after'] = $next.$last.$options['after']; $options['first'] = $options['last'] = false; - return parent::_modulusNumbers($templater, $params, $options) ; + if ($options['modulus'] !== false && $params['pageCount'] > $options['modulus']) { + $out = $this->_modulusNumbers($templater, $params, $options); + } else { + $out = $this->_numbers($templater, $params, $options); + } + + if (isset($options['templates'])) { + $templater->pop(); + } + + return $out; } public function prev ($title = '<< Previous', array $options = []) { From 51bb76cf09359553bd0e27c5004b5befa15f6548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Mon, 22 Feb 2016 16:32:36 +0100 Subject: [PATCH 082/312] Add easyIcon for last and prev. Add option to remove ellipsis. --- src/View/Helper/BootstrapPaginatorHelper.php | 30 +++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 2254425..23ea802 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -83,7 +83,8 @@ public function numbers (array $options = []) { $defaults = [ 'before' => null, 'after' => null, 'model' => $this->defaultModel(), 'modulus' => 8, 'first' => null, 'last' => null, 'url' => [], - 'class' => '', 'size' => false + 'prev' => null, 'next' => null, + 'ellipsis' => true, 'class' => '', 'size' => false ]; $options += $defaults; @@ -119,7 +120,7 @@ public function numbers (array $options = []) { /* Previous and Next buttons (addition from standard PaginatorHelper). */ - if (isset($options['prev'])) { + if ($options['prev']) { $title = $options['prev'] ; $opts = [] ; if (is_array($title)) { @@ -128,10 +129,10 @@ public function numbers (array $options = []) { $opts = $options['prev'] ; } $prev = $this->prev($title, $opts) ; - unset($options['prev']); } + unset($options['prev']); - if (isset($options['next'])) { + if ($options['next']) { $title = $options['next'] ; $opts = [] ; if (is_array($title)) { @@ -140,19 +141,21 @@ public function numbers (array $options = []) { $opts = $options['next']; } $next = $this->next($title, $opts); - unset($options['next']); } + unset($options['next']); /* Custom First and Last. */ - $ellipsis = $templater->format('ellipsis', []); + $ellipsis = $options['ellipsis']; + unset($options['ellipsis']); + $ellipsis = $ellipsis ? $templater->format('ellipsis', []) : ""; list($start, $end) = $this->_getNumbersStartAndEnd($params, $options); - if (isset($options['last'])) { + if ($options['last']) { $last = $this->_lastNumber($ellipsis, $params, $end, $options); } - if (isset($options['last'])) { + if ($options['last']) { $first = $this->_firstNumber($ellipsis, $params, $start, $options); } @@ -174,13 +177,20 @@ public function numbers (array $options = []) { } public function prev ($title = '<< Previous', array $options = []) { - return $this->_easyIcon ('parent::prev', $title, $options); + return $this->_easyIcon('parent::prev', $title, $options); } public function next ($title = 'Next >>', array $options = []) { - return $this->_easyIcon ('parent::next', $title, $options); + return $this->_easyIcon('parent::next', $title, $options); + } + /* + public function first($first = '<< first', array $options = []) { + return $this->_easyIcon('parent::first', first, $options); } + public function last($last = 'last >>', array $options = []) { + return $this->_easyIcon('parent::last', $last, $options); + }*/ } From a0d4ab7cb793d3652b5fa60e0c22be3b9cd65b67 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 22 Feb 2016 17:01:10 +0100 Subject: [PATCH 083/312] Add easyIcon for last and first. --- src/View/Helper/BootstrapPaginatorHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index 23ea802..bb15ce7 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -183,14 +183,14 @@ public function prev ($title = '<< Previous', array $options = []) { public function next ($title = 'Next >>', array $options = []) { return $this->_easyIcon('parent::next', $title, $options); } - /* + public function first($first = '<< first', array $options = []) { return $this->_easyIcon('parent::first', first, $options); } public function last($last = 'last >>', array $options = []) { return $this->_easyIcon('parent::last', $last, $options); - }*/ + } } From c82194a95ce019c48d344db598a21dbc3fb90bb8 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 22 Feb 2016 17:07:07 +0100 Subject: [PATCH 084/312] Correct bug in easyIcon when title is not a string. --- src/View/Helper/BootstrapPaginatorHelper.php | 2 +- src/View/Helper/BootstrapTrait.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index bb15ce7..a00b670 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -185,7 +185,7 @@ public function next ($title = 'Next >>', array $options = []) { } public function first($first = '<< first', array $options = []) { - return $this->_easyIcon('parent::first', first, $options); + return $this->_easyIcon('parent::first', $first, $options); } public function last($last = 'last >>', array $options = []) { diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 5f398d4..3abd81e 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -143,7 +143,7 @@ protected function _extractType ($options, $key = 'type', $default = 'info', **/ protected function _makeIcon ($title, &$converted = false) { $converted = false ; - if (!$this->easyIcon) { + if (!$this->easyIcon || !is_string($title)) { return $title ; } $title = preg_replace_callback('#(^|\s+)i:([a-zA-Z0-9\\-_]+)(\s+|$)#', function ($matches) { From ab07e948c717ae19804ea66718eb07c34cecacd8 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 22 Feb 2016 17:26:27 +0100 Subject: [PATCH 085/312] Update first and last option position regarding prev and next and ellipsis. --- src/View/Helper/BootstrapPaginatorHelper.php | 23 +++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php index a00b670..275983e 100644 --- a/src/View/Helper/BootstrapPaginatorHelper.php +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -83,8 +83,7 @@ public function numbers (array $options = []) { $defaults = [ 'before' => null, 'after' => null, 'model' => $this->defaultModel(), 'modulus' => 8, 'first' => null, 'last' => null, 'url' => [], - 'prev' => null, 'next' => null, - 'ellipsis' => true, 'class' => '', 'size' => false + 'prev' => null, 'next' => null, 'class' => '', 'size' => false ]; $options += $defaults; @@ -146,21 +145,28 @@ public function numbers (array $options = []) { /* Custom First and Last. */ - $ellipsis = $options['ellipsis']; - unset($options['ellipsis']); - $ellipsis = $ellipsis ? $templater->format('ellipsis', []) : ""; list($start, $end) = $this->_getNumbersStartAndEnd($params, $options); if ($options['last']) { + $ellipsis = isset($options['ellipsis']) ? + $options['ellipsis'] : is_int($options['last']); + $ellipsis = $ellipsis ? $templater->format('ellipsis', []) : ''; $last = $this->_lastNumber($ellipsis, $params, $end, $options); } - if ($options['last']) { + if ($options['first']) { + $ellipsis = isset($options['ellipsis']) ? + $options['ellipsis'] : is_int($options['first']); + $ellipsis = $ellipsis ? $templater->format('ellipsis', []) : ''; $first = $this->_firstNumber($ellipsis, $params, $start, $options); } - $options['before'] = $options['before'].$first.$prev; - $options['after'] = $next.$last.$options['after']; + unset($options['ellipsis']); + + $before = is_int($options['first']) ? $prev.$first : $first.$prev; + $after = is_int($options['last']) ? $last.$next : $next.$last; + $options['before'] = $options['before'].$before;; + $options['after'] = $after.$options['after']; $options['first'] = $options['last'] = false; if ($options['modulus'] !== false && $params['pageCount'] > $options['modulus']) { @@ -173,6 +179,7 @@ public function numbers (array $options = []) { $templater->pop(); } + return $out; } From 1afc168f876977bcc4a87bd4c44ce1b5dbf8be9d Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 22 Feb 2016 20:06:32 +0100 Subject: [PATCH 086/312] Change license from Apache 2.0 to MIT. --- LICENSE | 222 ++++++------------------------------------------------ README.md | 18 +++-- 2 files changed, 34 insertions(+), 206 deletions(-) diff --git a/LICENSE b/LICENSE index 5c304d1..22a9deb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +The MIT License (MIT) + +Copyright (c) 2013-2016, Mikaël Capelle. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index e6ed226..47d45bd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ CakePHP 3.x Helpers for Bootstrap ================================= -[![GitHub license](https://img.shields.io/github/license/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](LICENSE) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) [![Travis](https://img.shields.io/travis/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](https://travis-ci.org/Holt59/cakephp3-bootstrap-helpers) CakePHP 3.0 Helpers to generate HTML with @Twitter Boostrap style: `Html`, `Form`, `Modal` and `Paginator` helpers available! @@ -55,10 +55,18 @@ Non-exhaustive list of projects using these helpers, if you want to be in this l Copyright and license ===================== -Copyright 2013 Mikaël Capelle. +The MIT License (MIT) -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: +Copyright (c) 2013-2016, Mikaël Capelle. -http://www.apache.org/licenses/LICENSE-2.0 +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +See [LICENSE](LICENSE). From beaf4ea33ed9af9f79e3bbdc605e3684c0874ef2 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 22 Feb 2016 20:31:00 +0100 Subject: [PATCH 087/312] Correct minor bug and clean code. --- src/View/Helper/BootstrapModalHelper.php | 124 +++++++++++++---------- src/View/Helper/BootstrapPanelHelper.php | 99 +++++++----------- src/View/Helper/BootstrapTrait.php | 44 ++++---- 3 files changed, 133 insertions(+), 134 deletions(-) diff --git a/src/View/Helper/BootstrapModalHelper.php b/src/View/Helper/BootstrapModalHelper.php index 43af18c..cda5556 100644 --- a/src/View/Helper/BootstrapModalHelper.php +++ b/src/View/Helper/BootstrapModalHelper.php @@ -31,13 +31,13 @@ class BootstrapModalHelper extends Helper { public $helpers = ['Html']; public $current = NULL ; - + /** - * - * Create a Twitter Bootstrap like modal. + * + * Create a Twitter Bootstrap like modal. * * @param array|string $title If array, works as $options, otherwize used as the modal title. - * @param array $options Options for the main div of the modal. + * @param array $options Options for the main div of the modal. * * Extra options (useless if $title not specified) : * - close: Add close buttons to header (default true) @@ -60,24 +60,26 @@ public function create($title = null, $options = array()) { $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'; - break; - case 'sm': - case 'small': - case 'modal-sm': - $size = 'modal-sm'; - break; - default: - $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'); + switch($options['size']) { + case 'lg': + case 'large': + case 'modal-lg': + $size = 'modal-lg'; + break; + case 'sm': + case 'small': + case 'modal-sm': + $size = 'modal-sm'; + break; + default: + $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'); if (is_string($title) && $title) { $res .= $this->_createHeader($title, array('close' => $close)) ; if (!$nobody) { @@ -85,15 +87,16 @@ public function create($title = null, $options = array()) { } } return $res ; - } - + } + /** - * - * End a modal. If $buttons is not null, the ModalHelper::footer functions is called with $buttons and $options arguments. + * + * End a modal. If $buttons is not null, the ModalHelper::footer functions is called + * with $buttons and $options arguments. * * @param array|null $buttons * @param array $options - * + * **/ public function end ($buttons = NULL, $options = array()) { $res = '' ; @@ -105,7 +108,7 @@ public function end ($buttons = NULL, $options = array()) { $res .= $this->footer($buttons, $options) ; } $res .= '' ; - return $res ; + return $res ; } protected function _cleanCurrent () { @@ -120,19 +123,27 @@ protected function _createHeader ($title, $options = array()) { $close = $this->_extractOption('close', $options, true); unset($options['close']) ; if ($close) { - $button = '' ; + $button = '' ; } else { $button = '' ; } - return $this->_cleanCurrent().$this->Html->div('modal-header '.$this->_extractOption('class', $options, ''), - $button.$this->Html->tag('h4', $title, array('class' => 'modal-title', 'id' => $this->currentId ? $this->currentId.'Label' : false)), - $options - ) ; + 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 _createBody ($text, $options = array()) { - return $this->_cleanCurrent().$this->Html->div('modal-body '.$this->_extractOption('class', $options, ''), $text, $options) ; + return $this->_cleanCurrent() + .$this->Html->div('modal-body '.$this->_extractOption('class', $options, ''), + $text, $options) ; } protected function _createFooter ($buttons = NULL, $options = array()) { @@ -140,22 +151,28 @@ protected function _createFooter ($buttons = NULL, $options = array()) { $close = $this->_extractOption('close', $options, true); unset($options['close']) ; if ($close) { - $buttons = '' ; + $buttons = '' ; } else { $buttons = '' ; } } - return $this->_cleanCurrent().$this->Html->div('modal-footer '.$this->_extractOption('class', $options, ''), $buttons, $options) ; + return $this->_cleanCurrent() + .$this->Html->div('modal-footer '.$this->_extractOption('class', $options, ''), + $buttons, $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) ; + return $res + .$this->Html->div('modal-'.$part.' '.$this->_extractOption('class', $options, ''), + null, $options) ; } protected function _endPart () { @@ -164,11 +181,12 @@ protected function _endPart () { /** * - * Create / Start the header. If $info is specified as a string, create and return the whole header, otherwize only open the header. - * + * 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 modal 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). * @@ -182,11 +200,12 @@ public function header ($info = NULL, $options = array()) { /** * - * Create / Start the body. If $info is not null, it is used as the body content, otherwize start the body div. - * + * 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 = array()) { @@ -199,17 +218,15 @@ public function body ($info = NULL, $options = array()) { return $this->_startPart('body', is_array($info) ? $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 buttons. - * - * @param array|string $buttons If string, use as the footer content, if list, concatenate values in the list as content (use for buttons purpose), otherwize works as $options. + * 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 buttons. + * + * @param array|string $buttons If string, use as the footer content, if list, concatenate + * values in the list as content (use for buttons purpose), otherwize works as $options. * @param array $options Options for the footer div. - * + * * Special option (if $buttons is NOT NULL but empty): * - close: Add the 'close' button to the footer (default true). * @@ -221,7 +238,8 @@ public function footer ($buttons = [], $options = []) { if (empty($buttons)) { return $this->_createFooter(NULL, $options) ; } - return $this->_createFooter(is_string($buttons) ? $buttons : implode('', $buttons), $options) ; + return $this->_createFooter(is_string($buttons) ? $buttons : implode('', $buttons), + $options) ; } } diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 109e5b1..ac8e911 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -68,13 +68,14 @@ public function startGroup($options = []) { $this->_groupPanelOpen = $options['open']; $this->_groupId = $options['id']; $options = $this->addClass($options, 'panel-group'); - $class = $options['class']; - unset($options['class'], $options['open'], $options['collapsible']); - return $this->Html->div($class, null, $options); + unset($options['open'], $options['collapsible']); + return $this->Html->tag('div', null, $options); } public function endGroup() { $this->_defaultCollapsible = false; + $this->_autoCloseOnCreate = false; + $this->_groupId = false; $out = ''; if (!$this->_lastPanelClosed) { $out = $this->end(); @@ -117,9 +118,6 @@ public function create($title = null, $options = []) { $this->_panelCount++; } - $class = $options['class']; - unset ($options['class']); - $out = ''; if ($this->_autoCloseOnCreate && !$this->_lastPanelClosed) { @@ -130,13 +128,13 @@ public function create($title = null, $options = []) { /* Increment panel counter for the current group. */ $this->_groupPanelCount++; - $out .= $this->Html->div($class, null, $options); + $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->_startPart('body'); + $out .= $this->_createBody(); } } @@ -166,8 +164,11 @@ public function end ($title = null, $options = []) { protected function _cleanCurrent () { $res = ''; if ($this->current) { - $res = $this->_endPart(); - $this->current = NULL ; + $res .= ''; + if ($this->_collapsible && $this->current == 'body') { + $res .= ''; + } + $this->current = null ; } return $res; } @@ -178,8 +179,6 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { } unset ($options['title']); $options = $this->addClass($options, 'panel-heading'); - $class = $options['class']; - unset ($options['class']); if ($this->_collapsible) { $options += [ 'role' => 'tab', @@ -203,60 +202,32 @@ protected function _createHeader ($title, $options = [], $titleOptions = []) { unset($titleOptions['tag']); $title = $titleOptions ? $this->Html->tag($tag, $title, $titleOptions) : $title; } - return $this->_cleanCurrent().$this->Html->div($class, $title, $options); + return $this->_cleanCurrent().$this->Html->tag('div', $title, $options); } - protected function _createBody ($text, $options = []) { + protected function _createBody ($text = null, $options = []) { $options = $this->addClass($options, 'panel-body'); - $class = $options['class']; - unset ($options['class']); - $body = $this->Html->div($class, $text, $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' : ''; - $body = $this->Html->div('panel-collapse collapse'.$open, $body, [ + $body = $this->Html->div('panel-collapse collapse'.$open, $text ? $body : null, [ 'role' => 'tabpanel', 'aria-labelledby' => $this->_headId, 'id' => $this->_bodyId - ]); + ]).($text ? '' : $body); } - return $this->_cleanCurrent().$body ; + $body = $this->_cleanCurrent().$body; + if (!$text) { + $this->current = 'body'; + } + return $body; } protected function _createFooter ($text = null, $options = []) { $options = $this->addClass($options, 'panel-footer'); - $class = $options['class']; - unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; - } - - protected function _startPart ($part, $options = []) { - $res = '' ; - if ($this->current != null) { - $res = $this->_endPart () ; - } - $this->current = $part ; - if ($this->_collapsible && $this->current == 'body') { - $open = ((is_int($this->_groupPanelOpen) - && $this->_groupPanelOpen === $this->_groupPanelCount) - || $this->_groupPanelOpen === $this->_bodyId) ? ' in' : ''; - $res .= $this->Html->div('panel-collapse collapse'.$open, null, [ - 'role' => 'tabpanel', - 'aria-labelledby' => $this->_headId, - 'id' => $this->_bodyId - ]); - } - return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', - $options, ''), - null, $options) ; - } - - protected function _endPart () { - if ($this->_collapsible && $this->current == 'body') { - return ''; - } - return '' ; + return $this->_cleanCurrent().$this->Html->tag('div', $text, $options) ; } /** @@ -272,13 +243,14 @@ protected function _endPart () { * **/ public function header ($info = null, $options = []) { + if (is_array($info)) { + $options = $info; + $info = null; + } $options += [ 'title' => true ]; - if (is_string($info)) { - return $this->_createHeader($info, $options) ; - } - return $this->_startPart('header', is_array($info) ? $info : $options) ; + return $this->_createHeader($info, $options) ; } /** @@ -292,13 +264,11 @@ public function header ($info = null, $options = []) { * **/ public function body ($info = null, $options = []) { - if (is_string($info)) { - if ($this->current != null) { - $this->_endPart() ; - } - return $this->_createBody($info, $options) ; + if (is_array($info)) { + $options = $info; + $info = null; } - return $this->_startPart('body', is_array($info) ? $info : $options) ; + return $this->_createBody($info, $options); } protected function _isAssociativeArray ($array) { @@ -315,10 +285,11 @@ protected function _isAssociativeArray ($array) { * **/ public function footer ($text = null, $options = []) { - if (is_string($text)) { - return $this->_createFooter($text, $options) ; + if (is_array($text)) { + $options = $text; + $text = null; } - return $this->_startPart('footer', is_array($text) ? $text : $options); + return $this->_createFooter($text, $options) ; } } diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 5f398d4..898c407 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -1,5 +1,5 @@ _extractOption('bootstrap-type', $options, $this->_defaultButtonType); @@ -84,7 +83,6 @@ protected function _addButtonClasses ($options) { } /** - * * Extract options from $options, returning $default if $key is not found. * * @param $key The key to search for. @@ -92,8 +90,8 @@ protected function _addButtonClasses ($options) { * @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] ; @@ -103,6 +101,18 @@ protected function _extractOption ($key, $options, $default = null) { /** * + * Check weither the specified array is associative or not. + * + * @param $array The array to check. + * + * @return true if the array is associative, false otherwize. + * + **/ + 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). @@ -126,18 +136,18 @@ protected function _extractType ($options, $key = 'type', $default = 'info', } return $type ; } - + /** - * * Try to convert the specified $text to a bootstrap icon. The $text is converted if it matches * a format "i:icon-name". * * @param $title The text to convert. - * @param $converted If specified, will contains true if the text was converted, false otherwize. + * @param $converted If specified, will contains true if the text was converted, + * false otherwize. * * @return The icon element if the conversion was successful, otherwize $text. * - * Note: This function will currently fail if the Html helper associated with the view is not + * Note: This function will currently fail if the Html helper associated with the view is not * BootstrapHtmlHelper. * **/ @@ -154,7 +164,6 @@ protected function _makeIcon ($title, &$converted = false) { } /** - * * This method will the function $callback with the specified argument ($title and $options) * after applying a filter on them. * @@ -164,7 +173,8 @@ protected function _makeIcon ($title, &$converted = false) { * * @return Whatever might be returned by $callback. * - * Note: Currently this method only works for function that take two arguments ($title and $options). + * Note: Currently this method only works for function that take + * two arguments ($title and $options). * **/ protected function _easyIcon ($callback, $title, $options) { From 476b66753d3f1629102a967e650fce9201615ea1 Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 22 Feb 2016 20:54:43 +0100 Subject: [PATCH 088/312] Restore files. --- README.md | 72 +++++++ src/View/Helper/BootstrapPaginatorHelper.php | 204 +++++++++++++++++++ 2 files changed, 276 insertions(+) create mode 100644 README.md create mode 100644 src/View/Helper/BootstrapPaginatorHelper.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..47d45bd --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +CakePHP 3.x Helpers for Bootstrap +================================= + +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) +[![Travis](https://img.shields.io/travis/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](https://travis-ci.org/Holt59/cakephp3-bootstrap-helpers) + +CakePHP 3.0 Helpers to generate HTML with @Twitter Boostrap style: `Html`, `Form`, `Modal` and `Paginator` helpers available! + +How to... ? +=========== + +**Installation** + +If you want the latest **Bootstrap 3** version of the plugin: +``` +composer require holt59/cakephp3-bootstrap-helpers:dev-master +``` +```php +// in config/bootstrap.php +Plugin::load('Bootstrap') ; +``` + +```php +// in your AppController +public $helpers = [ + 'Form' => [ + 'className' => 'Bootstrap.BootstrapForm' + ], + /* ... */ +] ; +``` +--- + + +If you want to test the **Bootstrap 4** version of the plugin (alpha): +``` +composer require holt59/cakephp3-bootstrap-helpers:dev-v4.0.0-alpha +``` + +**Documentation** + +The full plugin documentation is available at https://holt59.github.io/cakephp3-bootstrap-helpers/. + +**Contributing** + +Do not hesitate to [**post a github issue**](https://github.com/Holt59/cakephp3-bootstrap-helpers/issues/new) or [**submit a pull request**](https://github.com/Holt59/cakephp3-bootstrap-helpers/pulls) if you find a bug or want a new feature. + +Who is using it? +================ + +Non-exhaustive list of projects using these helpers, if you want to be in this list, do not hesitate to [email me](mailto:capelle.mikael@gmail.com) or post a comment on [this issue](https://github.com/Holt59/cakephp3-bootstrap-helpers/issues/32). + + - [**CakeAdmin**] (https://github.com/cakemanager/cakeadmin-lightstrap), LightStrap Theme for CakeAdmin + +Copyright and license +===================== + +The MIT License (MIT) + +Copyright (c) 2013-2016, Mikaël Capelle. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +See [LICENSE](LICENSE). diff --git a/src/View/Helper/BootstrapPaginatorHelper.php b/src/View/Helper/BootstrapPaginatorHelper.php new file mode 100644 index 0000000..275983e --- /dev/null +++ b/src/View/Helper/BootstrapPaginatorHelper.php @@ -0,0 +1,204 @@ + [], + 'templates' => [ + 'nextActive' => '
  • {{text}}
  • ', + 'nextDisabled' => '
  • {{text}}
  • ', + 'prevActive' => '
  • {{text}}
  • ', + 'prevDisabled' => '
  • {{text}}
  • ', + 'counterRange' => '{{start}} - {{end}} of {{count}}', + 'counterPages' => '{{page}} of {{pages}}', + 'first' => '
  • {{text}}
  • ', + 'last' => '
  • {{text}}
  • ', + 'number' => '
  • {{text}}
  • ', + 'current' => '
  • {{text}}
  • ', + 'ellipsis' => '
  • ...
  • ', + 'sort' => '{{text}}', + 'sortAsc' => '{{text}}', + 'sortDesc' => '{{text}}', + 'sortAscLocked' => '{{text}}', + 'sortDescLocked' => '{{text}}', + ] + ]; + + /** + * + * Get pagination link list. + * + * @param $options Options for link element + * + * Extra options: + * - size small/normal/large (default normal) + * + **/ + public function numbers (array $options = []) { + + $defaults = [ + 'before' => null, 'after' => null, 'model' => $this->defaultModel(), + 'modulus' => 8, 'first' => null, 'last' => null, 'url' => [], + 'prev' => null, 'next' => null, 'class' => '', 'size' => false + ]; + $options += $defaults; + + $options = $this->addClass($options, 'pagination'); + + switch ($options['size']) { + case 'small': + $options = $this->addClass($options, 'pagination-sm') ; + break ; + case 'large': + $options = $this->addClass($options, 'pagination-lg') ; + break ; + } + unset($options['size']) ; + + $options['before'] .= $this->Html->tag('ul', null, ['class' => $options['class']]); + $options['after'] = ''.$options['after'] ; + unset($options['class']); + + $params = (array)$this->params($options['model']) + ['page' => 1]; + if ($params['pageCount'] <= 1) { + return false; + } + + $templater = $this->templater(); + if (isset($options['templates'])) { + $templater->push(); + $method = is_string($options['templates']) ? 'load' : 'add'; + $templater->{$method}($options['templates']); + } + + $first = $prev = $next = $last = ''; + + /* Previous and Next buttons (addition from standard PaginatorHelper). */ + + if ($options['prev']) { + $title = $options['prev'] ; + $opts = [] ; + if (is_array($title)) { + $title = $title['title'] ; + unset ($options['prev']['title']) ; + $opts = $options['prev'] ; + } + $prev = $this->prev($title, $opts) ; + } + unset($options['prev']); + + if ($options['next']) { + $title = $options['next'] ; + $opts = [] ; + if (is_array($title)) { + $title = $title['title']; + unset ($options['next']['title']); + $opts = $options['next']; + } + $next = $this->next($title, $opts); + } + unset($options['next']); + + /* Custom First and Last. */ + + list($start, $end) = $this->_getNumbersStartAndEnd($params, $options); + + if ($options['last']) { + $ellipsis = isset($options['ellipsis']) ? + $options['ellipsis'] : is_int($options['last']); + $ellipsis = $ellipsis ? $templater->format('ellipsis', []) : ''; + $last = $this->_lastNumber($ellipsis, $params, $end, $options); + } + + if ($options['first']) { + $ellipsis = isset($options['ellipsis']) ? + $options['ellipsis'] : is_int($options['first']); + $ellipsis = $ellipsis ? $templater->format('ellipsis', []) : ''; + $first = $this->_firstNumber($ellipsis, $params, $start, $options); + } + + unset($options['ellipsis']); + + $before = is_int($options['first']) ? $prev.$first : $first.$prev; + $after = is_int($options['last']) ? $last.$next : $next.$last; + $options['before'] = $options['before'].$before;; + $options['after'] = $after.$options['after']; + $options['first'] = $options['last'] = false; + + if ($options['modulus'] !== false && $params['pageCount'] > $options['modulus']) { + $out = $this->_modulusNumbers($templater, $params, $options); + } else { + $out = $this->_numbers($templater, $params, $options); + } + + if (isset($options['templates'])) { + $templater->pop(); + } + + + return $out; + } + + public function prev ($title = '<< Previous', array $options = []) { + return $this->_easyIcon('parent::prev', $title, $options); + } + + public function next ($title = 'Next >>', array $options = []) { + return $this->_easyIcon('parent::next', $title, $options); + } + + public function first($first = '<< first', array $options = []) { + return $this->_easyIcon('parent::first', $first, $options); + } + + public function last($last = 'last >>', array $options = []) { + return $this->_easyIcon('parent::last', $last, $options); + } + +} + +?> From 63f00e251e268725fd6248044c5cf18b0f3fe6fc Mon Sep 17 00:00:00 2001 From: Holt59 Date: Mon, 22 Feb 2016 20:57:55 +0100 Subject: [PATCH 089/312] Resolve conflict. --- src/View/Helper/BootstrapPanelHelper.php | 117 ----------------------- src/View/Helper/BootstrapTrait.php | 2 +- 2 files changed, 1 insertion(+), 118 deletions(-) diff --git a/src/View/Helper/BootstrapPanelHelper.php b/src/View/Helper/BootstrapPanelHelper.php index 9436e5e..ac8e911 100644 --- a/src/View/Helper/BootstrapPanelHelper.php +++ b/src/View/Helper/BootstrapPanelHelper.php @@ -32,8 +32,6 @@ class BootstrapPanelHelper extends Helper { public $current = NULL ; -<<<<<<< HEAD -======= /* Protected attributes used to generate ID for collapsible panels. */ protected $_panelCount = 0; protected $_bodyId = null; @@ -85,7 +83,6 @@ public function endGroup() { return $out.''; } ->>>>>>> panel-helper /** * * Create a Twitter Bootstrap like panel. @@ -99,28 +96,6 @@ public function endGroup() { public function create($title = null, $options = []) { if (is_array($title)) { -<<<<<<< HEAD - $options = $title ; - } - - $nobody = $this->_extractOption('no-body', $options, false); - unset ($options['no-body']); - $type = $this->_extractOption('type', $options, 'default'); - unset ($options['type']); - - $options = $this->addClass($options, ['panel', 'panel-'.$type]); - $class = $options['class']; - unset ($options['class']); - - $res = $this->Html->div($class, null, $options); - if (is_string($title) && $title) { - $res .= $this->_createHeader($title, []) ; - if (!$nobody) { - $res .= $this->_startPart('body'); - } - } - return $res ; -======= $options = $title; } @@ -164,16 +139,11 @@ public function create($title = null, $options = []) { } return $out ; ->>>>>>> panel-helper } /** * -<<<<<<< HEAD - * End a panel. If $title is not null, the ModalHelper::footer functions -======= * End a panel. If $title is not null, the PanelHelper::footer functions ->>>>>>> panel-helper * is called with $title and $options arguments. * * @param string|null $buttons @@ -181,17 +151,9 @@ public function create($title = null, $options = []) { * **/ public function end ($title = null, $options = []) { -<<<<<<< HEAD - $res = '' ; - if ($this->current != null) { - $this->current = null ; - $res .= $this->_endPart(); - } -======= $this->_lastPanelClosed = true; $res = '' ; $res .= $this->_cleanCurrent(); ->>>>>>> panel-helper if ($title !== null) { $res .= $this->footer($title, $options) ; } @@ -200,38 +162,6 @@ public function end ($title = null, $options = []) { } protected function _cleanCurrent () { -<<<<<<< HEAD - if ($this->current) { - $this->current = NULL ; - return $this->_endPart(); - } - return '' ; - } - - protected function _createHeader ($title, $options = [], $titleOptions = []) { - $options += [ - '_title' => [] - ]; - if (empty($titleOptions)) - $titleOptions = $options['_title']; - unset ($options['_title']); - $options = $this->addClass($options, 'panel-heading'); - $class = $options['class']; - unset ($options['class']); - $titleOptions = $this->addClass($titleOptions, 'panel-title'); - return $this->_cleanCurrent().$this->Html->div($class, - $this->Html->tag('h3', $title, - $titleOptions), - $options - ) ; - } - - protected function _createBody ($text, $options = []) { - $options = $this->addClass($options, 'panel-body'); - $class = $options['class']; - unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; -======= $res = ''; if ($this->current) { $res .= ''; @@ -293,33 +223,11 @@ protected function _createBody ($text = null, $options = []) { $this->current = 'body'; } return $body; ->>>>>>> panel-helper } protected function _createFooter ($text = null, $options = []) { $options = $this->addClass($options, 'panel-footer'); -<<<<<<< HEAD - $class = $options['class']; - unset ($options['class']); - return $this->_cleanCurrent().$this->Html->div($class, $text, $options) ; - } - - protected function _startPart ($part, $options = []) { - $res = '' ; - if ($this->current != null) { - $res = $this->_endPart () ; - } - $this->current = $part ; - return $res.$this->Html->div('panel-'.$part.' '.$this->_extractOption('class', - $options, ''), - null, $options) ; - } - - protected function _endPart () { - return '' ; -======= return $this->_cleanCurrent().$this->Html->tag('div', $text, $options) ; ->>>>>>> panel-helper } /** @@ -327,11 +235,7 @@ protected function _endPart () { * Create / Start the header. If $info is specified as a string, create and return the * whole header, otherwize only open the header. * -<<<<<<< HEAD - * @param array|string $info If string, use as the modal title, otherwize works as $options. -======= * @param array|string $info If string, use as the panel title, otherwize works as $options. ->>>>>>> panel-helper * @param array $options Options for the header div. * * Special option (if $info is string): @@ -339,12 +243,6 @@ protected function _endPart () { * **/ public function header ($info = null, $options = []) { -<<<<<<< HEAD - if (is_string($info)) { - return $this->_createHeader($info, $options) ; - } - return $this->_startPart('header', is_array($info) ? $info : $options) ; -======= if (is_array($info)) { $options = $info; $info = null; @@ -353,7 +251,6 @@ public function header ($info = null, $options = []) { 'title' => true ]; return $this->_createHeader($info, $options) ; ->>>>>>> panel-helper } /** @@ -367,21 +264,11 @@ public function header ($info = null, $options = []) { * **/ public function body ($info = null, $options = []) { -<<<<<<< HEAD - if (is_string($info)) { - if ($this->current != null) { - $this->_endPart() ; - } - return $this->_createBody($info, $options) ; - } - return $this->_startPart('body', is_array($info) ? $info : $options) ; -======= if (is_array($info)) { $options = $info; $info = null; } return $this->_createBody($info, $options); ->>>>>>> panel-helper } protected function _isAssociativeArray ($array) { @@ -397,15 +284,11 @@ protected function _isAssociativeArray ($array) { * @param array $options Options for the footer div. * **/ -<<<<<<< HEAD - public function footer ($text = "", $options = []) { -======= public function footer ($text = null, $options = []) { if (is_array($text)) { $options = $text; $text = null; } ->>>>>>> panel-helper return $this->_createFooter($text, $options) ; } diff --git a/src/View/Helper/BootstrapTrait.php b/src/View/Helper/BootstrapTrait.php index 13715a6..898c407 100644 --- a/src/View/Helper/BootstrapTrait.php +++ b/src/View/Helper/BootstrapTrait.php @@ -153,7 +153,7 @@ protected function _extractType ($options, $key = 'type', $default = 'info', **/ protected function _makeIcon ($title, &$converted = false) { $converted = false ; - if (!$this->easyIcon || !is_string($title)) { + if (!$this->easyIcon) { return $title ; } $title = preg_replace_callback('#(^|\s+)i:([a-zA-Z0-9\\-_]+)(\s+|$)#', function ($matches) { From 20e6d6031c128d78f2901668afc230bae4613a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 23 Feb 2016 08:09:08 +0100 Subject: [PATCH 090/312] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 47d45bd..8f1d629 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ CakePHP 3.x Helpers for Bootstrap [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) [![Travis](https://img.shields.io/travis/Holt59/cakephp3-bootstrap-helpers.svg?style=flat-square)](https://travis-ci.org/Holt59/cakephp3-bootstrap-helpers) -CakePHP 3.0 Helpers to generate HTML with @Twitter Boostrap style: `Html`, `Form`, `Modal` and `Paginator` helpers available! +CakePHP 3.0 Helpers to generate HTML with @Twitter Boostrap style: `Flash`, `Form`, `Html`, `Modal`, `Navbar`, `Panel` and `Paginator` helpers available! How to... ? =========== 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 091/312] 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 092/312] 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 093/312] 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 094/312] 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 095/312] 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 096/312] 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 097/312] 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 098/312] 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 099/312] 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 100/312] 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 101/312] 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 102/312] 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 103/312] 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 104/312] 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 105/312] 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 106/312] 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 107/312] 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 108/312] 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 109/312] 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 110/312] 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 111/312] 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 112/312] 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 113/312] 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 115/312] 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 116/312] 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 117/312] 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 118/312] 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 119/312] 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 120/312] 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 121/312] 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 122/312] 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 123/312] 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 124/312] 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 125/312] 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 126/312] 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 127/312] 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 128/312] 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 129/312] 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 130/312] 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 131/312] 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 132/312] 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 133/312] 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 134/312] 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 135/312] 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 136/312] 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 137/312] 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 138/312] 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 139/312] 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 140/312] 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 141/312] 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 142/312] 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 143/312] 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 144/312] 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 145/312] 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 `