Commit 986c300114cf13cf30faf1a17f330d6f1a2897f3

Authored by Conrad Vermeulen
1 parent 80790675

KTS-3306

"Search should return folders"
Implemented.

KTS-3671 
"Support for searching of symbolic links"
Implemented.

Committed By: Conrad Vermeulen
Reviewed By: Megan Watson

git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@9399 c91229c3-7414-0410-bfa2-8a42b809f60b
search2.php
... ... @@ -35,15 +35,20 @@
35 35 * Contributor( s): ______________________________________
36 36 */
37 37  
  38 +// TODO: do we have to serialise/unserialise the results. this is not optimal!!!
  39 +
38 40 session_start();
39 41 require_once("config/dmsDefaults.php");
  42 +require_once(KT_DIR . '/search2/indexing/indexerCore.inc.php');
  43 +
40 44 require_once(KT_LIB_DIR . "/unitmanagement/Unit.inc");
41 45  
42 46 require_once(KT_LIB_DIR . "/templating/templating.inc.php");
43 47 require_once(KT_LIB_DIR . "/dispatcher.inc.php");
44 48 require_once(KT_LIB_DIR . "/widgets/forms.inc.php");
45 49 require_once(KT_LIB_DIR . "/actions/bulkaction.php");
46   -require_once(KT_DIR . '/search2/search/search.inc.php');
  50 +
  51 +require_once(KT_LIB_DIR . '/browse/DocumentCollection.inc.php');
47 52 require_once(KT_LIB_DIR . '/documentmanagement/Document.inc');
48 53 require_once(KT_LIB_DIR . '/browse/PartialQuery.inc.php');
49 54  
... ... @@ -117,7 +122,6 @@ function search2QuerySort($sSortColumn, $sSortOrder)
117 122 return;
118 123 }
119 124  
120   -
121 125 $results = unserialize($_SESSION['search2_results']);
122 126  
123 127 usort($results, 'search2queryCompare');
... ... @@ -132,38 +136,70 @@ function search2QuerySort($sSortColumn, $sSortOrder)
132 136 */
133 137 class Search2Query extends PartialQuery
134 138 {
135   - function getFolderCount() { return 0; }
  139 + function _count($type)
  140 + {
  141 + $count = 0;
  142 + $results = unserialize($_SESSION['search2_results']);
  143 +
  144 + switch ($type)
  145 + {
  146 + case 'Document':
  147 + return count($results['docs']) + count($results['shortdocs']);
  148 + case 'Folder':
  149 + return count($results['folders']) + count($results['shortfolders']);
  150 + default:
  151 + return 0;
  152 + }
  153 + }
  154 +
  155 + function getFolderCount()
  156 + {
  157 + return $this->_count('Folder');
  158 + }
136 159 function getDocumentCount()
137 160 {
138   - $results = $_SESSION['search2_results'];
139   - if(isset($results) && !empty($results)){
140   - return count(unserialize($results));
  161 + return $this->_count('Document');
  162 + }
  163 +
  164 + function getItems($type, $iStart, $iSize, $sSortColumn, $sSortOrder)
  165 + {
  166 + // TODO: quick hack. do this more optimally!!!!
  167 + $results = unserialize($_SESSION['search2_results']);
  168 +
  169 + switch ($type)
  170 + {
  171 + case 'Document':
  172 + $type = 'docs';
  173 + break;
  174 + case 'Folder':
  175 + $type = 'folders';
  176 + break;
141 177 }
142   - return 0;
  178 +
  179 + $resultArray = $results[$type];
  180 + foreach($results['short' . $type] as $rec)
  181 + {
  182 + $resultArray[] = $rec;
  183 + }
  184 +
  185 + $resultArray = array_slice($resultArray, $iStart, $iSize);
  186 + $results = array();
  187 + foreach($resultArray as $rec)
  188 + {
  189 + $results[] = array('id'=>$rec->Id);
  190 + }
  191 +
  192 + return $results;
143 193 }
144 194  
145 195 function getFolders($iBatchSize, $iBatchStart, $sSortColumn, $sSortOrder, $sJoinClause = null, $aJoinParams = null)
146 196 {
147   - return array();
  197 + return $this->getItems('Folder', $iBatchStart, $iBatchSize, $sSortColumn, $sSortOrder);
148 198 }
149 199  
150 200 function getDocuments($iBatchSize, $iBatchStart, $sSortColumn, $sSortOrder, $sJoinClause = null, $aJoinParams = null)
151 201 {
152   - search2QuerySort($_GET['sort_on'], $_GET['sort_order']);
153   - $results = unserialize($_SESSION['search2_results']);
154   -
155   - $batch = array();
156   -
157   - $no_results = count($results);
158   - for($i=0;$i<$no_results;$i++)
159   - {
160   - if ($i < $iBatchStart) continue;
161   - if ($i > $iBatchStart + $iBatchSize) continue;
162   -
163   - $batch[] = array('id'=>$results[$i]->DocumentID);
164   - }
165   -
166   - return $batch;
  202 + return $this->getItems('Document', $iBatchStart, $iBatchSize, $sSortColumn, $sSortOrder);
167 203 }
168 204 }
169 205  
... ... @@ -208,10 +244,12 @@ class SearchDispatcher extends KTStandardDispatcher {
208 244 {
209 245 $expr = parseExpression($query);
210 246  
211   - $result = $expr->evaluate();
212   - usort($result, 'rank_compare');
  247 + $results = $expr->evaluate();
  248 + $results = resolveSearchShortcuts($results);
213 249  
214   - $_SESSION['search2_results'] = serialize($result);
  250 + usort($result['docs'], 'rank_compare');
  251 +
  252 + $_SESSION['search2_results'] = serialize($results);
215 253 $_SESSION['search2_query'] = $query;
216 254 $_SESSION['search2_sort'] = 'rank';
217 255  
... ... @@ -410,6 +448,15 @@ class SearchDispatcher extends KTStandardDispatcher {
410 448  
411 449 $results = unserialize($_SESSION['search2_results']);
412 450  
  451 + // NOTE: sorting results (when it is implemented) might have to be done per section, as it is done with the browse view
  452 +
  453 + $resultArray = $results['docs'];
  454 + foreach($results['folders'] as $f) $resultArray[] = $f;
  455 + foreach($results['shortdocs'] as $d) $resultArray[] = $d;
  456 + foreach($results['shortfolders'] as $f) $resultArray[] = $f;
  457 +
  458 + $results = $resultArray;
  459 +
413 460 if (!is_array($results) || count($results) == 0)
414 461 {
415 462 $results=array();
... ...
search2/indexing/indexerCore.inc.php
... ... @@ -41,17 +41,142 @@ define(&#39;SEARCH2_INDEXER_DIR&#39;,realpath(dirname(__FILE__)) . &#39;/&#39;);
41 41 require_once('indexing/extractorCore.inc.php');
42 42 require_once(KT_DIR . '/plugins/ktcore/scheduler/schedulerUtil.php');
43 43  
44   -
45 44 class IndexerInconsistencyException extends Exception {};
46 45  
  46 +// TODO: Query Result Items code should be moved into the Search section. It has less to do with indexing...
  47 +
47 48 class QueryResultItem
48 49 {
49   - protected $document_id;
  50 + protected $id;
50 51 protected $title;
51 52 protected $rank;
52 53 protected $text;
53   - protected $filesize;
54 54 protected $fullpath;
  55 +
  56 + public function __construct($id, $title, $rank, $text, $fullpath)
  57 + {
  58 + $this->id = $id;
  59 + $this->title = $title;
  60 + $this->rank = $rank;
  61 + $this->text = $text;
  62 + $this->fullpath = $fullpath;
  63 + }
  64 +
  65 + public function getId() { return $this->id; }
  66 +
  67 + public function getIsProxy() { return $this instanceof ProxyResultItem; }
  68 + public function getIsFolder() { return substr(get_class($this), 0, 6) == 'Folder' ; }
  69 + public function getIsDocument() { return substr(get_class($this), 0, 8) == 'Document' ; }
  70 +
  71 + public function setRank($value)
  72 + {
  73 + $this->rank = number_format($value,2,'.',',');
  74 + }
  75 +
  76 + public function getIsLive()
  77 + {
  78 + return true;
  79 + }
  80 +
  81 + public function setTitle($value)
  82 + {
  83 + $this->title = $value;
  84 + }
  85 +
  86 + public function setText($value)
  87 + {
  88 + $this->text = $value;
  89 + }
  90 +
  91 + public function getRelevance() { return (float) $this->rank; }
  92 + public function getRank() { return $this->getRelevance(); }
  93 + public function getText() { return (string) $this->text; }
  94 + public function getTitle() { return (string) $this->title; }
  95 + public function getFullPath() { return (string) $this->fullpath; }
  96 +
  97 + protected function __get($property)
  98 + {
  99 + if (empty($property))
  100 + {
  101 + return '';
  102 + }
  103 +
  104 + $method = 'get' . $property;
  105 + if (method_exists($this, $method))
  106 + {
  107 + return $this->$method();
  108 + }
  109 + return $this->getUnknown();
  110 + }
  111 +
  112 + protected function getUnknown()
  113 + {
  114 + return _kt('n/a');
  115 + }
  116 +
  117 + protected function __set($property, $value)
  118 + {
  119 + if (empty($property))
  120 + {
  121 + return '';
  122 + }
  123 +
  124 + $method = 'set' . $property;
  125 + if (method_exists($this, $method))
  126 + {
  127 + return $this->$method($value);
  128 + }
  129 + throw new Exception("Unknown property '$property' to set on QueryResultItem");
  130 + }
  131 +}
  132 +
  133 +class ProxyResultItem extends QueryResultItem
  134 +{
  135 + protected $proxy;
  136 + protected $proxyId;
  137 +
  138 + public function __construct($proxyId, $proxy)
  139 + {
  140 + parent::__construct($proxyId, $proxy->getTitle, $proxy->getRank(), $proxy->getText(), $proxy->getFullPath());
  141 + $this->proxyId = $proxyId;
  142 + $this->proxy = $proxy;
  143 + }
  144 +
  145 + public function getId() { return $this->proxyId; }
  146 + public function getTitle() { return $this->proxy->getTitle(); }
  147 + public function getRealId() { return $this->proxy->getId(); }
  148 +
  149 + protected function __get($property)
  150 + {
  151 + $method = 'get' . $property;
  152 +
  153 + if (method_exists($this, $method))
  154 + {
  155 + return $this->$method();
  156 + }
  157 + else
  158 + {
  159 + return $this->proxy->$method();
  160 + }
  161 + }
  162 +
  163 + protected function __set($property, $value)
  164 + {
  165 + $method = 'set' . $property;
  166 + if (method_exists($this, $method))
  167 + {
  168 + return $this->$method($value);
  169 + }
  170 + else
  171 + {
  172 + return $this->proxy->$method($value);
  173 + }
  174 + }
  175 +}
  176 +
  177 +class DocumentResultItem extends QueryResultItem
  178 +{
  179 + protected $filesize;
55 180 protected $live;
56 181 protected $version;
57 182 protected $mimeType;
... ... @@ -78,17 +203,14 @@ class QueryResultItem
78 203 protected $mimeDisplay;
79 204 protected $oemDocumentNo;
80 205  
81   - public function __construct($document_id, $rank=null, $title=null, $text=null)
  206 + public function __construct($document_id, $rank=null, $title=null, $text=null, $fullpath = null)
82 207 {
83   - $this->document_id=(int) $document_id;
84   - $this->rank= $rank;
85   - $this->title=$title;
86   - $this->text = $text;
  208 + parent::__construct($document_id, $title, $rank, $text, $fullpath);
87 209 $this->live = true;
88 210 $this->loadDocumentInfo();
89 211 }
90 212  
91   - protected function __isset($property)
  213 + /*protected function __isset($property)
92 214 {
93 215 switch($property)
94 216 {
... ... @@ -101,8 +223,9 @@ class QueryResultItem
101 223 throw new Exception("Unknown property '$property' to get on QueryResultItem");
102 224 }
103 225 return true; // should not be reached
104   - }
  226 + }*/
105 227  
  228 + // TODO: this is bad. must refactor to do the query on the group of documents.
106 229 public function loadDocumentInfo()
107 230 {
108 231 global $default;
... ... @@ -127,7 +250,7 @@ class QueryResultItem
127 250 LEFT JOIN users cbu ON d.creator_id=cbu.id
128 251 LEFT JOIN users ou ON d.owner_id=ou.id
129 252 WHERE
130   - d.id=$this->document_id";
  253 + d.id=$this->id";
131 254  
132 255 $result = DBUtil::getOneResult($sql);
133 256  
... ... @@ -186,107 +309,116 @@ class QueryResultItem
186 309  
187 310 $this->mimeType = $result['mimetype'];
188 311 $this->mimeIconPath = $result['mime_icon_path'];
  312 + if (empty($this->mimeIconPath))
  313 + {
  314 + $this->mimeIconPath = 'unspecified_type';
  315 + }
189 316 $this->mimeDisplay = $result['mime_display'];
190 317  
191 318 $this->storagePath = $result['storage_path'];
192 319 $this->status = Document::getStatusString($result['status_id']);
193 320 }
194 321  
195   - protected function __get($property)
196   - {
197   - switch($property)
198   - {
199   - case null: return '';
200   - case 'DocumentID': return (int) $this->document_id;
201   - case 'Relevance':
202   - case 'Rank': return (float) $this->rank;
203   - case 'Text': return (string) $this->text;
204   - case 'Title': return (string) $this->title;
205   - case 'FullPath': return (string) $this->fullpath;
206   - case 'IsLive': return (bool) $this->live;
207   - case 'Filesize': return $this->filesize;
208   - case 'Version': return (string) $this->version;
209   - case 'Filename': return (string)$this->filename;
210   - case 'FolderId': return (int)$this->folderId;
211   - case 'OemDocumentNo': return (string) $this->oemDocumentNo;
212   - case 'Document':
213   - if (is_null($this->document))
  322 + public function getDocumentID() { return $this->getId(); }
  323 + public function getIsLive() { return (bool) $this->live; }
  324 + public function getFilesize() { return $this->filesize; }
  325 + public function getVersion() { return (string) $this->version; }
  326 + public function getFilename() { return (string)$this->filename; }
  327 + public function getFolderId() { return (int)$this->folderId; }
  328 + public function getOemDocumentNo() { return (string) $this->oemDocumentNo; }
  329 + public function getDocument() { if (is_null($this->document))
214 330 {
215   - $this->document = Document::get($this->document_id);
  331 + $this->document = Document::get($this->id);
216 332 }
217   - return $this->document;
218   - case 'IsAvailable':
219   - return $this->Document->isLive();
220   - case 'CheckedOutUser':
221   - case 'CheckedOutBy':
222   - return (string) $this->checkedOutUser;
223   - case 'WorkflowOnly':
224   - case 'Workflow':
225   - return (string)$this->workflow;
226   - case 'WorkflowStateOnly':
227   - case 'WorkflowState':
228   - return (string)$this->workflowState;
229   - case 'WorkflowAndState':
230   - if (is_null($this->workflow))
  333 + return $this->document; }
  334 + public function getIsAvailable() { return $this->Document->isLive(); }
  335 + public function getCheckedOutUser() { return (string) $this->checkedOutUser; }
  336 + public function getCheckedOutByr() { return $this->getCheckedOutUser(); }
  337 + public function getWorkflowOnly() { return (string)$this->workflow; }
  338 + public function getWorkflow() { return $this->getWorkflow(); }
  339 + public function getWorkflowStateOnly() { return (string)$this->workflowState; }
  340 + public function getWorkflowState() { return $this->getWorkflowStateOnly(); }
  341 + public function getWorkflowAndState() { if (is_null($this->workflow))
231 342 {
232 343 return '';
233 344 }
234   - return "$this->workflow - $this->workflowState";
235   - case 'MimeType':
236   - return (string) $this->mimeType;
237   - case 'MimeIconPath':
238   - return (string) $this->mimeIconPath;
239   - case 'MimeDisplay':
240   - return (string) $this->mimeDisplay;
241   - case 'DateCheckedOut':
242   - return (string) $this->dateCheckedout;
243   - case 'ModifiedBy':
244   - return (string) $this->modifiedBy;
245   - case 'DateModified':
246   - return (string) $this->dateModified;
247   - case 'CreatedBy':
248   - return (string) $this->createdBy;
249   - case 'DateCreated':
250   - return (string) $this->dateCreated;
251   - case 'Owner':
252   - case 'OwnedBy':
253   - return (string) $this->owner;
254   - case 'IsImmutable':
255   - case 'Immutable':
256   - return (bool) $this->immutable;
257   - case 'Status':
258   - return $this->status;
259   - case 'StoragePath':
260   - return $this->storagePath;
261   - case 'DocumentType':
262   - return $this->documentType;
263   - case 'Permissions':
264   - return 'not available';
265   - case 'CanBeReadByUser':
266   - if (!$this->live)
  345 + return "$this->workflow - $this->workflowState"; }
  346 + public function getMimeType() { return (string) $this->mimeType; }
  347 + public function getMimeIconPath() { return (string) $this->mimeIconPath; }
  348 + public function getMimeDisplay() { return (string) $this->mimeDisplay; }
  349 + public function getDateCheckedOut() { return (string) $this->dateCheckedout; }
  350 + public function getModifiedBy() { return (string) $this->modifiedBy; }
  351 + public function getDateModified() { return (string) $this->dateModified; }
  352 + public function getCreatedBy() { return (string) $this->createdBy; }
  353 + public function getDateCreated() { return (string) $this->dateCreated; }
  354 + public function getOwner() { return (string) $this->owner; }
  355 + public function getOwnedBy() { return $this->getOwner(); }
  356 + public function getIsImmutable() { return (bool) $this->immutable; }
  357 + public function getImmutable() { return $this->getIsImmutable(); }
  358 + public function getStatus() { return $this->status; }
  359 + public function getStoragePath() { return $this->storagePath; }
  360 + public function getDocumentType() { return $this->documentType; }
  361 + public function getPermissions() { return 'not available'; }
  362 + public function getCanBeReadByUser() { if (!$this->live)
267 363 return false;
268 364 if (Permission::userHasDocumentReadPermission($this->Document))
269 365 return true;
270 366 if (Permission::adminIsInAdminMode())
271 367 return true;
272   - return false;
273   - default:
274   - throw new Exception("Unknown property '$property' to get on QueryResultItem");
275   - }
276   - return ''; // Should not be reached
  368 + return false; }
  369 +
  370 +
  371 +}
  372 +
  373 +class FolderResultItem extends QueryResultItem
  374 +{
  375 + protected $folder;
  376 + protected $createdBy;
  377 + protected $parentId;
  378 +
  379 + public function __construct($folder_id, $rank=null, $title=null, $text=null, $fullpath = null)
  380 + {
  381 + parent::__construct($folder_id, $title, $rank, $text, $fullpath);
  382 + $this->loadDocumentInfo();
277 383 }
278 384  
279   - protected function __set($property, $value)
  385 + public function getFolderID() { return $this->getId(); }
  386 + public function getParentID() { return $this->parentId; }
  387 + public function getCreatedBy() { return $this->createdBy; }
  388 + public function getMimeIconPath() { return 'folder'; }
  389 + public function getFolder() {
  390 + return $this->folder; }
  391 +
  392 + public function loadDocumentInfo()
280 393 {
281   - switch($property)
  394 + global $default;
  395 + $this->folder = $folder = Folder::get($this->getFolderID());
  396 + if (PEAR::isError($folder))
282 397 {
283   - case 'Rank': $this->rank = number_format($value,2,'.',','); break;
284   - case 'Title': $this->title = $value; break;
285   - case 'Text': $this->text = $value; break;
286   - default:
287   - throw new Exception("Unknown property '$property' to set on QueryResultItem");
  398 + throw new Exception('Database exception! There appears to be an error in the system: ' .$result->getMessage());
288 399 }
  400 + $this->title = $folder->getName();
  401 + $this->fullpath = '/' . $folder->getFullPath();
  402 + $this->parentId = $folder->getParentId();
  403 +
  404 + $user = User::get($folder->getCreatorID());
  405 + $this->createdBy = (PEAR::isError($user))?_kt('Unknown'):$user->getName();
289 406 }
  407 +
  408 +}
  409 +
  410 +class DocumentShortcutResultItem extends ProxyResultItem
  411 +{
  412 + public function getDocumentID() { return $this->getId(); }
  413 + public function getMimeIconPath() { return $this->proxy->getMimeIconPath() . '_shortcut'; }
  414 +
  415 +}
  416 +
  417 +class FolderShortcutResultItem extends ProxyResultItem
  418 +{
  419 + public function getFolderID() { return $this->getId(); }
  420 + public function getMimeIconPath() { return 'folder_shortcut'; }
  421 +
290 422 }
291 423  
292 424 function MatchResultCompare($a, $b)
... ...
search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php
... ... @@ -187,14 +187,11 @@ class JavaXMLRPCLuceneIndexer extends Indexer
187 187 {
188 188 try
189 189 {
190   - $item = new QueryResultItem($document_id);
191   - $item->Title = $hit->Title;
192   - $item->Text = $hit->Content;
193   - $item->Rank = $hit->Rank;
  190 + $item = new DocumentResultItem($document_id, $hit->Rank, $hit->Title, $hit->Content);
194 191  
195 192 if ($item->CanBeReadByUser)
196 193 {
197   - $results[$document_id] = $item;
  194 + $results['docs'][$document_id] = $item;
198 195 }
199 196 }
200 197 catch(IndexerInconsistencyException $ex)
... ...
search2/search/expr.inc.php
... ... @@ -43,6 +43,8 @@
43 43 *
44 44 */
45 45  
  46 +// TODO: search expression evaluation needs some optimisation
  47 +
46 48 require_once('indexing/indexerCore.inc.php');
47 49 require_once('search/fieldRegistry.inc.php');
48 50 require_once('search/exprConstants.inc.php');
... ... @@ -157,11 +159,28 @@ class Expr
157 159  
158 160 protected $expr_id;
159 161  
  162 + protected $context;
  163 +
160 164 public function __construct()
161 165 {
162 166 $this->expr_id = Expr::$node_id++;
163 167 }
164 168  
  169 + public function appliesToContext()
  170 + {
  171 + return ExprContext::DOCUMENT;
  172 + }
  173 +
  174 + public function setContext($context)
  175 + {
  176 + $this->context = $context;
  177 + }
  178 +
  179 + public function getContext()
  180 + {
  181 + return $this->context;
  182 + }
  183 +
165 184 public function getExprId()
166 185 {
167 186 return $this->expr_id;
... ... @@ -406,6 +425,14 @@ class FieldExpr extends Expr
406 425 }
407 426 }
408 427  
  428 +class ExprContext
  429 +{
  430 + const DOCUMENT = 1;
  431 + const FOLDER = 2;
  432 + const DOCUMENT_AND_FOLDER = 3;
  433 +}
  434 +
  435 +
409 436 class DBFieldExpr extends FieldExpr
410 437 {
411 438 /**
... ... @@ -966,24 +993,43 @@ class SQLQueryBuilder implements QueryBuilder
966 993 private $sql;
967 994 private $db;
968 995 private $metadata;
  996 + private $context;
969 997  
970   - public function __construct()
  998 + public function __construct($context)
971 999 {
972   - $this->used_tables = array(
973   - 'documents'=>1,
974   - 'document_metadata_version'=>1,
975   - 'document_content_version'=>0,
976   - 'tag_words'=>0,
977   - 'document_fields_link'=>0
978   - );
979   -
980   - $this->aliases = array(
981   - 'documents'=>'d',
982   - 'document_metadata_version'=>'dmv',
983   - 'document_content_version'=>'dcv',
984   - 'tag_words'=>'tw',
985   - 'document_fields_link'=>'pdfl'
986   - );
  1000 + $this->context = $context;
  1001 +
  1002 + switch ($context)
  1003 + {
  1004 + case ExprContext::DOCUMENT:
  1005 + $this->used_tables = array(
  1006 + 'documents'=>1,
  1007 + 'document_metadata_version'=>1,
  1008 + 'document_content_version'=>0,
  1009 + 'tag_words'=>0,
  1010 + 'document_fields_link'=>0
  1011 + );
  1012 +
  1013 + $this->aliases = array(
  1014 + 'documents'=>'d',
  1015 + 'document_metadata_version'=>'dmv',
  1016 + 'document_content_version'=>'dcv',
  1017 + 'tag_words'=>'tw',
  1018 + 'document_fields_link'=>'pdfl'
  1019 + );
  1020 + break;
  1021 + case ExprContext::FOLDER:
  1022 + $this->used_tables = array(
  1023 + 'folders'=>1,
  1024 + );
  1025 +
  1026 + $this->aliases = array(
  1027 + 'folders'=>'f',
  1028 + );
  1029 + break;
  1030 + default:
  1031 + throw new Exception('This was not expected - Context = ' . $context);
  1032 + }
987 1033  
988 1034 $this->sql = '';
989 1035 $this->db = array();
... ... @@ -1013,8 +1059,15 @@ class SQLQueryBuilder implements QueryBuilder
1013 1059 }
1014 1060 elseif ($expr->isDBExpr())
1015 1061 {
1016   - $this->db[] = & $parent;
1017   - $this->used_tables[$expr->getTable()]++;
  1062 + if (($this->context & $expr->appliesToContext()) == $this->context)
  1063 + {
  1064 + $this->db[] = & $parent;
  1065 + $tablename = $expr->getTable();
  1066 + if (array_key_exists($tablename, $this->used_tables))
  1067 + {
  1068 + $this->used_tables[$tablename]++;
  1069 + }
  1070 + }
1018 1071 }
1019 1072 elseif ($expr->isOpExpr())
1020 1073 {
... ... @@ -1042,12 +1095,16 @@ class SQLQueryBuilder implements QueryBuilder
1042 1095  
1043 1096 if ($field->isMetadataField())
1044 1097 {
1045   - $this->metadata[] = $expr->getParent();
  1098 + $this->metadata[] = $expr->getParent();
1046 1099 }
1047 1100 elseif ($field->isDBExpr())
1048 1101 {
1049   - $this->db[] = $expr->getParent();
1050   - $this->used_tables[$field->getTable()]++;
  1102 + $this->db[] = $expr->getParent();
  1103 + $tablename = $field->getTable();
  1104 + if (array_key_exists($tablename, $this->used_tables))
  1105 + {
  1106 + $this->used_tables[$tablename]++;
  1107 + }
1051 1108 }
1052 1109 }
1053 1110 }
... ... @@ -1076,29 +1133,40 @@ class SQLQueryBuilder implements QueryBuilder
1076 1133 $left = $expr->left();
1077 1134 $right = $expr->right();
1078 1135 $isNot = $expr->not();
1079   - if ($left->isMetadataField())
  1136 + if ($left->isMetadataField() )
1080 1137 {
1081   - $offset = $this->resolveMetadataOffset($expr) + 1;
1082   -
1083   - $fieldset = $left->getField();
1084   - $query = '(';
1085   -
1086   - if ($isNot)
1087   - {
1088   - $query .= "df$offset.name IS NULL OR ";
1089   - }
1090   -
1091   - $query .= '(' . "df$offset.name='$fieldset' AND " . $right->getSQL($left, "dfl$offset.value", $expr->op(), $isNot) . ')';
1092   -
1093   -
1094   - $query .= ')';
1095   -
  1138 + if ($this->context == ExprContext::DOCUMENT)
  1139 + {
  1140 + $offset = $this->resolveMetadataOffset($expr) + 1;
  1141 +
  1142 + $fieldset = $left->getField();
  1143 + $query = '(';
  1144 +
  1145 + if ($isNot)
  1146 + {
  1147 + $query .= "df$offset.name IS NULL OR ";
  1148 + }
  1149 +
  1150 + $query .= '(' . "df$offset.name='$fieldset' AND " . $right->getSQL($left, "dfl$offset.value", $expr->op(), $isNot) . ')';
  1151 +
  1152 + $query .= ')';
  1153 + }
  1154 + else
  1155 + {
  1156 + $query = 'false';
  1157 + }
1096 1158 }
1097 1159 else
1098 1160 {
1099   - $fieldname = $this->getFieldnameFromExpr($expr);
1100   -
1101   - $query = $right->getSQL($left, $left->modifyName($fieldname), $expr->op(), $isNot);
  1161 + if ($this->context == ExprContext::FOLDER && $left->getContext() != ExprContext::FOLDER)
  1162 + {
  1163 + $query = 'false';
  1164 + }
  1165 + else
  1166 + {
  1167 + $fieldname = $this->getFieldnameFromExpr($expr);
  1168 + $query = $right->getSQL($left, $left->modifyName($fieldname), $expr->op(), $isNot);
  1169 + }
1102 1170 }
1103 1171 return $query;
1104 1172 }
... ... @@ -1107,17 +1175,30 @@ class SQLQueryBuilder implements QueryBuilder
1107 1175 {
1108 1176 if (count($this->metadata) + count($this->db) == 0)
1109 1177 {
1110   - throw new Exception('nothing to do');
  1178 + // return empty result set
  1179 + return 'select 1 from documents where false';
1111 1180 }
1112 1181  
1113   - // we are doing this because content table is dependant on metadata table
1114   - if ($this->used_tables['document_content_version'] > 0) $this->used_tables['document_metadata_version']++;
1115   -
1116 1182 $sql =
1117 1183 'SELECT ' . "\n";
1118 1184  
1119   - $sql .=
  1185 + if ($this->context == ExprContext::DOCUMENT)
  1186 + {
  1187 + // we are doing this because content table is dependant on metadata table
  1188 + if ($this->used_tables['document_content_version'] > 0)
  1189 + {
  1190 + $this->used_tables['document_metadata_version']++;
  1191 + }
  1192 +
  1193 + $sql .=
1120 1194 ' DISTINCT d.id, dmv.name as title';
  1195 + }
  1196 + else
  1197 + {
  1198 + $sql .=
  1199 + ' DISTINCT f.id, f.name as title';
  1200 + }
  1201 +
1121 1202  
1122 1203 $offset=0;
1123 1204 foreach($this->db as $expr)
... ... @@ -1133,26 +1214,36 @@ class SQLQueryBuilder implements QueryBuilder
1133 1214 }
1134 1215  
1135 1216 $sql .=
1136   - "\n" . 'FROM ' ."\n" .
1137   - ' documents d ' ."\n";
  1217 + "\n" . 'FROM ' ."\n";
1138 1218  
1139   - if ($this->used_tables['document_metadata_version'] > 0)
1140   - {
1141   - $sql .= ' INNER JOIN document_metadata_version dmv ON d.metadata_version_id=dmv.id' . "\n";
1142   - }
1143   - if ($this->used_tables['document_content_version'] > 0)
1144   - {
1145   - $sql .= ' INNER JOIN document_content_version dcv ON dmv.content_version_id=dcv.id ' . "\n";
1146   - }
1147   - if ($this->used_tables['document_fields_link'] > 0)
  1219 + if ($this->context == ExprContext::DOCUMENT)
1148 1220 {
1149   - $sql .= ' LEFT JOIN document_fields_link pdfl ON dmv.id=pdfl.metadata_version_id ' . "\n";
1150   - }
  1221 + $primaryAlias = 'd';
  1222 + $sql .= ' documents d ' ."\n";
1151 1223  
1152   - if ($this->used_tables['tag_words'] > 0)
  1224 + if ($this->used_tables['document_metadata_version'] > 0)
  1225 + {
  1226 + $sql .= ' INNER JOIN document_metadata_version dmv ON d.metadata_version_id=dmv.id' . "\n";
  1227 + }
  1228 + if ($this->used_tables['document_content_version'] > 0)
  1229 + {
  1230 + $sql .= ' INNER JOIN document_content_version dcv ON dmv.content_version_id=dcv.id ' . "\n";
  1231 + }
  1232 + if ($this->used_tables['document_fields_link'] > 0)
  1233 + {
  1234 + $sql .= ' LEFT JOIN document_fields_link pdfl ON dmv.id=pdfl.metadata_version_id ' . "\n";
  1235 + }
  1236 +
  1237 + if ($this->used_tables['tag_words'] > 0)
  1238 + {
  1239 + $sql .= ' LEFT OUTER JOIN document_tags dt ON dt.document_id=d.id ' . "\n" .
  1240 + ' LEFT OUTER JOIN tag_words tw ON dt.tag_id = tw.id ' . "\n";
  1241 + }
  1242 + }
  1243 + else
1153 1244 {
1154   - $sql .= ' LEFT OUTER JOIN document_tags dt ON dt.document_id=d.id ' . "\n" .
1155   - ' LEFT OUTER JOIN tag_words tw ON dt.tag_id = tw.id ' . "\n";
  1245 + $primaryAlias = 'f';
  1246 + $sql .= ' folders f ' ."\n";
1156 1247 }
1157 1248  
1158 1249 $offset = 0;
... ... @@ -1171,17 +1262,18 @@ class SQLQueryBuilder implements QueryBuilder
1171 1262 $offset++;
1172 1263 }
1173 1264  
1174   -
1175   -
1176   - $offset=0;
1177   - foreach($this->metadata as $expr)
  1265 + if ($this->context == ExprContext::DOCUMENT)
1178 1266 {
1179   - $offset++;
1180   - $field = $expr->left();
  1267 + $offset=0;
  1268 + foreach($this->metadata as $expr)
  1269 + {
  1270 + $offset++;
  1271 + $field = $expr->left();
1181 1272  
1182   - $fieldid = $field->getFieldId();
1183   - $sql .= " LEFT JOIN document_fields_link dfl$offset ON dfl$offset.metadata_version_id=d.metadata_version_id AND dfl$offset.document_field_id=$fieldid" . "\n";
1184   - $sql .= " LEFT JOIN document_fields df$offset ON df$offset.id=dfl$offset.document_field_id" . "\n";
  1273 + $fieldid = $field->getFieldId();
  1274 + $sql .= " LEFT JOIN document_fields_link dfl$offset ON dfl$offset.metadata_version_id=d.metadata_version_id AND dfl$offset.document_field_id=$fieldid" . "\n";
  1275 + $sql .= " LEFT JOIN document_fields df$offset ON df$offset.id=dfl$offset.document_field_id" . "\n";
  1276 + }
1185 1277 }
1186 1278  
1187 1279 // Add permissions sql for read access
... ... @@ -1189,15 +1281,16 @@ class SQLQueryBuilder implements QueryBuilder
1189 1281 $permId = $oPermission->getID();
1190 1282 $oUser = User::get($_SESSION['userID']);
1191 1283 $aPermissionDescriptors = KTPermissionUtil::getPermissionDescriptorsForUser($oUser);
1192   - $sPermissionDescriptors = (!empty($aPermissionDescriptors)) ? implode(',', $aPermissionDescriptors) : '';
  1284 + $sPermissionDescriptors = empty($aPermissionDescriptors)? -1: implode(',', $aPermissionDescriptors);
1193 1285  
1194   - $sql .= 'LEFT JOIN folders f ON d.folder_id = f.id '. "\n";
1195   -
1196   - $sql .= 'INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id '. "\n";
  1286 + $sql .= "INNER JOIN permission_lookups AS PL ON $primaryAlias.permission_lookup_id = PL.id\n";
1197 1287 $sql .= 'INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = '.$permId. " \n";
  1288 + $sql .= "WHERE PLA.permission_descriptor_id IN ($sPermissionDescriptors) AND ";
1198 1289  
1199   - $sql .= "WHERE PLA.permission_descriptor_id IN ($sPermissionDescriptors) AND dmv.status_id=1 AND d.status_id=1 AND \n ";
1200   -
  1290 + if ($this->context == ExprContext::DOCUMENT)
  1291 + {
  1292 + $sql .= "dmv.status_id=1 AND d.status_id=1 AND \n ";
  1293 + }
1201 1294 return $sql;
1202 1295 }
1203 1296  
... ... @@ -1222,8 +1315,6 @@ class SQLQueryBuilder implements QueryBuilder
1222 1315  
1223 1316 private function resolveJoinOffset($expr)
1224 1317 {
1225   -
1226   -
1227 1318 $offset=0;
1228 1319 foreach($this->db as $item)
1229 1320 {
... ... @@ -1242,11 +1333,18 @@ class SQLQueryBuilder implements QueryBuilder
1242 1333 $right = $expr->right();
1243 1334 if (DefaultOpCollection::isBoolean($expr))
1244 1335 {
1245   - $query = '(' . $this->buildCoreSQLExpr($left) . ' ' . $expr->op() . ' ' . $this->buildCoreSQLExpr($right) . ')';
  1336 + $query = '(' . $this->buildCoreSQLExpr($left) . ' ' . $expr->op() . ' ' . $this->buildCoreSQLExpr($right) . ')';
1246 1337 }
1247 1338 else
1248 1339 {
1249   - $query = $this->getSQLEvalExpr($expr);
  1340 + if (($this->context & $expr->appliesToContext()) == $this->context)
  1341 + {
  1342 + $query = $this->getSQLEvalExpr($expr);
  1343 + }
  1344 + else
  1345 + {
  1346 + $query = 'false';
  1347 + }
1250 1348 }
1251 1349  
1252 1350 return $query;
... ... @@ -1259,7 +1357,11 @@ class SQLQueryBuilder implements QueryBuilder
1259 1357  
1260 1358 $sql = $this->buildCoreSQL();
1261 1359  
1262   - $sql .= $this->buildCoreSQLExpr($expr);
  1360 + $expr = $this->buildCoreSQLExpr($expr);
  1361 + if ($expr != 'false')
  1362 + {
  1363 + $sql .= $expr;
  1364 + }
1263 1365  
1264 1366 return $sql;
1265 1367 }
... ... @@ -1299,19 +1401,22 @@ class SQLQueryBuilder implements QueryBuilder
1299 1401 $sql .= $value->getSQL($field, $left->modifyName($fieldname), $expr->op(), $expr->not());
1300 1402 }
1301 1403  
1302   - $moffset=0;
1303   - foreach($this->metadata as $expr)
  1404 + if ($this->context == ExprContext::DOCUMENT)
1304 1405 {
1305   - $moffset++;
1306   - if ($offset++)
  1406 + $moffset=0;
  1407 + foreach($this->metadata as $expr)
1307 1408 {
1308   - $sql .= " $op\n " ;
1309   - }
  1409 + $moffset++;
  1410 + if ($offset++)
  1411 + {
  1412 + $sql .= " $op\n " ;
  1413 + }
1310 1414  
1311   - $field = $expr->left();
1312   - $value = $expr->right();
  1415 + $field = $expr->left();
  1416 + $value = $expr->right();
1313 1417  
1314   - $sql .= $value->getSQL($field, "dfl$moffset.value", $expr->getOp());
  1418 + $sql .= $value->getSQL($field, "dfl$moffset.value", $expr->getOp());
  1419 + }
1315 1420 }
1316 1421  
1317 1422 return $sql;
... ... @@ -1331,7 +1436,6 @@ class SQLQueryBuilder implements QueryBuilder
1331 1436  
1332 1437 if (substr($col, 0, 4) == 'expr' && is_numeric(substr($col, 4)))
1333 1438 {
1334   -
1335 1439 $exprno = substr($col, 4);
1336 1440 if ($exprno <= count($this->db))
1337 1441 {
... ... @@ -1379,8 +1483,6 @@ class SQLQueryBuilder implements QueryBuilder
1379 1483 }
1380 1484 return '(' . implode(') AND (', $text) . ')';
1381 1485 }
1382   -
1383   -
1384 1486 }
1385 1487  
1386 1488  
... ... @@ -1444,6 +1546,13 @@ class OpExpr extends Expr
1444 1546 $this->has_text=$value;
1445 1547 }
1446 1548  
  1549 + public function setContext($context)
  1550 + {
  1551 + parent::setContext($context);
  1552 + $this->left()->setContext($context);
  1553 + $this->right()->setContext($context);
  1554 + }
  1555 +
1447 1556 public function getHasDb()
1448 1557 {
1449 1558 return $this->has_db;
... ... @@ -1619,7 +1728,6 @@ class OpExpr extends Expr
1619 1728  
1620 1729 $point = null;
1621 1730  
1622   -
1623 1731 if ($left_op_match && $right_op_match) { $point = 'point'; }
1624 1732  
1625 1733 $left_op_match_flex = $left_op_match || ($left->isOpExpr());
... ... @@ -1682,6 +1790,11 @@ class OpExpr extends Expr
1682 1790 return $this->getHasDb() && $this->getHasText();
1683 1791 }
1684 1792  
  1793 + public function appliesToContext()
  1794 + {
  1795 + return $this->left()->appliesToContext() | $this->right()->appliesToContext();
  1796 + }
  1797 +
1685 1798 /**
1686 1799 * Enter description here...
1687 1800 *
... ... @@ -1805,7 +1918,7 @@ class OpExpr extends Expr
1805 1918 * @param array $rightres
1806 1919 * @return array
1807 1920 */
1808   - protected static function intersect($leftres, $rightres)
  1921 + protected static function _intersect($leftres, $rightres)
1809 1922 {
1810 1923 if (empty($leftres) || empty($rightres))
1811 1924 {
... ... @@ -1814,7 +1927,7 @@ class OpExpr extends Expr
1814 1927 $result = array();
1815 1928 foreach($leftres as $item)
1816 1929 {
1817   - $document_id = $item->DocumentID;
  1930 + $document_id = $item->Id;
1818 1931  
1819 1932 if (!$item->IsLive)
1820 1933 {
... ... @@ -1831,6 +1944,22 @@ class OpExpr extends Expr
1831 1944 return $result;
1832 1945 }
1833 1946  
  1947 + protected static function intersect($leftres, $rightres)
  1948 + {
  1949 + return array(
  1950 + 'docs'=>self::_intersect($leftres['docs'],$rightres['docs']),
  1951 + 'folders'=>self::_intersect($leftres['folders'],$rightres['folders'])
  1952 + );
  1953 + }
  1954 +
  1955 + protected static function union($leftres, $rightres)
  1956 + {
  1957 + return array(
  1958 + 'docs'=>self::_union($leftres['docs'],$rightres['docs']),
  1959 + 'folders'=>self::_union($leftres['folders'],$rightres['folders'])
  1960 + );
  1961 + }
  1962 +
1834 1963 /**
1835 1964 * The objective of this function is to merge the results so that there is a union of the results,
1836 1965 * but there should be no duplicates.
... ... @@ -1839,7 +1968,7 @@ class OpExpr extends Expr
1839 1968 * @param array $rightres
1840 1969 * @return array
1841 1970 */
1842   - protected static function union($leftres, $rightres)
  1971 + protected static function _union($leftres, $rightres)
1843 1972 {
1844 1973 if (empty($leftres))
1845 1974 {
... ... @@ -1855,15 +1984,15 @@ class OpExpr extends Expr
1855 1984 {
1856 1985 if ($item->IsLive)
1857 1986 {
1858   - $result[$item->DocumentID] = $item;
  1987 + $result[$item->Id] = $item;
1859 1988 }
1860 1989 }
1861 1990  
1862 1991 foreach($rightres as $item)
1863 1992 {
1864   - if (!array_key_exists($item->DocumentID, $result) || $item->Rank > $result[$item->DocumentID]->Rank)
  1993 + if (!array_key_exists($item->Id, $result) || $item->Rank > $result[$item->Id]->Rank)
1865 1994 {
1866   - $result[$item->DocumentID] = $item;
  1995 + $result[$item->Id] = $item;
1867 1996 }
1868 1997 }
1869 1998 return $result;
... ... @@ -1996,7 +2125,7 @@ class OpExpr extends Expr
1996 2125 {
1997 2126 if (empty($group)) { return array(); }
1998 2127  
1999   - $exprbuilder = new SQLQueryBuilder();
  2128 + $exprbuilder = new SQLQueryBuilder($this->getContext());
2000 2129  
2001 2130 if (count($group) == 1)
2002 2131 {
... ... @@ -2020,11 +2149,18 @@ class OpExpr extends Expr
2020 2149  
2021 2150 foreach($rs as $item)
2022 2151 {
2023   - $document_id = $item['id'];
  2152 + $id = $item['id'];
2024 2153 $rank = $exprbuilder->getRanking($item);
2025   - if (!array_key_exists($document_id, $results) || $rank > $results[$document_id]->Rank)
  2154 + if (!array_key_exists($id, $results) || $rank > $results[$id]->Rank)
2026 2155 {
2027   - $results[$document_id] = new QueryResultItem($document_id, $rank, $item['title'], $exprbuilder->getResultText($item));
  2156 + if ($this->context == ExprContext::DOCUMENT)
  2157 + {
  2158 + $results[$id] = new DocumentResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item));
  2159 + }
  2160 + else
  2161 + {
  2162 + $results[$id] = new FolderResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item));
  2163 + }
2028 2164 }
2029 2165 }
2030 2166  
... ... @@ -2034,7 +2170,10 @@ class OpExpr extends Expr
2034 2170  
2035 2171 private function exec_text_query($op, $group)
2036 2172 {
2037   - if (empty($group)) { return array(); }
  2173 + if (($this->getContext() != ExprContext::DOCUMENT) || empty($group))
  2174 + {
  2175 + return array();
  2176 + }
2038 2177  
2039 2178 $exprbuilder = new TextQueryBuilder();
2040 2179  
... ... @@ -2060,12 +2199,21 @@ class OpExpr extends Expr
2060 2199 }
2061 2200  
2062 2201 return $results;
2063   -
2064   -
2065 2202 }
2066 2203  
2067   - public function evaluate()
  2204 + public function evaluate($context = ExprContext::DOCUMENT_AND_FOLDER)
2068 2205 {
  2206 + if ($context == ExprContext::DOCUMENT_AND_FOLDER)
  2207 + {
  2208 + $docs = $this->evaluate(ExprContext::DOCUMENT);
  2209 + $folders = $this->evaluate(ExprContext::FOLDER);
  2210 +
  2211 + return array(
  2212 + 'docs' => $docs['docs'],
  2213 + 'folders' => $folders['folders']);
  2214 + }
  2215 + $this->setContext($context);
  2216 +
2069 2217 $left = $this->left();
2070 2218 $right = $this->right();
2071 2219 $op = $this->op();
... ... @@ -2075,12 +2223,12 @@ class OpExpr extends Expr
2075 2223 {
2076 2224 $point = 'point';
2077 2225 }
  2226 + $resultContext = ($this->getContext() == ExprContext::DOCUMENT)?'docs':'folders';
2078 2227  
2079 2228 if ($point == 'merge')
2080 2229 {
2081   -
2082   - $leftres = $left->evaluate();
2083   - $rightres = $right->evaluate();
  2230 + $leftres = $left->evaluate($context);
  2231 + $rightres = $right->evaluate($context);
2084 2232 switch ($op)
2085 2233 {
2086 2234 case ExprOp::OP_AND:
... ... @@ -2099,31 +2247,33 @@ class OpExpr extends Expr
2099 2247 {
2100 2248 if ($this->isDBonly())
2101 2249 {
2102   - $result = $this->exec_db_query($op, array($this));
  2250 + $result[$resultContext] = $this->exec_db_query($op, array($this));
2103 2251 }
2104 2252 elseif ($this->isTextOnly())
2105 2253 {
2106   - $result = $this->exec_text_query($op, array($this));
  2254 + $result[$resultContext] = $this->exec_text_query($op, array($this));
2107 2255 }
2108 2256 elseif (in_array($op, array(ExprOp::OP_OR, ExprOp::OP_AND)))
2109 2257 {
  2258 + // do we get to this???
  2259 + // TODO: remove me please.... the simpleQuery stuff should go???
2110 2260 $db_group = array();
2111 2261 $text_group = array();
2112 2262 $this->explore($left, $right, $db_group, 'db');
2113 2263 $this->explore($left, $right, $text_group, 'text');
2114 2264  
2115   - $db_result = $this->exec_db_query($op, $db_group);
2116   - $text_result = $this->exec_text_query($op, $text_group);
  2265 + $db_result[$resultContext] = $this->exec_db_query($op, $db_group);
  2266 + $text_result[$resultContext] = $this->exec_text_query($op, $text_group);
2117 2267  
2118 2268 switch ($op)
2119 2269 {
2120 2270 case ExprOp::OP_AND:
2121 2271 if ($this->debug) print "\n\npoint: intersect\n\n";
2122   - $result = OpExpr::intersect($db_result, $text_result);
  2272 + $result[$resultContext] = OpExpr::intersect($db_result, $text_result);
2123 2273 break;
2124 2274 case ExprOp::OP_OR:
2125 2275 if ($this->debug) print "\n\nmerge: union\n\n";
2126   - $result = OpExpr::union($db_result, $text_result);
  2276 + $result[$resultContext] = OpExpr::union($db_result, $text_result);
2127 2277 break;
2128 2278 default:
2129 2279 throw new Exception('how did this happen??');
... ... @@ -2141,9 +2291,9 @@ class OpExpr extends Expr
2141 2291 }
2142 2292  
2143 2293 $permResults = array();
2144   - foreach($result as $idx=>$item)
  2294 + foreach($result[$resultContext] as $idx=>$item)
2145 2295 {
2146   - $permResults[$idx] = $item;
  2296 + $permResults[$resultContext][$idx] = $item;
2147 2297 }
2148 2298  
2149 2299 return $permResults;
... ...
search2/search/fields/CreatedByField.inc.php
... ... @@ -7,31 +7,31 @@
7 7 * Document Management Made Simple
8 8 * Copyright (C) 2008 KnowledgeTree Inc.
9 9 * Portions copyright The Jam Warehouse Software (Pty) Limited
10   - *
  10 + *
11 11 * This program is free software; you can redistribute it and/or modify it under
12 12 * the terms of the GNU General Public License version 3 as published by the
13 13 * Free Software Foundation.
14   - *
  14 + *
15 15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 18 * details.
19   - *
  19 + *
20 20 * You should have received a copy of the GNU General Public License
21 21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22   - *
23   - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
  22 + *
  23 + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
24 24 * California 94120-7775, or email info@knowledgetree.com.
25   - *
  25 + *
26 26 * The interactive user interfaces in modified source and object code versions
27 27 * of this program must display Appropriate Legal Notices, as required under
28 28 * Section 5 of the GNU General Public License version 3.
29   - *
  29 + *
30 30 * In accordance with Section 7(b) of the GNU General Public License version 3,
31 31 * these Appropriate Legal Notices must retain the display of the "Powered by
32   - * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  32 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
33 33 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
34   - * must display the words "Powered by KnowledgeTree" and retain the original
  34 + * must display the words "Powered by KnowledgeTree" and retain the original
35 35 * copyright notice.
36 36 * Contributor( s): ______________________________________
37 37 *
... ... @@ -47,6 +47,25 @@ class CreatedByField extends DBFieldExpr
47 47 $this->matchField('name');
48 48 }
49 49  
  50 + public function appliesToContext()
  51 + {
  52 + return ExprContext::DOCUMENT | ExprContext::FOLDER;
  53 + }
  54 +
  55 + public function setContext($context)
  56 + {
  57 + parent::setContext($context);
  58 + switch($context)
  59 + {
  60 + case ExprContext::DOCUMENT:
  61 + $this->table = 'documents';
  62 + break;
  63 + case ExprContext::FOLDER:
  64 + $this->table = 'folders';
  65 + break;
  66 + }
  67 + }
  68 +
50 69 public function getInputRequirements()
51 70 {
52 71 return array('value'=>array('type'=>FieldInputType::USER_LIST));
... ...
search2/search/fields/FullPathField.inc.php
... ... @@ -7,31 +7,31 @@
7 7 * Document Management Made Simple
8 8 * Copyright (C) 2008 KnowledgeTree Inc.
9 9 * Portions copyright The Jam Warehouse Software (Pty) Limited
10   - *
  10 + *
11 11 * This program is free software; you can redistribute it and/or modify it under
12 12 * the terms of the GNU General Public License version 3 as published by the
13 13 * Free Software Foundation.
14   - *
  14 + *
15 15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 18 * details.
19   - *
  19 + *
20 20 * You should have received a copy of the GNU General Public License
21 21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22   - *
23   - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
  22 + *
  23 + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
24 24 * California 94120-7775, or email info@knowledgetree.com.
25   - *
  25 + *
26 26 * The interactive user interfaces in modified source and object code versions
27 27 * of this program must display Appropriate Legal Notices, as required under
28 28 * Section 5 of the GNU General Public License version 3.
29   - *
  29 + *
30 30 * In accordance with Section 7(b) of the GNU General Public License version 3,
31 31 * these Appropriate Legal Notices must retain the display of the "Powered by
32   - * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  32 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
33 33 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
34   - * must display the words "Powered by KnowledgeTree" and retain the original
  34 + * must display the words "Powered by KnowledgeTree" and retain the original
35 35 * copyright notice.
36 36 * Contributor( s): ______________________________________
37 37 *
... ... @@ -47,6 +47,25 @@ class FullPathField extends DBFieldExpr
47 47 $this->setAlias('FullPath');
48 48 }
49 49  
  50 + public function setContext($context)
  51 + {
  52 + parent::setContext($context);
  53 + switch($context)
  54 + {
  55 + case ExprContext::DOCUMENT:
  56 + $this->table = 'documents';
  57 + break;
  58 + case ExprContext::FOLDER:
  59 + $this->table = 'folders';
  60 + break;
  61 + }
  62 + }
  63 +
  64 + public function appliesToContext($context)
  65 + {
  66 + return ExprContext::DOCUMENT | ExprContext::FOLDER;
  67 + }
  68 +
50 69 public function getInputRequirements()
51 70 {
52 71 return array('value'=>array('type'=>FieldInputType::TEXT));
... ...
search2/search/fields/TitleField.inc.php
... ... @@ -7,31 +7,31 @@
7 7 * Document Management Made Simple
8 8 * Copyright (C) 2008 KnowledgeTree Inc.
9 9 * Portions copyright The Jam Warehouse Software (Pty) Limited
10   - *
  10 + *
11 11 * This program is free software; you can redistribute it and/or modify it under
12 12 * the terms of the GNU General Public License version 3 as published by the
13 13 * Free Software Foundation.
14   - *
  14 + *
15 15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 18 * details.
19   - *
  19 + *
20 20 * You should have received a copy of the GNU General Public License
21 21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22   - *
23   - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
  22 + *
  23 + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
24 24 * California 94120-7775, or email info@knowledgetree.com.
25   - *
  25 + *
26 26 * The interactive user interfaces in modified source and object code versions
27 27 * of this program must display Appropriate Legal Notices, as required under
28 28 * Section 5 of the GNU General Public License version 3.
29   - *
  29 + *
30 30 * In accordance with Section 7(b) of the GNU General Public License version 3,
31 31 * these Appropriate Legal Notices must retain the display of the "Powered by
32   - * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  32 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
33 33 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
34   - * must display the words "Powered by KnowledgeTree" and retain the original
  34 + * must display the words "Powered by KnowledgeTree" and retain the original
35 35 * copyright notice.
36 36 * Contributor( s): ______________________________________
37 37 *
... ... @@ -39,13 +39,31 @@
39 39  
40 40 class TitleField extends DBFieldExpr
41 41 {
42   -
43 42 public function __construct()
44 43 {
45 44 parent::__construct('name', 'document_metadata_version', _kt('Title'));
46 45 $this->setAlias('Title');
47 46 }
48 47  
  48 + public function setContext($context)
  49 + {
  50 + parent::setContext($context);
  51 + switch($context)
  52 + {
  53 + case ExprContext::DOCUMENT:
  54 + $this->table = 'document_metadata_version';
  55 + break;
  56 + case ExprContext::FOLDER:
  57 + $this->table = 'folders';
  58 + break;
  59 + }
  60 + }
  61 +
  62 + public function appliesToContext()
  63 + {
  64 + return ExprContext::DOCUMENT | ExprContext::FOLDER;
  65 + }
  66 +
49 67 public function getInputRequirements()
50 68 {
51 69 return array('value'=>array('type'=>FieldInputType::TEXT));
... ...
search2/search/search.inc.php
... ... @@ -42,6 +42,8 @@ require_once(&#39;search/fieldRegistry.inc.php&#39;);
42 42 require_once('search/expr.inc.php');
43 43 require_once(KT_LIB_DIR . '/security/Permission.inc');
44 44  
  45 +// TODO: move standalone functions into a class.... what was I thinking?
  46 +
45 47 function rank_compare($a, $b)
46 48 {
47 49 if ($a->Rank == $b->Rank)
... ... @@ -66,6 +68,8 @@ function searchfix($str)
66 68 return str_replace(array("\n","\r"), array('',''), addslashes($str));
67 69 }
68 70  
  71 +// TODO: replace manual json construction with json_encode().
  72 +
69 73 class SearchHelper
70 74 {
71 75 public static function correctPath($path)
... ... @@ -366,8 +370,6 @@ class SearchHelper
366 370 dtfl.document_type_id=$documentTypeID
367 371 ORDER BY
368 372 df.name";
369   -
370   -
371 373 }
372 374 else
373 375 {
... ... @@ -566,7 +568,8 @@ function processSearchExpression($query)
566 568 {
567 569 $expr = parseExpression($query);
568 570  
569   - $rs = $expr->evaluate();
  571 + $rs = $expr->evaluate(ExprContext::DOCUMENT);
  572 + $rs = $rs['docs'];
570 573 usort($rs, 'rank_compare');
571 574  
572 575 $results = array();
... ... @@ -629,4 +632,57 @@ function processSearchExpression($query)
629 632 }
630 633 }
631 634  
  635 +function resolveSearchShortcuts($result)
  636 +{
  637 + $oPermission =& KTPermission::getByName('ktcore.permissions.read');
  638 + $permId = $oPermission->getID();
  639 +
  640 + $oUser = User::get($_SESSION['userID']);
  641 + $aPermissionDescriptors = KTPermissionUtil::getPermissionDescriptorsForUser($oUser);
  642 + $sPermissionDescriptors = empty($aPermissionDescriptors)? -1: implode(',', $aPermissionDescriptors);
  643 +
  644 + $documentIds = implode(',',array_keys($result['docs']));
  645 + $linkedDocuments = array();
  646 + if (!empty($documentIds))
  647 + {
  648 + $sql = "SELECT d.id, d.linked_document_id from documents d ";
  649 + $sql .= 'INNER JOIN permission_lookups AS PL ON d.permission_lookup_id = PL.id '. "\n";
  650 + $sql .= 'INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = '.$permId. " \n";
  651 + $sql .= " WHERE d.linked_document_id in ($documentIds) AND PLA.permission_descriptor_id IN ($sPermissionDescriptors)";
  652 +
  653 + $rs = DBUtil::getResultArray($sql);
  654 +
  655 + foreach($rs as $row)
  656 + {
  657 + $id = $row['id'];
  658 + $linked_id = $row['linked_document_id'];
  659 +
  660 + $result['shortdocs'][$id] = new DocumentShortcutResultItem($id, $result['docs'][$linked_id]);
  661 + }
  662 + }
  663 +
  664 + $folderIds = implode(',',array_keys($result['folders']));
  665 + $linkedFolders = array();
  666 +
  667 + if (!empty($folderIds))
  668 + {
  669 +
  670 + $sql = "SELECT f.id, f.linked_folder_id from folders f ";
  671 + $sql .= 'INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id '. "\n";
  672 + $sql .= 'INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = '.$permId. " \n";
  673 + $sql .= " WHERE f.linked_folder_id in ($folderIds) AND PLA.permission_descriptor_id IN ($sPermissionDescriptors)";
  674 +
  675 + $rs = DBUtil::getResultArray($sql);
  676 +
  677 + foreach($rs as $row)
  678 + {
  679 + $id = $row['id'];
  680 + $linked_id = $row['linked_folder_id'];
  681 +
  682 + $result['shortfolders'][$id] = new FolderShortcutResultItem($id, $result['folders'][$linked_id]);
  683 + }
  684 + }
  685 + return $result;
  686 +}
  687 +
632 688 ?>
... ...
templates/ktcore/search2/search_results.smarty
... ... @@ -46,7 +46,6 @@ function saveSearch()
46 46 url='{/literal}{$rootUrl}{literal}/search2/ajax/saveExpr.php?txtName='+ escape(txtName.value) +
47 47 '&txtQuery=' + escape('{/literal}{$txtQuery|escape:'quotes'}{literal}');
48 48  
49   -
50 49 Ext.Ajax.request(
51 50 {
52 51 url: url,
... ... @@ -132,57 +131,82 @@ function onShowAll(showall)
132 131 <table border=0 cellpadding="1" cellspacing="1" width="100%" align=center>
133 132 {assign var=cbid value=0}
134 133  
135   - {foreach item=document from=$results}
  134 + {i18n arg_count=$numResults}Search results found: #count#{/i18n}
  135 + <br/>
  136 + <br/>
  137 +
  138 + {foreach item=hit from=$results}
136 139 <tr><td>
137   - <input type="checkbox" name="selection_d[]" id="cb{$cbid}" value="{$document->DocumentID}"><nobr>&nbsp;&nbsp;<a href="{$rootUrl}/view.php?fDocumentId={$document->DocumentID}"><B>{$document->Title|truncate:80}</b></a>
138   - {if $document->Title != $document->Filename}
  140 + <input type="checkbox" {if $hit->IsDocument}name="selection_d[]"{else}name="selection_f[]"{/if} id="cb{$cbid}" value="{$hit->Id}"><nobr>
  141 + <span class="contenttype {$hit->MimeIconPath}">
  142 +
  143 + {if $hit->IsDocument}
  144 + &nbsp;&nbsp;<a href="{$rootUrl}/view.php?fDocumentId={$hit->Id}"><B>{$hit->Title|truncate:80}</b></a>
  145 + {if $hit->Title != $hit->Filename}
139 146 &nbsp;&nbsp;
140   - <font style=" color: green "> - Filename: {$document->Filename|truncate:40}</font>
  147 + <font style=" color: green "> - Filename: {$hit->Filename|truncate:40}</font>
141 148 {/if}
142   - {if $document->IsAvailable}
  149 + {if $hit->IsAvailable}
143 150 &nbsp;&nbsp;
144   - <img src="{$rootUrl}/resources/graphics/download.png" title="Download Document" onclick="document.location='{$rootUrl}/action.php?kt_path_info=ktcore.actions.document.view&fDocumentId={$document->DocumentID}';"></a>
  151 + <img src="{$rootUrl}/resources/graphics/download.png" title="Download Document" onclick="document.location='{$rootUrl}/action.php?kt_path_info=ktcore.actions.document.view&fDocumentId={$hit->Id}';"></a>
145 152  
146 153 {/if}
147 154  
148 155  
149   - {if !$document->IsAvailable}
  156 + {if !$hit->IsAvailable}
150 157 &nbsp;&nbsp;
151 158 <font style=" color: red "><B> * {i18n}NOT AVAILABLE{/i18n} * </B></font>
152 159 {/if}
153 160  
  161 + {else}
  162 + &nbsp;&nbsp;<a href="{$rootUrl}/browse.php?fFolderId={$hit->RealId}{if $hit->IsProxy}&fShortcutFolder={$hit->Id}{/if}"><B>{$hit->Title|truncate:80}</b></a>
  163 +
  164 + {/if}
  165 +
154 166 </nobr>
155 167 </td><td align=right>
156   - <font style="color: green ">{i18n}Document ID:{/i18n} {$document->DocumentID}</font>
157   - &nbsp;&nbsp;
158   - <font style=" color: orange ">{i18n}Version:{/i18n} {$document->Version}</font></td>
  168 + {if $hit->IsDocument}
  169 + <font style="color: green ">{i18n}Document ID:{/i18n} {$hit->Id}</font>
  170 + &nbsp;&nbsp;
  171 + <font style=" color: orange ">{i18n}Version:{/i18n} {$hit->Version}</font></td>
  172 + {else}
  173 + <font style="color: green ">{i18n}Folder ID:{/i18n} {$hit->Id}</font>
  174 + {/if}
159 175 </tr>
160   - <tr><td colspan=2>{$document->Text}</td></tr>
161   - <tr><td><font style=" color: green "><a href="{$rootUrl}/browse.php?fFolderId={$document->FolderId}" tag="{$document->FullPath}">{$document->FullPath|truncate:40}</a>/{$document->Title|truncate:40} - {$document->Filesize}</font>
162   -
  176 + <tr><td colspan=2>{$hit->Text}</td></tr>
  177 + <tr><td>
  178 + {if $hit->IsDocument}
  179 + <font style=" color: green "><a href="{$rootUrl}/browse.php?fFolderId={$hit->FolderId}" tag="{$hit->FullPath}">{$hit->FullPath|truncate:40}</a>/{$hit->Title|truncate:40} - {$hit->Filesize}</font>
  180 + {else}
  181 + <font style=" color: green "><a href="{$rootUrl}/browse.php?fFolderId={$hit->FolderId}" tag="{$hit->FullPath}">{$hit->FullPath|truncate:40}</a></font>
  182 + {/if}
163 183 </td>
164 184 <td align=right><nobr>
165 185 <font style="color: orange ">
166   - {i18n}Created By:{/i18n} {$document->CreatedBy}
167   - {i18n}on{/i18n} {$document->DateCreated}
  186 + {i18n}Created By:{/i18n} {$hit->CreatedBy}
  187 + {if $hit->IsDocument}
  188 + {i18n}on{/i18n} {$hit->DateCreated}
  189 + {/if}
168 190 </font>
169 191 </td>
  192 + {if $hit->IsDocument}
170 193 <tr><td>
171 194 {if $workflow != ''}
172   - <font style="color: orange ">{i18n}Workflow:{/i18n} $document->Workflow}</font>
  195 + <font style="color: orange ">{i18n}Workflow:{/i18n} $hit->Workflow}</font>
173 196 {/if}
174 197 </td>
175 198 <td align=right><nobr>
176 199 <font style="color: brown ">
177   - {if $document->CheckedOutUser != ''}
178   - <b>{i18n}Checked out by:{/i18n} {$document->CheckedOutUser}
179   - {i18n}on{/i18n} {$document->DateCheckedOut}</b>
  200 + {if $hit->CheckedOutUser != ''}
  201 + <b>{i18n}Checked out by:{/i18n} {$hit->CheckedOutUser}
  202 + {i18n}on{/i18n} {$hit->DateCheckedOut}</b>
180 203 {else}
181   - {i18n}Modified by:{/i18n} {$document->ModifiedBy}
182   - {i18n}on{/i18n} {$document->DateModified}
  204 + {i18n}Modified by:{/i18n} {$hit->ModifiedBy}
  205 + {i18n}on{/i18n} {$hit->DateModified}
183 206 {/if}
184 207 </font>
185 208 </td>
  209 + {/if}
186 210  
187 211 <tr><td colspan=2><br></br></td></tr></tr>
188 212  
... ...