views_handler_argument.inc 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360
  1. <?php
  2. /**
  3. * @file
  4. * Definition of views_handler_argument.
  5. */
  6. /**
  7. * @defgroup views_argument_handlers Views argument handlers
  8. * Handlers to tell Views how to contextually filter queries.
  9. * @{
  10. */
  11. /**
  12. * Base class for arguments.
  13. *
  14. * The basic argument works for very simple arguments such as nid and uid
  15. *
  16. * Definition terms for this handler:
  17. * - name field: The field to use for the name to use in the summary, which is
  18. * the displayed output. For example, for the node: nid argument, the argument
  19. * itself is the nid, but node.title is displayed.
  20. * - name table: The table to use for the name, should it not be in the same
  21. * table as the argument.
  22. * - empty field name: For arguments that can have no value, such as taxonomy
  23. * which can have "no term", this is the string which will be displayed for
  24. * this lack of value. Be sure to use t().
  25. * - validate type: A little used string to allow an argument to restrict
  26. * which validator is available to just one. Use the validator ID. This
  27. * probably should not be used at all, and may disappear or change.
  28. * - numeric: If set to TRUE this field is numeric and will use %d instead of
  29. * %s in queries.
  30. *
  31. * @ingroup views_argument_handlers
  32. */
  33. class views_handler_argument extends views_handler {
  34. /**
  35. * @var object
  36. */
  37. public $validator = NULL;
  38. /**
  39. * @var mixed
  40. */
  41. public $argument = NULL;
  42. /**
  43. * @var mixed
  44. */
  45. public $value = NULL;
  46. /**
  47. * The table to use for the name, if not the same table as the argument.
  48. *
  49. * @var string
  50. */
  51. public $name_table;
  52. /**
  53. * The field to use for the name to use in the summary.
  54. *
  55. * Used as the displayed output. For example, for the node: nid argument, the
  56. * argument itself is the nid, but node.title is displayed.
  57. *
  58. * @var string
  59. */
  60. public $name_field;
  61. /**
  62. * {@inheritdoc}
  63. */
  64. public function construct() {
  65. parent::construct();
  66. if (!empty($this->definition['name field'])) {
  67. $this->name_field = $this->definition['name field'];
  68. }
  69. if (!empty($this->definition['name table'])) {
  70. $this->name_table = $this->definition['name table'];
  71. }
  72. }
  73. /**
  74. * {@inheritdoc}
  75. */
  76. public function init(&$view, &$options) {
  77. parent::init($view, $options);
  78. // Compatibility: The new UI changed several settings.
  79. if (!empty($options['wildcard']) && !isset($options['exception']['value'])) {
  80. $this->options['exception']['value'] = $options['wildcard'];
  81. }
  82. if (!empty($options['wildcard_substitution']) && !isset($options['exception']['title'])) {
  83. // Enable the checkbox if the title is filled in.
  84. $this->options['exception']['title_enable'] = 1;
  85. $this->options['exception']['title'] = $options['wildcard_substitution'];
  86. }
  87. if (!isset($options['summary']['format']) && !empty($options['style_plugin'])) {
  88. $this->options['summary']['format'] = $options['style_plugin'];
  89. }
  90. // Setup default value.
  91. $options['style_options'] = isset($options['style_options']) ? $options['style_options'] : array();
  92. if (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary asc') {
  93. $this->options['default_action'] = 'summary';
  94. $this->options['summary']['sort_order'] = 'asc';
  95. $this->options['summary']['number_of_records'] = 0;
  96. $this->options['summary_options'] = $options['style_options'];
  97. }
  98. elseif (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary desc') {
  99. $this->options['default_action'] = 'summary';
  100. $this->options['summary']['sort_order'] = 'desc';
  101. $this->options['summary']['number_of_records'] = 0;
  102. $this->options['summary_options'] = $options['style_options'];
  103. }
  104. elseif (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary asc by count') {
  105. $this->options['default_action'] = 'summary';
  106. $this->options['summary']['sort_order'] = 'asc';
  107. $this->options['summary']['number_of_records'] = 1;
  108. $this->options['summary_options'] = $options['style_options'];
  109. }
  110. elseif (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary desc by count') {
  111. $this->options['default_action'] = 'summary';
  112. $this->options['summary']['sort_order'] = 'desc';
  113. $this->options['summary']['number_of_records'] = 1;
  114. $this->options['summary_options'] = $options['style_options'];
  115. }
  116. if (!empty($options['title']) && !isset($options['title_enable'])) {
  117. $this->options['title_enable'] = 1;
  118. }
  119. if (!empty($options['breadcrumb']) && !isset($options['breadcrumb_enable'])) {
  120. $this->options['breadcrumb_enable'] = 1;
  121. }
  122. if (!empty($options['validate_type']) && !isset($options['validate']['type'])) {
  123. $this->options['validate']['type'] = $options['validate_type'];
  124. $this->options['specify_validation'] = 1;
  125. }
  126. if (!empty($options['validate_fail']) && !isset($options['validate']['fail'])) {
  127. $this->options['validate']['fail'] = $options['validate_fail'];
  128. $this->options['specify_validation'] = 1;
  129. }
  130. }
  131. /**
  132. * Give an argument the opportunity to modify the breadcrumb, if it wants.
  133. *
  134. * Only gets called on displays where a breadcrumb is actually used.
  135. *
  136. * The breadcrumb will be in the form of an array, with the keys being
  137. * the path and the value being the already sanitized title of the path.
  138. */
  139. public function set_breadcrumb(&$breadcrumb) {
  140. }
  141. /**
  142. * Determine if the argument can generate a breadcrumb
  143. *
  144. * @return bool
  145. */
  146. public function uses_breadcrumb() {
  147. $info = $this->default_actions($this->options['default_action']);
  148. return !empty($info['breadcrumb']);
  149. }
  150. /**
  151. * {@inheritdoc}
  152. */
  153. public function is_exception($arg = NULL) {
  154. if (!isset($arg)) {
  155. $arg = isset($this->argument) ? $this->argument : NULL;
  156. }
  157. return !empty($this->options['exception']['value']) && ($this->options['exception']['value'] === $arg);
  158. }
  159. /**
  160. * Work out which title to use.
  161. *
  162. * @return string
  163. * The title string to use.
  164. */
  165. public function exception_title() {
  166. // If title overriding is off for the exception, return the normal title.
  167. if (empty($this->options['exception']['title_enable'])) {
  168. return $this->get_title();
  169. }
  170. return $this->options['exception']['title'];
  171. }
  172. /**
  173. * Determine if the argument needs a style plugin.
  174. *
  175. * @return bool
  176. */
  177. public function needs_style_plugin() {
  178. $info = $this->default_actions($this->options['default_action']);
  179. $validate_info = $this->default_actions($this->options['validate']['fail']);
  180. return !empty($info['style plugin']) || !empty($validate_info['style plugin']);
  181. }
  182. /**
  183. * {@inheritdoc}
  184. */
  185. public function option_definition() {
  186. $options = parent::option_definition();
  187. $options['default_action'] = array('default' => 'ignore');
  188. $options['exception'] = array(
  189. 'contains' => array(
  190. 'value' => array('default' => 'all'),
  191. 'title_enable' => array('default' => FALSE, 'bool' => TRUE),
  192. 'title' => array('default' => 'All', 'translatable' => TRUE),
  193. ),
  194. );
  195. $options['title_enable'] = array('default' => FALSE, 'bool' => TRUE);
  196. $options['title'] = array('default' => '', 'translatable' => TRUE);
  197. $options['breadcrumb_enable'] = array('default' => FALSE, 'bool' => TRUE);
  198. $options['breadcrumb'] = array('default' => '', 'translatable' => TRUE);
  199. $options['default_argument_type'] = array('default' => 'fixed', 'export' => 'export_plugin');
  200. $options['default_argument_options'] = array('default' => array(), 'export' => FALSE);
  201. $options['default_argument_skip_url'] = array('default' => FALSE, 'bool' => TRUE);
  202. $options['summary_options'] = array('default' => array(), 'export' => FALSE);
  203. $options['summary'] = array(
  204. 'contains' => array(
  205. 'sort_order' => array('default' => 'asc'),
  206. 'number_of_records' => array('default' => 0),
  207. 'format' => array('default' => 'default_summary', 'export' => 'export_summary'),
  208. ),
  209. );
  210. $options['specify_validation'] = array('default' => FALSE, 'bool' => TRUE);
  211. $options['validate'] = array(
  212. 'contains' => array(
  213. 'type' => array('default' => 'none', 'export' => 'export_validation'),
  214. 'fail' => array('default' => 'not found'),
  215. ),
  216. );
  217. $options['validate_options'] = array('default' => array(), 'export' => FALSE);
  218. return $options;
  219. }
  220. /**
  221. * {@inheritdoc}
  222. */
  223. public function options_form(&$form, &$form_state) {
  224. parent::options_form($form, $form_state);
  225. $argument_text = $this->view->display_handler->get_argument_text();
  226. $form['#pre_render'][] = 'views_ui_pre_render_move_argument_options';
  227. $form['description'] = array(
  228. '#markup' => $argument_text['description'],
  229. '#theme_wrappers' => array('container'),
  230. '#attributes' => array('class' => array('description')),
  231. );
  232. $form['no_argument'] = array(
  233. '#type' => 'fieldset',
  234. '#title' => $argument_text['filter value not present'],
  235. );
  236. // Everything in the fieldset is floated, so the last element needs to
  237. // clear those floats.
  238. $form['no_argument']['clearfix'] = array(
  239. '#weight' => 1000,
  240. '#markup' => '<div class="clearfix"></div>',
  241. );
  242. $form['default_action'] = array(
  243. '#type' => 'radios',
  244. '#process' => array('views_ui_process_container_radios'),
  245. '#default_value' => $this->options['default_action'],
  246. '#fieldset' => 'no_argument',
  247. );
  248. $form['exception'] = array(
  249. '#type' => 'fieldset',
  250. '#title' => t('Exceptions'),
  251. '#collapsible' => TRUE,
  252. '#collapsed' => TRUE,
  253. '#fieldset' => 'no_argument',
  254. );
  255. $form['exception']['value'] = array(
  256. '#type' => 'textfield',
  257. '#title' => t('Exception value'),
  258. '#size' => 20,
  259. '#default_value' => $this->options['exception']['value'],
  260. '#description' => t('If this value is received, the filter will be ignored; i.e, "all values"'),
  261. );
  262. $form['exception']['title_enable'] = array(
  263. '#type' => 'checkbox',
  264. '#title' => t('Override title'),
  265. '#default_value' => $this->options['exception']['title_enable'],
  266. );
  267. $form['exception']['title'] = array(
  268. '#type' => 'textfield',
  269. '#title' => t('Override title'),
  270. '#title_display' => 'invisible',
  271. '#size' => 20,
  272. '#default_value' => $this->options['exception']['title'],
  273. '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'),
  274. '#dependency' => array(
  275. 'edit-options-exception-title-enable' => array('1'),
  276. ),
  277. );
  278. $options = array();
  279. $defaults = $this->default_actions();
  280. $validate_options = array();
  281. foreach ($defaults as $id => $info) {
  282. $options[$id] = $info['title'];
  283. if (empty($info['default only'])) {
  284. $validate_options[$id] = $info['title'];
  285. }
  286. if (!empty($info['form method'])) {
  287. $this->{$info['form method']}($form, $form_state);
  288. }
  289. }
  290. $form['default_action']['#options'] = $options;
  291. $form['argument_present'] = array(
  292. '#type' => 'fieldset',
  293. '#title' => $argument_text['filter value present'],
  294. );
  295. $form['title_enable'] = array(
  296. '#type' => 'checkbox',
  297. '#title' => t('Override title'),
  298. '#default_value' => $this->options['title_enable'],
  299. '#fieldset' => 'argument_present',
  300. );
  301. $form['title'] = array(
  302. '#type' => 'textfield',
  303. '#title' => t('Provide title'),
  304. '#title_display' => 'invisible',
  305. '#default_value' => $this->options['title'],
  306. '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'),
  307. '#dependency' => array(
  308. 'edit-options-title-enable' => array('1'),
  309. ),
  310. '#fieldset' => 'argument_present',
  311. );
  312. $form['breadcrumb_enable'] = array(
  313. '#type' => 'checkbox',
  314. '#title' => t('Override breadcrumb'),
  315. '#default_value' => $this->options['breadcrumb_enable'],
  316. '#fieldset' => 'argument_present',
  317. );
  318. $form['breadcrumb'] = array(
  319. '#type' => 'textfield',
  320. '#title' => t('Provide breadcrumb'),
  321. '#title_display' => 'invisible',
  322. '#default_value' => $this->options['breadcrumb'],
  323. '#description' => t('Enter a breadcrumb name you would like to use. See "Title" for percent substitutions.'),
  324. '#dependency' => array(
  325. 'edit-options-breadcrumb-enable' => array('1'),
  326. ),
  327. '#fieldset' => 'argument_present',
  328. );
  329. $form['specify_validation'] = array(
  330. '#type' => 'checkbox',
  331. '#title' => t('Specify validation criteria'),
  332. '#default_value' => $this->options['specify_validation'],
  333. '#fieldset' => 'argument_present',
  334. );
  335. $form['validate'] = array(
  336. '#type' => 'container',
  337. '#fieldset' => 'argument_present',
  338. );
  339. // @todo The mockup wanted to use "Validate using" here, but it doesn't
  340. // work well with many options (they'd need to be changed as well)
  341. $form['validate']['type'] = array(
  342. '#type' => 'select',
  343. '#title' => t('Validator'),
  344. '#default_value' => $this->options['validate']['type'],
  345. '#dependency' => array(
  346. 'edit-options-specify-validation' => array('1'),
  347. ),
  348. );
  349. $validate_types = array('none' => t('- Basic validation -'));
  350. $plugins = views_fetch_plugin_data('argument validator');
  351. foreach ($plugins as $id => $info) {
  352. if (!empty($info['no ui'])) {
  353. continue;
  354. }
  355. $valid = TRUE;
  356. if (!empty($info['type'])) {
  357. $valid = FALSE;
  358. if (empty($this->definition['validate type'])) {
  359. continue;
  360. }
  361. foreach ((array) $info['type'] as $type) {
  362. if ($type == $this->definition['validate type']) {
  363. $valid = TRUE;
  364. break;
  365. }
  366. }
  367. }
  368. // If we decide this validator is ok, add it to the list.
  369. if ($valid) {
  370. $plugin = $this->get_plugin('argument validator', $id);
  371. if ($plugin) {
  372. if ($plugin->access() || $this->options['validate']['type'] == $id) {
  373. $form['validate']['options'][$id] = array(
  374. '#prefix' => '<div id="edit-options-validate-options-' . $id . '-wrapper">',
  375. '#suffix' => '</div>',
  376. '#type' => 'item',
  377. // Even if the plugin has no options, add the key to the
  378. // form_state. Trick it into checking input to make #process run.
  379. '#input' => TRUE,
  380. '#dependency' => array(
  381. 'edit-options-specify-validation' => array('1'),
  382. 'edit-options-validate-type' => array($id),
  383. ),
  384. '#dependency_count' => 2,
  385. '#id' => 'edit-options-validate-options-' . $id,
  386. );
  387. $plugin->options_form($form['validate']['options'][$id], $form_state);
  388. $validate_types[$id] = $info['title'];
  389. }
  390. }
  391. }
  392. }
  393. asort($validate_types);
  394. $form['validate']['type']['#options'] = $validate_types;
  395. $form['validate']['fail'] = array(
  396. '#type' => 'select',
  397. '#title' => t('Action to take if filter value does not validate'),
  398. '#default_value' => $this->options['validate']['fail'],
  399. '#options' => $validate_options,
  400. '#dependency' => array(
  401. 'edit-options-specify-validation' => array('1'),
  402. ),
  403. '#fieldset' => 'argument_present',
  404. );
  405. }
  406. /**
  407. * {@inheritdoc}
  408. */
  409. public function options_validate(&$form, &$form_state) {
  410. if (empty($form_state['values']['options'])) {
  411. return;
  412. }
  413. // Let the plugins do validation.
  414. $default_id = $form_state['values']['options']['default_argument_type'];
  415. $plugin = $this->get_plugin('argument default', $default_id);
  416. if ($plugin && isset($form['argument_default'][$default_id]) && isset($form_state['values']['options']['argument_default'][$default_id])) {
  417. $plugin->options_validate($form['argument_default'][$default_id], $form_state, $form_state['values']['options']['argument_default'][$default_id]);
  418. }
  419. // Validate summary plugin options if one is present.
  420. if (isset($form_state['values']['options']['summary']['format'])) {
  421. $summary_id = $form_state['values']['options']['summary']['format'];
  422. $plugin = $this->get_plugin('style', $summary_id);
  423. if ($plugin) {
  424. $plugin->options_validate($form['summary']['options'][$summary_id], $form_state, $form_state['values']['options']['summary']['options'][$summary_id]);
  425. }
  426. }
  427. $validate_id = $form_state['values']['options']['validate']['type'];
  428. $plugin = $this->get_plugin('argument validator', $validate_id);
  429. if ($plugin) {
  430. $plugin->options_validate($form['validate']['options'][$default_id], $form_state, $form_state['values']['options']['validate']['options'][$validate_id]);
  431. }
  432. }
  433. /**
  434. * {@inheritdoc}
  435. */
  436. public function options_submit(&$form, &$form_state) {
  437. if (empty($form_state['values']['options'])) {
  438. return;
  439. }
  440. // Let the plugins make submit modifications if necessary.
  441. $default_id = $form_state['values']['options']['default_argument_type'];
  442. $plugin = $this->get_plugin('argument default', $default_id);
  443. if ($plugin) {
  444. $options = &$form_state['values']['options']['argument_default'][$default_id];
  445. $plugin->options_submit($form['argument_default'][$default_id], $form_state, $options);
  446. // Copy the now submitted options to their final resting place so they
  447. // get saved.
  448. $form_state['values']['options']['default_argument_options'] = $options;
  449. }
  450. // Handle summary plugin options if one is present.
  451. if (isset($form_state['values']['options']['summary']['format'])) {
  452. $summary_id = $form_state['values']['options']['summary']['format'];
  453. $plugin = $this->get_plugin('style', $summary_id);
  454. if ($plugin) {
  455. $options = &$form_state['values']['options']['summary']['options'][$summary_id];
  456. $plugin->options_submit($form['summary']['options'][$summary_id], $form_state, $options);
  457. // Copy the now submitted options to their final resting place so they
  458. // get saved.
  459. $form_state['values']['options']['summary_options'] = $options;
  460. }
  461. }
  462. $validate_id = $form_state['values']['options']['validate']['type'];
  463. $plugin = $this->get_plugin('argument validator', $validate_id);
  464. if ($plugin) {
  465. $options = &$form_state['values']['options']['validate']['options'][$validate_id];
  466. $plugin->options_submit($form['validate']['options'][$validate_id], $form_state, $options);
  467. // Copy the now submitted options to their final resting place so they
  468. // get saved.
  469. $form_state['values']['options']['validate_options'] = $options;
  470. }
  471. // Clear out the content of title if it's not enabled.
  472. $options =& $form_state['values']['options'];
  473. if (empty($options['title_enable'])) {
  474. $options['title'] = '';
  475. }
  476. }
  477. /**
  478. * Provide a list of default behaviors for this argument if the argument
  479. * is not present.
  480. *
  481. * Override this method to provide additional (or fewer) default behaviors.
  482. */
  483. public function default_actions($which = NULL) {
  484. $defaults = array(
  485. 'ignore' => array(
  486. 'title' => t('Display all results for the specified field'),
  487. 'method' => 'default_ignore',
  488. // Generate a breadcrumb to here.
  489. 'breadcrumb' => TRUE,
  490. ),
  491. 'default' => array(
  492. 'title' => t('Provide default value'),
  493. 'method' => 'default_default',
  494. 'form method' => 'default_argument_form',
  495. 'has default argument' => TRUE,
  496. // This can only be used for missing argument, not validation failure.
  497. 'default only' => TRUE,
  498. // Generate a breadcrumb to here.
  499. 'breadcrumb' => TRUE,
  500. ),
  501. 'not found' => array(
  502. 'title' => t('Hide view'),
  503. 'method' => 'default_not_found',
  504. // This is a hard fail condition.
  505. 'hard fail' => TRUE,
  506. ),
  507. 'summary' => array(
  508. 'title' => t('Display a summary'),
  509. 'method' => 'default_summary',
  510. 'form method' => 'default_summary_form',
  511. 'style plugin' => TRUE,
  512. // Generate a breadcrumb to here.
  513. 'breadcrumb' => TRUE,
  514. ),
  515. 'empty' => array(
  516. 'title' => t('Display contents of "No results found"'),
  517. 'method' => 'default_empty',
  518. // Generate a breadcrumb to here.
  519. 'breadcrumb' => TRUE,
  520. ),
  521. 'access denied' => array(
  522. 'title' => t('Display "Access Denied"'),
  523. 'method' => 'default_access_denied',
  524. // Generate a breadcrumb to here.
  525. 'breadcrumb' => FALSE,
  526. ),
  527. );
  528. if ($this->view->display_handler->has_path()) {
  529. $defaults['not found']['title'] = t('Show "Page not found"');
  530. }
  531. if ($which) {
  532. if (!empty($defaults[$which])) {
  533. return $defaults[$which];
  534. }
  535. }
  536. else {
  537. return $defaults;
  538. }
  539. }
  540. /**
  541. * Provide a form for selecting the default argument.
  542. *
  543. * Used when the default action is set to provide default argument.
  544. */
  545. public function default_argument_form(&$form, &$form_state) {
  546. $plugins = views_fetch_plugin_data('argument default');
  547. $options = array();
  548. $form['default_argument_skip_url'] = array(
  549. '#type' => 'checkbox',
  550. '#title' => t('Skip default argument for view URL'),
  551. '#default_value' => $this->options['default_argument_skip_url'],
  552. '#description' => t('Select whether to include this default argument when constructing the URL for this view. Skipping default arguments is useful e.g. in the case of feeds.'),
  553. );
  554. $form['default_argument_type'] = array(
  555. '#prefix' => '<div id="edit-options-default-argument-type-wrapper">',
  556. '#suffix' => '</div>',
  557. '#type' => 'select',
  558. '#id' => 'edit-options-default-argument-type',
  559. '#title' => t('Type'),
  560. '#default_value' => $this->options['default_argument_type'],
  561. '#dependency' => array(
  562. 'radio:options[default_action]' => array(
  563. 'default',
  564. ),
  565. ),
  566. // Views custom key, moves this element to the appropriate container
  567. // under the radio button.
  568. '#argument_option' => 'default',
  569. );
  570. foreach ($plugins as $id => $info) {
  571. if (!empty($info['no ui'])) {
  572. continue;
  573. }
  574. $plugin = $this->get_plugin('argument default', $id);
  575. if ($plugin) {
  576. if ($plugin->access() || $this->options['default_argument_type'] == $id) {
  577. $form['argument_default']['#argument_option'] = 'default';
  578. $form['argument_default'][$id] = array(
  579. '#prefix' => '<div id="edit-options-argument-default-options-' . $id . '-wrapper">',
  580. '#suffix' => '</div>',
  581. '#id' => 'edit-options-argument-default-options-' . $id,
  582. '#type' => 'item',
  583. // Even if the plugin has no options add the key to the form_state.
  584. '#input' => TRUE,
  585. '#dependency' => array(
  586. 'radio:options[default_action]' => array('default'),
  587. 'edit-options-default-argument-type' => array($id),
  588. ),
  589. '#dependency_count' => 2,
  590. );
  591. $options[$id] = $info['title'];
  592. $plugin->options_form($form['argument_default'][$id], $form_state);
  593. }
  594. }
  595. }
  596. asort($options);
  597. $form['default_argument_type']['#options'] = $options;
  598. }
  599. /**
  600. * Provide a form for selecting further summary options when the default
  601. * action is set to display one.
  602. */
  603. public function default_summary_form(&$form, &$form_state) {
  604. $style_plugins = views_fetch_plugin_data('style');
  605. $summary_plugins = array();
  606. $format_options = array();
  607. foreach ($style_plugins as $key => $plugin) {
  608. if (isset($plugin['type']) && $plugin['type'] == 'summary') {
  609. $summary_plugins[$key] = $plugin;
  610. $format_options[$key] = $plugin['title'];
  611. }
  612. }
  613. $form['summary'] = array(
  614. // Views custom key, moves this element to the appropriate container
  615. // under the radio button.
  616. '#argument_option' => 'summary',
  617. );
  618. $form['summary']['sort_order'] = array(
  619. '#type' => 'radios',
  620. '#title' => t('Sort order'),
  621. '#options' => array('asc' => t('Ascending'), 'desc' => t('Descending')),
  622. '#default_value' => $this->options['summary']['sort_order'],
  623. '#dependency' => array('radio:options[default_action]' => array('summary')),
  624. );
  625. $form['summary']['number_of_records'] = array(
  626. '#type' => 'radios',
  627. '#title' => t('Sort by'),
  628. '#default_value' => $this->options['summary']['number_of_records'],
  629. '#options' => array(
  630. 0 => $this->get_sort_name(),
  631. 1 => t('Number of records'),
  632. ),
  633. '#dependency' => array('radio:options[default_action]' => array('summary')),
  634. );
  635. $form['summary']['format'] = array(
  636. '#type' => 'radios',
  637. '#title' => t('Format'),
  638. '#options' => $format_options,
  639. '#default_value' => $this->options['summary']['format'],
  640. '#dependency' => array('radio:options[default_action]' => array('summary')),
  641. );
  642. foreach ($summary_plugins as $id => $info) {
  643. if (empty($info['uses options'])) {
  644. continue;
  645. }
  646. $plugin = $this->get_plugin('style', $id);
  647. if ($plugin) {
  648. $form['summary']['options'][$id] = array(
  649. '#prefix' => '<div id="edit-options-summary-options-' . $id . '-wrapper">',
  650. '#suffix' => '</div>',
  651. '#id' => 'edit-options-summary-options-' . $id,
  652. '#type' => 'item',
  653. // Trick it into checking input to make #process run.
  654. '#input' => TRUE,
  655. '#dependency' => array(
  656. 'radio:options[default_action]' => array('summary'),
  657. 'radio:options[summary][format]' => array($id),
  658. ),
  659. '#dependency_count' => 2,
  660. );
  661. $options[$id] = $info['title'];
  662. $plugin->options_form($form['summary']['options'][$id], $form_state);
  663. }
  664. }
  665. }
  666. /**
  667. * Handle the default action, which means our argument wasn't present.
  668. *
  669. * Override this method only with extreme care.
  670. *
  671. * @return bool
  672. * A boolean value; if TRUE, continue building this view. If FALSE,
  673. * building the view will be aborted here.
  674. */
  675. public function default_action($info = NULL) {
  676. if (!isset($info)) {
  677. $info = $this->default_actions($this->options['default_action']);
  678. }
  679. if (!$info) {
  680. return FALSE;
  681. }
  682. if (!empty($info['method args'])) {
  683. return call_user_func_array(array(&$this, $info['method']), $info['method args']);
  684. }
  685. else {
  686. return $this->{$info['method']}();
  687. }
  688. }
  689. /**
  690. * How to act if validation fails.
  691. */
  692. public function validate_fail() {
  693. $info = $this->default_actions($this->options['validate']['fail']);
  694. return $this->default_action($info);
  695. }
  696. /**
  697. * Default action: ignore.
  698. *
  699. * If an argument was expected and was not given, in this case, simply ignore
  700. * the argument entirely.
  701. */
  702. public function default_ignore() {
  703. return TRUE;
  704. }
  705. /**
  706. * Default action: not found.
  707. *
  708. * If an argument was expected and was not given, in this case, report the
  709. * view as 'not found' or hide it.
  710. */
  711. public function default_not_found() {
  712. // Set a failure condition and let the display manager handle it.
  713. $this->view->build_info['fail'] = TRUE;
  714. return FALSE;
  715. }
  716. /**
  717. * Default action: access denied.
  718. *
  719. * If an argument was expected and was not given, in this case, report the
  720. * view as 'access denied'.
  721. */
  722. public function default_access_denied() {
  723. $this->view->build_info['denied'] = TRUE;
  724. return FALSE;
  725. }
  726. /**
  727. * Default action: empty
  728. *
  729. * If an argument was expected and was not given, in this case, display the
  730. * view's empty text
  731. */
  732. public function default_empty() {
  733. // We return with no query; this will force the empty text.
  734. $this->view->built = TRUE;
  735. $this->view->executed = TRUE;
  736. $this->view->result = array();
  737. return FALSE;
  738. }
  739. /**
  740. * This just returns true.
  741. *
  742. * The view argument builder will know where to find the argument from.
  743. *
  744. * @todo Why is this needed?
  745. */
  746. public function default_default() {
  747. return TRUE;
  748. }
  749. /**
  750. * Determine if the argument is set to provide a default argument.
  751. */
  752. public function has_default_argument() {
  753. $info = $this->default_actions($this->options['default_action']);
  754. return !empty($info['has default argument']);
  755. }
  756. /**
  757. * Get a default argument, if available.
  758. */
  759. public function get_default_argument() {
  760. $plugin = $this->get_plugin('argument default');
  761. if ($plugin) {
  762. return $plugin->get_argument();
  763. }
  764. }
  765. /**
  766. * Process the summary arguments for display.
  767. *
  768. * For example, the validation plugin may want to alter an argument for use in
  769. * the URL.
  770. */
  771. public function process_summary_arguments(&$args) {
  772. if ($this->options['validate']['type'] != 'none') {
  773. if (isset($this->validator) || $this->validator = $this->get_plugin('argument validator')) {
  774. $this->validator->process_summary_arguments($args);
  775. }
  776. }
  777. }
  778. /**
  779. * Default action: summary.
  780. *
  781. * If an argument was expected and was not given, in this case, display a
  782. * summary query.
  783. */
  784. public function default_summary() {
  785. $this->view->build_info['summary'] = TRUE;
  786. $this->view->build_info['summary_level'] = $this->options['id'];
  787. // Change the display style to the summary style for this argument.
  788. $this->view->plugin_name = $this->options['summary']['format'];
  789. $this->view->style_options = $this->options['summary_options'];
  790. // Clear out the normal primary field and whatever else may have been added
  791. // and let the summary do the work.
  792. $this->query->clear_fields();
  793. $this->summary_query();
  794. $by = $this->options['summary']['number_of_records'] ? 'num_records' : NULL;
  795. $this->summary_sort($this->options['summary']['sort_order'], $by);
  796. // Summaries have their own sorting and fields, so tell the View not
  797. // to build these.
  798. $this->view->build_sort = $this->view->build_fields = FALSE;
  799. return TRUE;
  800. }
  801. /**
  802. * Build the info for the summary query.
  803. *
  804. * This must:
  805. * - add_groupby: group on this field in order to create summaries.
  806. * - add_field: add a 'num_nodes' field for the count. Usually it will be a
  807. * count on $view->base_field
  808. * - set_count_field: Reset the count field so we get the right paging.
  809. *
  810. * @return string
  811. * The alias used to get the number of records (count) for this entry.
  812. */
  813. public function summary_query() {
  814. $this->ensure_my_table();
  815. // Add the field.
  816. $this->base_alias = $this->query->add_field($this->table_alias, $this->real_field);
  817. $this->summary_name_field();
  818. return $this->summary_basics();
  819. }
  820. /**
  821. * Add the name field, which is the field displayed in summary queries.
  822. *
  823. * This is often used when the argument is numeric.
  824. */
  825. public function summary_name_field() {
  826. // Add the 'name' field. For example, if this is a uid argument, the name
  827. // field would be 'name' (i.e, the username).
  828. if (isset($this->name_table)) {
  829. // If the alias is different then we're probably added, not ensured, so
  830. // look up the join and add it instead.
  831. if ($this->table_alias != $this->name_table) {
  832. $j = views_get_table_join($this->name_table, $this->table);
  833. if ($j) {
  834. $join = clone $j;
  835. $join->left_table = $this->table_alias;
  836. $this->name_table_alias = $this->query->add_table($this->name_table, $this->relationship, $join);
  837. }
  838. }
  839. else {
  840. $this->name_table_alias = $this->query->ensure_table($this->name_table, $this->relationship);
  841. }
  842. }
  843. else {
  844. $this->name_table_alias = $this->table_alias;
  845. }
  846. if (isset($this->name_field)) {
  847. $this->name_alias = $this->query->add_field($this->name_table_alias, $this->name_field);
  848. }
  849. else {
  850. $this->name_alias = $this->base_alias;
  851. }
  852. }
  853. /**
  854. * Some basic summary behavior.
  855. *
  856. * This doesn't need to be repeated as much as code that goes into
  857. * summary_query().
  858. */
  859. public function summary_basics($count_field = TRUE) {
  860. // Add the number of nodes counter.
  861. $distinct = ($this->view->display_handler->get_option('distinct') && empty($this->query->no_distinct));
  862. $count_alias = $this->query->add_field($this->query->base_table,
  863. $this->query->base_field, 'num_records',
  864. array(
  865. 'count' => TRUE,
  866. 'distinct' => $distinct,
  867. ));
  868. $this->query->add_groupby($this->name_alias);
  869. if ($count_field) {
  870. $this->query->set_count_field($this->table_alias, $this->real_field);
  871. }
  872. $this->count_alias = $count_alias;
  873. }
  874. /**
  875. * Sorts the summary based upon the user's selection.
  876. *
  877. * The base variant of this is usually adequte.
  878. *
  879. * @param string $order
  880. * The order selected in the UI.
  881. */
  882. public function summary_sort($order, $by = NULL) {
  883. $this->query->add_orderby(NULL, NULL, $order, (!empty($by) ? $by : $this->name_alias));
  884. }
  885. /**
  886. * Provide the argument to use to link from the summary to the next level.
  887. *
  888. * This will be called once per row of a summary, and used as part of
  889. * $view->get_url().
  890. *
  891. * @param object $data
  892. * The query results for the row.
  893. */
  894. public function summary_argument($data) {
  895. return $data->{$this->base_alias};
  896. }
  897. /**
  898. * Provides the name to use for the summary.
  899. *
  900. * By default this is just the name field.
  901. *
  902. * @param object $data
  903. * The query results for the row.
  904. *
  905. * @return string
  906. * The summary.
  907. */
  908. public function summary_name($data) {
  909. $value = $data->{$this->name_alias};
  910. if (empty($value) && !empty($this->definition['empty field name'])) {
  911. $value = $this->definition['empty field name'];
  912. }
  913. return check_plain($value);
  914. }
  915. /**
  916. * Set up the query for this argument.
  917. *
  918. * The argument sent may be found at $this->argument.
  919. *
  920. * @param bool $group_by
  921. * Whether the query uses a group-by.
  922. */
  923. public function query($group_by = FALSE) {
  924. $this->ensure_my_table();
  925. $this->query->add_where(0, "$this->table_alias.$this->real_field", $this->argument);
  926. }
  927. /**
  928. * Get the title this argument will assign the view, given the argument.
  929. *
  930. * This usually needs to be overridden to provide a proper title.
  931. */
  932. public function title() {
  933. return check_plain($this->argument);
  934. }
  935. /**
  936. * Called by the view object to get the title.
  937. *
  938. * This may be set by a validator so we don't necessarily call through to
  939. * title().
  940. */
  941. public function get_title() {
  942. if (isset($this->validated_title)) {
  943. return $this->validated_title;
  944. }
  945. else {
  946. return $this->title();
  947. }
  948. }
  949. /**
  950. * Validate that this argument works. By default, all arguments are valid.
  951. */
  952. public function validate_arg($arg) {
  953. // By using % in URLs, arguments could be validated twice; this eases
  954. // that pain.
  955. if (isset($this->argument_validated)) {
  956. return $this->argument_validated;
  957. }
  958. if ($this->is_exception($arg)) {
  959. return $this->argument_validated = TRUE;
  960. }
  961. if ($this->options['validate']['type'] == 'none') {
  962. return $this->argument_validated = $this->validate_argument_basic($arg);
  963. }
  964. $plugin = $this->get_plugin('argument validator');
  965. if ($plugin) {
  966. return $this->argument_validated = $plugin->validate_argument($arg);
  967. }
  968. // If the plugin isn't found, fall back to the basic validation path.
  969. return $this->argument_validated = $this->validate_argument_basic($arg);
  970. }
  971. /**
  972. * Called by the menu system to validate an argument.
  973. *
  974. * This checks to see if this is a 'soft fail', which means that if the
  975. * argument fails to validate, but there is an action to take anyway, then
  976. * validation cannot actually fail.
  977. */
  978. public function validate_argument($arg) {
  979. $validate_info = $this->default_actions($this->options['validate']['fail']);
  980. if (empty($validate_info['hard fail'])) {
  981. return TRUE;
  982. }
  983. $rc = $this->validate_arg($arg);
  984. // If the validator has changed the validate fail condition to a soft fail,
  985. // deal with that.
  986. $validate_info = $this->default_actions($this->options['validate']['fail']);
  987. if (empty($validate_info['hard fail'])) {
  988. return TRUE;
  989. }
  990. return $rc;
  991. }
  992. /**
  993. * Provide a basic argument validation.
  994. *
  995. * This can be overridden for more complex types; the basic validator only
  996. * checks to see if the argument is not NULL or is numeric if the definition
  997. * says it's numeric.
  998. *
  999. * @return bool
  1000. * Whether or not the argument validates.
  1001. */
  1002. public function validate_argument_basic($arg) {
  1003. if (!isset($arg) || $arg === '') {
  1004. return FALSE;
  1005. }
  1006. if (!empty($this->definition['numeric']) && !isset($this->options['break_phrase']) && !is_numeric($arg)) {
  1007. return FALSE;
  1008. }
  1009. return TRUE;
  1010. }
  1011. /**
  1012. * Set the input for this argument
  1013. *
  1014. * @return bool
  1015. * TRUE if it successfully validates; FALSE if it does not.
  1016. */
  1017. public function set_argument($arg) {
  1018. $this->argument = $arg;
  1019. return $this->validate_arg($arg);
  1020. }
  1021. /**
  1022. * Get the value of this argument.
  1023. *
  1024. * @return string
  1025. * The value.
  1026. */
  1027. public function get_value() {
  1028. // If we already processed this argument, we're done.
  1029. if (isset($this->argument)) {
  1030. return $this->argument;
  1031. }
  1032. // Otherwise, we have to pretend to process ourself to find the value.
  1033. $value = NULL;
  1034. // Find the position of this argument within the view.
  1035. $position = 0;
  1036. foreach ($this->view->argument as $id => $argument) {
  1037. if ($id == $this->options['id']) {
  1038. break;
  1039. }
  1040. $position++;
  1041. }
  1042. $arg = isset($this->view->args[$position]) ? $this->view->args[$position] : NULL;
  1043. $this->position = $position;
  1044. // Clone ourselves so that we don't break things when we're really
  1045. // processing the arguments.
  1046. $argument = clone $this;
  1047. if (!isset($arg) && $argument->has_default_argument()) {
  1048. $arg = $argument->get_default_argument();
  1049. }
  1050. // Set the argument, which will also validate that the argument can be set.
  1051. if ($argument->set_argument($arg)) {
  1052. $value = $argument->argument;
  1053. }
  1054. unset($argument);
  1055. return $value;
  1056. }
  1057. /**
  1058. * Export handler for summary export.
  1059. *
  1060. * Arguments can have styles for the summary view. This special export
  1061. * handler makes sure this works properly.
  1062. *
  1063. * @return string
  1064. * The export summary.
  1065. */
  1066. public function export_summary($indent, $prefix, $storage, $option, $definition, $parents) {
  1067. $output = '';
  1068. $name = $this->options['summary'][$option];
  1069. $options = $this->options['summary_options'];
  1070. $plugin = views_get_plugin('style', $name);
  1071. if ($plugin) {
  1072. $plugin->init($this->view, $this->view->display_handler->display, $options);
  1073. // Write which plugin to use.
  1074. $output .= $indent . $prefix . "['summary']['$option'] = '$name';\n";
  1075. // Pass off to the plugin to export itself.
  1076. $output .= $plugin->export_options($indent, $prefix . "['summary_options']");
  1077. }
  1078. return $output;
  1079. }
  1080. /**
  1081. * Export handler for validation export.
  1082. *
  1083. * Arguments use validation plugins. This special export handler makes sure
  1084. * this works properly.
  1085. *
  1086. * @return string
  1087. * The validation response.
  1088. */
  1089. public function export_validation($indent, $prefix, $storage, $option, $definition, $parents) {
  1090. $output = '';
  1091. $name = $this->options['validate'][$option];
  1092. $options = $this->options['validate_options'];
  1093. $plugin = views_get_plugin('argument validator', $name);
  1094. if ($plugin) {
  1095. $plugin->init($this->view, $this->display, $options);
  1096. // Write which plugin to use.
  1097. $output .= $indent . $prefix . "['validate']['$option'] = '$name';\n";
  1098. // Pass off to the plugin to export itself.
  1099. $output .= $plugin->export_options($indent, $prefix . "['validate_options']");
  1100. }
  1101. return $output;
  1102. }
  1103. /**
  1104. * Generic plugin export handler.
  1105. *
  1106. * Since style and validation plugins have their own export handlers, this
  1107. * one is currently only used for default argument plugins.
  1108. *
  1109. * @return string
  1110. * Export string.
  1111. */
  1112. public function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
  1113. $output = '';
  1114. if ($option == 'default_argument_type') {
  1115. $type = 'argument default';
  1116. $option_name = 'default_argument_options';
  1117. }
  1118. $plugin = $this->get_plugin($type);
  1119. $name = $this->options[$option];
  1120. if ($plugin) {
  1121. // Write which plugin to use.
  1122. $output .= $indent . $prefix . "['$option'] = '$name';\n";
  1123. // Pass off to the plugin to export itself.
  1124. $output .= $plugin->export_options($indent, $prefix . "['$option_name']");
  1125. }
  1126. return $output;
  1127. }
  1128. /**
  1129. * Get the display or row plugin, if it exists.
  1130. */
  1131. public function get_plugin($type = 'argument default', $name = NULL) {
  1132. $options = array();
  1133. switch ($type) {
  1134. case 'argument default':
  1135. $plugin_name = $this->options['default_argument_type'];
  1136. $options_name = 'default_argument_options';
  1137. break;
  1138. case 'argument validator':
  1139. $plugin_name = $this->options['validate']['type'];
  1140. $options_name = 'validate_options';
  1141. break;
  1142. case 'style':
  1143. $plugin_name = $this->options['summary']['format'];
  1144. $options_name = 'summary_options';
  1145. break;
  1146. }
  1147. if (!$name) {
  1148. $name = $plugin_name;
  1149. }
  1150. // We only fetch the options if we're fetching the plugin actually in use.
  1151. if ($name == $plugin_name) {
  1152. $options = $this->options[$options_name];
  1153. }
  1154. $plugin = views_get_plugin($type, $name);
  1155. if ($plugin) {
  1156. // Style plugins expects different parameters as argument related plugins.
  1157. if ($type == 'style') {
  1158. $plugin->init($this->view, $this->view->display_handler->display, $options);
  1159. }
  1160. else {
  1161. $plugin->init($this->view, $this, $options);
  1162. }
  1163. return $plugin;
  1164. }
  1165. }
  1166. /**
  1167. * Return a description of how the argument would normally be sorted.
  1168. *
  1169. * Subclasses should override this to specify what the default sort order of
  1170. * their argument is (e.g. alphabetical, numeric, date).
  1171. *
  1172. * @return string
  1173. * The label for the sorter.
  1174. */
  1175. public function get_sort_name() {
  1176. return t('Default sort', array(), array('context' => 'Sort order'));
  1177. }
  1178. }
  1179. /**
  1180. * A special handler to take the place of missing or broken handlers.
  1181. *
  1182. * @ingroup views_argument_handlers
  1183. */
  1184. class views_handler_argument_broken extends views_handler_argument {
  1185. /**
  1186. * {@inheritdoc}
  1187. */
  1188. public function ui_name($short = FALSE) {
  1189. return t('Broken/missing handler');
  1190. }
  1191. /**
  1192. * {@inheritdoc}
  1193. */
  1194. public function ensure_my_table() {
  1195. // No table to ensure!
  1196. }
  1197. /**
  1198. * {@inheritdoc}
  1199. */
  1200. public function query($group_by = FALSE) {
  1201. // No query to run.
  1202. }
  1203. /**
  1204. * {@inheritdoc}
  1205. */
  1206. public function options_form(&$form, &$form_state) {
  1207. $form['markup'] = array(
  1208. '#markup' => '<div class="form-item description">' . t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.') . '</div>',
  1209. );
  1210. }
  1211. /**
  1212. * {@inheritdoc}
  1213. */
  1214. public function broken() {
  1215. return TRUE;
  1216. }
  1217. }
  1218. /**
  1219. * @}
  1220. */