1 <?php
2 /**
3 * WhToggleColumn widget class
4 * Renders a button to toggle values of a column
5 * @author Antonio Ramirez <amigo.cobos@gmail.com>
6 * @copyright Copyright © 2amigos.us 2013-
7 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
8 * @package YiiWheels.widgets.toggle
9 * @uses YiiStrap.helpers.TbHtml
10 * @uses YiiStrap.widgets.TbDataColumn
11 */
12 Yii::import('bootstrap.helpers.TbHtml');
13 Yii::import('bootstrap.widgets.TbDataColumn');
14
15 class WhToggleColumn extends TbDataColumn
16 {
17 /**
18 * @var string the attribute name of the data model. Used for column sorting, filtering and to render the corresponding
19 * attribute value in each data cell. If {@link value} is specified it will be used to rendered the data cell instead of the attribute value.
20 * @see value
21 * @see sortable
22 */
23 public $name;
24
25 /**
26 * @var array the HTML options for the data cell tags.
27 */
28 public $htmlOptions = array('class' => 'toggle-column');
29
30 /**
31 * @var array the HTML options for the header cell tag.
32 */
33 public $headerHtmlOptions = array('class' => 'toggle-column');
34
35 /**
36 * @var array the HTML options for the footer cell tag.
37 */
38 public $footerHtmlOptions = array('class' => 'toggle-column');
39
40 /**
41 * @var string the label for the toggle button. Defaults to "Check".
42 * Note that the label will not be HTML-encoded when rendering.
43 */
44 public $checkedButtonLabel;
45
46 /**
47 * @var string the label for the toggle button. Defaults to "Uncheck".
48 * Note that the label will not be HTML-encoded when rendering.
49 */
50 public $uncheckedButtonLabel;
51
52 /**
53 * @var string the label for the NULL value toggle button. Defaults to "Not Set".
54 * Note that the label will not be HTML-encoded when rendering.
55 */
56 public $emptyButtonLabel;
57
58 /**
59 * @var string the glyph icon toggle button "checked" state.
60 * You may set this property to be false to render a text link instead.
61 */
62 public $checkedIcon = 'glyphicon-ok-circle';
63
64 /**
65 * @var string the glyph icon toggle button "unchecked" state.
66 * You may set this property to be false to render a text link instead.
67 */
68 public $uncheckedIcon = 'glyphicon-remove-sign';
69
70 /**
71 * @var string the glyph icon toggle button "empty" state (example for null value)
72 */
73 public $emptyIcon = 'glyphicon-question-sign';
74
75 /**
76 * @var boolean display button with text or only icon with label tooltip
77 */
78 public $displayText = false;
79
80 /**
81 * @var boolean whether the column is sortable. If so, the header cell will contain a link that may trigger the sorting.
82 * Defaults to true. Note that if {@link name} is not set, or if {@link name} is not allowed by {@link CSort},
83 * this property will be treated as false.
84 * @see name
85 */
86 public $sortable = true;
87
88 /**
89 * @var mixed the HTML code representing a filter input (eg a text field, a dropdown list)
90 * that is used for this data column. This property is effective only when
91 * {@link CGridView::filter} is set.
92 * If this property is not set, a text field will be generated as the filter input;
93 * If this property is an array, a dropdown list will be generated that uses this property value as
94 * the list options.
95 * If you don't want a filter for this data column, set this value to false.
96 * @since 1.1.1
97 */
98 public $filter;
99
100 /**
101 * @var string Name of the action to call and toggle values
102 * @see bootstrap.action.TbToggleAction for an easy way to use with your controller
103 */
104 public $toggleAction = 'toggle';
105
106 /**
107 * @var string a javascript function that will be invoked after the toggle ajax call.
108 *
109 * The function signature is <code>function(data)</code>
110 * <ul>
111 * <li><code>success</code> status of the ajax call, true if the ajax call was successful, false if the ajax call failed.
112 * <li><code>data</code> the data returned by the server in case of a successful call or XHR object in case of error.
113 * </ul>
114 * Note that if success is true it does not mean that the delete was successful, it only means that the ajax call was successful.
115 *
116 * Example:
117 * <pre>
118 * array(
119 * class'=>'TbToggleColumn',
120 * 'afterToggle'=>'function(success,data){ if (success) alert("Toggled successfuly"); }',
121 * ),
122 * </pre>
123 */
124 public $afterToggle;
125
126 /**
127 * @var string suffix substituted to a name class of the tag <a>
128 */
129 public $uniqueClassSuffix = '';
130
131 /**
132 * @var array the configuration for toggle button.
133 */
134 protected $toggleOptions = array();
135
136 /**
137 * Initializes the column.
138 * This method registers necessary client script for the button column.
139 */
140 public function init()
141 {
142 if ($this->name === null) {
143 throw new CException(Yii::t(
144 'zii',
145 '"{attribute}" attribute cannot be empty.',
146 array('{attribute}' => "name")
147 ));
148 }
149
150 $this->initButton();
151 $this->registerClientScript();
152 }
153
154 /**
155 * Initializes the default buttons (toggle).
156 */
157 protected function initButton()
158 {
159 if ($this->checkedButtonLabel === null) {
160 $this->checkedButtonLabel = Yii::t('zii', 'Uncheck');
161 }
162 if ($this->uncheckedButtonLabel === null) {
163 $this->uncheckedButtonLabel = Yii::t('zii', 'Check');
164 }
165 if ($this->emptyButtonLabel === null) {
166 $this->emptyButtonLabel = Yii::t('zii', 'Not set');
167 }
168
169 $this->toggleOptions = array(
170 'url' => 'Yii::app()->controller->createUrl("' . $this->toggleAction . '",array("id"=>$data->primaryKey,"attribute"=>"' . $this->name . '"))',
171 'htmlOptions' => array('class' => $this->name . '_toggle' . $this->uniqueClassSuffix),
172 );
173
174 if (Yii::app()->request->enableCsrfValidation) {
175 $csrfTokenName = Yii::app()->request->csrfTokenName;
176 $csrfToken = Yii::app()->request->csrfToken;
177 $csrf = "\n\t\tdata:{ '$csrfTokenName':'$csrfToken' },";
178 } else {
179 $csrf = '';
180 }
181
182 if ($this->afterToggle === null) {
183 $this->afterToggle = 'function(){}';
184 }
185
186 $this->toggleOptions['click'] = "js:
187 function() {
188 var th=this;
189 var afterToggle={$this->afterToggle};
190 $.fn.yiiGridView.update('{$this->grid->id}', {
191 type:'POST',
192 url:$(this).attr('href'),{$csrf}
193 success:function(data) {
194 $.fn.yiiGridView.update('{$this->grid->id}');
195 afterToggle(true, data);
196 },
197 error:function(XHR){
198 afterToggle(false,XHR);
199 }
200 });
201 return false;
202 }";
203 }
204
205 /**
206 * Renders the data cell content.
207 * This method renders the view, update and toggle buttons in the data cell.
208 * @param integer $row the row number (zero-based)
209 * @param mixed $data the data associated with the row
210 */
211 protected function renderDataCellContent($row, $data)
212 {
213 $checked = CHtml::value($data, $this->name);
214 $toggleOptions = $this->toggleOptions;
215 $toggleOptions['icon'] = $checked === null
216 ? $this->emptyIcon
217 : ($checked
218 ? $this->checkedIcon
219 : $this->uncheckedIcon);
220
221 $toggleOptions['url'] = isset($toggleOptions['url'])
222 ? $this->evaluateExpression($toggleOptions['url'], array('data' => $data, 'row' => $row))
223 : '#';
224
225 if (!$this->displayText) {
226 $htmlOptions = TbArray::getValue('htmlOptions', $this->toggleOptions, array());
227 $htmlOptions['title'] = $this->getButtonLabel($checked);
228 $htmlOptions['rel'] = 'tooltip';
229 echo CHtml::link(TbHtml::icon($toggleOptions['icon']), $toggleOptions['url'], $htmlOptions);
230 } else {
231 echo TbHtml::button($this->getButtonLabel($checked), $toggleOptions);
232 }
233 }
234
235 /**
236 * Registers the client scripts for the button column.
237 */
238 protected function registerClientScript()
239 {
240 $js = array();
241
242 $function = CJavaScript::encode(TbArray::popValue('click', $this->toggleOptions, ''));
243
244 $class = preg_replace('/\s+/', '.', $this->toggleOptions['htmlOptions']['class']);
245 $js[] = "$(document).on('click','#{$this->grid->id} a.{$class}',$function);";
246
247 Yii::app()->getClientScript()->registerScript( $this->name. '#ReadyJS', implode("\n", $js));
248 }
249
250 /**
251 * Returns the button label
252 * @param $value
253 * @return string
254 */
255 private function getButtonLabel($value)
256 {
257 return $value === null ? $this->emptyButtonLabel : ($value ? $this->checkedButtonLabel : $this->uncheckedButtonLabel);
258 }
259 }
260