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,15 +35,20 @@
35 * Contributor( s): ______________________________________ 35 * Contributor( s): ______________________________________
36 */ 36 */
37 37
  38 +// TODO: do we have to serialise/unserialise the results. this is not optimal!!!
  39 +
38 session_start(); 40 session_start();
39 require_once("config/dmsDefaults.php"); 41 require_once("config/dmsDefaults.php");
  42 +require_once(KT_DIR . '/search2/indexing/indexerCore.inc.php');
  43 +
40 require_once(KT_LIB_DIR . "/unitmanagement/Unit.inc"); 44 require_once(KT_LIB_DIR . "/unitmanagement/Unit.inc");
41 45
42 require_once(KT_LIB_DIR . "/templating/templating.inc.php"); 46 require_once(KT_LIB_DIR . "/templating/templating.inc.php");
43 require_once(KT_LIB_DIR . "/dispatcher.inc.php"); 47 require_once(KT_LIB_DIR . "/dispatcher.inc.php");
44 require_once(KT_LIB_DIR . "/widgets/forms.inc.php"); 48 require_once(KT_LIB_DIR . "/widgets/forms.inc.php");
45 require_once(KT_LIB_DIR . "/actions/bulkaction.php"); 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 require_once(KT_LIB_DIR . '/documentmanagement/Document.inc'); 52 require_once(KT_LIB_DIR . '/documentmanagement/Document.inc');
48 require_once(KT_LIB_DIR . '/browse/PartialQuery.inc.php'); 53 require_once(KT_LIB_DIR . '/browse/PartialQuery.inc.php');
49 54
@@ -117,7 +122,6 @@ function search2QuerySort($sSortColumn, $sSortOrder) @@ -117,7 +122,6 @@ function search2QuerySort($sSortColumn, $sSortOrder)
117 return; 122 return;
118 } 123 }
119 124
120 -  
121 $results = unserialize($_SESSION['search2_results']); 125 $results = unserialize($_SESSION['search2_results']);
122 126
123 usort($results, 'search2queryCompare'); 127 usort($results, 'search2queryCompare');
@@ -132,38 +136,70 @@ function search2QuerySort($sSortColumn, $sSortOrder) @@ -132,38 +136,70 @@ function search2QuerySort($sSortColumn, $sSortOrder)
132 */ 136 */
133 class Search2Query extends PartialQuery 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 function getDocumentCount() 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 function getFolders($iBatchSize, $iBatchStart, $sSortColumn, $sSortOrder, $sJoinClause = null, $aJoinParams = null) 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 function getDocuments($iBatchSize, $iBatchStart, $sSortColumn, $sSortOrder, $sJoinClause = null, $aJoinParams = null) 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,10 +244,12 @@ class SearchDispatcher extends KTStandardDispatcher {
208 { 244 {
209 $expr = parseExpression($query); 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 $_SESSION['search2_query'] = $query; 253 $_SESSION['search2_query'] = $query;
216 $_SESSION['search2_sort'] = 'rank'; 254 $_SESSION['search2_sort'] = 'rank';
217 255
@@ -410,6 +448,15 @@ class SearchDispatcher extends KTStandardDispatcher { @@ -410,6 +448,15 @@ class SearchDispatcher extends KTStandardDispatcher {
410 448
411 $results = unserialize($_SESSION['search2_results']); 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 if (!is_array($results) || count($results) == 0) 460 if (!is_array($results) || count($results) == 0)
414 { 461 {
415 $results=array(); 462 $results=array();
search2/indexing/indexerCore.inc.php
@@ -41,17 +41,142 @@ define(&#39;SEARCH2_INDEXER_DIR&#39;,realpath(dirname(__FILE__)) . &#39;/&#39;); @@ -41,17 +41,142 @@ define(&#39;SEARCH2_INDEXER_DIR&#39;,realpath(dirname(__FILE__)) . &#39;/&#39;);
41 require_once('indexing/extractorCore.inc.php'); 41 require_once('indexing/extractorCore.inc.php');
42 require_once(KT_DIR . '/plugins/ktcore/scheduler/schedulerUtil.php'); 42 require_once(KT_DIR . '/plugins/ktcore/scheduler/schedulerUtil.php');
43 43
44 -  
45 class IndexerInconsistencyException extends Exception {}; 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 class QueryResultItem 48 class QueryResultItem
48 { 49 {
49 - protected $document_id; 50 + protected $id;
50 protected $title; 51 protected $title;
51 protected $rank; 52 protected $rank;
52 protected $text; 53 protected $text;
53 - protected $filesize;  
54 protected $fullpath; 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 protected $live; 180 protected $live;
56 protected $version; 181 protected $version;
57 protected $mimeType; 182 protected $mimeType;
@@ -78,17 +203,14 @@ class QueryResultItem @@ -78,17 +203,14 @@ class QueryResultItem
78 protected $mimeDisplay; 203 protected $mimeDisplay;
79 protected $oemDocumentNo; 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 $this->live = true; 209 $this->live = true;
88 $this->loadDocumentInfo(); 210 $this->loadDocumentInfo();
89 } 211 }
90 212
91 - protected function __isset($property) 213 + /*protected function __isset($property)
92 { 214 {
93 switch($property) 215 switch($property)
94 { 216 {
@@ -101,8 +223,9 @@ class QueryResultItem @@ -101,8 +223,9 @@ class QueryResultItem
101 throw new Exception("Unknown property '$property' to get on QueryResultItem"); 223 throw new Exception("Unknown property '$property' to get on QueryResultItem");
102 } 224 }
103 return true; // should not be reached 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 public function loadDocumentInfo() 229 public function loadDocumentInfo()
107 { 230 {
108 global $default; 231 global $default;
@@ -127,7 +250,7 @@ class QueryResultItem @@ -127,7 +250,7 @@ class QueryResultItem
127 LEFT JOIN users cbu ON d.creator_id=cbu.id 250 LEFT JOIN users cbu ON d.creator_id=cbu.id
128 LEFT JOIN users ou ON d.owner_id=ou.id 251 LEFT JOIN users ou ON d.owner_id=ou.id
129 WHERE 252 WHERE
130 - d.id=$this->document_id"; 253 + d.id=$this->id";
131 254
132 $result = DBUtil::getOneResult($sql); 255 $result = DBUtil::getOneResult($sql);
133 256
@@ -186,107 +309,116 @@ class QueryResultItem @@ -186,107 +309,116 @@ class QueryResultItem
186 309
187 $this->mimeType = $result['mimetype']; 310 $this->mimeType = $result['mimetype'];
188 $this->mimeIconPath = $result['mime_icon_path']; 311 $this->mimeIconPath = $result['mime_icon_path'];
  312 + if (empty($this->mimeIconPath))
  313 + {
  314 + $this->mimeIconPath = 'unspecified_type';
  315 + }
189 $this->mimeDisplay = $result['mime_display']; 316 $this->mimeDisplay = $result['mime_display'];
190 317
191 $this->storagePath = $result['storage_path']; 318 $this->storagePath = $result['storage_path'];
192 $this->status = Document::getStatusString($result['status_id']); 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 return ''; 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 return false; 363 return false;
268 if (Permission::userHasDocumentReadPermission($this->Document)) 364 if (Permission::userHasDocumentReadPermission($this->Document))
269 return true; 365 return true;
270 if (Permission::adminIsInAdminMode()) 366 if (Permission::adminIsInAdminMode())
271 return true; 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 function MatchResultCompare($a, $b) 424 function MatchResultCompare($a, $b)
search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php
@@ -187,14 +187,11 @@ class JavaXMLRPCLuceneIndexer extends Indexer @@ -187,14 +187,11 @@ class JavaXMLRPCLuceneIndexer extends Indexer
187 { 187 {
188 try 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 if ($item->CanBeReadByUser) 192 if ($item->CanBeReadByUser)
196 { 193 {
197 - $results[$document_id] = $item; 194 + $results['docs'][$document_id] = $item;
198 } 195 }
199 } 196 }
200 catch(IndexerInconsistencyException $ex) 197 catch(IndexerInconsistencyException $ex)
search2/search/expr.inc.php
@@ -43,6 +43,8 @@ @@ -43,6 +43,8 @@
43 * 43 *
44 */ 44 */
45 45
  46 +// TODO: search expression evaluation needs some optimisation
  47 +
46 require_once('indexing/indexerCore.inc.php'); 48 require_once('indexing/indexerCore.inc.php');
47 require_once('search/fieldRegistry.inc.php'); 49 require_once('search/fieldRegistry.inc.php');
48 require_once('search/exprConstants.inc.php'); 50 require_once('search/exprConstants.inc.php');
@@ -157,11 +159,28 @@ class Expr @@ -157,11 +159,28 @@ class Expr
157 159
158 protected $expr_id; 160 protected $expr_id;
159 161
  162 + protected $context;
  163 +
160 public function __construct() 164 public function __construct()
161 { 165 {
162 $this->expr_id = Expr::$node_id++; 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 public function getExprId() 184 public function getExprId()
166 { 185 {
167 return $this->expr_id; 186 return $this->expr_id;
@@ -406,6 +425,14 @@ class FieldExpr extends Expr @@ -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 class DBFieldExpr extends FieldExpr 436 class DBFieldExpr extends FieldExpr
410 { 437 {
411 /** 438 /**
@@ -966,24 +993,43 @@ class SQLQueryBuilder implements QueryBuilder @@ -966,24 +993,43 @@ class SQLQueryBuilder implements QueryBuilder
966 private $sql; 993 private $sql;
967 private $db; 994 private $db;
968 private $metadata; 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 $this->sql = ''; 1034 $this->sql = '';
989 $this->db = array(); 1035 $this->db = array();
@@ -1013,8 +1059,15 @@ class SQLQueryBuilder implements QueryBuilder @@ -1013,8 +1059,15 @@ class SQLQueryBuilder implements QueryBuilder
1013 } 1059 }
1014 elseif ($expr->isDBExpr()) 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 elseif ($expr->isOpExpr()) 1072 elseif ($expr->isOpExpr())
1020 { 1073 {
@@ -1042,12 +1095,16 @@ class SQLQueryBuilder implements QueryBuilder @@ -1042,12 +1095,16 @@ class SQLQueryBuilder implements QueryBuilder
1042 1095
1043 if ($field->isMetadataField()) 1096 if ($field->isMetadataField())
1044 { 1097 {
1045 - $this->metadata[] = $expr->getParent(); 1098 + $this->metadata[] = $expr->getParent();
1046 } 1099 }
1047 elseif ($field->isDBExpr()) 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,29 +1133,40 @@ class SQLQueryBuilder implements QueryBuilder
1076 $left = $expr->left(); 1133 $left = $expr->left();
1077 $right = $expr->right(); 1134 $right = $expr->right();
1078 $isNot = $expr->not(); 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 else 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 return $query; 1171 return $query;
1104 } 1172 }
@@ -1107,17 +1175,30 @@ class SQLQueryBuilder implements QueryBuilder @@ -1107,17 +1175,30 @@ class SQLQueryBuilder implements QueryBuilder
1107 { 1175 {
1108 if (count($this->metadata) + count($this->db) == 0) 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 $sql = 1182 $sql =
1117 'SELECT ' . "\n"; 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 ' DISTINCT d.id, dmv.name as title'; 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 $offset=0; 1203 $offset=0;
1123 foreach($this->db as $expr) 1204 foreach($this->db as $expr)
@@ -1133,26 +1214,36 @@ class SQLQueryBuilder implements QueryBuilder @@ -1133,26 +1214,36 @@ class SQLQueryBuilder implements QueryBuilder
1133 } 1214 }
1134 1215
1135 $sql .= 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 $offset = 0; 1249 $offset = 0;
@@ -1171,17 +1262,18 @@ class SQLQueryBuilder implements QueryBuilder @@ -1171,17 +1262,18 @@ class SQLQueryBuilder implements QueryBuilder
1171 $offset++; 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 // Add permissions sql for read access 1279 // Add permissions sql for read access
@@ -1189,15 +1281,16 @@ class SQLQueryBuilder implements QueryBuilder @@ -1189,15 +1281,16 @@ class SQLQueryBuilder implements QueryBuilder
1189 $permId = $oPermission->getID(); 1281 $permId = $oPermission->getID();
1190 $oUser = User::get($_SESSION['userID']); 1282 $oUser = User::get($_SESSION['userID']);
1191 $aPermissionDescriptors = KTPermissionUtil::getPermissionDescriptorsForUser($oUser); 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 $sql .= 'INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = '.$permId. " \n"; 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 return $sql; 1294 return $sql;
1202 } 1295 }
1203 1296
@@ -1222,8 +1315,6 @@ class SQLQueryBuilder implements QueryBuilder @@ -1222,8 +1315,6 @@ class SQLQueryBuilder implements QueryBuilder
1222 1315
1223 private function resolveJoinOffset($expr) 1316 private function resolveJoinOffset($expr)
1224 { 1317 {
1225 -  
1226 -  
1227 $offset=0; 1318 $offset=0;
1228 foreach($this->db as $item) 1319 foreach($this->db as $item)
1229 { 1320 {
@@ -1242,11 +1333,18 @@ class SQLQueryBuilder implements QueryBuilder @@ -1242,11 +1333,18 @@ class SQLQueryBuilder implements QueryBuilder
1242 $right = $expr->right(); 1333 $right = $expr->right();
1243 if (DefaultOpCollection::isBoolean($expr)) 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 else 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 return $query; 1350 return $query;
@@ -1259,7 +1357,11 @@ class SQLQueryBuilder implements QueryBuilder @@ -1259,7 +1357,11 @@ class SQLQueryBuilder implements QueryBuilder
1259 1357
1260 $sql = $this->buildCoreSQL(); 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 return $sql; 1366 return $sql;
1265 } 1367 }
@@ -1299,19 +1401,22 @@ class SQLQueryBuilder implements QueryBuilder @@ -1299,19 +1401,22 @@ class SQLQueryBuilder implements QueryBuilder
1299 $sql .= $value->getSQL($field, $left->modifyName($fieldname), $expr->op(), $expr->not()); 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 return $sql; 1422 return $sql;
@@ -1331,7 +1436,6 @@ class SQLQueryBuilder implements QueryBuilder @@ -1331,7 +1436,6 @@ class SQLQueryBuilder implements QueryBuilder
1331 1436
1332 if (substr($col, 0, 4) == 'expr' && is_numeric(substr($col, 4))) 1437 if (substr($col, 0, 4) == 'expr' && is_numeric(substr($col, 4)))
1333 { 1438 {
1334 -  
1335 $exprno = substr($col, 4); 1439 $exprno = substr($col, 4);
1336 if ($exprno <= count($this->db)) 1440 if ($exprno <= count($this->db))
1337 { 1441 {
@@ -1379,8 +1483,6 @@ class SQLQueryBuilder implements QueryBuilder @@ -1379,8 +1483,6 @@ class SQLQueryBuilder implements QueryBuilder
1379 } 1483 }
1380 return '(' . implode(') AND (', $text) . ')'; 1484 return '(' . implode(') AND (', $text) . ')';
1381 } 1485 }
1382 -  
1383 -  
1384 } 1486 }
1385 1487
1386 1488
@@ -1444,6 +1546,13 @@ class OpExpr extends Expr @@ -1444,6 +1546,13 @@ class OpExpr extends Expr
1444 $this->has_text=$value; 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 public function getHasDb() 1556 public function getHasDb()
1448 { 1557 {
1449 return $this->has_db; 1558 return $this->has_db;
@@ -1619,7 +1728,6 @@ class OpExpr extends Expr @@ -1619,7 +1728,6 @@ class OpExpr extends Expr
1619 1728
1620 $point = null; 1729 $point = null;
1621 1730
1622 -  
1623 if ($left_op_match && $right_op_match) { $point = 'point'; } 1731 if ($left_op_match && $right_op_match) { $point = 'point'; }
1624 1732
1625 $left_op_match_flex = $left_op_match || ($left->isOpExpr()); 1733 $left_op_match_flex = $left_op_match || ($left->isOpExpr());
@@ -1682,6 +1790,11 @@ class OpExpr extends Expr @@ -1682,6 +1790,11 @@ class OpExpr extends Expr
1682 return $this->getHasDb() && $this->getHasText(); 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 * Enter description here... 1799 * Enter description here...
1687 * 1800 *
@@ -1805,7 +1918,7 @@ class OpExpr extends Expr @@ -1805,7 +1918,7 @@ class OpExpr extends Expr
1805 * @param array $rightres 1918 * @param array $rightres
1806 * @return array 1919 * @return array
1807 */ 1920 */
1808 - protected static function intersect($leftres, $rightres) 1921 + protected static function _intersect($leftres, $rightres)
1809 { 1922 {
1810 if (empty($leftres) || empty($rightres)) 1923 if (empty($leftres) || empty($rightres))
1811 { 1924 {
@@ -1814,7 +1927,7 @@ class OpExpr extends Expr @@ -1814,7 +1927,7 @@ class OpExpr extends Expr
1814 $result = array(); 1927 $result = array();
1815 foreach($leftres as $item) 1928 foreach($leftres as $item)
1816 { 1929 {
1817 - $document_id = $item->DocumentID; 1930 + $document_id = $item->Id;
1818 1931
1819 if (!$item->IsLive) 1932 if (!$item->IsLive)
1820 { 1933 {
@@ -1831,6 +1944,22 @@ class OpExpr extends Expr @@ -1831,6 +1944,22 @@ class OpExpr extends Expr
1831 return $result; 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 * The objective of this function is to merge the results so that there is a union of the results, 1964 * The objective of this function is to merge the results so that there is a union of the results,
1836 * but there should be no duplicates. 1965 * but there should be no duplicates.
@@ -1839,7 +1968,7 @@ class OpExpr extends Expr @@ -1839,7 +1968,7 @@ class OpExpr extends Expr
1839 * @param array $rightres 1968 * @param array $rightres
1840 * @return array 1969 * @return array
1841 */ 1970 */
1842 - protected static function union($leftres, $rightres) 1971 + protected static function _union($leftres, $rightres)
1843 { 1972 {
1844 if (empty($leftres)) 1973 if (empty($leftres))
1845 { 1974 {
@@ -1855,15 +1984,15 @@ class OpExpr extends Expr @@ -1855,15 +1984,15 @@ class OpExpr extends Expr
1855 { 1984 {
1856 if ($item->IsLive) 1985 if ($item->IsLive)
1857 { 1986 {
1858 - $result[$item->DocumentID] = $item; 1987 + $result[$item->Id] = $item;
1859 } 1988 }
1860 } 1989 }
1861 1990
1862 foreach($rightres as $item) 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 return $result; 1998 return $result;
@@ -1996,7 +2125,7 @@ class OpExpr extends Expr @@ -1996,7 +2125,7 @@ class OpExpr extends Expr
1996 { 2125 {
1997 if (empty($group)) { return array(); } 2126 if (empty($group)) { return array(); }
1998 2127
1999 - $exprbuilder = new SQLQueryBuilder(); 2128 + $exprbuilder = new SQLQueryBuilder($this->getContext());
2000 2129
2001 if (count($group) == 1) 2130 if (count($group) == 1)
2002 { 2131 {
@@ -2020,11 +2149,18 @@ class OpExpr extends Expr @@ -2020,11 +2149,18 @@ class OpExpr extends Expr
2020 2149
2021 foreach($rs as $item) 2150 foreach($rs as $item)
2022 { 2151 {
2023 - $document_id = $item['id']; 2152 + $id = $item['id'];
2024 $rank = $exprbuilder->getRanking($item); 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,7 +2170,10 @@ class OpExpr extends Expr
2034 2170
2035 private function exec_text_query($op, $group) 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 $exprbuilder = new TextQueryBuilder(); 2178 $exprbuilder = new TextQueryBuilder();
2040 2179
@@ -2060,12 +2199,21 @@ class OpExpr extends Expr @@ -2060,12 +2199,21 @@ class OpExpr extends Expr
2060 } 2199 }
2061 2200
2062 return $results; 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 $left = $this->left(); 2217 $left = $this->left();
2070 $right = $this->right(); 2218 $right = $this->right();
2071 $op = $this->op(); 2219 $op = $this->op();
@@ -2075,12 +2223,12 @@ class OpExpr extends Expr @@ -2075,12 +2223,12 @@ class OpExpr extends Expr
2075 { 2223 {
2076 $point = 'point'; 2224 $point = 'point';
2077 } 2225 }
  2226 + $resultContext = ($this->getContext() == ExprContext::DOCUMENT)?'docs':'folders';
2078 2227
2079 if ($point == 'merge') 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 switch ($op) 2232 switch ($op)
2085 { 2233 {
2086 case ExprOp::OP_AND: 2234 case ExprOp::OP_AND:
@@ -2099,31 +2247,33 @@ class OpExpr extends Expr @@ -2099,31 +2247,33 @@ class OpExpr extends Expr
2099 { 2247 {
2100 if ($this->isDBonly()) 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 elseif ($this->isTextOnly()) 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 elseif (in_array($op, array(ExprOp::OP_OR, ExprOp::OP_AND))) 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 $db_group = array(); 2260 $db_group = array();
2111 $text_group = array(); 2261 $text_group = array();
2112 $this->explore($left, $right, $db_group, 'db'); 2262 $this->explore($left, $right, $db_group, 'db');
2113 $this->explore($left, $right, $text_group, 'text'); 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 switch ($op) 2268 switch ($op)
2119 { 2269 {
2120 case ExprOp::OP_AND: 2270 case ExprOp::OP_AND:
2121 if ($this->debug) print "\n\npoint: intersect\n\n"; 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 break; 2273 break;
2124 case ExprOp::OP_OR: 2274 case ExprOp::OP_OR:
2125 if ($this->debug) print "\n\nmerge: union\n\n"; 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 break; 2277 break;
2128 default: 2278 default:
2129 throw new Exception('how did this happen??'); 2279 throw new Exception('how did this happen??');
@@ -2141,9 +2291,9 @@ class OpExpr extends Expr @@ -2141,9 +2291,9 @@ class OpExpr extends Expr
2141 } 2291 }
2142 2292
2143 $permResults = array(); 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 return $permResults; 2299 return $permResults;
search2/search/fields/CreatedByField.inc.php
@@ -7,31 +7,31 @@ @@ -7,31 +7,31 @@
7 * Document Management Made Simple 7 * Document Management Made Simple
8 * Copyright (C) 2008 KnowledgeTree Inc. 8 * Copyright (C) 2008 KnowledgeTree Inc.
9 * Portions copyright The Jam Warehouse Software (Pty) Limited 9 * Portions copyright The Jam Warehouse Software (Pty) Limited
10 - * 10 + *
11 * This program is free software; you can redistribute it and/or modify it under 11 * This program is free software; you can redistribute it and/or modify it under
12 * the terms of the GNU General Public License version 3 as published by the 12 * the terms of the GNU General Public License version 3 as published by the
13 * Free Software Foundation. 13 * Free Software Foundation.
14 - * 14 + *
15 * This program is distributed in the hope that it will be useful, but WITHOUT 15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * details. 18 * details.
19 - * 19 + *
20 * You should have received a copy of the GNU General Public License 20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. 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 * California 94120-7775, or email info@knowledgetree.com. 24 * California 94120-7775, or email info@knowledgetree.com.
25 - * 25 + *
26 * The interactive user interfaces in modified source and object code versions 26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under 27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU General Public License version 3. 28 * Section 5 of the GNU General Public License version 3.
29 - * 29 + *
30 * In accordance with Section 7(b) of the GNU General Public License version 3, 30 * In accordance with Section 7(b) of the GNU General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by 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 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices 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 * copyright notice. 35 * copyright notice.
36 * Contributor( s): ______________________________________ 36 * Contributor( s): ______________________________________
37 * 37 *
@@ -47,6 +47,25 @@ class CreatedByField extends DBFieldExpr @@ -47,6 +47,25 @@ class CreatedByField extends DBFieldExpr
47 $this->matchField('name'); 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 public function getInputRequirements() 69 public function getInputRequirements()
51 { 70 {
52 return array('value'=>array('type'=>FieldInputType::USER_LIST)); 71 return array('value'=>array('type'=>FieldInputType::USER_LIST));
search2/search/fields/FullPathField.inc.php
@@ -7,31 +7,31 @@ @@ -7,31 +7,31 @@
7 * Document Management Made Simple 7 * Document Management Made Simple
8 * Copyright (C) 2008 KnowledgeTree Inc. 8 * Copyright (C) 2008 KnowledgeTree Inc.
9 * Portions copyright The Jam Warehouse Software (Pty) Limited 9 * Portions copyright The Jam Warehouse Software (Pty) Limited
10 - * 10 + *
11 * This program is free software; you can redistribute it and/or modify it under 11 * This program is free software; you can redistribute it and/or modify it under
12 * the terms of the GNU General Public License version 3 as published by the 12 * the terms of the GNU General Public License version 3 as published by the
13 * Free Software Foundation. 13 * Free Software Foundation.
14 - * 14 + *
15 * This program is distributed in the hope that it will be useful, but WITHOUT 15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * details. 18 * details.
19 - * 19 + *
20 * You should have received a copy of the GNU General Public License 20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. 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 * California 94120-7775, or email info@knowledgetree.com. 24 * California 94120-7775, or email info@knowledgetree.com.
25 - * 25 + *
26 * The interactive user interfaces in modified source and object code versions 26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under 27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU General Public License version 3. 28 * Section 5 of the GNU General Public License version 3.
29 - * 29 + *
30 * In accordance with Section 7(b) of the GNU General Public License version 3, 30 * In accordance with Section 7(b) of the GNU General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by 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 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices 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 * copyright notice. 35 * copyright notice.
36 * Contributor( s): ______________________________________ 36 * Contributor( s): ______________________________________
37 * 37 *
@@ -47,6 +47,25 @@ class FullPathField extends DBFieldExpr @@ -47,6 +47,25 @@ class FullPathField extends DBFieldExpr
47 $this->setAlias('FullPath'); 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 public function getInputRequirements() 69 public function getInputRequirements()
51 { 70 {
52 return array('value'=>array('type'=>FieldInputType::TEXT)); 71 return array('value'=>array('type'=>FieldInputType::TEXT));
search2/search/fields/TitleField.inc.php
@@ -7,31 +7,31 @@ @@ -7,31 +7,31 @@
7 * Document Management Made Simple 7 * Document Management Made Simple
8 * Copyright (C) 2008 KnowledgeTree Inc. 8 * Copyright (C) 2008 KnowledgeTree Inc.
9 * Portions copyright The Jam Warehouse Software (Pty) Limited 9 * Portions copyright The Jam Warehouse Software (Pty) Limited
10 - * 10 + *
11 * This program is free software; you can redistribute it and/or modify it under 11 * This program is free software; you can redistribute it and/or modify it under
12 * the terms of the GNU General Public License version 3 as published by the 12 * the terms of the GNU General Public License version 3 as published by the
13 * Free Software Foundation. 13 * Free Software Foundation.
14 - * 14 + *
15 * This program is distributed in the hope that it will be useful, but WITHOUT 15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 * details. 18 * details.
19 - * 19 + *
20 * You should have received a copy of the GNU General Public License 20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. 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 * California 94120-7775, or email info@knowledgetree.com. 24 * California 94120-7775, or email info@knowledgetree.com.
25 - * 25 + *
26 * The interactive user interfaces in modified source and object code versions 26 * The interactive user interfaces in modified source and object code versions
27 * of this program must display Appropriate Legal Notices, as required under 27 * of this program must display Appropriate Legal Notices, as required under
28 * Section 5 of the GNU General Public License version 3. 28 * Section 5 of the GNU General Public License version 3.
29 - * 29 + *
30 * In accordance with Section 7(b) of the GNU General Public License version 3, 30 * In accordance with Section 7(b) of the GNU General Public License version 3,
31 * these Appropriate Legal Notices must retain the display of the "Powered by 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 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices 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 * copyright notice. 35 * copyright notice.
36 * Contributor( s): ______________________________________ 36 * Contributor( s): ______________________________________
37 * 37 *
@@ -39,13 +39,31 @@ @@ -39,13 +39,31 @@
39 39
40 class TitleField extends DBFieldExpr 40 class TitleField extends DBFieldExpr
41 { 41 {
42 -  
43 public function __construct() 42 public function __construct()
44 { 43 {
45 parent::__construct('name', 'document_metadata_version', _kt('Title')); 44 parent::__construct('name', 'document_metadata_version', _kt('Title'));
46 $this->setAlias('Title'); 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 public function getInputRequirements() 67 public function getInputRequirements()
50 { 68 {
51 return array('value'=>array('type'=>FieldInputType::TEXT)); 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,6 +42,8 @@ require_once(&#39;search/fieldRegistry.inc.php&#39;);
42 require_once('search/expr.inc.php'); 42 require_once('search/expr.inc.php');
43 require_once(KT_LIB_DIR . '/security/Permission.inc'); 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 function rank_compare($a, $b) 47 function rank_compare($a, $b)
46 { 48 {
47 if ($a->Rank == $b->Rank) 49 if ($a->Rank == $b->Rank)
@@ -66,6 +68,8 @@ function searchfix($str) @@ -66,6 +68,8 @@ function searchfix($str)
66 return str_replace(array("\n","\r"), array('',''), addslashes($str)); 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 class SearchHelper 73 class SearchHelper
70 { 74 {
71 public static function correctPath($path) 75 public static function correctPath($path)
@@ -366,8 +370,6 @@ class SearchHelper @@ -366,8 +370,6 @@ class SearchHelper
366 dtfl.document_type_id=$documentTypeID 370 dtfl.document_type_id=$documentTypeID
367 ORDER BY 371 ORDER BY
368 df.name"; 372 df.name";
369 -  
370 -  
371 } 373 }
372 else 374 else
373 { 375 {
@@ -566,7 +568,8 @@ function processSearchExpression($query) @@ -566,7 +568,8 @@ function processSearchExpression($query)
566 { 568 {
567 $expr = parseExpression($query); 569 $expr = parseExpression($query);
568 570
569 - $rs = $expr->evaluate(); 571 + $rs = $expr->evaluate(ExprContext::DOCUMENT);
  572 + $rs = $rs['docs'];
570 usort($rs, 'rank_compare'); 573 usort($rs, 'rank_compare');
571 574
572 $results = array(); 575 $results = array();
@@ -629,4 +632,57 @@ function processSearchExpression($query) @@ -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,7 +46,6 @@ function saveSearch()
46 url='{/literal}{$rootUrl}{literal}/search2/ajax/saveExpr.php?txtName='+ escape(txtName.value) + 46 url='{/literal}{$rootUrl}{literal}/search2/ajax/saveExpr.php?txtName='+ escape(txtName.value) +
47 '&txtQuery=' + escape('{/literal}{$txtQuery|escape:'quotes'}{literal}'); 47 '&txtQuery=' + escape('{/literal}{$txtQuery|escape:'quotes'}{literal}');
48 48
49 -  
50 Ext.Ajax.request( 49 Ext.Ajax.request(
51 { 50 {
52 url: url, 51 url: url,
@@ -132,57 +131,82 @@ function onShowAll(showall) @@ -132,57 +131,82 @@ function onShowAll(showall)
132 <table border=0 cellpadding="1" cellspacing="1" width="100%" align=center> 131 <table border=0 cellpadding="1" cellspacing="1" width="100%" align=center>
133 {assign var=cbid value=0} 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 <tr><td> 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 &nbsp;&nbsp; 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 {/if} 148 {/if}
142 - {if $document->IsAvailable} 149 + {if $hit->IsAvailable}
143 &nbsp;&nbsp; 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 {/if} 153 {/if}
147 154
148 155
149 - {if !$document->IsAvailable} 156 + {if !$hit->IsAvailable}
150 &nbsp;&nbsp; 157 &nbsp;&nbsp;
151 <font style=" color: red "><B> * {i18n}NOT AVAILABLE{/i18n} * </B></font> 158 <font style=" color: red "><B> * {i18n}NOT AVAILABLE{/i18n} * </B></font>
152 {/if} 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 </nobr> 166 </nobr>
155 </td><td align=right> 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 </tr> 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 </td> 183 </td>
164 <td align=right><nobr> 184 <td align=right><nobr>
165 <font style="color: orange "> 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 </font> 190 </font>
169 </td> 191 </td>
  192 + {if $hit->IsDocument}
170 <tr><td> 193 <tr><td>
171 {if $workflow != ''} 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 {/if} 196 {/if}
174 </td> 197 </td>
175 <td align=right><nobr> 198 <td align=right><nobr>
176 <font style="color: brown "> 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 {else} 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 {/if} 206 {/if}
184 </font> 207 </font>
185 </td> 208 </td>
  209 + {/if}
186 210
187 <tr><td colspan=2><br></br></td></tr></tr> 211 <tr><td colspan=2><br></br></td></tr></tr>
188 212