/[drupal]/contributions/modules/views/handlers/views_handler_filter_in_operator.inc
ViewVC logotype

Contents of /contributions/modules/views/handlers/views_handler_filter_in_operator.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.12 - (show annotations) (download) (as text)
Fri Sep 25 00:29:43 2009 UTC (2 months ago) by merlinofchaos
Branch: MAIN
CVS Tags: DRUPAL-6--2-7, HEAD
Branch point for: DRUPAL-6--2, DRUPAL-7--3
Changes since 1.11: +15 -11 lines
File MIME type: text/x-php
#511908 by dww: Too many check plains in selects because of problems with checkboxes.
1 <?php
2 // $Id: views_handler_filter_in_operator.inc,v 1.11 2009/07/01 22:30:40 merlinofchaos Exp $
3 /**
4 * Simple filter to handle matching of multiple options selectable via checkboxes
5 *
6 * Definition items:
7 * - numeric: If set to true, this item will use numeric operators instead of string.
8 *
9 */
10 class views_handler_filter_in_operator extends views_handler_filter {
11 var $value_form_type = 'checkboxes';
12
13 function construct() {
14 parent::construct();
15 $this->value_title = t('Options');
16 $this->value_options = NULL;
17 }
18
19 /**
20 * Child classes should be used to override this function and set the
21 * 'value options', unless 'options callback' is defined as a valid function
22 * or static public method to generate these values.
23 *
24 * This can use a guard to be used to reduce database hits as much as
25 * possible.
26 */
27 function get_value_options() {
28 if (isset($this->value_options)) {
29 return;
30 }
31
32 if (isset($this->definition['options callback']) && is_callable($this->definition['options callback'])) {
33 $this->value_options = call_user_func($this->definition['options callback']);
34 }
35 else {
36 $this->value_options = array(t('Yes'), t('No'));
37 }
38 }
39
40 function expose_options() {
41 parent::expose_options();
42 $this->options['expose']['reduce'] = FALSE;
43 }
44
45 function expose_form_right(&$form, &$form_state) {
46 parent::expose_form_right($form, $form_state);
47 $form['expose']['reduce'] = array(
48 '#type' => 'checkbox',
49 '#title' => t('Limit list to selected items'),
50 '#description' => t('If checked, the only items presented to the user will be the ones selected here.'),
51 '#default_value' => !empty($this->options['expose']['reduce']), // safety
52 );
53 }
54
55 function option_definition() {
56 $options = parent::option_definition();
57
58 $options['operator']['default'] = 'in';
59 $options['value']['default'] = array();
60
61 return $options;
62 }
63
64 /**
65 * This kind of construct makes it relatively easy for a child class
66 * to add or remove functionality by overriding this function and
67 * adding/removing items from this array.
68 */
69 function operators() {
70 $operators = array(
71 'in' => array(
72 'title' => t('Is one of'),
73 'short' => t('in'),
74 'short_single' => t('='),
75 'method' => 'op_simple',
76 'values' => 1,
77 ),
78 'not in' => array(
79 'title' => t('Is not one of'),
80 'short' => t('not in'),
81 'short_single' => t('<>'),
82 'method' => 'op_simple',
83 'values' => 1,
84 ),
85 );
86 // if the definition allows for the empty operator, add it.
87 if (!empty($this->definition['allow empty'])) {
88 $operators += array(
89 'empty' => array(
90 'title' => t('Is empty (NULL)'),
91 'method' => 'op_empty',
92 'short' => t('empty'),
93 'values' => 0,
94 ),
95 'not empty' => array(
96 'title' => t('Is not empty (NULL)'),
97 'method' => 'op_empty',
98 'short' => t('not empty'),
99 'values' => 0,
100 ),
101 );
102 }
103
104 return $operators;
105 }
106
107 /**
108 * Build strings from the operators() for 'select' options
109 */
110 function operator_options($which = 'title') {
111 $options = array();
112 foreach ($this->operators() as $id => $info) {
113 $options[$id] = $info[$which];
114 }
115
116 return $options;
117 }
118
119 function operator_values($values = 1) {
120 $options = array();
121 foreach ($this->operators() as $id => $info) {
122 if (isset($info['values']) && $info['values'] == $values) {
123 $options[] = $id;
124 }
125 }
126
127 return $options;
128 }
129
130 function value_form(&$form, &$form_state) {
131 $form['value'] = array();
132
133 $this->get_value_options();
134 $options = $this->value_options;
135 $default_value = (array) $this->value;
136
137 $which = 'all';
138 if (!empty($form['operator'])) {
139 $source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
140 }
141 if (!empty($form_state['exposed'])) {
142 $identifier = $this->options['expose']['identifier'];
143
144 if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator'])) {
145 // exposed and locked.
146 $which = in_array($this->operator, $this->operator_values(1)) ? 'value' : 'none';
147 }
148 else {
149 $source = 'edit-' . form_clean_id($this->options['expose']['operator']);
150 }
151
152 if (!empty($this->options['expose']['reduce'])) {
153 $options = $this->reduce_value_options();
154
155 if (empty($this->options['expose']['single']) && !empty($this->options['expose']['optional'])) {
156 $default_value = array();
157 }
158 }
159
160 if (!empty($this->options['expose']['single'])) {
161 if (!empty($this->options['expose']['optional']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
162 $default_value = 'All';
163 }
164 else if (empty($default_value)) {
165 $keys = array_keys($options);
166 $default_value = array_shift($keys);
167 }
168 else {
169 $copy = $default_value;
170 $default_value = array_shift($copy);
171 }
172 }
173 }
174
175 if ($which == 'all' || $which == 'value') {
176 $form['value'] = array(
177 '#type' => $this->value_form_type,
178 '#title' => $this->value_title,
179 '#options' => $options,
180 '#default_value' => $default_value,
181 // These are only valid for 'select' type, but do no harm to checkboxes.
182 '#multiple' => TRUE,
183 '#size' => count($options) > 8 ? 8 : count($options),
184 );
185 if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
186 $form_state['input'][$identifier] = $default_value;
187 }
188
189 $process = array();
190 if ($this->value_form_type == 'checkboxes') {
191 // If this form element will use checkboxes in the UI, we need to
192 // check_plain() all the options ourselves since FAPI is inconsistent
193 // about this. However, instead of directly doing that to the #options
194 // right now, we define a #process callback since we might change our
195 // mind later and convert this into a 'select' form element, which
196 // would lead to double-escaping the options.
197 $process[] = 'views_process_check_options';
198 }
199 if ($which == 'all') {
200 if (empty($form_state['exposed']) && ($this->value_form_type == 'checkboxes' || $this->value_form_type == 'radios')) {
201 $process[] = "expand_$this->value_form_type";
202 $form['value']['#prefix'] = '<div id="edit-options-value-wrapper">';
203 $form['value']['#suffix'] = '</div>';
204 }
205 $process[] = 'views_process_dependency';
206 $form['value']['#dependency'] = array($source => $this->operator_values(1));
207 }
208 if (!empty($process)) {
209 $form['value']['#process'] = $process;
210 }
211 }
212 }
213
214 /**
215 * When using exposed filters, we may be required to reduce the set.
216 */
217 function reduce_value_options($input = NULL) {
218 if (!isset($input)) {
219 $input = $this->value_options;
220 }
221
222 // Because options may be an array of strings, or an array of mixed arrays
223 // and strings (optgroups) or an array of objects, we have to
224 // step through and handle each one individually.
225 $options = array();
226 foreach ($input as $id => $option) {
227 if (is_array($option)) {
228 $options[$id] = $this->reduce_value_options($option);
229 continue;
230 }
231 else if (is_object($option)) {
232 $keys = array_keys($option->option);
233 $key = array_shift($keys);
234 if (isset($this->options['value'][$key])) {
235 $options[$id] = $option;
236 }
237 }
238 else if (isset($this->options['value'][$id])) {
239 $options[$id] = $option;
240 }
241 }
242 return $options;
243 }
244
245 function accept_exposed_input($input) {
246 // A very special override because the All state for this type of
247 // filter could have a default:
248 if (empty($this->options['exposed'])) {
249 return TRUE;
250 }
251
252 // If this is single and optional, this says that yes this filter will
253 // participate, but using the default settings, *if* 'limit is true.
254 if (!empty($this->options['expose']['single']) && !empty($this->options['expose']['optional']) && !empty($this->options['expose']['limit'])) {
255 $identifier = $this->options['expose']['identifier'];
256 if ($input[$identifier] == 'All') {
257 return TRUE;
258 }
259 }
260
261 return parent::accept_exposed_input($input);
262 }
263
264 function value_submit($form, &$form_state) {
265 // Drupal's FAPI system automatically puts '0' in for any checkbox that
266 // was not set, and the key to the checkbox if it is set.
267 // Unfortunately, this means that if the key to that checkbox is 0,
268 // we are unable to tell if that checkbox was set or not.
269
270 // Luckily, the '#value' on the checkboxes form actually contains
271 // *only* a list of checkboxes that were set, and we can use that
272 // instead.
273
274 $form_state['values']['options']['value'] = $form['value']['#value'];
275 // $form_state['values']['options']['value'] = array_filter($form_state['values']['options']['value']);
276 }
277
278 function admin_summary() {
279 if (!empty($this->options['exposed'])) {
280 return t('exposed');
281 }
282 $info = $this->operators();
283
284 $this->get_value_options();
285
286 if (!is_array($this->value)) {
287 return;
288 }
289
290 $operator = check_plain($info[$this->operator]['short']);
291 $values = '';
292 if (in_array($this->operator, $this->operator_values(1))) {
293 if (count($this->value) == 1) {
294 // If any, use the 'single' short name of the operator instead.
295 if (isset($info[$this->operator]['short_single'])) {
296 $operator = check_plain($info[$this->operator]['short_single']);
297 }
298
299 $keys = $this->value;
300 $key = array_shift($keys);
301 if (isset($this->value_options[$key])) {
302 $values = check_plain($this->value_options[$key]);
303 }
304 else {
305 $values = t('Unknown');
306 }
307 }
308 else {
309 foreach ($this->value as $value) {
310 if ($values !== '') {
311 $values .= ', ';
312 }
313 if (strlen($values) > 8) {
314 $values .= '...';
315 break;
316 }
317 $values .= check_plain($this->value_options[$value]);
318 }
319 }
320 }
321
322 return $operator . (($values !== '') ? ' ' . $values : '');
323 }
324
325 function query() {
326 $info = $this->operators();
327 if (!empty($info[$this->operator]['method'])) {
328 $this->{$info[$this->operator]['method']}();
329 }
330 }
331
332 function op_simple() {
333 if (empty($this->value)) {
334 return;
335 }
336 $this->ensure_my_table();
337 $placeholder = !empty($this->definition['numeric']) ? '%d' : "'%s'";
338
339 $replace = array_fill(0, sizeof($this->value), $placeholder);
340 $in = ' (' . implode(", ", $replace) . ')';
341
342 // We use array_values() because the checkboxes keep keys and that can cause
343 // array addition problems.
344 $this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field " . $this->operator . $in, array_values($this->value));
345 }
346
347 function op_empty() {
348 $this->ensure_my_table();
349 $field = "$this->table_alias.$this->real_field";
350
351 if ($this->operator == 'empty') {
352 $operator = "IS NULL";
353 }
354 else {
355 $operator = "IS NOT NULL";
356 }
357
358 $this->query->add_where($this->options['group'], "$field $operator");
359 }
360 }

  ViewVC Help
Powered by ViewVC 1.1.2