Browse Source

- apply 1.9.2.10 to this branch
- hook_menu implementation converted to D6 API

Frederic G. Marand 15 năm trước cách đây
mục cha
commit
0622f6d4a2
1 tập tin đã thay đổi với 345 bổ sung211 xóa
  1. 345 211
      qbf.module

+ 345 - 211
qbf.module

@@ -1,5 +1,6 @@
 <?php
 /**
+ * @file
  * Query By Form
  *
  * This module allows node modules to add a query by form tab for their node
@@ -7,11 +8,23 @@
  *
  * @copyright 2008 Ouest Systemes Informatiques (OSInet)
  * @author Frederic G. MARAND
- * @version $Id: qbf.module,v 1.9 2008-09-02 08:25:24 marand Exp $
  * @license CeCILL 2.0
  * @package QBF
  */
 
+// $Id: qbf.module,v 1.9.4.1 2009-03-17 10:59:14 marand Exp $
+
+/**
+ * Saved error reporting level.
+ *
+ * QBF module is supposed to pass parsing at E_ALL|E_STRICT, but other modules
+ * may not be so strict, so we save the level at the start of the module and
+ * restore it at the end of the module.
+ */
+global $_qbf_er;
+
+$_qbf_er = error_reporting(E_ALL | E_STRICT);
+
 /**
  * Remove this element from the generated form
  */
@@ -33,9 +46,24 @@ define('QBF_LEVEL_OPTIONAL',         2);
 define('QBF_LEVEL_REQUIRED',         3);
 
 /**
- * The main QBF path
+ * The main QBF path.
+ *
+ * It MUST be a single component path, without a "/", otherwise qbf_menu() will
+ * need to be changed.
+ *
+ * @ingroup paths
+ * @see qbf_menu()
+ */
+define('QBF_PATH_MAIN',              'qbf');
+/**
+ * The QBF autocomplete path for search fields
+ * @ingroup paths
+ */
+define('QBF_PATH_AC',                'qbf/ac');
+/**
+ * The path to the QBF settings page
  */
-define('QBF_PATH_MAIN',             'qbf');
+define('QBF_PATH_SETTINGS',          'admin/settings/qbf');
 
 /**
  * Authorize use of QBF searches
@@ -51,6 +79,15 @@ define('QBF_PERM_ADMIN',             'administer QBF');
  */
 define('QBF_TABLE_NAME',             'qbf_queries');
 
+/**
+ * Notify owner about saved query deletions, variable name.
+ */
+define('QBF_VAR_NOTIFY_DELETE',      'qbf_notify_delete');
+/**
+ * Notify owner about saved query deletions, default value.
+ */
+define('QBF_DEF_NOTIFY_DELETE',      FALSE);
+
 /**
  * Transform a form array for QBF.
  *
@@ -60,45 +97,42 @@ define('QBF_TABLE_NAME',             'qbf_queries');
  * Any additional parameter passed to the function is transmitted to the form
  * generating function.
  *
+ * @ingroup forms
  * @param string $form_id
  * @return array
  */
-function qbf_transform_form($form_id)
-  {
-  $arArgs = func_get_args();
-//dsm(array('qtf' => $arArgs));
+function qbf_transform_form($form_id) {
+  $ar_args = func_get_args();
+//dsm(array('qtf' => $ar_args));
   // Fetch the basic form and rename it, passing it the caller's arguments
-  $form = call_user_func_array('drupal_retrieve_form', $arArgs);
-  $newFormId = "qbf_$form_id";
+  $form = call_user_func_array('drupal_retrieve_form', $ar_args);
+  $new_form_id = "qbf_$form_id";
 
   // Only keep the children of the form and QBF properties on the form itself
   $elements = array();
-  $newForm = array();
-  $newForm['#qbf_source_form_id'] = $form_id;
-  if (in_array('#qbf', element_properties($form)))
-    {
-    $newForm += $form['#qbf'];
-    }
+  $new_form = array();
+  $new_form['#qbf_source_form_id'] = $form_id;
+  if (in_array('#qbf', element_properties($form))) {
+    $new_form += $form['#qbf'];
+  }
 
-  foreach (element_children($form) as $key)
-    {
+  foreach (element_children($form) as $key) {
     // dsm("Transforming $key, type " . $form[$key]['#type']);
-    $newElement = _qbf_transform_element($key, $form[$key]);
-    if (!is_null($newElement))
-      {
-      $newForm[$key] = $newElement;
-      }
+    $new_element = _qbf_transform_element($key, $form[$key]);
+    if (!is_null($new_element)) {
+      $new_form[$key] = $new_element;
     }
+  }
 
-  $newForm['#id'] = $newFormId;
-  $newForm['#multistep'] = TRUE;
+  $new_form['#id'] = $new_form_id;
+  $new_form['#multistep'] = TRUE;
   // Do not set #redirect, even to FALSE (submit handlers)
-  // $newForm['#redirect']  = FALSE;
-  $newForm['#after_build'][] = 'qbf_after_build';
-  $newForm['#submit'] = array('qbf_submit' => array());
-// dsm($newForm);
-  return $newForm;
-  }
+  // $new_form['#redirect']  = FALSE;
+  $new_form['#after_build'][] = 'qbf_after_build';
+  $new_form['#submit'] = array('qbf_submit' => array());
+// dsm($new_form);
+  return $new_form;
+}
 
 /**
  * Transform a form element for QBF.
@@ -107,14 +141,14 @@ function qbf_transform_form($form_id)
  * - #qbf : array of properties
  * - #level: only within #qbf
  *
- * @see QBF_* constants
+ * See QBF_* constants
  *
+ * @ingroup forms
  * @param string $key
  * @param array $element
  * @return void
  */
-function _qbf_transform_element($key, $element)
-  {
+function _qbf_transform_element($key, $element) {
   // dsm(array('key' => $key, 'element' => $element));
 
   /**
@@ -122,7 +156,7 @@ function _qbf_transform_element($key, $element)
    *
    * Types without a default transformation are not transformed
    */
-  static $arDefaultTypeTransformations = array
+  static $ar_default_type_transformations = array
     (
     'button'         => NULL,
     'file'           => NULL,
@@ -140,7 +174,7 @@ function _qbf_transform_element($key, $element)
    *
    * Properties without a default transformation are not transformed
    */
-  static $arDefaultPropertyTransformations = array
+  static $ar_default_property_transformations = array
     (
     // Standard properties
     '#action'        => NULL,
@@ -165,156 +199,139 @@ function _qbf_transform_element($key, $element)
    *
    * The key is the property name, the value is the one causing removal.
    */
-  static $arKillerProperties = array
+  static $ar_killer_properties = array
     (
     '#disabled'      => TRUE,
     );
 
   // Transform type
-  $sourceType = $element['#type'];
+  $source_type = $element['#type'];
   // .. Default transformation
-  $destType = array_key_exists($sourceType, $arDefaultTypeTransformations)
-    ? $arDefaultTypeTransformations[$sourceType]
-    : $sourceType;
+  $dest_type = array_key_exists($source_type, $ar_default_type_transformations)
+    ? $ar_default_type_transformations[$source_type]
+    : $source_type;
   // .. Apply form-defined type override
-  if (isset($element['#qbf']['#type']))
-    {
-    $destType = $element['#qbf']['#type'];
-    }
+  if (isset($element['#qbf']['#type'])) {
+    $dest_type = $element['#qbf']['#type'];
+  }
 
-  if (is_null($destType))
-    {
+  if (is_null($dest_type)) {
     $ret = NULL;
-    }
-  else
-    {
+  }
+  else {
     $ret = $element;
-    $ret['#type'] = $destType;
-    if (!array_key_exists('#qbf', $element) || $element['#qbf']['#level'] == QBF_LEVEL_REMOVE)
-      {
+    $ret['#type'] = $dest_type;
+    if (!array_key_exists('#qbf', $element) || $element['#qbf']['#level'] == QBF_LEVEL_REMOVE) {
       $ret = NULL;
-      }
-    else
-      {
-      foreach (element_properties($element) as $propertyName)
-        {
+    }
+    else {
+      foreach (element_properties($element) as $property_name) {
         // Apply killer properties first to avoid useless work
-        if (array_key_exists($propertyName, $arKillerProperties)
-          && ($element[$propertyName] = $arKillerProperties[$propertyName]))
-          {
+        if (array_key_exists($property_name, $ar_killer_properties)
+          && ($element[$property_name] = $ar_killer_properties[$property_name])) {
           $ret = NULL;
           break;
-          }
+        }
 
         // Now transform or copy remaining properties
-        if (array_key_exists($propertyName, $arDefaultPropertyTransformations))
-          {
-          $ret[$propertyName] = $arDefaultPropertyTransformations[$propertyName];
-          }
-        else
-          {
-          $ret[$propertyName] = $element[$propertyName];
-          }
+        if (array_key_exists($property_name, $ar_default_property_transformations)) {
+          $ret[$property_name] = $ar_default_property_transformations[$property_name];
+        }
+        else {
+          $ret[$property_name] = $element[$property_name];
+        }
 
         // And apply form-defined property overrides
-        if ($propertyName == '#qbf')
-          {
-          foreach ($element[$propertyName] as $overrideName => $overrideValue)
-            {
-          	$ret[$overrideName] = $overrideValue;
-            }
+        if ($property_name == '#qbf') {
+          foreach ($element[$property_name] as $override_name => $override_value) {
+            $ret[$override_name] = $override_value;
           }
         }
+      }
 
       // Recursively transform children
-      foreach (element_children($element) as $childName)
-        {
-        $child = _qbf_transform_element($childName, $element[$childName]);
-        if (is_null($child))
-          {
-          unset($ret[$childName]);
-          }
-        else
-          {
-          $ret[$childName] = $child;
-          }
+      foreach (element_children($element) as $child_name) {
+        $child = _qbf_transform_element($child_name, $element[$child_name]);
+        if (is_null($child)) {
+          unset($ret[$child_name]);
+        }
+        else {
+          $ret[$child_name] = $child;
         }
       }
     }
+  }
 
   //dsm(array('key' => $key, 'transformed element' => $ret));
   return $ret;
-  }
+}
 
 /**
  * Implement hook_perm().
  *
+ * @ingroup hooks
  * @return array
  */
-function qbf_perm()
-  {
+function qbf_perm() {
   $ret = array
     (
+    QBF_PERM_ADMIN,
     QBF_PERM_QUERY,
     );
 
   return $ret;
-  }
+}
 
 /**
  * Implement hook_forms().
  *
+ * @ingroup forms
+ * @ingroup hooks
  * @return array
  */
-function qbf_forms()
-  {
-  $hookName = 'qbf_register';
-
-  foreach (module_implements($hookName) as $module)
-    {
-    foreach (module_invoke($module, $hookName) as $formName)
-      {
-    	$forms["qbf_$formName"] = array
-    	  (
-    	  'callback'       => 'qbf_transform_form',
-    	  'callback arguments' => array($formName),
-    	  );
-      }
+function qbf_forms() {
+  $hook_name = 'qbf_register';
+
+  foreach (module_implements($hook_name) as $module) {
+    foreach (module_invoke($module, $hook_name) as $form_name) {
+      $forms["qbf_$form_name"] = array
+        (
+        'callback'           => 'qbf_transform_form',
+        'callback arguments' => array($form_name),
+        );
     }
+  }
 
   return $forms;
-  }
+}
 
 /**
  * Insert the query results at the bottom of the query form.
  *
+ * @ingroup forms
  * @param array $form
  * @param array $form_values
  * @return array
  */
-function qbf_after_build($form, $form_values)
-  {
-  if (empty($form['#post']))
-    {
+function qbf_after_build($form, $form_values) {
+  if (empty($form['#post'])) {
     return $form;
-    }
+  }
 
-  // If #post is not emtpy, we are indeed querying
-  $arQuery = _qbf_extract_query($form, $form_values);
+  // If #post is not empty, we are indeed querying
+  $ar_query = _qbf_extract_query($form, $form_values);
 
   /* This function is called at the end of the form building process, which
    * means that child properties of #qbf have already been upgraded to element
    * properties. So we look for $form['#callback'] and not
    * $form['#qbf']['#callback']
    */
-  if (isset($form['#callback']) && function_exists($function = $form['#callback']))
-    {
-    $results = $function($arQuery);
-    }
-  else
-    {
+  if (isset($form['#callback']) && function_exists($function = $form['#callback'])) {
+    $results = $function($ar_query);
+  }
+  else {
     drupal_set_message(t('QBF: incorrect callback function for search'), 'error');
-    }
+  }
   $form['qbf_query_results'] = array
     (
     '#type'    => 'markup',
@@ -322,7 +339,7 @@ function qbf_after_build($form, $form_values)
     '#weight'  => 10,
     );
   return $form;
-  }
+}
 
 /**
  * Recursively build a query array from the form and its values
@@ -330,129 +347,129 @@ function qbf_after_build($form, $form_values)
  * In the current version, element names are supposed to be unique, even at
  * different levels in the tree.
  *
+ * @ingroup forms
  * @param array $form
  * @param array $form_values
  */
-function _qbf_extract_query($form, $form_values)
-  {
+function _qbf_extract_query($form, $form_values) {
   $name = $form['#parents'][0];
   // Elements which are removed or display-only have no place in the query
   if (array_key_exists('#qbf', $form) && array_key_exists('#level', $form['#qbf'])
-    && $form['#qbf']['#level'] >= QBF_LEVEL_OPTIONAL)
-    {
+    && $form['#qbf']['#level'] >= QBF_LEVEL_OPTIONAL) {
     $ret = array($name => $form_values[$name]);
-    }
-  else
-    {
+  }
+  else {
     $ret = array();
-    }
+  }
 
   // QBF level is not inherited, so this loop is outside the "if" above
-  foreach (element_children($form) as $childName)
-    {
-  	$ret += _qbf_extract_query($form[$childName], $form_values);
-    }
+  foreach (element_children($form) as $child_name) {
+    $ret += _qbf_extract_query($form[$child_name], $form_values);
+  }
 
   return $ret;
-  }
+}
 
 /**
  * Provide an optional automatic mapping mechanism for query building.
  *
- * This function takes a partly built query map $arQueryMap, and a defaults
- * array to complete it in $arDefaults, and returns a fully built query array
+ * This function takes a partly built query map $ar_queryMap, and a defaults
+ * array to complete it in $ar_defaults, and returns a fully built query array
  * ready to be used for querying.
  *
- * @param array $arQuery
- * @param array $arDefaults
+ * @param array $ar_query
+ * @param array $ar_defaults
  * @return array
  */
-function qbf_query_mapper($arQueryMap = array(), $arDefaults = array())
-  {
+function qbf_query_mapper($ar_queryMap = array(), $ar_defaults = array()) {
   $ret = array();
 
-  foreach ($arQueryMap as $name => $value)
-    {
-    if (!is_array($value)) // accept NULL, empty strings...
-      {
+  foreach ($ar_queryMap as $name => $value) {
+    // accept NULL, empty strings...
+    if (!is_array($value)) {
       $value = array();
+    }
+    $item = $value;
+
+    foreach ($ar_defaults as $default_key => $default_value) {
+      if (!array_key_exists($default_key, $item)) {
+        $item[$default_key] = is_null($default_value)
+          ? $name
+          : $default_value;
       }
-  	$item = $value;
-
-  	foreach ($arDefaults as $defaultKey => $defaultValue)
-  	  {
-  	  if (!array_key_exists($defaultKey, $item))
-  	    {
-  	    $item[$defaultKey] = is_null($defaultValue)
-  	      ? $name
-  	      : $defaultValue;
-  	    }
-  	  // else if is already in $item, so we don't touch it
-  	  }
-  	$ret[$name] = $item;
+      // else if is already in $item, so we don't touch it
     }
-  return $ret;
+    $ret[$name] = $item;
   }
+  return $ret;
+}
 
 /**
  * Load a form_values array into a form used by QBF.
  *
- * This is typically useful when loading saved queries using qbf_load().
+ * This is typically useful when loading saved queries using qbf_query_load().
  * For other cases, the mechanisms built within FAPI should be used instead.
  *
- * @see qbf_load()
+ * @see qbf_query_load()
  *
+ * @ingroup forms
  * @param array $form
  * @param array $form_values
  * @return array The modified form
  */
-function qbf_import_values($element, $form_values)
-  {
-  foreach (element_children($element) as $childName)
-    {
-  	if (!empty($form_values[$childName]))
-  	  {
-  	  $element[$childName]['#qbf']['#default_value'] = $form_values[$childName];
-  	  }
-  	$element[$childName] = qbf_import_values($element[$childName], $form_values);
+function qbf_import_values($element, $form_values) {
+  foreach (element_children($element) as $child_name) {
+    if (!empty($form_values[$child_name])) {
+      $element[$child_name]['#qbf']['#default_value'] = $form_values[$child_name];
     }
-  return $element;
+    $element[$child_name] = qbf_import_values($element[$child_name], $form_values);
   }
+  return $element;
+}
 
 /**
  * Load a saved QBF query.
  *
+ * It is not named qbf_load() although this would seem more natural, because a
+ * hook_load() exists and this is not an implementation of this hook.
+ *
  * @see qbf_import_values()
  *
  * @param int $qid
  * @return array A form_values array usable by qbf_import_values
  */
-function qbf_load($qid)
-  {
-  $sq = 'SELECT qq.qid, qq.uid, qq.query '
-      . 'FROM {%s} qq '
-      . 'WHERE qq.qid = %d ';
+function qbf_query_load($qid) {
+  $sq = 'SELECT qq.qid, qq.uid, qq.query, qq.name '
+      .'FROM {%s} qq '
+      .'WHERE qq.qid = %d ';
   // db_rewrite_sql does not apply here until we add more advanced support for access control
   $q = db_query($sq, QBF_TABLE_NAME, $qid);
   $ret = db_fetch_object($q); // 0 or 1 row: we are querying on the primary key
-  if ($ret === FALSE)
-    {
+  // FALSE does not happen
+  if ($ret === NULL) {
     $ret = NULL;
-    }
-  else
-    {
+  }
+  else {
     $ret->query = unserialize($ret->query);
-    }
-  return $ret;
+    //dsm($ret);
   }
+  return $ret;
+}
 
-function qbf_submit($form_id, $form_values)
-  {
-  switch ($form_values['op'])
-    {
+/**
+ * Submit handler for query save form.
+ *
+ * @ingroup forms
+ * @param $form_id string
+ * @param $form_values array
+ * @return string
+ */
+function qbf_submit($form_id, $form_values) {
+  switch ($form_values['op']) {
     case t('Search'):
       $ret = FALSE;
       break;
+
     case t('Save query'):
       _qbf_save($form_id, $form_values);
       drupal_set_message(t('Your query was saved as "@name".',
@@ -460,10 +477,10 @@ function qbf_submit($form_id, $form_values)
       global $user;
       $ret = "user/$user->uid/edit/job";;
       break;
-    }
+  }
   //dsm(array('QS' => $form_values));
   return $ret;
-  }
+}
 
 /**
  * List queries owned by a given user.
@@ -471,47 +488,164 @@ function qbf_submit($form_id, $form_values)
  * @param int $uid > 0
  * @return array
  */
-function qbf_get_queries_by_user($uid)
-  {
-  $sq = 'SELECT qq.qid, qq.uid, qq.name, qq.query '
-      . 'FROM {%s} qq '
-      . 'WHERE qq.uid = %d '
-      . 'ORDER BY qq.name ';
+function qbf_get_queries_by_user($uid = NULL) {
+  if (is_null($uid)) {
+    global $user;
+    $uid = $user->uid;
+  }
+
+  $sq = 'SELECT qq.qid, qq.uid, qq.name, qq.query, qq.updated '
+      .'FROM {%s} qq '
+      .'WHERE qq.uid = %d '
+      .'ORDER BY qq.name ';
   $q = db_query($sq, QBF_TABLE_NAME, $uid);
   $ret = array();
-  while ($o = db_fetch_object($q))
-    {
+  while ($o = db_fetch_object($q)) {
     $ret[$o->qid] = $o; // qid is the PK, so it is present and unique
-    }
-  return $ret;
   }
+  return $ret;
+}
 
 /**
  * Save a query and return its qid.
  *
- * @global $user
- * @param string $form_id
- * @param array $form_values
+ * @ingroup forms
+ * @param $form_id string
+ * @param $form_values array
  * @return int
  */
-function _qbf_save($form_id, $form_values)
-  {
+function _qbf_save($form_id, $form_values) {
   global $user;
 
-  if ($user->uid == 0)
-    {
+  if ($user->uid == 0) {
     $warning = t('Attempt by anonymous user to save a QBF query. Should not happen.');
     drupal_set_message($warning, 'error');
     watchdog('qbf', $warning, WATCHDOG_WARNING);
     $ret = 0;
+  }
+  else {
+    $form = drupal_retrieve_form($form_id);
+    drupal_prepare_form($form_id, $form);
+    $name = $form_values['save-name'];
+    $form_values = _qbf_extract_query($form, $form_values);
+    $ar_values = array();
+    foreach ($form_values as $key => $value) {
+      if (empty($value)) {
+        continue;
+      }
+      $ar_values[$key] = $value;
     }
-  else
-    {
-    $sq = 'INSERT INTO {%s} (qid, uid, name, query) '
-        . "VALUES           (%d,  %d,  '%s', '%s' ) ";
-    $ret = db_next_id('qbf_qid');
-    $q = db_query($sq, QBF_TABLE_NAME, $ret, $user->uid, $form_values['save-name'], serialize($form_values));
+
+    // Avoid duplicates
+    if (!empty($name)) {
+      $sq = "DELETE FROM {%s} WHERE name = '%s'";
+      db_query($sq, QBF_TABLE_NAME, $name);
     }
 
+    $sq = 'INSERT INTO {%s} (qid, uid, name, query, updated) '
+         ."VALUES           (%d,  %d,  '%s', '%s',  '%d' ) ";
+    $ret = db_next_id('qbf_qid');
+    $q = db_query($sq, QBF_TABLE_NAME, $ret, $user->uid, $name, serialize($ar_values), time());
+  }
+
   return $ret;
-  }
+}
+
+/**
+ * Implement hook_menu().
+ *
+ * @param $may_cache boolean
+ * @return array
+ */
+function qbf_menu()
+  {
+  $items = array();
+  $admin_access  = array(QBF_PERM_ADMIN);
+  $queror_access = array(QBF_PERM_QUERY);
+
+  $items[QBF_PATH_SETTINGS] = array
+    (
+    'title'              => t('Query-By-Form'),
+    'access arguments'   => $admin_access,
+    'page callback'      => 'drupal_get_form',
+    'page arguments'     => array('qbf_admin_settings'),
+    'type'               => MENU_NORMAL_ITEM,
+    );
+
+  $items[QBF_PATH_MAIN . '/%qbf_query/delete'] = array
+    (
+    'type'               => MENU_CALLBACK,
+    'access arguments'   => $queror_access,
+    'page callback'      => '_qbf_query_delete',
+    'page arguments'     => array(1),
+    );
+
+  return $items;
+  }
+
+/**
+ * Delete a query by qid
+ *
+ * $qid has been tested in qbf_menu() to be a positive integer, so it is a safe
+ * number, but we still need to know more about it.
+ *
+ * @param $qid integer
+ */
+function _qbf_query_delete($qid) {
+
+  global $user;
+
+  $query = qbf_query_load($qid);
+  $notify = variable_get(QBF_VAR_NOTIFY_DELETE, QBF_DEF_NOTIFY_DELETE);
+  $link = l($qid, QBF_PATH_MAIN .'/'. $qid .'/delete');
+
+  // only valid if valid query, and owner or admin
+  if (isset($query->uid) && (($query->uid == $user->uid) || user_access(QBF_PERM_ADMIN))) {
+    $sq = 'DELETE FROM %s WHERE qid = %d ';
+    $q = db_query($sq, QBF_TABLE_NAME, $qid);
+    $message = t('Query @id "@name" has been deleted.', array
+      (
+      '@id'    => $qid,
+      '@name' => $query->name,
+      ));
+    drupal_set_message($message, 'status');
+    watchdog('qbf', $message, WATCHDOG_NOTICE, $link);
+    if (variable_get(QBF_VAR_NOTIFY_DELETE, QBF_DEF_NOTIFY_DELETE) && $query->uid != $user->uid) {
+      $ret = drupal_mail(__FUNCTION__, $user->mail, $message, $message, $user->mail);
+      $account = user_load(array('uid' => $query->uid));
+      drupal_set_message(t('User !link has been informed', array
+        (
+        '!link' => l($account->name, 'user/'. $query->uid),
+        )));
+      dsm($ret);
+    }
+  }
+  else {
+    $message = t('Failed attempt to delete query @qid. Administrator has been alerted.', array
+      (
+      '@qid' => $qid,
+      ));
+    drupal_set_message($message, 'error');
+    watchdog('qbf', $message, WATCHDOG_ERROR, $link);
+    }
+  drupal_goto();
+}
+
+/**
+ * Implement the former hook_settings().
+ *
+ * @return array
+ */
+function qbf_admin_settings() {
+
+  $form = array();
+  $form[QBF_VAR_NOTIFY_DELETE] = array
+    (
+    '#type'          => 'checkbox',
+    '#default_value' => variable_get(QBF_VAR_NOTIFY_DELETE, QBF_DEF_NOTIFY_DELETE),
+    '#title'         => t('Notify users when one of their saved searches has been deleted'),
+    );
+  return system_settings_form($form);
+}
+
+error_reporting($_qbf_er);