flag_plugin_argument_validate_flaggability.inc 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <?php
  2. /**
  3. * @file
  4. * Contains the flaggability validator handler.
  5. */
  6. /**
  7. * Validates whether an argument is a flaggable/flagged object.
  8. *
  9. * @ingroup views
  10. */
  11. class flag_plugin_argument_validate_flaggability extends views_plugin_argument_validate {
  12. function construct() {
  13. parent::construct();
  14. $this->flag_type = $this->definition['flag type'];
  15. }
  16. function option_definition() {
  17. $options = parent::option_definition();
  18. $options['flag_name'] = array('default' => '*relationship*');
  19. $options['flag_test'] = array('default' => 'flaggable');
  20. $options['flag_id_type'] = array('default' => 'id');
  21. return $options;
  22. }
  23. function options_form(&$form, &$form_state) {
  24. // If there are no flag relationships set, skip this form of validation.
  25. $flags = flag_get_flags($this->flag_type);
  26. if (!$flags) {
  27. return array();
  28. }
  29. $options = $this->flags_options();
  30. $form['flag_name'] = array(
  31. '#type' => 'radios',
  32. '#title' => t('Flag'),
  33. '#options' => $options,
  34. '#default_value' => $this->options['flag_name'],
  35. '#description' => t('Select the flag to validate against.'),
  36. );
  37. if (!$options) {
  38. $form['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' => FLAG_ADMIN_PATH)) . '</p>';
  39. }
  40. $form['flag_test'] = array(
  41. '#type' => 'select',
  42. '#title' => t('Validate the @type only if', array('@type' => $this->flag_type)),
  43. '#options' => $this->tests_options(),
  44. '#default_value' => $this->options['flag_test'],
  45. );
  46. // This validator supports the "multiple IDs" syntax. It may not be
  47. // extremely useful, but similar validators do support this and it's a good
  48. // thing to be compatible.
  49. $form['flag_id_type'] = array(
  50. '#type' => 'select',
  51. '#title' => t('Argument type'),
  52. '#options' => array(
  53. 'id' => t('ID'),
  54. 'ids' => t('IDs separated by , or +'),
  55. ),
  56. '#default_value' => $this->options['flag_id_type'],
  57. );
  58. }
  59. /**
  60. * Returns form #options for the flags.
  61. *
  62. * Returns empty array if no flags were found.
  63. */
  64. function flags_options() {
  65. $flags = flag_get_flags($this->flag_type);
  66. if (!$flags) {
  67. return array();
  68. }
  69. else {
  70. foreach ($flags as $flag) {
  71. $options[$flag->name] = $flag->get_title();
  72. }
  73. $options['*relationship*'] = t('<em>Pick the first flag mentioned in the relationships.</em>');
  74. return $options;
  75. }
  76. }
  77. /**
  78. * Migrate existing Views 2 options to Views 3.
  79. */
  80. function convert_options(&$options) {
  81. $prefix = 'validate_argument_' . $this->flag_type . '_';
  82. if (!isset($options['flag_name']) && !empty($this->argument->options[$prefix . 'flag_name'])) {
  83. $options['flag_name'] = $this->argument->options[$prefix . 'flag_name'];
  84. $options['flag_test'] = $this->argument->options[$prefix . 'flag_test'];
  85. $options['flag_id_type'] = $this->argument->options[$prefix . 'flag_id_type'];
  86. }
  87. }
  88. /**
  89. * Declares all tests.
  90. *
  91. * This scheme makes it easy for derived classes to add and remove tests.
  92. */
  93. function tests_info($which = NULL) {
  94. return array(
  95. 'flaggable' => array(
  96. 'title' => t('It is flaggable'),
  97. 'callback' => 'test_flaggable',
  98. ),
  99. 'flagged' => array(
  100. 'title' => t('It is flagged at least once'),
  101. 'callback' => 'test_flagged',
  102. ),
  103. 'flagged_by_current_user' => array(
  104. 'title' => t('It is flagged by the current user'),
  105. 'callback' => 'test_flagged_by_current_user',
  106. ),
  107. );
  108. }
  109. function tests_options() {
  110. $options = array();
  111. foreach ($this->tests_info() as $id => $info) {
  112. $options[$id] = $info['title'];
  113. }
  114. return $options;
  115. }
  116. function get_flag() {
  117. $flag_name = $this->options['flag_name'];
  118. if ($flag_name == '*relationship*') {
  119. // Pick the first flag mentioned in the relationships.
  120. foreach ($this->view->relationship as $id => $handler) {
  121. // Note: we can't do $handler->field, because the relationship handler's
  122. // init() may overwrite it.
  123. if (strpos($handler->options['field'], 'flag') !== FALSE && !empty($handler->options['flag'])) {
  124. $flag = flag_get_flag($handler->options['flag']);
  125. if ($flag && $flag->entity_type == $this->flag_type) {
  126. return $flag;
  127. }
  128. }
  129. }
  130. }
  131. return flag_get_flag($flag_name);
  132. }
  133. /**
  134. * Tests whether the argument is flaggable, or flagged, or flagged by current
  135. * user. These are three possible tests, and which of the three to actually
  136. * carry out is determined by 'flag_test'.
  137. */
  138. function validate_argument($argument) {
  139. $flag_test = $this->options['flag_test'];
  140. $id_type = $this->options['flag_id_type'];
  141. $flag = $this->get_flag();
  142. if (!$flag) {
  143. // Validator is misconfigured somehow.
  144. return TRUE;
  145. }
  146. if ($id_type == 'id') {
  147. if (!is_numeric($argument)) {
  148. // If a user is being smart and types several IDs where only one is
  149. // expected, we invalidate this.
  150. return FALSE;
  151. }
  152. }
  153. $ids = views_break_phrase($argument, $this);
  154. if ($ids->value == array(-1)) {
  155. // Malformed argument syntax. Invalidate.
  156. return FALSE;
  157. }
  158. $ids = $ids->value;
  159. // Delegate the testing to the particual test method. $passed then
  160. // holds the IDs that passed the test.
  161. $tests_info = $this->tests_info();
  162. $method = $tests_info[$flag_test]['callback'];
  163. if (method_exists($this, $method)) {
  164. $passed = $this->$method($ids, $flag);
  165. }
  166. else {
  167. $passed = array();
  168. }
  169. // If some IDs exist that haven't $passed our test then validation fails.
  170. $failed = array_diff($ids, $passed);
  171. return empty($failed);
  172. }
  173. //
  174. // The actual tests. They return the IDs that passed.
  175. //
  176. function test_flaggable($ids, $flag) {
  177. return array_filter($ids, array($flag, 'applies_to_entity_id'));
  178. }
  179. function test_flagged($ids, $flag) {
  180. // view_break_phrase() is guaranteed to return only integers, so this is SQL
  181. // safe.
  182. $flattened_ids = implode(',', $ids);
  183. return $this->_test_by_sql("SELECT entity_id FROM {flag_counts} WHERE fid = :fid AND entity_id IN ($flattened_ids) AND count > 0", array(':fid' => $flag->fid));
  184. }
  185. function test_flagged_by_current_user($ids, $flag) {
  186. global $user;
  187. if (!$user->uid) {
  188. // Anonymous user.
  189. return array();
  190. }
  191. $flattened_ids = implode(',', $ids);
  192. return $this->_test_by_sql("SELECT entity_id FROM {flagging} WHERE fid = :fid AND entity_id IN ($flattened_ids) AND uid = :uid", array(':fid' => $flag->fid, ':uid' => $user->uid));
  193. }
  194. // Helper: executes an SQL query and returns all the entity_id's.
  195. function _test_by_sql($sql, $args) {
  196. $passed = array();
  197. $result = db_query($sql, $args);
  198. foreach ($result as $row) {
  199. $passed[] = $row->entity_id;
  200. }
  201. return $passed;
  202. }
  203. }