[TASK] Disable save button after submit
[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 ' . htmlspecialchars($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 $attributesString = '';
149 foreach ($attributes as $key => $value) {
150 $attributesString .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
151 }
152 $content = '
153 <div class="btn-group t3js-splitbutton">
154 <button' . $attributesString . '>
155 ' . $items['primary']->getIcon()->render() . '
156 ' . htmlspecialchars($items['primary']->getTitle()) . '
157 </button>
158 <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
159 <span class="caret"></span>
160 <span class="sr-only">Toggle Dropdown</span>
161 </button>
162 <ul class="dropdown-menu">';
163
164 /** @var InputButton $option */
165 foreach ($items['options'] as $option) {
166 $optionAttributes = [
167 'href' => '#',
168 'data-name' => $option->getName(),
169 'data-value' => $option->getValue()
170 ];
171 if (!empty($option->getClasses())) {
172 $optionAttributes['class'] = htmlspecialchars($option->getClasses());
173 }
174 if (!empty($option->getOnClick())) {
175 $optionAttributes['onclick'] = $option->getOnClick();
176 }
177 $optionAttributesString = '';
178 foreach ($optionAttributes as $key => $value) {
179 $optionAttributesString .= ' ' . htmlspecialchars($key) . '="' . htmlspecialchars($value) . '"';
180 }
181 $content .= '
182 <li>
183 <a' . $optionAttributesString . '>' . $option->getIcon()->render() . ' '
184 . htmlspecialchars($option->getTitle()) . '</a>
185 </li>
186 ';
187 }
188 $content .= '
189 </ul>
190 </div>
191 ';
192 return $content;
193 }
194
195 /**
196 * Magic method so Fluid can access a button via {button}
197 *
198 * @return string
199 */
200 public function __toString()
201 {
202 return $this->render();
203 }
204 }