qbf.module 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. <?php
  2. /**
  3. * @file
  4. * Query By Form
  5. *
  6. * This module allows node modules to add a query by form tab for their node
  7. * types to the default search form
  8. *
  9. * @copyright 2008-2009 Ouest Systemes Informatiques (OSInet)
  10. * @author Frederic G. MARAND
  11. * @license Licensed under the CeCILL 2.0 and the General Public Licence version 2 or later
  12. * @package QBF
  13. */
  14. // $Id: qbf.module,v 1.9.4.2 2009-03-17 12:59:28 marand Exp $
  15. // ======== D6 LIMIT ==================================================================================================
  16. /* TODO New hook_mail implementation
  17. Because of changes to drupal_mail function, you need to move the variables
  18. setup and string replace commands into the hook_mail implementation and then
  19. call drupal_mail with the name of the module which contains this
  20. implementation, the mailkey, the recipient, the language of the user the mail
  21. goes to and some arbitrary parameters. */
  22. /* TODO db_next_id() is gone, and replaced as db_last_insert_id()
  23. Since db_next_id() introduce some problems, and the use of this function
  24. can be replaced by database level auto increment handling, db_next_id()
  25. is now gone and replaced as db_last_insert_id() with help of serial type
  26. under Schema API (check out http://drupal.org/node/149176 for more details).
  27. Please refer to drupal_write_record() as demonstration. */
  28. /* TODO FormAPI image buttons are now supported.
  29. FormAPI now offers the 'image_button' element type, allowing developers to
  30. use icons or other custom images in place of traditional HTML submit buttons.
  31. $form['my_image_button'] = array(
  32. '#type' => 'image_button',
  33. '#title' => t('My button'),
  34. '#return_value' => 'my_data',
  35. '#src' => 'my/image/path.jpg',
  36. ); */
  37. /* TODO Check node access before emailing content
  38. Modules like Organic Groups and Project Issue send the same content as an
  39. email notifications to many users. They should now be using the new 3rd
  40. parameter to node_access() to check access on the content before emailing it.
  41. Note that db_rewrite_sql() provodes no protection because the recipient is not
  42. the logged in user who is receiving the content. */
  43. /* TODO hook_user('view')
  44. The return value of hook_user('view') has changed, to match the process that
  45. nodes use for rendering. Modules should add their custom HTML to
  46. $account->content element. Further, this HTML should be in the form that
  47. drupal_render() recognizes. */
  48. /* TODO Node previews and adding form fields to the node form.
  49. There is a subtle but important difference in the way node previews (and other
  50. such operations) are carried out when adding or editing a node. With the new
  51. Forms API, the node form is handled as a multi-step form. When the node form
  52. is previewed, all the form values are submitted, and the form is rebuilt with
  53. those form values put into $form['#node']. Thus, form elements that are added
  54. to the node form will lose any user input unless they set their '#default_value'
  55. elements using this embedded node object. */
  56. /* TODO New user_mail_tokens() method may be useful.
  57. user.module now provides a user_mail_tokens() function to return an array
  58. of the tokens available for the email notification messages it sends when
  59. accounts are created, activated, blocked, etc. Contributed modules that
  60. wish to make use of the same tokens for their own needs are encouraged
  61. to use this function. */
  62. /* TODO
  63. There is a new hook_watchdog in core. This means that contributed modules
  64. can implement hook_watchdog to log Drupal events to custom destinations.
  65. Two core modules are included, dblog.module (formerly known as watchdog.module),
  66. and syslog.module. Other modules in contrib include an emaillog.module,
  67. included in the logging_alerts module. See syslog or emaillog for an
  68. example on how to implement hook_watchdog.
  69. function example_watchdog($log = array()) {
  70. if ($log['severity'] == WATCHDOG_ALERT) {
  71. mysms_send($log['user']->uid,
  72. $log['type'],
  73. $log['message'],
  74. $log['variables'],
  75. $log['severity'],
  76. $log['referer'],
  77. $log['ip'],
  78. format_date($log['timestamp']));
  79. }
  80. } */
  81. /* TODO Implement the hook_theme registry. Combine all theme registry entries
  82. into one hook_theme function in each corresponding module file.
  83. function qbf_theme() {
  84. return array(
  85. );
  86. }; */
  87. /* TODO
  88. An argument for replacements has been added to format_plural(),
  89. escaping and/or theming the values just as done with t().*/
  90. /* TODO $form['#base'] is gone
  91. In FormAPI, many forms with different form_ids can share the same validate,
  92. submit, and theme handlers. This can be done by manually populating the
  93. $form['#submit'], $form['#validate'], and $form['#theme'] elements with
  94. the proper function names. */
  95. /**
  96. * Saved error reporting level.
  97. *
  98. * QBF module is supposed to pass parsing at E_ALL|E_STRICT, but other modules
  99. * may not be so strict, so we save the level at the start of the module and
  100. * restore it at the end of the module.
  101. */
  102. global $_qbf_er;
  103. $_qbf_er = error_reporting(E_ALL | E_STRICT);
  104. /**
  105. * Remove this element from the generated form
  106. */
  107. define('QBF_LEVEL_REMOVE', 0);
  108. /**
  109. * This element is only for display in the generated form: do not include it
  110. * in the query vector.
  111. */
  112. define('QBF_LEVEL_DISPLAY', 1);
  113. /**
  114. * Include this element in the generated form and in the query vector, but do
  115. * not mark it as required.
  116. */
  117. define('QBF_LEVEL_OPTIONAL', 2);
  118. /**
  119. * Include this element in the generated form and in the query vector, and
  120. * mark it as required.
  121. */
  122. define('QBF_LEVEL_REQUIRED', 3);
  123. /**
  124. * The main QBF path.
  125. *
  126. * It MUST be a single component path, without a "/", otherwise qbf_menu() will
  127. * need to be changed.
  128. *
  129. * @ingroup paths
  130. * @see qbf_menu()
  131. */
  132. define('QBF_PATH_MAIN', 'qbf');
  133. /**
  134. * The QBF autocomplete path for search fields
  135. * @ingroup paths
  136. */
  137. define('QBF_PATH_AC', 'qbf/ac');
  138. /**
  139. * The path to the QBF settings page
  140. */
  141. define('QBF_PATH_SETTINGS', 'admin/settings/qbf');
  142. /**
  143. * Authorize use of QBF searches
  144. */
  145. define('QBF_PERM_QUERY', 'use QBF search functions');
  146. /**
  147. * Authorize QBF administration
  148. */
  149. define('QBF_PERM_ADMIN', 'administer QBF');
  150. /**
  151. * The name of the table used to store queries
  152. */
  153. define('QBF_TABLE_NAME', 'qbf_queries');
  154. /**
  155. * Notify owner about saved query deletions, variable name.
  156. */
  157. define('QBF_VAR_NOTIFY_DELETE', 'qbf_notify_delete');
  158. /**
  159. * Notify owner about saved query deletions, default value.
  160. */
  161. define('QBF_DEF_NOTIFY_DELETE', FALSE);
  162. /**
  163. * Transform a form array for QBF.
  164. *
  165. * This function obtains the form array using Forms API, and transforms it by
  166. * modifying widgets to other types where needed.
  167. *
  168. * Any additional parameter passed to the function is transmitted to the form
  169. * generating function.
  170. *
  171. * @ingroup forms
  172. * @param string $form_id
  173. * @return array
  174. */
  175. function qbf_transform_form($form_id) {
  176. // @todo GW: function qbf_transform_form(&$form_state, $form_id) {
  177. $ar_args = func_get_args();
  178. //dsm(array('qtf' => $ar_args));
  179. // Fetch the basic form and rename it, passing it the caller's arguments
  180. $form = call_user_func_array('drupal_retrieve_form', $ar_args);
  181. $new_form_id = "qbf_$form_id";
  182. // Only keep the children of the form and QBF properties on the form itself
  183. $elements = array();
  184. $new_form = array();
  185. $new_form['#qbf_source_form_id'] = $form_id;
  186. if (in_array('#qbf', element_properties($form))) {
  187. $new_form += $form['#qbf'];
  188. }
  189. foreach (element_children($form) as $key) {
  190. // dsm("Transforming $key, type " . $form[$key]['#type']);
  191. $new_element = _qbf_transform_element($key, $form[$key]);
  192. if (!is_null($new_element)) {
  193. $new_form[$key] = $new_element;
  194. }
  195. }
  196. $new_form['#id'] = $new_form_id;
  197. $new_form['#multistep'] = TRUE;
  198. // Do not set #redirect, even to FALSE (submit handlers)
  199. // $new_form['#redirect'] = FALSE;
  200. $new_form['#after_build'][] = 'qbf_after_build';
  201. $new_form['#submit'] = array('qbf_submit' => array());
  202. // dsm($new_form);
  203. return $new_form;
  204. }
  205. /**
  206. * Transform a form element for QBF.
  207. *
  208. * QBF-specific properties are:
  209. * - #qbf : array of properties
  210. * - #level: only within #qbf
  211. *
  212. * See QBF_* constants
  213. *
  214. * @ingroup forms
  215. * @param string $key
  216. * @param array $element
  217. * @return void
  218. */
  219. function _qbf_transform_element($key, $element) {
  220. // dsm(array('key' => $key, 'element' => $element));
  221. /**
  222. * List default type transformations applied to widget by FAPI.
  223. *
  224. * Types without a default transformation are not transformed
  225. */
  226. static $ar_default_type_transformations = array
  227. (
  228. 'button' => NULL,
  229. 'file' => NULL,
  230. // 'hidden' => NULL,
  231. 'markup' => NULL,
  232. 'password' => NULL,
  233. 'radio' => NULL,
  234. 'submit' => NULL,
  235. 'textarea' => 'textfield',
  236. // 'value' => 'value',
  237. );
  238. /**
  239. * List default property transformations applied to widget by FAPI property.
  240. *
  241. * Properties without a default transformation are not transformed
  242. */
  243. static $ar_default_property_transformations = array
  244. (
  245. // Standard properties
  246. '#action' => NULL,
  247. '#after_build' => NULL,
  248. '#base' => NULL,
  249. '#button_type' => NULL,
  250. '#built' => NULL,
  251. '#description' => NULL,
  252. '#method' => NULL,
  253. '#parents' => NULL,
  254. '#redirect' => NULL,
  255. '#ref' => NULL,
  256. '#required' => NULL,
  257. '#rows' => NULL,
  258. '#submit' => NULL,
  259. '#tree' => NULL,
  260. '#validate' => NULL,
  261. );
  262. /**
  263. * List properties causing causing element removal.
  264. *
  265. * The key is the property name, the value is the one causing removal.
  266. */
  267. static $ar_killer_properties = array
  268. (
  269. '#disabled' => TRUE,
  270. );
  271. // Transform type
  272. $source_type = $element['#type'];
  273. // .. Default transformation
  274. $dest_type = array_key_exists($source_type, $ar_default_type_transformations)
  275. ? $ar_default_type_transformations[$source_type]
  276. : $source_type;
  277. // .. Apply form-defined type override
  278. if (isset($element['#qbf']['#type'])) {
  279. $dest_type = $element['#qbf']['#type'];
  280. }
  281. if (is_null($dest_type)) {
  282. $ret = NULL;
  283. }
  284. else {
  285. $ret = $element;
  286. $ret['#type'] = $dest_type;
  287. if (!array_key_exists('#qbf', $element) || $element['#qbf']['#level'] == QBF_LEVEL_REMOVE) {
  288. $ret = NULL;
  289. }
  290. else {
  291. foreach (element_properties($element) as $property_name) {
  292. // Apply killer properties first to avoid useless work
  293. if (array_key_exists($property_name, $ar_killer_properties)
  294. && ($element[$property_name] = $ar_killer_properties[$property_name])) {
  295. $ret = NULL;
  296. break;
  297. }
  298. // Now transform or copy remaining properties
  299. if (array_key_exists($property_name, $ar_default_property_transformations)) {
  300. $ret[$property_name] = $ar_default_property_transformations[$property_name];
  301. }
  302. else {
  303. $ret[$property_name] = $element[$property_name];
  304. }
  305. // And apply form-defined property overrides
  306. if ($property_name == '#qbf') {
  307. foreach ($element[$property_name] as $override_name => $override_value) {
  308. $ret[$override_name] = $override_value;
  309. }
  310. }
  311. }
  312. // Recursively transform children
  313. foreach (element_children($element) as $child_name) {
  314. $child = _qbf_transform_element($child_name, $element[$child_name]);
  315. if (is_null($child)) {
  316. unset($ret[$child_name]);
  317. }
  318. else {
  319. $ret[$child_name] = $child;
  320. }
  321. }
  322. }
  323. }
  324. //dsm(array('key' => $key, 'transformed element' => $ret));
  325. return $ret;
  326. }
  327. /**
  328. * Implement hook_perm().
  329. *
  330. * @ingroup hooks
  331. * @return array
  332. */
  333. function qbf_perm() {
  334. $ret = array
  335. (
  336. QBF_PERM_ADMIN,
  337. QBF_PERM_QUERY,
  338. );
  339. return $ret;
  340. }
  341. /**
  342. * Implement hook_forms().
  343. *
  344. * @ingroup forms
  345. * @ingroup hooks
  346. * @return array
  347. */
  348. function qbf_forms() {
  349. $hook_name = 'qbf_register';
  350. foreach (module_implements($hook_name) as $module) {
  351. foreach (module_invoke($module, $hook_name) as $form_name) {
  352. $forms["qbf_$form_name"] = array
  353. (
  354. 'callback' => 'qbf_transform_form',
  355. 'callback arguments' => array($form_name),
  356. );
  357. }
  358. }
  359. return $forms;
  360. }
  361. /**
  362. * Insert the query results at the bottom of the query form.
  363. *
  364. * @ingroup forms
  365. * @param array $form
  366. * @param array $form_values
  367. * @return array
  368. */
  369. function qbf_after_build($form, $form_values) {
  370. if (empty($form['#post'])) {
  371. return $form;
  372. }
  373. // If #post is not empty, we are indeed querying
  374. $ar_query = _qbf_extract_query($form, $form_values);
  375. /* This function is called at the end of the form building process, which
  376. * means that child properties of #qbf have already been upgraded to element
  377. * properties. So we look for $form['#callback'] and not
  378. * $form['#qbf']['#callback']
  379. */
  380. if (isset($form['#callback']) && function_exists($function = $form['#callback'])) {
  381. $results = $function($ar_query);
  382. }
  383. else {
  384. drupal_set_message(t('QBF: incorrect callback function for search'), 'error');
  385. }
  386. $form['qbf_query_results'] = array
  387. (
  388. '#type' => 'markup',
  389. '#value' => $results,
  390. '#weight' => 10,
  391. );
  392. return $form;
  393. }
  394. /**
  395. * Recursively build a query array from the form and its values
  396. *
  397. * In the current version, element names are supposed to be unique, even at
  398. * different levels in the tree.
  399. *
  400. * @ingroup forms
  401. * @param array $form
  402. * @param array $form_values
  403. */
  404. function _qbf_extract_query($form, $form_values) {
  405. $name = $form['#parents'][0];
  406. // Elements which are removed or display-only have no place in the query
  407. if (array_key_exists('#qbf', $form) && array_key_exists('#level', $form['#qbf'])
  408. && $form['#qbf']['#level'] >= QBF_LEVEL_OPTIONAL) {
  409. $ret = array($name => $form_values[$name]);
  410. }
  411. else {
  412. $ret = array();
  413. }
  414. // QBF level is not inherited, so this loop is outside the "if" above
  415. foreach (element_children($form) as $child_name) {
  416. $ret += _qbf_extract_query($form[$child_name], $form_values);
  417. }
  418. return $ret;
  419. }
  420. /**
  421. * Provide an optional automatic mapping mechanism for query building.
  422. *
  423. * This function takes a partly built query map $ar_queryMap, and a defaults
  424. * array to complete it in $ar_defaults, and returns a fully built query array
  425. * ready to be used for querying.
  426. *
  427. * @param array $ar_query
  428. * @param array $ar_defaults
  429. * @return array
  430. */
  431. function qbf_query_mapper($ar_queryMap = array(), $ar_defaults = array()) {
  432. $ret = array();
  433. foreach ($ar_queryMap as $name => $value) {
  434. // accept NULL, empty strings...
  435. if (!is_array($value)) {
  436. $value = array();
  437. }
  438. $item = $value;
  439. foreach ($ar_defaults as $default_key => $default_value) {
  440. if (!array_key_exists($default_key, $item)) {
  441. $item[$default_key] = is_null($default_value)
  442. ? $name
  443. : $default_value;
  444. }
  445. // else if is already in $item, so we don't touch it
  446. }
  447. $ret[$name] = $item;
  448. }
  449. return $ret;
  450. }
  451. /**
  452. * Load a form_values array into a form used by QBF.
  453. *
  454. * This is typically useful when loading saved queries using qbf_query_load().
  455. * For other cases, the mechanisms built within FAPI should be used instead.
  456. *
  457. * @see qbf_query_load()
  458. *
  459. * @ingroup forms
  460. * @param array $form
  461. * @param array $form_values
  462. * @return array The modified form
  463. */
  464. function qbf_import_values($element, $form_values) {
  465. foreach (element_children($element) as $child_name) {
  466. if (!empty($form_values[$child_name])) {
  467. $element[$child_name]['#qbf']['#default_value'] = $form_values[$child_name];
  468. }
  469. $element[$child_name] = qbf_import_values($element[$child_name], $form_values);
  470. }
  471. return $element;
  472. }
  473. /**
  474. * Load a saved QBF query.
  475. *
  476. * It is not named qbf_load() although this would seem more natural, because a
  477. * hook_load() exists and this is not an implementation of this hook.
  478. *
  479. * @see qbf_import_values()
  480. *
  481. * @param int $qid
  482. * @return array A form_values array usable by qbf_import_values
  483. */
  484. function qbf_query_load($qid) {
  485. $sq = 'SELECT qq.qid, qq.uid, qq.query, qq.name '
  486. .'FROM {%s} qq '
  487. .'WHERE qq.qid = %d ';
  488. // db_rewrite_sql does not apply here until we add more advanced support for access control
  489. $q = db_query($sq, QBF_TABLE_NAME, $qid);
  490. $ret = db_fetch_object($q); // 0 or 1 row: we are querying on the primary key
  491. // FALSE does not happen
  492. if ($ret === NULL) {
  493. $ret = NULL;
  494. }
  495. else {
  496. $ret->query = unserialize($ret->query);
  497. //dsm($ret);
  498. }
  499. return $ret;
  500. }
  501. /**
  502. * Submit handler for query save form.
  503. *
  504. * @ingroup forms
  505. * @param $form_id string
  506. * @param $form_values array
  507. * @return string
  508. */
  509. function qbf_submit($form_id, $form_values) {
  510. /*
  511. function qbf_submit($form, &$form_state) {
  512. /* @TODO GW The 'op' element in the form values is deprecated.
  513. Each button can have #validate and #submit functions associated with it.
  514. Thus, there should be one button that submits the form and which invokes
  515. the normal form_id_validate and form_id_submit handlers. Any additional
  516. buttons which need to invoke different validate or submit functionality
  517. should have button-specific functions.
  518. switch ($form_state['values']['op']) { */
  519. switch ($form_values['op']) {
  520. case t('Search'):
  521. $ret = FALSE;
  522. break;
  523. case t('Save query'):
  524. _qbf_save($form_id, $form_values);
  525. //@TODO GW _qbf_save($form_state['values']['form_id'], $form_state['values']);
  526. drupal_set_message(t('Your query was saved as "@name".',
  527. array('@name' => $form_values['save-name'])));
  528. //@TODO GW _qbf_save($form_state['values']['form_id'], $form_state['values']);
  529. global $user;
  530. $ret = "user/$user->uid/edit/job";;
  531. break;
  532. }
  533. //dsm(array('QS' => $form_values));
  534. return $ret;
  535. /* @todo GW
  536. //dsm(array('QS' => $form_state['values']));
  537. $form_state['redirect'] = $ret;
  538. */
  539. }
  540. /**
  541. * List queries owned by a given user.
  542. *
  543. * @param int $uid > 0
  544. * @return array
  545. */
  546. function qbf_get_queries_by_user($uid = NULL) {
  547. if (is_null($uid)) {
  548. global $user;
  549. $uid = $user->uid;
  550. }
  551. $sq = 'SELECT qq.qid, qq.uid, qq.name, qq.query, qq.updated '
  552. .'FROM {%s} qq '
  553. .'WHERE qq.uid = %d '
  554. .'ORDER BY qq.name ';
  555. $q = db_query($sq, QBF_TABLE_NAME, $uid);
  556. $ret = array();
  557. while ($o = db_fetch_object($q)) {
  558. $ret[$o->qid] = $o; // qid is the PK, so it is present and unique
  559. }
  560. return $ret;
  561. }
  562. /**
  563. * Save a query and return its qid.
  564. *
  565. * @ingroup forms
  566. * @param $form_id string
  567. * @param $form_values array
  568. * @return int
  569. */
  570. function _qbf_save($form_id, $form_values) {
  571. global $user;
  572. if ($user->uid == 0) {
  573. $warning = t('Attempt by anonymous user to save a QBF query. Should not happen.');
  574. drupal_set_message($warning, 'error');
  575. watchdog('qbf', $warning, WATCHDOG_WARNING);
  576. $ret = 0;
  577. }
  578. else {
  579. /* @TODO GW drupal_retrieve_form() now accepts a form_state parameter.
  580. $form = drupal_retrieve_form($form_id, $form_state);
  581. */
  582. $form = drupal_retrieve_form($form_id);
  583. drupal_prepare_form($form_id, $form);
  584. $name = $form_values['save-name'];
  585. $form_values = _qbf_extract_query($form, $form_values);
  586. $ar_values = array();
  587. foreach ($form_values as $key => $value) {
  588. if (empty($value)) {
  589. continue;
  590. }
  591. $ar_values[$key] = $value;
  592. }
  593. // Avoid duplicates
  594. if (!empty($name)) {
  595. $sq = "DELETE FROM {%s} WHERE name = '%s'";
  596. db_query($sq, QBF_TABLE_NAME, $name);
  597. }
  598. $sq = 'INSERT INTO {%s} (qid, uid, name, query, updated) '
  599. ."VALUES (%d, %d, '%s', '%s', '%d' ) ";
  600. $ret = db_next_id('qbf_qid');
  601. $q = db_query($sq, QBF_TABLE_NAME, $ret, $user->uid, $name, serialize($ar_values), time());
  602. }
  603. return $ret;
  604. }
  605. /**
  606. * Implement hook_menu().
  607. *
  608. * @param $may_cache boolean
  609. * @return array
  610. */
  611. function qbf_menu()
  612. {
  613. /* TODO
  614. Non menu code that was placed in hook_menu under the '!$may_cache' block
  615. so that it could be run during initialization, should now be moved to hook_init.
  616. Previously we called hook_init twice, once early in the bootstrap process, second
  617. just after the bootstrap has finished. The first instance is now called boot
  618. instead of init.
  619. In Drupal 6, there are now two hooks that can be used by modules to execute code
  620. at the beginning of a page request. hook_boot() replaces hook_boot() in Drupal 5
  621. and runs on each page request, even for cached pages. hook_boot() now only runs
  622. for non-cached pages and thus can be used for code that was previously placed in
  623. hook_menu() with $may_cache = FALSE:
  624. Dynamic menu items under a '!$may_cache' block can often be simplified
  625. to remove references to arg(n) and use of '%<function-name>' to check
  626. conditions. See http://drupal.org/node/103114.
  627. The title and description arguments should not have strings wrapped in t(),
  628. because translation of these happen in a later stage in the menu system.
  629. */
  630. $items = array();
  631. $admin_access = array(QBF_PERM_ADMIN);
  632. $queror_access = array(QBF_PERM_QUERY);
  633. $items[QBF_PATH_SETTINGS] = array
  634. (
  635. 'title' => t('Query-By-Form'),
  636. 'access arguments' => $admin_access,
  637. 'page callback' => 'drupal_get_form',
  638. 'page arguments' => array('qbf_admin_settings'),
  639. 'type' => MENU_NORMAL_ITEM,
  640. );
  641. $items[QBF_PATH_MAIN . '/%qbf_query/delete'] = array
  642. (
  643. 'type' => MENU_CALLBACK,
  644. 'access arguments' => $queror_access,
  645. 'page callback' => '_qbf_query_delete',
  646. 'page arguments' => array(1),
  647. );
  648. return $items;
  649. }
  650. /**
  651. * Delete a query by qid
  652. *
  653. * $qid has been tested in qbf_menu() to be a positive integer, so it is a safe
  654. * number, but we still need to know more about it.
  655. *
  656. * @param $qid integer
  657. */
  658. function _qbf_query_delete($qid) {
  659. global $user;
  660. $query = qbf_query_load($qid);
  661. $notify = variable_get(QBF_VAR_NOTIFY_DELETE, QBF_DEF_NOTIFY_DELETE);
  662. $link = l($qid, QBF_PATH_MAIN .'/'. $qid .'/delete');
  663. // only valid if valid query, and owner or admin
  664. if (isset($query->uid) && (($query->uid == $user->uid) || user_access(QBF_PERM_ADMIN))) {
  665. $sq = 'DELETE FROM %s WHERE qid = %d ';
  666. $q = db_query($sq, QBF_TABLE_NAME, $qid);
  667. $message = t('Query @id "@name" has been deleted.', array
  668. (
  669. '@id' => $qid,
  670. '@name' => $query->name,
  671. ));
  672. drupal_set_message($message, 'status');
  673. watchdog('qbf', $message, WATCHDOG_NOTICE, $link);
  674. if (variable_get(QBF_VAR_NOTIFY_DELETE, QBF_DEF_NOTIFY_DELETE) && $query->uid != $user->uid) {
  675. $ret = drupal_mail(__FUNCTION__, $user->mail, $message, $message, $user->mail);
  676. /* @todo GW
  677. // $ret = drupal_mail(__FUNCTION__, $user->mail, $message, $message, $user->mail);
  678. $ret = /* TODO Create a hook_mail($key, &$message, $params) function to generate
  679. $ret = the message body when called by drupal_mail.
  680. $ret = $account = array(); // Set this as needed
  681. $ret = $language = user_preferred_language($account);
  682. $ret = $object = array(); // Replace this as needed
  683. $ret = $context['subject'] = $subject;
  684. $ret = $context['body'] = $body;
  685. $ret = $params = array('account' => $account, 'object' => $object, 'context' => $context);
  686. $ret = drupal_mail('qbf', __FUNCTION__, $user->mail, $language, $params, $user->mail);
  687. */
  688. $account = user_load(array('uid' => $query->uid));
  689. drupal_set_message(t('User !link has been informed', array
  690. (
  691. '!link' => l($account->name, 'user/'. $query->uid),
  692. )));
  693. dsm($ret);
  694. }
  695. }
  696. else {
  697. $message = t('Failed attempt to delete query @qid. Administrator has been alerted.', array
  698. (
  699. '@qid' => $qid,
  700. ));
  701. drupal_set_message($message, 'error');
  702. watchdog('qbf', $message, WATCHDOG_ERROR, $link);
  703. }
  704. drupal_goto();
  705. }
  706. /**
  707. * Implement the former hook_settings().
  708. *
  709. * @return array
  710. */
  711. function qbf_admin_settings() {
  712. $form = array();
  713. $form[QBF_VAR_NOTIFY_DELETE] = array
  714. (
  715. '#type' => 'checkbox',
  716. '#default_value' => variable_get(QBF_VAR_NOTIFY_DELETE, QBF_DEF_NOTIFY_DELETE),
  717. '#title' => t('Notify users when one of their saved searches has been deleted'),
  718. );
  719. return system_settings_form($form);
  720. }
  721. error_reporting($_qbf_er);