Core  3.2
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
Class.DocSearch.php
Go to the documentation of this file.
1 <?php
2 /*
3  * @author Anakeen
4  * @package FDL
5 */
6 /**
7  * Document searches classes
8  *
9  * @author Anakeen
10  * @version $Id: Class.DocSearch.php,v 1.56 2008/11/19 08:47:46 eric Exp $
11  * @package FDL
12  */
13 /**
14  */
15 
16 include_once ("FDL/Class.PDocSearch.php");
17 include_once ("FDL/Lib.Dir.php");
18 
19 class DocSearch extends PDocSearch
20 {
21 
22  public $defDoctype = 'S';
23  public $defaultedit = "FREEDOM:EDITSEARCH";
24 
25  public $tol = array(
26  "and" => "and", #N_("and")
27  "or" => "or"
28  ); #N_("or")
29 
30 
31  /**
32  * max recursive level
33  * @public int
34  */
36 
37  function __construct($dbaccess = '', $id = '', $res = '', $dbid = 0)
38  {
39 
40  PDocSearch::__construct($dbaccess, $id, $res, $dbid);
41  if (((!isset($this->fromid))) || ($this->fromid == "")) $this->fromid = FAM_SEARCH;
42  }
43 
44  public function preConsultation()
45  {
46  $famId = $this->getRawValue(\Dcp\AttributeIdentifiers\Search::se_famid);
47  if ($famId) {
48  $doc = new_Doc($this->dbaccess, abs($famId) , true);
49  if (!is_object($doc) || !$doc->isAlive() || $doc->defDoctype != 'C') {
50  $err = sprintf(_('Family [%s] not found') , abs($famId));
51  return $err;
52  }
53  }
54  return '';
55  }
56 
57  public function preCreated()
58  {
59  return $this->updateSearchAuthor();
60  }
61  /**
62  * the author is the current user if not already set
63  * @return string
64  */
65  public function updateSearchAuthor()
66  {
67  $err = '';
68  if (!$this->getRawValue("se_author")) {
69  $err = $this->setValue("se_author", $this->getUserId());
70  }
71  return $err;
72  }
73  /**
74  * to affect a special query to a SEARCH document
75  * must be call after the add method When use this method others filter parameters are ignored.
76  * @param string $tquery the sql query
77  * @return string error message (empty if no error)
78  */
79  function addStaticQuery($tquery)
80  {
81  $this->setValue("se_static", "1");
82  $err = $this->addQuery($tquery);
83  return $err;
84  }
85 
86  function AddQuery($tquery)
87  {
88  // insert query in search document
89  if (is_array($tquery)) $query = implode(";\n", $tquery);
90  else $query = $tquery;
91 
92  if ($query == "") return "";
93  if ($this->id == "") return "";
94 
95  $oqd = new QueryDir($this->dbaccess);
96  $oqd->dirid = $this->id;
97  $oqd->qtype = "M"; // multiple
98  $oqd->query = $query;
99 
100  if ($this->id > 0) $this->exec_query("delete from fld where dirid=" . intval($this->id) . " and qtype='M'");
101  $err = $oqd->Add();
102  if ($err == "") {
103  $this->setValue("SE_SQLSELECT", $query);
104  $err = $this->modify();
105  }
106 
107  return $err;
108  }
109  /**
110  * Test if current user can add or delete document in this folder
111  * always false for a search
112  * @return string error message, if no error empty string
113  */
114  function canModify()
115  {
116  return _("containt of searches cannot be modified");
117  }
118  /**
119  * return true if the search has parameters
120  */
121  function isParameterizable()
122  {
123  return false;
124  }
125 
126  function GetQueryOld()
127  {
128  $query = new QueryDb($this->dbaccess, "QueryDir");
129  $query->AddQuery("dirid=" . $this->id);
130  $query->AddQuery("qtype != 'S'");
131  $tq = $query->Query(0, 0, "TABLE");
132 
133  if ($query->nb > 0) {
134  return $tq[0]["query"];
135  }
136  return "";
137  }
138  /**
139  * return SQL query(ies) needed to search documents
140  * @return array string
141  */
142  function getQuery()
143  {
144  if (!$this->isStaticSql()) {
145  $query = $this->ComputeQuery($this->getRawValue("se_key") , $this->getRawValue("se_famid") , $this->getRawValue("se_latest") , $this->getRawValue("se_case") == "yes", $this->getRawValue("se_idfld") , $this->getRawValue("se_sublevel") === "", $this->getRawValue("se_case") == "full");
146  // print "<HR>getQuery1:[$query]";
147 
148  } else {
149  $query[] = $this->getRawValue("SE_SQLSELECT");
150  // print "<BR><HR>".$this->getRawValue("se_latest")."/".$this->getRawValue("se_case")."/".$this->getRawValue("se_key");
151  // print "getQuery2:[$query]";
152 
153  }
154 
155  return $query;
156  }
157  /**
158  * @param bool $full set to true if wan't use full text indexing
159  */
161  {
162  $filters = array();
163 
164  $acls = $this->getMultipleRawValues("se_acl");
165  if ((count($acls) > 0 && ($this->userid != 1))) {
166  // print_r2($acls);
167  foreach ($acls as $acl) {
168  $dacl = $this->dacls[$acl];
169  if ($dacl) {
170  $posacl = $dacl["pos"];
171  $filters[] = sprintf("hasaprivilege('%s', profid, %d)", DocPerm::getMemberOfVector($this->userid) , (1 << intval($posacl)));
172  }
173  }
174  }
175 
176  if ($latest == "fixed") {
177  $filters[] = "locked = -1";
178  $filters[] = "lmodify = 'L'";
179  } else if ($latest == "allfixed") {
180  $filters[] = "locked = -1";
181  }
182  if ($latest == "lastfixed") {
183  $filters[] = "locked = -1";
184  }
185 
186  if ($this->getRawValue("se_archive") > 0) {
187  $filters[] = sprintf("archiveid = %d", $this->getRawValue("se_archive"));
188  }
189  if ($keyword) {
190  if ($keyword[0] == '~') {
191  $full = false; // force REGEXP
192  $keyword = substr($keyword, 1);
193  } else if ($keyword[0] == '*') {
194  $full = true; // force FULLSEARCH
195  $keyword = substr($keyword, 1);
196  }
197  }
198  if ($full) {
199  $this->getFullSqlFilters($keyword, $sqlfilters, $order, $tkeys);
200  $filters = array_merge($filters, $sqlfilters);
201  $this->setValue("se_orderby", $order);
202  } else {
203  $op = ($sensitive) ? '~' : '~*';
204  // $filters[] = "usefor != 'D'";
205  $keyword = str_replace("^", "£", $keyword);
206  $keyword = str_replace("$", "£", $keyword);
207  if (strtolower(substr($keyword, 0, 5)) == "::get") { // only get method allowed
208  // it's method call
209  $keyword = $this->ApplyMethod($keyword);
210  $filters[] = sprintf("svalues %s '%s'", $op, pg_escape_string($keyword));
211  } else if ($keyword != "") {
212  // transform conjonction
213  $tkey = explode(" ", $keyword);
214  $ing = false;
215  $ckey = '';
216  foreach ($tkey as $k => $v) {
217  if ($ing) {
218  if ($v[strlen($v) - 1] == '"') {
219  $ing = false;
220  $ckey.= " " . substr($v, 0, -1);
221  $filters[] = sprintf("svalues %s '%s'", $op, pg_escape_string($ckey));
222  } else {
223  $ckey.= " " . $v;
224  }
225  } else if ($v && $v[0] == '"') {
226  if ($v[strlen($v) - 1] == '"') {
227  $ckey = substr($v, 1, -1);
228  $filters[] = sprintf("svalues %s '%s'", $op, pg_escape_string($ckey));
229  } else {
230  $ing = true;
231  $ckey = substr($v, 1);
232  }
233  } else {
234  $filters[] = sprintf("svalues %s '%s'", $op, pg_escape_string($v));
235  }
236  }
237  }
238  $this->setValue("se_orderby", " ");
239  }
240  if ($this->getRawValue("se_sysfam") == 'no' && (!$this->getRawValue("se_famid"))) {
241  $filters[] = sprintf("usefor !~ '^S'");
242  $filters[] = sprintf("doctype != 'C'");
243  }
244  return $filters;
245  }
246  /**
247  * return sqlfilters for a simple query in fulltext mode
248  * @param string $keyword the word(s) searched
249  * @param array &$sqlfilters return array of sql conditions
250  * @param string &$sqlorder return sql order by
251  * @param string &$fullkeys return tsearch2 keys for use it in headline sql function
252  * @return void
253  */
254  static function getFullSqlFilters($keyword, &$sqlfilters, &$sqlorder, &$fullkeys)
255  {
256  $fullkeys = "";
257  $sqlorder = "";
258  $sqlfilters = array(
259  "true"
260  );
261  if ($keyword == "") return;
262  $pspell_link = false;
263  if (function_exists('pspell_new')) {
264  $pspell_link = pspell_new("fr", "", "", "utf-8", PSPELL_FAST);
265  }
266  $tkeys = array();
267  $sqlfilters = array();
268 
269  $keyword = preg_replace('/\s+(OR)\s+/u', '|', $keyword);
270  $keyword = preg_replace('/\s+(AND)\s+/u', ' ', $keyword);
271  $tkeys = explode(" ", $keyword);
272  $sqlfiltersbrut = array();
273  $tsearchkeys = array();
274  foreach ($tkeys as $k => $key) {
275  $key = trim($key);
276  if ($key) {
277  $tsearchkeys[$k] = $key;
278  if ($pspell_link !== false) {
279  if ((!is_numeric($key)) && (strstr($key, '|') === false) && (strstr($key, '&') === false) && (ord($key[0]) > 47) && (!pspell_check($pspell_link, $key))) {
280  $suggestions = pspell_suggest($pspell_link, $key);
281  $sug = $suggestions[0];
282  //foreach ($suggestions as $k=>$suggestion) { echo "$k : $suggestion\n"; }
283  if ($sug && (unaccent($sug) != $key) && (!strstr($sug, ' '))) $tsearchkeys[$k] = "$key|$sug";
284  }
285  }
286  if (strstr($key, '"') !== false) {
287  // add more filter for search complete and exact expression
288  if (strstr($key, '|') === false) {
289  $sqlfiltersbrut[] = "svalues ~* E'\\\\y" . pg_escape_string(str_replace(array(
290  '"',
291  '&',
292  '(',
293  ')'
294  ) , array(
295  "",
296  ' ',
297  '',
298  ''
299  ) , $key)) . "\\\\y' ";
300  } else {
301  list($left, $right) = explode("|", $key);
302  if (strstr($left, '"') !== false) $q1 = "svalues ~* E'\\\\y" . pg_escape_string(str_replace(array(
303  '"',
304  '&',
305  '(',
306  ')'
307  ) , array(
308  "",
309  ' ',
310  '',
311  ''
312  ) , $left)) . "\\\\y' ";
313  else $q1 = "";
314  if (strstr($right, '"') !== false) $q2 = "svalues ~* E'\\\\y" . pg_escape_string(str_replace(array(
315  '"',
316  '&',
317  '(',
318  ')'
319  ) , array(
320  "",
321  ' ',
322  '',
323  ''
324  ) , $right)) . "\\\\y' ";
325  else $q2 = "";
326  $q3 = "fulltext @@ to_tsquery('french','" . pg_escape_string(unaccent($left)) . "') ";
327  $q4 = "fulltext @@ to_tsquery('french','" . pg_escape_string(unaccent($right)) . "') ";
328 
329  if ((!$q1) && $q2) $sqlfiltersbrut[] = "($q4 and $q2) or $q3";
330  elseif ((!$q2) && $q1) $sqlfiltersbrut[] = "($q3 and $q1) or $q4";
331  elseif ($q2 && $q1) $sqlfiltersbrut[] = "($q3 and $q1) or ($q4 and $q2)";
332  }
333  }
334  }
335  }
336 
337  if (count($tsearchkeys) > 0) {
338  $fullkeys = '(' . implode(")&(", $tsearchkeys) . ')';
339  $fullkeys = unaccent($fullkeys);
340  $fullkeys = pg_escape_string($fullkeys);
341  $sqlfilters[] = sprintf("fulltext @@ to_tsquery('french','%s') ", pg_escape_string($fullkeys));
342  }
343  if (count($sqlfiltersbrut) > 0) $sqlfilters = array_merge($sqlfilters, $sqlfiltersbrut);
344  $sqlorder = sprintf("ts_rank(fulltext,to_tsquery('french','%s')) desc", pg_escape_string($fullkeys));
345  }
346 
347  function ComputeQuery($keyword = "", $famid = - 1, $latest = "yes", $sensitive = false, $dirid = - 1, $subfolder = true, $full = false)
348  {
349  if ($dirid > 0) {
350  if ($subfolder) $cdirid = getRChildDirId($this->dbaccess, $dirid, array() , 0, $this->folderRecursiveLevel);
351  else $cdirid = $dirid;
352  } else $cdirid = 0;
353  if ($keyword) {
354  if ($keyword[0] == '~') {
355  $full = false;
356  $keyword = substr($keyword, 1);
357  } else if ($keyword[0] == '*') {
358  $full = true;
359  $keyword = substr($keyword, 1);
360  }
361  }
362  $filters = $this->getSqlGeneralFilters($keyword, $latest, $sensitive, $full);
363 
364  $only = '';
365  if ($this->getRawValue("se_famonly") == "yes") {
366  if (!is_numeric($famid)) $famid = getFamIdFromName($this->dbaccess, $famid);
367  $only = "only";
368  }
369 
370  $query = getSqlSearchDoc($this->dbaccess, $cdirid, $famid, $filters, false, $latest == "yes", $this->getRawValue("se_trash") , false, $level = 2, $join = '', $only);
371 
372  return $query;
373  }
374  /**
375  * return true if the sqlselect is writted by hand
376  * @return bool
377  */
378  function isStaticSql()
379  {
380  return ($this->getRawValue("se_static") != "") || (($this->getRawValue("se_latest") == "") && ($this->getRawValue("se_case") == "") && ($this->getRawValue("se_key") == ""));
381  }
382  /**
383  * return error if query filters are not compatibles
384  * @return string error message , empty if no errors
385  */
386  function getSqlParseError()
387  {
388  return "";
389  }
390 
391  function preRefresh()
392  {
393  $err = "";
394 
395  if (!$this->isStaticSql()) {
396  if (!$this->isParameterizable()) $query = $this->getQuery();
397  else $query = 'select id from only doc where false';
398  $err = $this->AddQuery($query);
399  }
400  if ($err == "") $err = $this->getSqlParseError();
401  return $err;
402  }
403  /**
404  * @templateController
405  * @return string
406  */
407  function editsearch()
408  {
409  global $action;
410 
411  $rtarget = getHttpVars("rtarget");
412  $this->lay->eset("rtarget", $rtarget);
413  $this->lay->set("restrict", false);
414  $this->lay->set("archive", false);
415 
416  $farch = new_doc($this->dbaccess, "ARCHIVING");
417  if ($farch) $this->lay->set("archive", ($farch->control("view") == ""));
418  $this->lay->eset("thekey", $this->getRawValue("se_key"));
419  $dirid = GetHttpVars("dirid"); // to set restriction family
420  $action->parent->AddJsRef($action->GetParam("CORE_PUBURL") . "/lib/jquery/jquery.js");
421  $action->parent->AddJsRef($action->GetParam("CORE_PUBURL") . "/FDL/Layout/edittable.js");
422  $action->parent->AddJsRef($action->GetParam("CORE_PUBURL") . "/FREEDOM/Layout/editdsearch.js");
423  $famid = $this->getRawValue("se_famid");
424  $classid = 0;
425  if ($dirid > 0) {
426  /*
427  * @var Dir $dir
428  */
429  $dir = new_Doc($this->dbaccess, $dirid);
430  if (method_exists($dir, "isAuthorized")) {
431  if ($dir->isAuthorized($classid)) {
432  // verify if classid is possible
433  if ($dir->hasNoRestriction()) {
434  $tclassdoc = GetClassesDoc($this->dbaccess, $action->user->id, $classid, "TABLE");
435  } else {
436  $tclassdoc = $dir->getAuthorizedFamilies();
437  $this->lay->set("restrict", true);
438  }
439  } else {
440  $tclassdoc = $dir->getAuthorizedFamilies();
441  $first = current($tclassdoc);
442  $famid = abs($first["id"]);
443  $this->lay->set("restrict", true);
444  }
445  } else {
446  $tclassdoc = GetClassesDoc($this->dbaccess, $action->user->id, $classid, "TABLE");
447  }
448  } else {
449  $tclassdoc = GetClassesDoc($this->dbaccess, $action->user->id, $classid, "TABLE");
450  }
451 
452  $this->lay->eset("selfam", _("no family"));
453  $selectclass = array();
454  foreach ($tclassdoc as $k => $cdoc) {
455  $selectclass[$k]["idcdoc"] = $cdoc["id"];
456  $selectclass[$k]["classname"] = $cdoc["title"];
457  $selectclass[$k]["system_fam"] = (substr($cdoc["usefor"], 0, 1) == 'S') ? true : false;
458  if (abs($cdoc["initid"]) == abs($famid)) {
459  $selectclass[$k]["selected"] = "selected";
460  if ($famid < 0) $this->lay->eset("selfam", $cdoc["title"] . " " . !!_("(only)"));
461  else $this->lay->eset("selfam", $cdoc["title"]);
462  } else $selectclass[$k]["selected"] = "";
463  }
464 
465  $this->lay->SetBlockData("SELECTCLASS", $selectclass);
466  $this->lay->set("has_permission_fdl_system", $action->parent->hasPermission('FDL', 'SYSTEM'));
467  $this->lay->set("se_sysfam", ($this->getRawValue('se_sysfam') == 'yes') ? true : false);
468 
469  $this->editattr();
470  }
471  /**
472  * @templateController
473  * @return string
474  */
475  function editspeedsearch()
476  {
477  return $this->editsearch();
478  }
479  /**
480  * return document includes in search folder
481  * @param bool $controlview if false all document are returned else only visible for current user document are return
482  * @param array $filter to add list sql filter for selected document
483  * @param int $famid family identifier to restrict search
484  * @return array array of document array
485  */
486  public function getContent($controlview = true, array $filter = array() , $famid = "", $qtype = "TABLE", $trash = "")
487  {
488  if ($controlview) $uid = $this->userid;
489  else $uid = 1;
490  $orderby = $this->getRawValue("se_orderby", "title");
491  $tdoc = internalGetDocCollection($this->dbaccess, $this->initid, 0, "ALL", $filter, $uid, "TABLE", $famid, false, $orderby, true, $this->getRawValue("se_trash"));
492  return $tdoc;
493  }
494 }
495 ?>
static getUserId()
Definition: Class.Doc.php:8960
static getMemberOfVector($uid=0, $strict=false)
$tdoc
__construct($dbaccess= '', $id= '', $res= '', $dbid=0)
global $action
getSqlSearchDoc($dbaccess, $dirid, $fromid, $sqlfilters=array(), $distinct=false, $latest=true, $trash="", $simplesearch=false, $folderRecursiveLevel=2, $join= '', $only="")
Definition: Lib.Dir.php:108
static getFullSqlFilters($keyword, &$sqlfilters, &$sqlorder, &$fullkeys)
exec_query($sql, $lvl=0, $prepare=false)
$full
$sensitive
getContent($controlview=true, array $filter=array(), $famid="", $qtype="TABLE", $trash="")
AddQuery($tquery)
getSqlGeneralFilters($keyword, $latest, $sensitive, $full=false)
modify($nopost=false, $sfields="", $nopre=false)
editattr($withtd=true)
Definition: Class.Doc.php:8129
GetClassesDoc($dbaccess, $userid, $classid=0, $qtype="LIST", $extraFilters=array())
Definition: Lib.Dir.php:906
setValue($attrid, $value, $index=-1, &$kvalue=null)
Definition: Class.Doc.php:3528
getMultipleRawValues($idAttr, $def="", $index=-1)
Definition: Class.Doc.php:3240
addStaticQuery($tquery)
getHttpVars($name, $def="", $scope="all")
Definition: Lib.Http.php:124
internalGetDocCollection($dbaccess, $dirid, $start="0", $slice="ALL", $sqlfilters=array(), $userid=1, $qtype="LIST", $fromid="", $distinct=false, $orderby="title", $latest=true, $trash="", &$debug=null, $folderRecursiveLevel=2, $join= '',\SearchDoc &$searchDoc=null)
Definition: Lib.Dir.php:428
getFamIdFromName($dbaccess, $name)
ComputeQuery($keyword="", $famid=-1, $latest="yes", $sensitive=false, $dirid=-1, $subfolder=true, $full=false)
getRChildDirId($dbaccess, $dirid, $rchilds=array(), $level=0, $levelmax=2)
Definition: Lib.Dir.php:822
new_Doc($dbaccess, $id= '', $latest=false)
$dir
Definition: resizeimg.php:144
__construct($dbaccess= '', $id= '', $res= '', $dbid=0)
$keyword
unaccent($s)
Definition: Lib.Util.php:569
if(($docid!==0)&&(!is_numeric($docid))) $query
const FAM_SEARCH
Definition: Class.Doc.php:33
if($file) if($subject==""&&$file) if($subject=="") $err
getRawValue($idAttr, $def="")
Definition: Class.Doc.php:3117
N_($s)
Definition: Lib.Common.php:18
$latest
← centre documentaire © anakeen