| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150 | <?php/** * @file * Query By Form * * This module allows node modules to add a query by form tab for their node * types to the default search form * * @copyright 2008-2009 Ouest Systemes Informatiques (OSInet) * @author Frederic G. MARAND * @license Licensed under the CeCILL 2.0 and the General Public Licence version 2 or later * @package QBF */// $Id: qbf.module,v 1.9.4.11 2009-03-22 14:30:41 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 */define('QBF_LEVEL_REMOVE',           0);/** * This element is only for display in the generated form: do not include it * in the query vector. */define('QBF_LEVEL_DISPLAY',          1);/** * Include this element in the generated form and in the query vector, but do * not mark it as required. */define('QBF_LEVEL_OPTIONAL',         2);/** * Include this element in the generated form and in the query vector, and * mark it as required. */define('QBF_LEVEL_REQUIRED',         3);/** * 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_SETTINGS',          'admin/settings/qbf');/** * Authorize use of QBF searches */define('QBF_PERM_QUERY',             'use QBF search functions');/** * Authorize QBF administration */define('QBF_PERM_ADMIN',             'administer QBF');/** * The name of the table used to store queries */define('QBF_TABLE_NAME',             'qbf_queries');/** * Notify owner about saved query deletions, variable name. * * @ingroup vars */define('QBF_VAR_NOTIFY_DELETE',      'qbf_notify_delete');/** * Name of the profile category under which the list of saved queries will be * displayed. * * @ingroup vars * * @see qbf_admin_settings(), qbf_profile_alter() */define('QBF_VAR_PROFILE_CATEGORY',   'qbf_profile_category');/** * Notify owner about saved query deletions, default value. * * @ingroup vars */define('QBF_DEF_NOTIFY_DELETE',      FALSE);/** * Default value for the profile category * * @ingroup vars * * See QBF_VAR_PROFILE_CATEGORY */define('QBF_DEF_PROFILE_CATEGORY',     'Saved queries');/** * A class wrapper for saved QBF queries */class Qbf_Query  {  public $qid;  public $uid;  public $name;  public $type;  public $query;  public $created;  public $updated;  /**   * Constructor   *   * @param string $name   * @param array $ar_values   * @return void   */  public function __construct($type, $name = NULL, $ar_values = NULL)    {    global $user;    $this->qid = 0; // will be autoset by the DB serial    $this->uid = $user->uid;    $this->type = $type;    $this->name = $name;    if (!empty($ar_values))      {      $this->query = serialize($ar_values);      }    $this->created = $this->updated = time();    }  /**   * Save a named query to the DB, erasing previous homonym queries is any exists.   *   * @return int   */  public function save()    {    // Avoid duplicates    if (!empty($this->name))      {      $sq = "DELETE FROM {%s} WHERE name = '%s' AND uid = '%d' ";      db_query($sq, QBF_TABLE_NAME, $this->name, $this->uid);      // $n = db_affected_rows(); // Know how many homonym queries we deleted      }    $ret = drupal_write_record(QBF_TABLE_NAME, $this); // no update param: we just deleted the previous version    if ($ret) // has to be SAVED_NEW, by construction      {      $ret = $this->qid; // from serial      }    return $ret;    }  }/** * Recursively build a query array from the form and its 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($element_id, $form, $form_values)  {  // Elements which are unnamed (form), removed, or display-only have no place in the query  if (!empty($element_id) && array_key_exists('#qbf', $form) && array_key_exists('#level', $form['#qbf'])    && $form['#qbf']['#level'] >= QBF_LEVEL_OPTIONAL)    {    $ret = array($element_id => $form_values[$element_id]);    }  else    {    $ret = array();    }  // QBF level is not inherited, so this loop is outside the "if" above  foreach (element_children($form) as $child_name)    {    $ret += _qbf_extract_query($child_name, $form[$child_name], $form_values);    }  return $ret;  }/** * Submit handler for qbf_form, Perform search button. * * @param array $form * @param array $form_state */function _qbf_form_perform_submit($form, &$form_state)  {  // dsm($form);  // dsm($form_state);  $callback = $form_state['values']['qbf_query'];  if (function_exists(($callback)))    {    $ar_query = _qbf_extract_query(NULL, $form, $form_state['values']);    $form_state['qbf_results'] = $callback($ar_query);    }  $form_state['rebuild'] = TRUE;  }/** * Validate handler for qbf_form, Perform search button. * * @param array $form * @param array $form_state *///function _qbf_form_perform_validate($form, &$form_state)//  {//  // @todo validate searches: checkboxes sets needs at least one value checked, otherwise there won't be any result//  }/** * Submit handler for qbf_form, Save search button. * * @param array $form * @param array $form_state * @return integer *   The id of the saved query. */function _qbf_form_save_submit($form, &$form_state)  {  $qid = _qbf_save($form_state['values']['form_id'], $form_state);  drupal_set_message(t('Your query was saved as "@name".',    array('@name' => $form_state['values']['qbf_save_name'])));  global $user;  $form_state['redirect'] = "user/$user->uid/edit/qbf";  return $qid;  }/** * Validate handler for qbf_form, Save search button. * * @param array $form * @param array $form_state *///function _qbf_form_save_validate($form, &$form_state)//  {//  // @todo validate saves. Check whether any validation is necessary.//  }/** * Return the human-readable for a query type. * * @param string $query_type * @return string */function _qbf_get_name_from_type($query_type)  {  static $labels = array();  if (empty($labels) || empty($labels[$query_type]))    {    $ar_forms = qbf_forms();    foreach ($ar_forms as /* $qbf_form_id => */ $form_info)      {      if ($query_type == $form_info['callback arguments'][0]['form'])        {        $labels[$query_type] = $form_info['callback arguments'][0]['label'];        break;        }      }    }  return $labels[$query_type];  }/** * Delete a query by qid * * In the qbf/<qid>/delete case, $query has been tested for validity and access * in qbf_query_load(), so it is safe and accessible. * * Outside this context, the function can also be invoken with just a qid, and * the same check via qbf_query_load() will be performed. * * @param mixed $query *   int or object */function _qbf_query_delete($query)  {  global $user;  if (is_int($query))    {    $query = qbf_query_load($query);    }  if ($query) // access already checked in explicit or implicit qbf_query_load    {    $qid = $query->qid;    $sq = 'DELETE FROM %s WHERE qid = %d ';    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');    $link = l($qid, QBF_PATH_MAIN .'/'. $qid .'/delete');    $notify = variable_get(QBF_VAR_NOTIFY_DELETE, QBF_DEF_NOTIFY_DELETE);    watchdog('qbf', $message, NULL, WATCHDOG_NOTICE, $link);    // access check: we only send the message to the query owner, so access is    // granted without an additional check    if ($notify /* && $query->uid != $user->uid */)      {      $owner = user_load(array('uid' => $query->uid));      $account = user_load(array('uid' => $query->uid));      $language = user_preferred_language($account);      $params = array        (        'query'    => $query,        'owner'    => $owner, // unused by default, but can be used in a hook_mail_alter() implementation        'deletor'  => $user,        'language' => $language,        );      /* $ret = */ drupal_mail('qbf', __FUNCTION__, $user->mail, $language, $params, $user->mail);      drupal_set_message(t('User !link has been informed', array        (        '!link' => l($account->name, 'user/'. $query->uid),        )));      // dsm(array("QQD, ret" => $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, NULL, WATCHDOG_ERROR, $link);    }  drupal_goto();}/** * Main query page. * * This returns the query form if a valid query id or query type is specified, * or the list of available query types if several exisit, or jumps to the single * available query type if only one exists. * * @param object $query *   Valid query, loaded by qbf_query_load(). * @return string */function _qbf_query_form($query = NULL )  {  if (!empty($query))    {    $qbf_form_id = 'qbf_' . $query->type;    $ret = drupal_get_form($qbf_form_id, $query);    }  else    {    $ar_forms = qbf_forms();    $arRet = array();    foreach ($ar_forms as $qbf_form_id => $form_info)      {      $form_id = $form_info['callback arguments'][0]['form'];      $arRet[QBF_PATH_MAIN . "/$form_id"] = l($form_info['callback arguments'][0]['label'],          QBF_PATH_MAIN . "/$form_id");      }    // If there is only one form type, no need to ask the user.    if (count($arRet) == 1)      {      reset($arRet);      drupal_goto(key($arRet));      }    else      {      $ret = theme('item_list', $arRet, t('Choose a query type'));      }    }  return $ret;  }/** * Save a query and return its qid. * * This is not a hook_save() implementation, hence the "_". * * @ingroup forms * * @param $form_id string * @param $form_state array * @return int */function _qbf_save($form_id, $form_state)  {  if (user_is_anonymous())    {    $warning = t('Attempt by anonymous user to save a QBF query. Should not happen.');    drupal_set_message($warning, 'error');    watchdog('qbf', $warning, NULL, WATCHDOG_WARNING);    $ret = 0;    }  else    {    // @FIXME check whether form_state is now needed. It wasn't in QBF for D5    $form = drupal_retrieve_form($form_id, $form_state);    // dsm($form, "retrieve");    drupal_prepare_form($form_id, $form, $form_state);    // dsm($form, "prepare");    $name = $form_state['values']['qbf_save_name'];    $type = $form_state['values']['qbf_save_type'];    // dsm($form_state);    $form_values = _qbf_extract_query(NULL, $form, $form_state['values']);    // dsm($form_values);    $ar_values = array();    foreach ($form_values as $key => $value)      {      if (empty($value))        {        continue;        }      $ar_values[$key] = $value;      }    $query = new Qbf_Query($type, $name, $ar_values);    $ret = $query->save();    }  return $ret;  }/** * Transform a form element for QBF. * * QBF-specific properties are: * - #qbf : array of properties * - #level: only within #qbf * * See QBF_* constants * * @ingroup forms * @param string $key * @param array $element * @return void */function _qbf_transform($key, $element, $form_state, $query)  {  // dsm(array('key' => $key, 'element' => $element));  /**   * List default type transformations applied to widget by FAPI.   * Types without a default transformation are not transformed   */  static $ar_default_type_transformations = array    (    'button'         => NULL, // no content    'file'           => NULL, // non-querable (yet ?)    'image_button'   => NULL, // new in D6    'markup'         => NULL, // no content    'password'       => NULL, // forbidden    'radio'          => NULL, // single radio is useless, unlike a set of them    'submit'         => NULL, // no content    'textarea'       => 'textfield', // reduce text for searches    // Don't transform these:    // 'checkbox'       => NULL,    // 'checkboxes'     => NULL,    // 'date'           => NULL,    // 'fieldset'       => NULL, // useful visually    // 'form'           => NULL, // removing it would delete the whole shebang    // 'hidden'         => NULL, // non-querable visually, but may be useful    // 'item'           => NULL,    // 'radios'         => NULL,    // 'select'         => NULL,    // 'textfield'      => NULL,    // 'value'          => 'value',    // 'weight'         => NULL,    );    /**     * List default property transformations applied to widget by FAPI property.     *     * Properties without a default transformation are not transformed     */    static $ar_default_property_transformations = array      (      // Standard properties      '#action'        => NULL,      '#after_build'   => NULL,      // '#base'          => NULL, // gone in D6      '#button_type'   => NULL,      '#built'         => NULL,      '#description'   => NULL,      '#method'        => NULL,      '#parents'       => NULL,      '#redirect'      => NULL,      '#ref'           => NULL,      '#required'      => NULL,      '#rows'          => NULL,      '#submit'        => NULL,      '#tree'          => NULL,      '#validate'      => NULL,      );    /**     * List properties causing causing element removal.     *     * The key is the property name, the value is the one causing removal.     */    static $ar_killer_properties = array      (      '#disabled'      => TRUE,      );    // Transform type    $source_type = $element['#type'];    // .. Default transformation    $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']))      {      $dest_type = $element['#qbf']['#type'];      }    if (is_null($dest_type))      {      $ret = NULL;      }    else      {      $ret = $element;      $ret['#type'] = $dest_type;      if (!array_key_exists('#qbf', $element) || $element['#qbf']['#level'] == QBF_LEVEL_REMOVE)        {        $ret = NULL;        }      else        {        foreach (element_properties($element) as $property_name)          {          // Apply killer properties first to avoid useless work          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($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 ($property_name == '#qbf')          {          foreach ($element[$property_name] as $override_name => $override_value)           {            $ret[$override_name] = $override_value;            }          }        }      if (isset($form_state['values'][$key]))        {        $ret['#default_value'] = $form_state['values'][$key];        }      elseif (isset($query->query[$key]))        {        $ret['#default_value'] = $query->query[$key];        }      // Recursively transform children      foreach (element_children($element) as $child_name)        {        $child = _qbf_transform($child_name, $element[$child_name], $form_state, $query);        if (is_null($child))          {          unset($ret[$child_name]);          }        else          {          $ret[$child_name] = $child;          }        }      }    }  //dsm(array('key' => $key, 'transformed element' => $ret));  return $ret;  }/** * 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'),    );  $form['queries'][QBF_VAR_PROFILE_CATEGORY] = array    (    '#type'          => 'textfield',    '#title'         => t('Name of profile category'),    '#description'   => t('Choose a title for the section of the user profiles where the list of search queries will be displayed. It may match an existing profile category.'),    '#default_value' => variable_get(QBF_VAR_PROFILE_CATEGORY, QBF_DEF_PROFILE_CATEGORY),    );  return system_settings_form($form);  }/** * The QBF form builder. * * @param array $form_state * @param array $query_info *   The query structure array * @param string $qbf_form_id *   The name of the QBF form * @param string $query *   The saved query. */function qbf_form(&$form_state, $query_info, $qbf_form_id, $query = NULL)  {  $form_id = $query_info['form'];  // Fetch the basic form and rename it, passing it the previous values  $node = new stdClass();  $form = $form_id($node, $form_state);  $qbf_form = array();  $qbf_form['#qbf_source_form_id'] = $form_id;  // On the form element itself, only keep the QBF properties and the children  if (in_array('#qbf', element_properties($form)))    {    $qbf_form += $form['#qbf'];    }  foreach (element_children($form) as $key)    {    $new_element = _qbf_transform($key, $form[$key], $form_state, $query);    if (!is_null($new_element))      {      $qbf_form[$key] = $new_element;      }    }  $qbf_form['#id'] = $qbf_form_id;  $qbf_form['qbf'] = array    (    '#type'   => 'fieldset',    '#title'  => t('Query'),    );  if (isset($form_state['values']) && !empty($form_state['values']))    {    if (isset($form_state['qbf_results']))      {      $qbf_form['qbf']['qbf_results'] = array        (        '#type'    => 'markup',        '#prefix'  => '<p>',        '#value'   => $form_state['qbf_results'],        '#suffix'  => '</p>',        );      }    }  $qbf_form['qbf']['qbf_save_type'] = array    (    '#type'          => 'hidden',    '#value'         => $query_info['form'],    );  $qbf_form['qbf']['qbf_query'] = array    (    '#type'          => 'hidden',    '#value'         => $query_info['callback'],    );  $qbf_form['qbf']['qbf_save_name'] = array    (    '#title'         => t('Name of query in your save list'),    '#type'          => 'textfield',    '#required'      => TRUE,    '#default_value' => empty($query->name)      ? t('@label - @time', array('@label' => $query_info['label'], '@time' => format_date(time(), 'large')))      : $query->name,    );  $qbf_form['qbf']['qbf_perform'] = array    (    '#submit'        => array('_qbf_form_perform_submit'),    '#validate'      => array('_qbf_form_perform_validate'),    '#type'          => 'submit',    '#value'         => t('Perform query'),    );  $qbf_form['qbf']['qbf_save'] = array    (    '#submit'        => array('_qbf_form_save_submit'),    '#validate'      => array('_qbf_form_save_validate'),    '#type'          => 'submit',    '#value'         => t('Save query'),    );  return $qbf_form;  }/** * Implement hook_forms(). * * @link http://drupal.org/node/144132#hook-forms @endlink * * hook_qbf_register() returns an array of QBF-able node types, indexed by the * node type, with the following properties: * - form: the name of the hook_form() implementation (a $form_id) * - label: the human-readable type name under which the queries are saved by QBF * - callback: the function QBF must invoke to query the node type. It will *   receive the query type and a filtered version of $form_state['values'] *   containing only valid node fields, and must return a themed grid of query *   results, which will be displayed as a #markup FAPI element. In advanced *   uses, a single callback can be used for several query types by using the *   query type parameter to know what the values apply to. * * @ingroup forms * @ingroup hooks * * @param array $args * @return array */function qbf_forms(/* $args = NULL */)  {  static $forms = array();  if (empty($forms))    {    $hook_name = 'qbf_register';    // dsm(array("QBF_forms $qbf_form_id" => $args));    // More efficient than using module_invoke_all: we avoid array-merging + re-looping    foreach (module_implements($hook_name) as $module)      {      $arImplementations = module_invoke($module, $hook_name);      // dsm($arImplementations);      foreach ($arImplementations as /* $node_type => */ $query_info)        {        $qbf_form_id = 'qbf_' . $query_info['form'];        $forms[$qbf_form_id] = array          (          'callback'           => 'qbf_form',          'callback arguments' => array($query_info, $qbf_form_id),          );        } // foreach implementation      } // foreach module    } // if empty  return $forms;  }/** * List queries owned by a given user. * * @param int $uid > 0 * @return array */function qbf_get_queries_by_user($uid = NULL)  {  if (is_null($uid))    {    global $user;    $uid = $user->uid;    }  $sq = 'SELECT qq.qid, qq.uid, qq.type, qq.name, qq.query, qq.updated '      . 'FROM {%s} qq '      . 'WHERE qq.uid = %d '      . 'ORDER BY qq.type, qq.name ';  // no db_rewrite_sql: this function is not in a menu callback, so it is up to  // the caller to check access  $q = db_query($sq, QBF_TABLE_NAME, $uid);  $ret = array();  while (is_object($o = db_fetch_object($q)))    {    $ret[$o->qid] = $o; // qid is the PK, so it is present and unique    }  return $ret;  }/** * Implement hook_mail(). * * @param string $key * @param array $message * @param array $params * @return void */function qbf_mail($key, &$message, $params)  {  // dsm(array('QBF_mail key' => $key, 'message' => $message, 'params' => $params));  $deletor_tokens = user_mail_tokens($params['deletor'], $params['language']->language);  $tokens = array_merge($deletor_tokens, array    (    '!qname' => $params['query']->name,    '!qid'   => $params['query']->qid,    ));  $message['subject'] = t('Effacement d\'une recherche !site enregistrée', $tokens);  $message['body'] = t("!date\n\nVotre recherche !qid: !qname\nsur le site !site vient d'être effacée par !username.", $tokens);  }/** * Implement hook_menu(). * * @return array */function qbf_menu()  {  $items = array();  $items[QBF_PATH_SETTINGS] = array    (    'title'            => 'Query-By-Form',    'access arguments' => array(QBF_PERM_ADMIN),    'page callback'    => 'drupal_get_form',    'page arguments'   => array('qbf_admin_settings'),    'type'             => MENU_NORMAL_ITEM,    );  $items[QBF_PATH_MAIN] = array    (    'type'             => MENU_CALLBACK,    'access arguments' => array(QBF_PERM_QUERY),    'page callback'    => '_qbf_query_form',    );  $items[QBF_PATH_MAIN . '/%qbf_query'] = array    (    'type'             => MENU_CALLBACK,    'access arguments' => array(QBF_PERM_QUERY),    'page callback'    => '_qbf_query_form',    'page arguments'   => array(1),    );  $items[QBF_PATH_MAIN . '/%qbf_query/delete'] = array    (    'type'             => MENU_CALLBACK,    'access arguments' => array(QBF_PERM_QUERY),    'page callback'    => '_qbf_query_delete',    'page arguments'   => array(1),    );  return $items;  }/** * Implement hook_perm(). * * @todo D7: Format will change * @see http://drupal.org/node/224333#descriptions-permissions * * @ingroup hooks * @return array */function qbf_perm()  {  $ret = array    (    QBF_PERM_ADMIN,    QBF_PERM_QUERY,    );  return $ret;  }/** * Load a saved QBF query, or an empty query by type * * @link http://drupal.org/node/109153#load @endlink * * @param int $us_qid * @return array *   A form_values array */function qbf_query_load($us_qid)  {  static $query = NULL;  // Only allow query loading by logged-in users  if (user_is_anonymous())    {    return FALSE;    }  // Filter out visibly invalid values  $qid = (is_numeric($us_qid) && ($us_qid > 0))    ? $us_qid    : 0;  // If this is not a saved query, it may be a QBF query type  if ($qid === 0)    {    $ar_forms = qbf_forms();    foreach ($ar_forms as /* $qbf_form_id => */ $form_info)      {      if ($us_qid === $form_info['callback arguments'][0]['form'])        {        $query = new Qbf_Query($us_qid);        break;        }      }    }  if (is_null($query) && $qid)    {    $sq = 'SELECT qq.qid, qq.uid, qq.type, qq.name, qq.query '        . 'FROM {%s} qq '        . 'WHERE qq.qid = %d ';    // db_rewrite_sql does not apply here: access control is further down    $q = db_query($sq, QBF_TABLE_NAME, $qid);    $query = db_fetch_object($q); // 0 or 1 row: we are querying on the primary key    if ($query !== FALSE)      {      $query->query = unserialize($query->query);      // dsm($query);      }    }  global $user;  $ret = (isset($query) && (($query->uid == $user->uid) || user_access(QBF_PERM_ADMIN)))    ? $query    : FALSE;  return $ret;  }/** * Provide an optional automatic mapping mechanism for query building. * * 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 $ar_query_map * @param array $ar_defaults * @return array */function qbf_query_mapper($ar_query_map = array(), $ar_defaults = array())  {  $ret = array();  foreach ($ar_query_map 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;        }      // else if is already in $item, so we don't touch it      }    $ret[$name] = $item;    }  return $ret;  }/** * Implement hook_user(). * * Display job searchs as an account form category * * Edit and account could be passed by reference, but are currently not modified. * * @ingroup hooks * * @param string $op * @param array &$edit * @param array $account * @param string $category * @return array|void */function qbf_user($op, $edit, $account, $category = NULL)  {  $qbf_category = variable_get(QBF_VAR_PROFILE_CATEGORY, QBF_DEF_PROFILE_CATEGORY);  // dsm("hook user($op, edit, $account->uid = $account->name, $category)");  switch ($op)    {    case 'categories':      // dsm("hook user($op)");      $ret = array();      $ret[] = array        (        'name'    => 'qbf',        'title'   => $qbf_category,        'weight'  => 2,        );      break;//    case 'view'://      // Only allow field to QBF admins and own user//      if ($user->uid != $account->uid && !user_access(QBF_PERM_ADMIN))//        {//        return;//        }////      $account->content['queries'] = array//        (//        '#type'    => 'user_profile_category',//        '#title'   => t('Saved job/internship queries'),//        // '#class'   => "job-user-$category",//        );//      $account->content['queries']['list'] = array//        (//        '#type'    => 'user_profile_item',//        '#title'   => t('List of searches'),//        '#value'   => '<p>Would appear here</p>',//        );//      $none_message = ($account->uid == $user->uid)//        ? t('None yet. !newQuery', array('!newQuery' => $new_query_link))//        : t('None yet.');//      $saved = ($count > 0)//        ? format_plural($count, 'One saved query. ', '@count saved queries. ')//          . l(t('View/edit'), "user/$account->uid/edit/qbf")//        : $none_message;//      dsm($account->content);//      break;    case 'form':      // dsm("hook user($op, $account->uid = $account->name, $category)");      if ($category != 'qbf')        {        $ret = NULL;        break;        }      // No access control: it is already controlled by the      // "administer users" permission in user.module      $ar_queries = qbf_get_queries_by_user($account->uid);      $count = count($ar_queries);      $ar_header = array        (        t('Query type'),        t('Query title'),        t('Saved on'),        // t('# results'),        t('Actions'),        );      $ar_data = array();      foreach ($ar_queries as $query)        {        $ar_data[] = array          (          _qbf_get_name_from_type($query->type),          l($query->name, QBF_PATH_MAIN . "/$query->qid"),          format_date($query->updated, 'small'),          // t('n.a.'),          l(t('Delete'), QBF_PATH_MAIN ."/$query->qid/delete", array('query' => "destination=user/$account->uid/edit/qbf")),          );        }      $data = theme('table', $ar_header, $ar_data);      $max_count = variable_get(JOB_VAR_MAX_QUERIES, JOB_DEF_MAX_QUERIES);      if ($count < $max_count)        {        $new_query_link = l(t('Create new query'), QBF_PATH_MAIN);        $data .= format_plural($max_count - $count,          '<p>You may still save one more query.',          '<p>You may still save @count more queries.');        $data .= " $new_query_link.</p>";        }      else        {        $data .= t("<p>You have reached the maximum number of saved queries (@count).</p>",          array('@count' => $count));        }      /**       * A first-level form element is needed by contrib module profile_privacy,       * at least in version 5.x-1.2 and 6.x-1-2. This mimics the       * user_profile_category/user_profile_item scheme provided by profile.module.       * We do not use these types, in order to use the full display area width       * for the queries table.       */      $ret['job'] = array        (        '#type' => 'markup', // in profile, usually user_profile_category        '#title' => NULL,        );      $ret['job']['queries'] = array        (        '#type'  => 'markup', // in profile, usually user_profile_item        '#value' => $data,        );      break;    }  return $ret;  }error_reporting($_qbf_er);
 |