| Commit | Line | Data |
|---|---|---|
| fe44beb7 EM |
1 | <?php |
| 2 | // $Id$ | |
| 3 | ||
| 4 | /** | |
| 5 | * Basic textfield filter to handle string filtering commands | |
| 6 | * including equality, like, not like, etc. | |
| 7 | */ | |
| 8 | class views_handler_filter_string extends views_handler_filter { | |
| 9 | // exposed filter options | |
| 10 | var $no_single = TRUE; | |
| 11 | ||
| 12 | function option_definition() { | |
| 13 | $options = parent::option_definition(); | |
| 14 | ||
| 15 | $options['case'] = array('default' => TRUE); | |
| 16 | ||
| 17 | return $options; | |
| 18 | } | |
| 19 | ||
| 20 | /** | |
| 21 | * This kind of construct makes it relatively easy for a child class | |
| 22 | * to add or remove functionality by overriding this function and | |
| 23 | * adding/removing items from this array. | |
| 24 | */ | |
| 25 | function operators() { | |
| 26 | $operators = array( | |
| 27 | '=' => array( | |
| 28 | 'title' => t('Is equal to'), | |
| 29 | 'short' => t('='), | |
| 30 | 'method' => 'op_equal', | |
| 31 | 'values' => 1, | |
| 32 | ), | |
| 33 | '!=' => array( | |
| 34 | 'title' => t('Is not equal to'), | |
| db30517e | 35 | 'short' => t('!='), |
| fe44beb7 EM |
36 | 'method' => 'op_equal', |
| 37 | 'values' => 1, | |
| 38 | ), | |
| 39 | 'contains' => array( | |
| 40 | 'title' => t('Contains'), | |
| 41 | 'short' => t('contains'), | |
| 42 | 'method' => 'op_contains', | |
| 43 | 'values' => 1, | |
| 44 | ), | |
| 45 | 'word' => array( | |
| 46 | 'title' => t('Contains any word'), | |
| 47 | 'short' => t('has word'), | |
| 48 | 'method' => 'op_word', | |
| 49 | 'values' => 1, | |
| 50 | ), | |
| 51 | 'allwords' => array( | |
| 52 | 'title' => t('Contains all words'), | |
| 53 | 'short' => t('has all'), | |
| 54 | 'method' => 'op_word', | |
| 55 | 'values' => 1, | |
| 56 | ), | |
| 57 | 'starts' => array( | |
| 58 | 'title' => t('Starts with'), | |
| 59 | 'short' => t('begins'), | |
| 60 | 'method' => 'op_starts', | |
| 61 | 'values' => 1, | |
| 62 | ), | |
| 5a324f22 EM |
63 | 'not_starts' => array( |
| 64 | 'title' => t('Does not start with'), | |
| 65 | 'short' => t('not_begins'), | |
| 66 | 'method' => 'op_not_starts', | |
| 67 | 'values' => 1, | |
| 68 | ), | |
| fe44beb7 EM |
69 | 'ends' => array( |
| 70 | 'title' => t('Ends with'), | |
| 71 | 'short' => t('ends'), | |
| 72 | 'method' => 'op_ends', | |
| 73 | 'values' => 1, | |
| 74 | ), | |
| 5a324f22 EM |
75 | 'not_ends' => array( |
| 76 | 'title' => t('Does not end with'), | |
| 77 | 'short' => t('not_ends'), | |
| 78 | 'method' => 'op_not_ends', | |
| 79 | 'values' => 1, | |
| 80 | ), | |
| fe44beb7 EM |
81 | 'not' => array( |
| 82 | 'title' => t('Does not contain'), | |
| 83 | 'short' => t('!has'), | |
| 84 | 'method' => 'op_not', | |
| 85 | 'values' => 1, | |
| 86 | ), | |
| 87 | ); | |
| 88 | // if the definition allows for the empty operator, add it. | |
| 89 | if (!empty($this->definition['allow empty'])) { | |
| 90 | $operators += array( | |
| 91 | 'empty' => array( | |
| 92 | 'title' => t('Is empty (NULL)'), | |
| 93 | 'method' => 'op_empty', | |
| 94 | 'short' => t('empty'), | |
| 95 | 'values' => 0, | |
| 96 | ), | |
| 97 | 'not empty' => array( | |
| 98 | 'title' => t('Is not empty (NULL)'), | |
| 99 | 'method' => 'op_empty', | |
| 100 | 'short' => t('not empty'), | |
| 101 | 'values' => 0, | |
| 102 | ), | |
| 103 | ); | |
| 104 | } | |
| 105 | ||
| 106 | return $operators; | |
| 107 | } | |
| 108 | ||
| 109 | /** | |
| 110 | * Build strings from the operators() for 'select' options | |
| 111 | */ | |
| 112 | function operator_options($which = 'title') { | |
| 113 | $options = array(); | |
| 114 | foreach ($this->operators() as $id => $info) { | |
| 115 | $options[$id] = $info[$which]; | |
| 116 | } | |
| 117 | ||
| 118 | return $options; | |
| 119 | } | |
| 120 | ||
| 121 | function admin_summary() { | |
| bd192632 EM |
122 | if (!empty($this->options['exposed'])) { |
| 123 | return t('exposed'); | |
| 124 | } | |
| 125 | ||
| fe44beb7 | 126 | $options = $this->operator_options('short'); |
| bd192632 EM |
127 | $output = check_plain($options[$this->operator]); |
| 128 | if (in_array($this->operator, $this->operator_values(1))) { | |
| 129 | $output .= ' ' . check_plain($this->value); | |
| 130 | } | |
| 131 | return $output; | |
| fe44beb7 EM |
132 | } |
| 133 | ||
| 134 | function options_form(&$form, &$form_state) { | |
| 135 | parent::options_form($form, $form_state); | |
| 136 | $form['case'] = array( | |
| 137 | '#type' => 'checkbox', | |
| 138 | '#title' => t('Case sensitive'), | |
| 139 | '#default_value' => $this->options['case'], | |
| 740306ef | 140 | '#description' => t('Case sensitive filters may be faster. MySQL might ignore case sensitivity.'), |
| fe44beb7 EM |
141 | ); |
| 142 | } | |
| 143 | ||
| 144 | function operator_values($values = 1) { | |
| 145 | $options = array(); | |
| 146 | foreach ($this->operators() as $id => $info) { | |
| 147 | if (isset($info['values']) && $info['values'] == $values) { | |
| 148 | $options[] = $id; | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | return $options; | |
| 153 | } | |
| 154 | ||
| 155 | /** | |
| 156 | * Provide a simple textfield for equality | |
| 157 | */ | |
| 158 | function value_form(&$form, &$form_state) { | |
| 159 | // We have to make some choices when creating this as an exposed | |
| 160 | // filter form. For example, if the operator is locked and thus | |
| 161 | // not rendered, we can't render dependencies; instead we only | |
| 162 | // render the form items we need. | |
| 163 | $which = 'all'; | |
| 164 | if (!empty($form['operator'])) { | |
| 165 | $source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator'; | |
| 166 | } | |
| 167 | if (!empty($form_state['exposed'])) { | |
| b1df91b0 EM |
168 | $identifier = $this->options['expose']['identifier']; |
| 169 | ||
| fe44beb7 EM |
170 | if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator'])) { |
| 171 | // exposed and locked. | |
| 172 | $which = in_array($this->operator, $this->operator_values(1)) ? 'value' : 'none'; | |
| 173 | } | |
| 174 | else { | |
| 175 | $source = 'edit-' . form_clean_id($this->options['expose']['operator']); | |
| 176 | } | |
| 177 | } | |
| 178 | ||
| 179 | if ($which == 'all' || $which == 'value') { | |
| 180 | $form['value'] = array( | |
| 181 | '#type' => 'textfield', | |
| 182 | '#title' => t('Value'), | |
| 183 | '#size' => 30, | |
| 184 | '#default_value' => $this->value, | |
| 185 | ); | |
| b1df91b0 EM |
186 | if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) { |
| 187 | $form_state['input'][$identifier] = $this->value; | |
| 188 | } | |
| 189 | ||
| fe44beb7 EM |
190 | if ($which == 'all') { |
| 191 | $form['value'] += array( | |
| 192 | '#process' => array('views_process_dependency'), | |
| 193 | '#dependency' => array($source => $this->operator_values(1)), | |
| 194 | ); | |
| 195 | } | |
| 196 | } | |
| 7a644111 EM |
197 | |
| 198 | if (!isset($form['value'])) { | |
| 199 | // Ensure there is something in the 'value'. | |
| 200 | $form['value'] = array( | |
| 201 | '#type' => 'value', | |
| 202 | '#value' => NULL | |
| 203 | ); | |
| 204 | } | |
| fe44beb7 EM |
205 | } |
| 206 | ||
| 207 | function case_transform() { | |
| 9f36926a | 208 | return !empty($this->options['case']) ? '' : 'UPPER'; |
| fe44beb7 EM |
209 | } |
| 210 | ||
| 211 | /** | |
| 212 | * Add this filter to the query. | |
| 213 | * | |
| 214 | * Due to the nature of fapi, the value and the operator have an unintended | |
| 215 | * level of indirection. You will find them in $this->operator | |
| 216 | * and $this->value respectively. | |
| 217 | */ | |
| 218 | function query() { | |
| 219 | $this->ensure_my_table(); | |
| 220 | $field = "$this->table_alias.$this->real_field"; | |
| 221 | $upper = $this->case_transform(); | |
| 222 | ||
| 223 | $info = $this->operators(); | |
| 224 | if (!empty($info[$this->operator]['method'])) { | |
| 225 | $this->{$info[$this->operator]['method']}($field, $upper); | |
| 226 | } | |
| 227 | } | |
| 228 | ||
| 229 | function op_equal($field, $upper) { | |
| 230 | // operator is either = or != | |
| 231 | $this->query->add_where($this->options['group'], "$upper(%s) $this->operator $upper('%s')", $field, $this->value); | |
| 232 | } | |
| 233 | ||
| 234 | function op_contains($field, $upper) { | |
| 235 | $this->query->add_where($this->options['group'], "$upper(%s) LIKE $upper('%%%s%%')", $field, $this->value); | |
| 236 | } | |
| 237 | ||
| 238 | function op_word($field, $upper) { | |
| 239 | $where = array(); | |
| 240 | preg_match_all('/ (-?)("[^"]+"|[^" ]+)/i', ' ' . $this->value, $matches, PREG_SET_ORDER); | |
| 241 | foreach ($matches as $match) { | |
| 242 | $phrase = false; | |
| 243 | // Strip off phrase quotes | |
| 244 | if ($match[2]{0} == '"') { | |
| 245 | $match[2] = substr($match[2], 1, -1); | |
| 246 | $phrase = true; | |
| 247 | } | |
| 248 | $words = trim($match[2], ',?!();:-'); | |
| 249 | $words = $phrase ? array($words) : preg_split('/ /', $words, -1, PREG_SPLIT_NO_EMPTY); | |
| 250 | foreach ($words as $word) { | |
| 251 | $where[] = "$upper(%s) LIKE $upper('%%%s%%')"; | |
| 252 | $values[] = $field; | |
| 253 | $values[] = trim($word, " ,!?"); | |
| 254 | } | |
| 255 | } | |
| 256 | ||
| 257 | if (!$where) { | |
| 258 | return; | |
| 259 | } | |
| 260 | ||
| 261 | if ($this->operator == 'word') { | |
| 262 | $where = '(' . implode(' OR ', $where) . ')'; | |
| 263 | } | |
| 264 | else { | |
| 265 | $where = implode(' AND ', $where); | |
| 266 | } | |
| 267 | // previously this was a call_user_func_array but that's unnecessary | |
| 268 | // as views will unpack an array that is a single arg. | |
| 269 | $this->query->add_where($this->options['group'], $where, $values); | |
| 270 | } | |
| 271 | ||
| 272 | function op_starts($field, $upper) { | |
| 273 | $this->query->add_where($this->options['group'], "$upper(%s) LIKE $upper('%s%%')", $field, $this->value); | |
| 274 | } | |
| 275 | ||
| 5a324f22 EM |
276 | function op_not_starts($field, $upper) { |
| 277 | $this->query->add_where($this->options['group'], "$upper(%s) NOT LIKE $upper('%s%%')", $field, $this->value); | |
| 278 | } | |
| 279 | ||
| fe44beb7 EM |
280 | function op_ends($field, $upper) { |
| 281 | $this->query->add_where($this->options['group'], "$upper(%s) LIKE $upper('%%%s')", $field, $this->value); | |
| 282 | } | |
| 283 | ||
| 5a324f22 EM |
284 | function op_not_ends($field, $upper) { |
| 285 | $this->query->add_where($this->options['group'], "$upper(%s) NOT LIKE $upper('%%%s')", $field, $this->value); | |
| 286 | } | |
| 287 | ||
| fe44beb7 EM |
288 | function op_not($field, $upper) { |
| 289 | $this->query->add_where($this->options['group'], "$upper(%s) NOT LIKE $upper('%%%s%%')", $field, $this->value); | |
| 290 | } | |
| 291 | ||
| 292 | function op_empty($field) { | |
| 293 | if ($this->operator == 'empty') { | |
| 294 | $operator = "IS NULL"; | |
| 295 | } | |
| 296 | else { | |
| 297 | $operator = "IS NOT NULL"; | |
| 298 | } | |
| 299 | ||
| 300 | $this->query->add_where($this->options['group'], "$field $operator"); | |
| 301 | } | |
| 302 | ||
| 303 | } |