86f2bc03707aed52d23a21dc2427a032b2c51a65
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Template / Components / Buttons / SplitButton.php
1 <?php
2 namespace TYPO3\CMS\Backend\Template\Components\Buttons;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * SplitButton
19 *
20 * This button type renders a bootstrap split button.
21 * It takes multiple button objects as parameters
22 *
23 * EXAMPLE USAGE TO ADD A SPLIT BUTTON TO THE FIRST BUTTON GROUP IN THE LEFT BAR:
24 *
25 * $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
26 *
27 * $saveButton = $buttonBar->makeInputButton()
28 * ->setName('save')
29 * ->setValue('1')
30 * ->setIcon($this->iconFactory->getIcon('actions-document-save', Icon::SIZE_SMALL))
31 * ->setTitle('Save');
32 *
33 * $saveAndCloseButton = $buttonBar->makeInputButton()
34 * ->setName('save_and_close')
35 * ->setValue('1')
36 * ->setTitle('Save and close')
37 * ->setIcon($this->iconFactory->getIcon('actions-document-save-close', Icon::SIZE_SMALL));
38 *
39 * $saveAndShowPageButton = $buttonBar->makeInputButton()
40 * ->setName('save_and_show')
41 * ->setValue('1')
42 * ->setTitle('Save and show')
43 * ->setIcon($this->iconFactory->getIcon('actions-document-save-view', Icon::SIZE_SMALL));
44 *
45 * $splitButtonElement = $buttonBar->makeSplitButton()
46 * ->addItem($saveButton, TRUE)
47 * ->addItem($saveAndCloseButton)
48 * ->addItem($saveAndShowPageButton);
49 */
50 class SplitButton extends AbstractButton implements ButtonInterface
51 {
52 /**
53 * Internal var that determines whether the split button has received any primary
54 * actions yet
55 *
56 * @var bool
57 */
58 protected $containsPrimaryAction = false;
59
60 /**
61 * Internal array of items in the split button
62 *
63 * @var array
64 */
65 protected $items = [];
66
67 /**
68 * Adds an instance of any button to the split button
69 *
70 * @param AbstractButton $item ButtonObject to add
71 * @param bool $primaryAction Is the button the primary action?
72 *
73 * @throws \InvalidArgumentException In case a button is not valid
74 *
75 * @return $this
76 */
77 public function addItem(AbstractButton $item, $primaryAction = false)
78 {
79 if (!$item->isValid($item)) {
80 throw new \InvalidArgumentException(
81 'Only valid items may be assigned to a split Button. "' .
82 $item->getType() .
83 '" did not pass validation',
84 1441706330
85 );
86 }
87 if ($primaryAction && $this->containsPrimaryAction) {
88 throw new \InvalidArgumentException('A splitButton may only contain one primary action', 1441706340);
89 }
90 if ($primaryAction) {
91 $this->containsPrimaryAction = true;
92 $this->items['primary'] = clone $item;
93 } else {
94 $this->items['options'][] = clone $item;
95 }
96 return $this;
97 }
98
99 /**
100 * Returns the current button
101 *
102 * @return array
103 */
104 public function getButton()
105 {
106 if (!isset($this->items['primary']) && isset($this->items['options'])) {
107 $primaryAction = array_shift($this->items['options']);
108 $this->items['primary'] = $primaryAction;
109 }
110 return $this->items;
111 }
112
113 /**
114 * Validates the current button
115 *
116 *
117 * @return bool
118 */
119 public function isValid()
120 {
121 $subject = $this->getButton();
122 if (isset($subject['primary'])
123 && ($subject['primary'] instanceof AbstractButton)
124 && isset($subject['options'])
125 ) {
126 return true;
127 }
128 return false;
129 }
130
131 /**
132 * Renders the HTML markup of the button
133 *
134 * @return string
135 */
136 public function render()
137 {
138 $items = $this->getButton();
139 $attributes = [
140 'type' => 'submit',
141 'class' => 'btn btn-sm btn-default ' . $items['primary']->getClasses(),
142 'name' => $items['primary']->getName(),
143 'value' => $items['primary']->getValue()
144 ];
145 if (!empty($items['primary']->getOnClick())) {
146 $attributes['onclick'] = $items['primary']->getOnClick();
147 }
148 if (!empty($items['primary']->getForm())) {
149 $attributes['form'] = $items['primary']->getForm();
150 }
151 $attributesString = '';
152 foreach ($attributes as $key => $value) {
153 $attributesString .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
154 }
155 $content = '
156 <div class="btn-group t3js-splitbutton">
157 <button' . $attributesString . '>
158 ' . $items['primary']->getIcon()->render() . '
159 ' . htmlspecialchars($items['primary']->getTitle()) . '
160 </button>
161 <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
162 <span class="caret"></span>
163 <span class="sr-only">Toggle Dropdown</span>
164 </button>
165 <ul class="dropdown-menu">';
166
167 /** @var InputButton $option */
168 foreach ($items['options'] as $option) {
169 $optionAttributes = [
170 'href' => '#',
171 'data-name' => $option->getName(),
172 'data-value' => $option->getValue(),
173 'data-form' => $option->getForm()
174 ];
175 if (!empty($option->getClasses())) {
176 $optionAttributes['class'] = $option->getClasses();
177 }
178 if (!empty($option->getOnClick())) {
179 $optionAttributes['onclick'] = $option->getOnClick();
180 }
181 $optionAttributesString = '';
182 foreach ($optionAttributes as $key => $value) {
183 $optionAttributesString .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
184 }
185 $content .= '
186 <li>
187 <a' . $optionAttributesString . '>' . $option->getIcon()->render() . ' '
188 . htmlspecialchars($option->getTitle()) . '</a>
189 </li>
190 ';
191 }
192 $content .= '
193 </ul>
194 </div>
195 ';
196 return $content;
197 }
198
199 /**
200 * Magic method so Fluid can access a button via {button}
201 *
202 * @return string
203 */
204 public function __toString()
205 {
206 return $this->render();
207 }
208 }