| 1 | <?php |
|---|
| 2 | // $Id: flag_plugin_argument_validate_flaggability.inc,v 1.1.2.3.2.1 2009/09/14 11:46:40 quicksketch Exp $ |
|---|
| 3 | |
|---|
| 4 | /** |
|---|
| 5 | * @file |
|---|
| 6 | * Contains the flaggability validator handler. |
|---|
| 7 | */ |
|---|
| 8 | |
|---|
| 9 | /** |
|---|
| 10 | * Validates whether an argument is a flaggable/flagged object. |
|---|
| 11 | * |
|---|
| 12 | * @ingroup views |
|---|
| 13 | */ |
|---|
| 14 | class flag_plugin_argument_validate_flaggability extends views_plugin_argument_validate { |
|---|
| 15 | |
|---|
| 16 | function construct() { |
|---|
| 17 | parent::construct(); |
|---|
| 18 | $this->flag_type = $this->definition['flag type']; |
|---|
| 19 | } |
|---|
| 20 | |
|---|
| 21 | function validate_form(&$form, &$form_state) { |
|---|
| 22 | $options = $this->flags_options(); |
|---|
| 23 | |
|---|
| 24 | $form[$this->_option_name('flag_name')] = array( |
|---|
| 25 | '#type' => 'radios', |
|---|
| 26 | // Add an ID to the surrounding div because radios don't get IDs |
|---|
| 27 | // as a whole group. This is needed for #dependency. |
|---|
| 28 | '#prefix' => '<div><div id="edit-options-' . views_css_safe($this->_option_name('flag_name')) . '">', |
|---|
| 29 | '#suffix' => '</div></div>', |
|---|
| 30 | '#title' => t('Flag'), |
|---|
| 31 | '#options' => $options, |
|---|
| 32 | '#default_value' => $this->_get_option('flag_name', '*relationship*'), |
|---|
| 33 | '#description' => t('Select the flag to validate against.'), |
|---|
| 34 | '#process' => array('expand_radios', 'views_process_dependency'), |
|---|
| 35 | '#dependency' => array('edit-options-validate-type' => array($this->id)), |
|---|
| 36 | ); |
|---|
| 37 | if (!$options) { |
|---|
| 38 | $form[$this->_option_name('flag_name')]['#description'] = '<p class="warning">' . t('No %type flags exist. You must first <a href="@create-url">create a %type flag</a> before being able to use one.', array('%type' => $this->flag_type, '@create-url' => 'admin/build/flags')) . '</p>'; |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | $form[$this->_option_name('flag_test')] = array( |
|---|
| 42 | '#type' => 'select', |
|---|
| 43 | '#title' => t('Validate the @type only if', array('@type' => $this->flag_type)), |
|---|
| 44 | '#options' => $this->tests_options(), |
|---|
| 45 | '#default_value' => $this->_get_option('flag_test', 'flaggable'), |
|---|
| 46 | '#process' => array('views_process_dependency'), |
|---|
| 47 | '#dependency' => array('edit-options-validate-type' => array($this->id)), |
|---|
| 48 | ); |
|---|
| 49 | |
|---|
| 50 | // This validator supports the "multiple IDs" syntax. It may not be |
|---|
| 51 | // extremely useful, but similar validators do support this and it's a good |
|---|
| 52 | // thing to be compatible. |
|---|
| 53 | $form[$this->_option_name('flag_id_type')] = array( |
|---|
| 54 | '#type' => 'select', |
|---|
| 55 | '#title' => t('Argument type'), |
|---|
| 56 | '#options' => array( |
|---|
| 57 | 'id' => t('ID'), |
|---|
| 58 | 'ids' => t('IDs separated by , or +'), |
|---|
| 59 | ), |
|---|
| 60 | '#default_value' => $this->_get_option('flag_id_type', 'id'), |
|---|
| 61 | '#process' => array('views_process_dependency'), |
|---|
| 62 | '#dependency' => array('edit-options-validate-type' => array($this->id)), |
|---|
| 63 | ); |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | /** |
|---|
| 67 | * Returns form #options for the flags. Returns empty array if no flags were |
|---|
| 68 | * found. |
|---|
| 69 | */ |
|---|
| 70 | function flags_options() { |
|---|
| 71 | $flags = flag_get_flags($this->flag_type); |
|---|
| 72 | if (!$flags) { |
|---|
| 73 | return array(); |
|---|
| 74 | } |
|---|
| 75 | else { |
|---|
| 76 | foreach ($flags as $flag) { |
|---|
| 77 | $options[$flag->name] = $flag->get_title(); |
|---|
| 78 | } |
|---|
| 79 | $options['*relationship*'] = t('<em>Pick the first flag mentioned in the relationships.</em>'); |
|---|
| 80 | return $options; |
|---|
| 81 | } |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | /** |
|---|
| 85 | * Validator arguments are stored in the argument object, not here, so we |
|---|
| 86 | * define a convenience method for fetching them. |
|---|
| 87 | */ |
|---|
| 88 | function _get_option($option, $default) { |
|---|
| 89 | $option = $this->_option_name($option); |
|---|
| 90 | return isset($this->argument->options[$option]) ? $this->argument->options[$option] : $default; |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | function _option_name($option) { |
|---|
| 94 | // We must embed the flag type in the option name or else validators of |
|---|
| 95 | // different types will clash with each other. It's a trait of Views that all |
|---|
| 96 | // validators on the system get their settings lumped onto the argument. |
|---|
| 97 | // Examine the view's 'Export' output to understand. |
|---|
| 98 | return 'validate_argument_' . $this->flag_type . '_' . $option; |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | /** |
|---|
| 102 | * Declares all tests. This scheme makes it easy for derived classes to add |
|---|
| 103 | * and remove tests. |
|---|
| 104 | */ |
|---|
| 105 | function tests_info($which = NULL) { |
|---|
| 106 | return array( |
|---|
| 107 | 'flaggable' => array( |
|---|
| 108 | 'title' => t('It is flaggable'), |
|---|
| 109 | 'callback' => 'test_flaggable', |
|---|
| 110 | ), |
|---|
| 111 | 'flagged' => array( |
|---|
| 112 | 'title' => t('It is flagged at least once'), |
|---|
| 113 | 'callback' => 'test_flagged', |
|---|
| 114 | ), |
|---|
| 115 | 'flagged_by_current_user' => array( |
|---|
| 116 | 'title' => t('It is flagged by the current user'), |
|---|
| 117 | 'callback' => 'test_flagged_by_current_user', |
|---|
| 118 | ), |
|---|
| 119 | ); |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | function tests_options() { |
|---|
| 123 | $options = array(); |
|---|
| 124 | foreach ($this->tests_info() as $id => $info) { |
|---|
| 125 | $options[$id] = $info['title']; |
|---|
| 126 | } |
|---|
| 127 | return $options; |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | function get_flag() { |
|---|
| 131 | $flag_name = $this->_get_option('flag_name', '*relationship*'); |
|---|
| 132 | |
|---|
| 133 | if ($flag_name == '*relationship*') { |
|---|
| 134 | // Pick the first flag mentioned in the relationships. |
|---|
| 135 | foreach ($this->view->relationship as $id => $handler) { |
|---|
| 136 | // Note: we can't do $handler->field, because the relationship handler's init() may overwrite it. |
|---|
| 137 | if (strpos($handler->options['field'], 'flag') !== FALSE && !empty($handler->options['flag'])) { |
|---|
| 138 | $flag = flag_get_flag($handler->options['flag']); |
|---|
| 139 | if ($flag && $flag->content_type == $this->flag_type) { |
|---|
| 140 | return $flag; |
|---|
| 141 | } |
|---|
| 142 | } |
|---|
| 143 | } |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | return flag_get_flag($flag_name); |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | /** |
|---|
| 150 | * Tests whether the argument is flaggable, or flagged, or flagged by current |
|---|
| 151 | * user. These are three possible tests, and which of the three to actually |
|---|
| 152 | * carry out is determined by 'flag_test'. |
|---|
| 153 | */ |
|---|
| 154 | function validate_argument($argument) { |
|---|
| 155 | $flag_test = $this->_get_option('flag_test', 'flaggable'); |
|---|
| 156 | $id_type = $this->_get_option('flag_id_type', 'id'); |
|---|
| 157 | |
|---|
| 158 | $flag = $this->get_flag(); |
|---|
| 159 | if (!$flag) { |
|---|
| 160 | // Validator is misconfigured somehow. |
|---|
| 161 | return TRUE; |
|---|
| 162 | } |
|---|
| 163 | |
|---|
| 164 | if ($id_type == 'id') { |
|---|
| 165 | if (!is_numeric($argument)) { |
|---|
| 166 | // If a user is being smart and types several IDs where only one is |
|---|
| 167 | // expected, we invalidate this. |
|---|
| 168 | return FALSE; |
|---|
| 169 | } |
|---|
| 170 | } |
|---|
| 171 | |
|---|
| 172 | $ids = views_break_phrase($argument); |
|---|
| 173 | if ($ids->value == array(-1)) { |
|---|
| 174 | // Malformed argument syntax. Invalidate. |
|---|
| 175 | return FALSE; |
|---|
| 176 | } |
|---|
| 177 | $ids = $ids->value; |
|---|
| 178 | |
|---|
| 179 | // Delegate the testing to the particual test method. $passed then |
|---|
| 180 | // holds the IDs that passed the test. |
|---|
| 181 | $tests_info = $this->tests_info(); |
|---|
| 182 | $method = $tests_info[$flag_test]['callback']; |
|---|
| 183 | if (method_exists($this, $method)) { |
|---|
| 184 | $passed = $this->$method($ids, $flag); |
|---|
| 185 | } |
|---|
| 186 | else { |
|---|
| 187 | $passed = array(); |
|---|
| 188 | } |
|---|
| 189 | |
|---|
| 190 | // If some IDs exist that haven't $passed our test then validation fails. |
|---|
| 191 | $failed = array_diff($ids, $passed); |
|---|
| 192 | return empty($failed); |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | // |
|---|
| 196 | // The actual tests. They return the IDs that passed. |
|---|
| 197 | // |
|---|
| 198 | |
|---|
| 199 | function test_flaggable($ids, $flag) { |
|---|
| 200 | return array_filter($ids, array($flag, 'applies_to_content_id')); |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | function test_flagged($ids, $flag) { |
|---|
| 204 | // view_break_phrase() is guaranteed to return only integers, so this is SQL safe. |
|---|
| 205 | $flattened_ids = implode(',', $ids); |
|---|
| 206 | return $this->_test_by_sql("SELECT content_id FROM {flag_counts} WHERE fid = %d AND content_id IN ($flattened_ids) AND count > 0", array($flag->fid)); |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | function test_flagged_by_current_user($ids, $flag) { |
|---|
| 210 | global $user; |
|---|
| 211 | if (!$user->uid) { |
|---|
| 212 | // Anonymous user |
|---|
| 213 | return FALSE; |
|---|
| 214 | } |
|---|
| 215 | $flattened_ids = implode(',', $ids); |
|---|
| 216 | return $this->_test_by_sql("SELECT content_id FROM {flag_content} WHERE fid = %d AND content_id IN ($flattened_ids) AND uid = %d", array($flag->fid, $user->uid)); |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | // Helper: executes an SQL query and returns all the content_id's. |
|---|
| 220 | function _test_by_sql($sql, $args) { |
|---|
| 221 | $passed = array(); |
|---|
| 222 | $result = db_query($sql, $args); |
|---|
| 223 | while ($row = db_fetch_object($result)) { |
|---|
| 224 | $passed[] = $row->content_id; |
|---|
| 225 | } |
|---|
| 226 | return $passed; |
|---|
| 227 | } |
|---|
| 228 | } |
|---|
| 229 | |
|---|