1 <?php
2
3 4 5 6 7 8 9 10
11
12 namespace Grido\Components\Columns;
13
14 15 16 17 18 19 20 21 22 23 24 25 26
27 abstract class Editable extends Column
28 {
29
30 protected $editable = FALSE;
31
32
33 protected $editableDisabled = FALSE;
34
35
36 protected $editableControl;
37
38
39 protected $editableCallback;
40
41
42 protected $editableValueCallback;
43
44
45 protected $editableRowCallback;
46
47 48 49 50 51 52
53 public function setEditable($callback = NULL, \Nette\Forms\IControl $control = NULL)
54 {
55 $this->editable = TRUE;
56 $this->setClientSideOptions();
57
58 $callback && $this->setEditableCallback($callback);
59 $control && $this->setEditableControl($control);
60
61 return $this;
62 }
63
64 65 66 67 68
69 public function setEditableControl(\Nette\Forms\IControl $control)
70 {
71 $this->isEditable() ?: $this->setEditable();
72 $this->editableControl = $control;
73
74 return $this;
75 }
76
77 78 79 80 81
82 public function setEditableCallback($callback)
83 {
84 $this->isEditable() ?: $this->setEditable();
85 $this->editableCallback = $callback;
86
87 return $this;
88 }
89
90 91 92 93 94
95 public function setEditableValueCallback($callback)
96 {
97 $this->isEditable() ?: $this->setEditable();
98 $this->editableValueCallback = $callback;
99
100 return $this;
101 }
102
103 104 105 106 107
108 public function setEditableRowCallback($callback)
109 {
110 $this->isEditable() ?: $this->setEditable();
111 $this->editableRowCallback = $callback;
112
113 return $this;
114 }
115
116 117 118
119 public function disableEditable()
120 {
121 $this->editable = FALSE;
122 $this->editableDisabled = TRUE;
123
124 return $this;
125 }
126
127 protected function setClientSideOptions()
128 {
129 $options = $this->grid->getClientSideOptions();
130 if (!isset($options['editable'])) {
131 $this->grid->setClientSideOptions(array('editable' => TRUE));
132 $this->grid->onRender[] = function(\Grido\Grid $grid)
133 {
134 foreach ($grid->getComponent(Column::ID)->getComponents() as $column) {
135 if (!$column instanceof Editable || !$column->isEditable()) {
136 continue;
137 }
138
139 $colDb = $column->getColumn();
140 $colName = $column->getName();
141 $isMissing = function ($method) use ($grid) {
142 return $grid->model instanceof \Grido\DataSources\Model
143 ? !method_exists($grid->model->dataSource, $method)
144 : TRUE;
145 };
146
147 if (($column->editableCallback === NULL && (!is_string($colDb) || strpos($colDb, '.'))) ||
148 ($column->editableCallback === NULL && $isMissing('update'))
149 ) {
150 $msg = "Column '$colName' has error: You must define callback via setEditableCallback().";
151 throw new \Exception($msg);
152 }
153
154 if ($column->editableRowCallback === NULL && $column->customRender && $isMissing('getRow')) {
155 $msg = "Column '$colName' has error: You must define callback via setEditableRowCallback().";
156 throw new \Exception($msg);
157 }
158 }
159 };
160 }
161 }
162
163
164
165 166 167 168
169 public function getHeaderPrototype()
170 {
171 $th = parent::getHeaderPrototype();
172
173 if ($this->isEditable()) {
174 $th->data['grido-editable-handler'] = $this->link('editable!');
175 $th->data['grido-editableControl-handler'] = $this->link('editableControl!');
176 }
177
178 return $th;
179 }
180
181 182 183 184 185
186 public function getCellPrototype($row = NULL)
187 {
188 $td = parent::getCellPrototype($row);
189
190 if ($this->isEditable() && $row !== NULL) {
191 $td->data['grido-editable-value'] = $this->editableValueCallback === NULL
192 ? parent::getValue($row)
193 : callback($this->editableValueCallback)->invokeArgs(array($row, $this));
194 }
195
196 return $td;
197 }
198
199 200 201 202
203 public function getEditableControl()
204 {
205 if ($this->editableControl === NULL) {
206 $this->editableControl = new \Nette\Forms\Controls\TextInput;
207 $this->editableControl->controlPrototype->class[] = 'form-control';
208 }
209
210 return $this->editableControl;
211 }
212
213 214 215 216
217 public function getEditableCallback()
218 {
219 return $this->editableCallback;
220 }
221
222 223 224 225
226 public function getEditableValueCallback()
227 {
228 return $this->editableValueCallback;
229 }
230
231 232 233 234
235 public function getEditableRowCallback()
236 {
237 return $this->editableRowCallback;
238 }
239
240 241 242 243
244 public function isEditable()
245 {
246 return $this->editable;
247 }
248
249 250 251 252
253 public function isEditableDisabled()
254 {
255 return $this->editableDisabled;
256 }
257
258
259
260 261 262
263 public function handleEditable($id, $newValue, $oldValue)
264 {
265 $this->grid->onRegistered($this->grid);
266
267 if (!$this->presenter->isAjax() || !$this->isEditable()) {
268 $this->presenter->terminate();
269 }
270
271 $success = $this->editableCallback
272 ? callback($this->editableCallback)->invokeArgs(array($id, $newValue, $oldValue, $this))
273 : $this->grid->model->update($id, array($this->getColumn() => $newValue), $this->grid->primaryKey);
274
275 if (is_callable($this->customRender)) {
276 $row = $this->editableRowCallback
277 ? callback($this->editableRowCallback)->invokeArgs(array($id, $this))
278 : $this->grid->model->getRow($id, $this->grid->primaryKey);
279 $html = callback($this->customRender)->invokeArgs(array($row));
280 } else {
281 $html = $this->formatValue($newValue);
282 }
283
284 $payload = array('updated' => (bool) $success, 'html' => (string) $html);
285 $response = new \Nette\Application\Responses\JsonResponse($payload);
286 $this->presenter->sendResponse($response);
287 }
288
289 290 291
292 public function handleEditableControl($value)
293 {
294 $this->grid->onRegistered($this->grid);
295
296 if (!$this->presenter->isAjax() || !$this->isEditable()) {
297 $this->presenter->terminate();
298 }
299
300 $control = $this->getEditableControl()->setValue($value);
301 $this->getForm()->addComponent($control, 'edit' . $this->getName());
302
303 $response = new \Nette\Application\Responses\TextResponse($control->getControl()->render());
304 $this->presenter->sendResponse($response);
305 }
306 }
307