Commit 9650f889b3dd6c2a51fc41dcb41cfdbb8fce9317

Authored by kevin_fourie
1 parent fc322e3f

Merged in from STABLE trunk...

KTS-3691
"CLONE -Is there a way to prevent from checking in the wrong file?(SUP-1064)"
Fixed. Added a config option to hide the checkbox for "Force Original Filename".

KTS-3736
"User should be made aware that setting the field limit for document filenames and titles beyond 255 is futile as only 255 characters are allowed in the fields in the DB."
Fixed. Added a better description of the config option.

KTC-592
"Better default tooltip needed on mouse over than 'ACME Corporation'."
Fixed. Added a better tooltip as the default value for the config option.

Committed by: Megan Watson
Reviewed by: Conrad Vermeulen

KTS-3741
"The Property Preview Panel lacks a scroll bar"
Fixed. Changed overflow css from hidden to auto.

Committed by: Megan Watson
Reviewed by: Conrad Vermeulen

KTC-613
"Error occur when creating a link to a new RSS feed"
Fixed. Removed the javascript for the tinymce editor which isn't used.

Committed by: Megan Watson
Reviewed by: Conrad Vermeulen

KTS-3306
"Search should return folders"
Implemented.

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

Committed By: Conrad Vermeulen
Reviewed By: Megan Watson

KTS-3306
"Search should return folders"
Implemented.

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

Committed By: Conrad Vermeulen
Reviewed By: Megan Watson

KTS-3630
"matching metadata is not translated on results page"
Fixed.

Committed By: Conrad Vermeulen
Reviewed By: Megan Watson


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/STABLE/branches/3.5.3a-Release-Branch@9404 c91229c3-7414-0410-bfa2-8a42b809f60b
plugins/ktcore/KTDocumentActions.php
... ... @@ -599,7 +599,8 @@ class KTDocumentCheckInAction extends KTDocumentAction {
599 599 $major_inc = sprintf('%d.%d', $this->oDocument->getMajorVersionNumber()+1, 0);
600 600 $minor_inc = sprintf('%d.%d', $this->oDocument->getMajorVersionNumber(), $this->oDocument->getMinorVersionNumber()+1);
601 601  
602   - $oForm->setWidgets(array(
  602 + // Set the widgets for the form
  603 + $aWidgets = array(
603 604 array('ktcore.widgets.file', array(
604 605 'label' => _kt('File'),
605 606 'description' => sprintf(_kt('Please specify the file you wish to upload. Unless you also indicate that you are changing its filename (see "Force Original Filename" below), this will need to be called <strong>%s</strong>'), htmlentities($this->oDocument->getFilename(),ENT_QUOTES,'UTF-8')),
... ... @@ -618,14 +619,10 @@ class KTDocumentCheckInAction extends KTDocumentAction {
618 619 'description' => _kt('Please describe the changes you made to the document. Bear in mind that you can use a maximum of <strong>250</strong> characters.'),
619 620 'name' => 'reason',
620 621 )),
621   - array('ktcore.widgets.boolean',array(
622   - 'label' => _kt('Force Original Filename'),
623   - 'description' => sprintf(_kt('If this is checked, the uploaded document must have the same filename as the original: <strong>%s</strong>'), htmlentities($this->oDocument->getFilename(),ENT_QUOTES,'UTF-8')),
624   - 'name' => 'forcefilename',
625   - 'value' => true,
626   - )),
627   - ));
628   - $oForm->setValidators(array(
  622 + );
  623 +
  624 + // Set the validators for the widgets
  625 + $aValidators = array(
629 626 array('ktcore.validators.string', array(
630 627 'test' => 'reason',
631 628 'max_length' => 250,
... ... @@ -639,12 +636,27 @@ class KTDocumentCheckInAction extends KTDocumentAction {
639 636 'test' => 'file',
640 637 'output' => 'file',
641 638 )),
642   - array('ktcore.validators.boolean', array(
  639 + );
  640 +
  641 + // Add the "Force Original Filename" option if applicable
  642 + global $default;
  643 + if(!$default->disableForceFilenameOption){
  644 + $aWidgets[] = array('ktcore.widgets.boolean',array(
  645 + 'label' => _kt('Force Original Filename'),
  646 + 'description' => sprintf(_kt('If this is checked, the uploaded document must have the same filename as the original: <strong>%s</strong>'), htmlentities($this->oDocument->getFilename(),ENT_QUOTES,'UTF-8')),
  647 + 'name' => 'forcefilename',
  648 + 'value' => true,
  649 + ));
  650 +
  651 + $aValidators[] = array('ktcore.validators.boolean', array(
643 652 'test' => 'forcefilename',
644 653 'output' => 'forcefilename',
645   - )),
646   - ));
  654 + ));
  655 + }
647 656  
  657 + // Add widgets and validators to the form
  658 + $oForm->setWidgets($aWidgets);
  659 + $oForm->setValidators($aValidators);
648 660 return $oForm;
649 661 }
650 662  
... ... @@ -669,8 +681,16 @@ class KTDocumentCheckInAction extends KTDocumentAction {
669 681  
670 682 $extra_errors = array();
671 683  
672   - if ($data['forcefilename'] && ($data['file']['name'] != $this->oDocument->getFilename())) {
673   - $extra_errors['file'] = sprintf(_kt('The file you uploaded was not called "%s". If you wish to change the filename, please set "Force Original Filename" below to false. '), htmlentities($this->oDocument->getFilename(),ENT_QUOTES,'UTF-8'));
  684 + // If the filename is different to the original check if "Force Original Filename" is set and return an error if it is.
  685 + $docFileName = $this->oDocument->getFilename();
  686 + if($data['file']['name'] != $docFileName){
  687 + global $default;
  688 +
  689 + if($default->disableForceFilenameOption){
  690 + $extra_errors['file'] = sprintf(_kt('The file you uploaded was not called "%s". The file must have the same name as the original file.'), htmlentities($docFileName,ENT_QUOTES,'UTF-8'));
  691 + }else if ($data['forcefilename']) {
  692 + $extra_errors['file'] = sprintf(_kt('The file you uploaded was not called "%s". If you wish to change the filename, please set "Force Original Filename" below to false. '), htmlentities($docFileName,ENT_QUOTES,'UTF-8'));
  693 + }
674 694 }
675 695  
676 696 if (!empty($res['errors']) || !empty($extra_errors)) {
... ... @@ -679,7 +699,7 @@ class KTDocumentCheckInAction extends KTDocumentAction {
679 699  
680 700 $sReason = $data['reason'];
681 701  
682   - $sCurrentFilename = $this->oDocument->getFileName();
  702 + $sCurrentFilename = $docFileName;
683 703 $sNewFilename = $data['file']['name'];
684 704  
685 705 $aOptions = array();
... ...
plugins/ktstandard/documentpreview/resources/container.css
... ... @@ -28,7 +28,7 @@
28 28 padding-bottom: 0px;
29 29 cursor: pointer;
30 30 height: 250px;
31   - overflow: hidden;
  31 + overflow: auto;
32 32 clear: both;
33 33 }
34 34  
... ...
plugins/ktstandard/documentpreview/resources/preview.js
... ... @@ -15,7 +15,7 @@ var showInfo = function(iDocId, sUrl, sDir, loading){
15 15 modal: true,
16 16 plain: false,
17 17 width: 500,
18   - height: 300,
  18 + height: 360,
19 19 minWidth: 300,
20 20 minHeight: 250
21 21 });
... ...
plugins/rssplugin/loadFeed.inc.php
... ... @@ -48,7 +48,7 @@
48 48 }
49 49  
50 50 // Check if the feed matches a url
51   - if(!preg_match("/^http:\/\/([^\/]+)(.*)$/", $feed, $matches)){
  51 + if(!preg_match("/^http[s]?:\/\/([^\/]+)(.*)$/", $feed, $matches)){
52 52 // If not, it is an internal feed
53 53 $aRSSArray = KTrss::getInternalFeed($user);
54 54 }else{
... ...
plugins/rssplugin/rss2array.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 *
... ... @@ -62,7 +62,7 @@
62 62 # if the URL looks ok
63 63 #
64 64  
65   - if(preg_match("/^http:\/\/([^\/]+)(.*)$/", $url, $matches)){
  65 + if(preg_match("/^http[s]?:\/\/([^\/]+)(.*)$/", $url, $matches)){
66 66  
67 67 $host = $matches[1];
68 68 $uri = $matches[2];
... ...
plugins/rssplugin/templates/RSSPlugin/addfeed.smarty
1   -{$context->oPage->requireJSResource("thirdpartyjs/tinymce/jscripts/tiny_mce/tiny_mce.js")}
2   -{capture assign=sJS}
3   -{literal}
4   -tinyMCE.init({
5   - mode : "textareas",
6   - theme : "simple",
7   -});
8   -{/literal}
9   -{/capture}
10   -{$context->oPage->requireJSStandalone($sJS)}
11   -
12   -
13   -
14 1 <h2>{i18n}New RSS Feed{/i18n}</h2>
15 2  
16 3 <p class="descriptiveText">{i18n}Create a rss feed which will be displayed on the dashboard{/i18n}</p>
... ...
plugins/rssplugin/templates/RSSPlugin/editfeed.smarty
1   -{$context->oPage->requireJSResource("thirdpartyjs/tinymce/jscripts/tiny_mce/tiny_mce.js")}
2   -{capture assign=sJS}
3   -{literal}
4   -tinyMCE.init({
5   - mode : "textareas",
6   - theme : "simple",
7   -});
8   -{/literal}
9   -{/capture}
10   -{$context->oPage->requireJSStandalone($sJS)}
11   -
12   -
13   -
14 1 <h2>{i18n}Edit RSS Feed{/i18n}</h2>
15 2  
16 3 <p class="descriptiveText">{i18n}Edit a RSS feed{/i18n}</p>
... ...
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,10 +187,7 @@ 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 {
... ...
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;
... ... @@ -363,7 +382,7 @@ class FieldExpr extends Expr
363 382 */
364 383 public function __toString()
365 384 {
366   - return $this->alias;
  385 + return $this->display;
367 386 }
368 387  
369 388 public function toViz(&$str, $phase)
... ... @@ -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 *
... ... @@ -1759,13 +1872,14 @@ class OpExpr extends Expr
1759 1872 */
1760 1873 public function __toString()
1761 1874 {
1762   - $expr = $this->left_expr . ' ' . $this->op .' ' . $this->right_expr;
  1875 + // _kt may not translate well here.
  1876 + $expr = $this->left_expr . ' ' . _kt($this->op) .' ' . $this->right_expr;
1763 1877  
1764 1878 if (is_null($this->parent))
1765 1879 {
1766 1880 if ($this->not())
1767 1881 {
1768   - $expr = "NOT $expr";
  1882 + $expr = _kt('NOT') . $expr;
1769 1883 }
1770 1884 return $expr;
1771 1885 }
... ... @@ -1805,7 +1919,7 @@ class OpExpr extends Expr
1805 1919 * @param array $rightres
1806 1920 * @return array
1807 1921 */
1808   - protected static function intersect($leftres, $rightres)
  1922 + protected static function _intersect($leftres, $rightres)
1809 1923 {
1810 1924 if (empty($leftres) || empty($rightres))
1811 1925 {
... ... @@ -1814,7 +1928,7 @@ class OpExpr extends Expr
1814 1928 $result = array();
1815 1929 foreach($leftres as $item)
1816 1930 {
1817   - $document_id = $item->DocumentID;
  1931 + $document_id = $item->Id;
1818 1932  
1819 1933 if (!$item->IsLive)
1820 1934 {
... ... @@ -1831,6 +1945,22 @@ class OpExpr extends Expr
1831 1945 return $result;
1832 1946 }
1833 1947  
  1948 + protected static function intersect($leftres, $rightres)
  1949 + {
  1950 + return array(
  1951 + 'docs'=>self::_intersect($leftres['docs'],$rightres['docs']),
  1952 + 'folders'=>self::_intersect($leftres['folders'],$rightres['folders'])
  1953 + );
  1954 + }
  1955 +
  1956 + protected static function union($leftres, $rightres)
  1957 + {
  1958 + return array(
  1959 + 'docs'=>self::_union($leftres['docs'],$rightres['docs']),
  1960 + 'folders'=>self::_union($leftres['folders'],$rightres['folders'])
  1961 + );
  1962 + }
  1963 +
1834 1964 /**
1835 1965 * The objective of this function is to merge the results so that there is a union of the results,
1836 1966 * but there should be no duplicates.
... ... @@ -1839,7 +1969,7 @@ class OpExpr extends Expr
1839 1969 * @param array $rightres
1840 1970 * @return array
1841 1971 */
1842   - protected static function union($leftres, $rightres)
  1972 + protected static function _union($leftres, $rightres)
1843 1973 {
1844 1974 if (empty($leftres))
1845 1975 {
... ... @@ -1855,15 +1985,15 @@ class OpExpr extends Expr
1855 1985 {
1856 1986 if ($item->IsLive)
1857 1987 {
1858   - $result[$item->DocumentID] = $item;
  1988 + $result[$item->Id] = $item;
1859 1989 }
1860 1990 }
1861 1991  
1862 1992 foreach($rightres as $item)
1863 1993 {
1864   - if (!array_key_exists($item->DocumentID, $result) || $item->Rank > $result[$item->DocumentID]->Rank)
  1994 + if (!array_key_exists($item->Id, $result) || $item->Rank > $result[$item->Id]->Rank)
1865 1995 {
1866   - $result[$item->DocumentID] = $item;
  1996 + $result[$item->Id] = $item;
1867 1997 }
1868 1998 }
1869 1999 return $result;
... ... @@ -1996,7 +2126,7 @@ class OpExpr extends Expr
1996 2126 {
1997 2127 if (empty($group)) { return array(); }
1998 2128  
1999   - $exprbuilder = new SQLQueryBuilder();
  2129 + $exprbuilder = new SQLQueryBuilder($this->getContext());
2000 2130  
2001 2131 if (count($group) == 1)
2002 2132 {
... ... @@ -2020,11 +2150,18 @@ class OpExpr extends Expr
2020 2150  
2021 2151 foreach($rs as $item)
2022 2152 {
2023   - $document_id = $item['id'];
  2153 + $id = $item['id'];
2024 2154 $rank = $exprbuilder->getRanking($item);
2025   - if (!array_key_exists($document_id, $results) || $rank > $results[$document_id]->Rank)
  2155 + if (!array_key_exists($id, $results) || $rank > $results[$id]->Rank)
2026 2156 {
2027   - $results[$document_id] = new QueryResultItem($document_id, $rank, $item['title'], $exprbuilder->getResultText($item));
  2157 + if ($this->context == ExprContext::DOCUMENT)
  2158 + {
  2159 + $results[$id] = new DocumentResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item));
  2160 + }
  2161 + else
  2162 + {
  2163 + $results[$id] = new FolderResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item));
  2164 + }
2028 2165 }
2029 2166 }
2030 2167  
... ... @@ -2034,7 +2171,10 @@ class OpExpr extends Expr
2034 2171  
2035 2172 private function exec_text_query($op, $group)
2036 2173 {
2037   - if (empty($group)) { return array(); }
  2174 + if (($this->getContext() != ExprContext::DOCUMENT) || empty($group))
  2175 + {
  2176 + return array();
  2177 + }
2038 2178  
2039 2179 $exprbuilder = new TextQueryBuilder();
2040 2180  
... ... @@ -2060,12 +2200,21 @@ class OpExpr extends Expr
2060 2200 }
2061 2201  
2062 2202 return $results;
2063   -
2064   -
2065 2203 }
2066 2204  
2067   - public function evaluate()
  2205 + public function evaluate($context = ExprContext::DOCUMENT_AND_FOLDER)
2068 2206 {
  2207 + if ($context == ExprContext::DOCUMENT_AND_FOLDER)
  2208 + {
  2209 + $docs = $this->evaluate(ExprContext::DOCUMENT);
  2210 + $folders = $this->evaluate(ExprContext::FOLDER);
  2211 +
  2212 + return array(
  2213 + 'docs' => $docs['docs'],
  2214 + 'folders' => $folders['folders']);
  2215 + }
  2216 + $this->setContext($context);
  2217 +
2069 2218 $left = $this->left();
2070 2219 $right = $this->right();
2071 2220 $op = $this->op();
... ... @@ -2075,12 +2224,12 @@ class OpExpr extends Expr
2075 2224 {
2076 2225 $point = 'point';
2077 2226 }
  2227 + $resultContext = ($this->getContext() == ExprContext::DOCUMENT)?'docs':'folders';
2078 2228  
2079 2229 if ($point == 'merge')
2080 2230 {
2081   -
2082   - $leftres = $left->evaluate();
2083   - $rightres = $right->evaluate();
  2231 + $leftres = $left->evaluate($context);
  2232 + $rightres = $right->evaluate($context);
2084 2233 switch ($op)
2085 2234 {
2086 2235 case ExprOp::OP_AND:
... ... @@ -2099,31 +2248,33 @@ class OpExpr extends Expr
2099 2248 {
2100 2249 if ($this->isDBonly())
2101 2250 {
2102   - $result = $this->exec_db_query($op, array($this));
  2251 + $result[$resultContext] = $this->exec_db_query($op, array($this));
2103 2252 }
2104 2253 elseif ($this->isTextOnly())
2105 2254 {
2106   - $result = $this->exec_text_query($op, array($this));
  2255 + $result[$resultContext] = $this->exec_text_query($op, array($this));
2107 2256 }
2108 2257 elseif (in_array($op, array(ExprOp::OP_OR, ExprOp::OP_AND)))
2109 2258 {
  2259 + // do we get to this???
  2260 + // TODO: remove me please.... the simpleQuery stuff should go???
2110 2261 $db_group = array();
2111 2262 $text_group = array();
2112 2263 $this->explore($left, $right, $db_group, 'db');
2113 2264 $this->explore($left, $right, $text_group, 'text');
2114 2265  
2115   - $db_result = $this->exec_db_query($op, $db_group);
2116   - $text_result = $this->exec_text_query($op, $text_group);
  2266 + $db_result[$resultContext] = $this->exec_db_query($op, $db_group);
  2267 + $text_result[$resultContext] = $this->exec_text_query($op, $text_group);
2117 2268  
2118 2269 switch ($op)
2119 2270 {
2120 2271 case ExprOp::OP_AND:
2121 2272 if ($this->debug) print "\n\npoint: intersect\n\n";
2122   - $result = OpExpr::intersect($db_result, $text_result);
  2273 + $result[$resultContext] = OpExpr::intersect($db_result, $text_result);
2123 2274 break;
2124 2275 case ExprOp::OP_OR:
2125 2276 if ($this->debug) print "\n\nmerge: union\n\n";
2126   - $result = OpExpr::union($db_result, $text_result);
  2277 + $result[$resultContext] = OpExpr::union($db_result, $text_result);
2127 2278 break;
2128 2279 default:
2129 2280 throw new Exception('how did this happen??');
... ... @@ -2141,9 +2292,9 @@ class OpExpr extends Expr
2141 2292 }
2142 2293  
2143 2294 $permResults = array();
2144   - foreach($result as $idx=>$item)
  2295 + foreach($result[$resultContext] as $idx=>$item)
2145 2296 {
2146   - $permResults[$idx] = $item;
  2297 + $permResults[$resultContext][$idx] = $item;
2147 2298 }
2148 2299  
2149 2300 return $permResults;
... ...
search2/search/exprConstants.inc.php
... ... @@ -6,31 +6,31 @@
6 6 * Document Management Made Simple
7 7 * Copyright (C) 2008 KnowledgeTree Inc.
8 8 * Portions copyright The Jam Warehouse Software (Pty) Limited
9   - *
  9 + *
10 10 * This program is free software; you can redistribute it and/or modify it under
11 11 * the terms of the GNU General Public License version 3 as published by the
12 12 * Free Software Foundation.
13   - *
  13 + *
14 14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 17 * details.
18   - *
  18 + *
19 19 * You should have received a copy of the GNU General Public License
20 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21   - *
22   - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
  21 + *
  22 + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco,
23 23 * California 94120-7775, or email info@knowledgetree.com.
24   - *
  24 + *
25 25 * The interactive user interfaces in modified source and object code versions
26 26 * of this program must display Appropriate Legal Notices, as required under
27 27 * Section 5 of the GNU General Public License version 3.
28   - *
  28 + *
29 29 * In accordance with Section 7(b) of the GNU General Public License version 3,
30 30 * these Appropriate Legal Notices must retain the display of the "Powered by
31   - * KnowledgeTree" logo and retain the original copyright notice. If the display of the
  31 + * KnowledgeTree" logo and retain the original copyright notice. If the display of the
32 32 * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices
33   - * must display the words "Powered by KnowledgeTree" and retain the original
  33 + * must display the words "Powered by KnowledgeTree" and retain the original
34 34 * copyright notice.
35 35 * Contributor( s): ______________________________________
36 36 *
... ... @@ -51,6 +51,22 @@ class ExprOp
51 51 const OP_OR = 'OR';
52 52 const IS_NOT = 'is not';
53 53  
  54 +
  55 + private static
  56 + function init()
  57 + {
  58 + // this isn't really called, but used to help with translation.
  59 + _kt('is');
  60 + _kt('contains');
  61 + _kt('between');
  62 + _kt('start with');
  63 + _kt('ends with');
  64 + _kt('like');
  65 + _kt('AND');
  66 + _kt('OR');
  67 + _kt('is not');
  68 + }
  69 +
54 70 }
55 71  
56 72  
... ...
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 ?>
... ...
sql/mysql/install/data.sql
... ... @@ -199,7 +199,7 @@ INSERT INTO `config_settings` VALUES
199 199 (25, 'session', 'Anonymous Login', 'Defines whether to allow anonymous users to log in automatically. Default is \'False\'. <br>Best practice is not to allow automatic login of anonymous users unless you understand KnowledgeTree\'s security mechanisms, and have sensibly applied the roles \'Everyone\' and \'Authenticated Users\'. ', 'allowAnonymousLogin', 'default', 'false', 'boolean', NULL, 1),
200 200 (26, 'ui', 'Company Logo', 'Specifies the path (relative to the KnowledgeTree directory) to the custom logo for the KnowledgeTree user interface. <br>The logo must be 50px tall, and on a white background.', 'companyLogo', 'default', '${rootUrl}/resources/companylogo.png', 'string', NULL, 1),
201 201 (27, 'ui', 'Company Logo Width', 'Defines the width, in pixels, of your custom logo.', 'companyLogoWidth', 'default', '313px', 'string', NULL, 1),
202   -(28, 'ui', 'Company Logo Title', 'Alternative text for the title of your custom company logo, for accessibility purposes.', 'companyLogoTitle', 'default', 'ACME Corporation', 'string', NULL, 1),
  202 +(28, 'ui', 'Company Logo Title', 'Alternative text for the title of your custom company logo, for accessibility purposes.', 'companyLogoTitle', 'default', 'Add Company Name', 'string', NULL, 1),
203 203 (29, 'ui', 'Always Show All Results', 'Defines, where \'show all users\' is an available action, whether to display the full list of users and groups on page load, without requiring the user to click \'show all users\'. Default is \'False\'.', 'alwaysShowAll', 'default', 'false', 'boolean', NULL, 1),
204 204 (30, 'ui', 'Condensed Admin UI', 'Defines whether to use a condensed (compact) version of the KnowledgeTree user interface for the admin user. Default is \'False\'.', 'condensedAdminUI', 'default', 'false', 'boolean', NULL, 1),
205 205 (31, 'ui', 'Fake Mimetype', 'Defines whether browsers may provide the option to \'open\' a document from download. Default is \'False\'.<br>Change to \'True\' to prevent (most) browsers from giving users the \'Open\' option.', 'fakeMimetype', 'default', 'false', 'boolean', NULL, 1),
... ... @@ -270,10 +270,11 @@ INSERT INTO `config_settings` VALUES
270 270 (96, 'KnowledgeTree', 'Redirect To Browse View: Exceptions', 'Specifies that, when \'Redirect To Browse\' is set to \'True\' all users, except for the users listed in the text field below are redirected to the Browse view on log in. The users listed for this setting are directed to the KnowledgeTree Dashboard. To define exceptions, add user names in the text field as follows, e.g. admin, joebloggs, etc.', 'redirectToBrowseExceptions', '', '', 'string', NULL, 1),
271 271 (97, 'session', 'Allow Automatic Sign In', 'Defines whether to automatically create a user account on first login for any user who does not yet exist in the system. Default is \'False\'.', 'allowAutoSignup', 'default', 'false', 'boolean', 'string', 1),
272 272 (98, 'ldapAuthentication', 'Create Groups Automatically', 'Defines whether to allow LDAP groups to be created automatically. Default is \'False\'.', 'autoGroupCreation', 'default', 'false', 'boolean', 'string', 1),
273   -(99, 'browse', 'Truncate Document and Folder Titles in Browse View', 'Defines the length of the document or folder title displayed in the browse view.', 'titleCharLength', 'default', '40', 'numeric_string', 'string', 1),
  273 +(99, 'browse', 'Truncate Document and Folder Titles in Browse View', 'Defines the maximum number of characters to display for a document or folder title in the browse view. The maximum allowable number of characters is 255.', 'titleCharLength', 'default', '40', 'numeric_string', 'string', 1),
274 274 (100, 'import', 'Disable Bulk Import', 'Disable the bulk import plugin', 'disableBulkImport', 'default', 'false', 'string', NULL, 1),
275 275 (101, 'session', 'Enable version check', 'Compares the system version with the database version to determine if a database upgrade is needed.','dbversioncompare', 'default', 'true', 'boolean', NULL, 0),
276   -(102, 'tweaks', 'Update Document Version (Content) on Editing Metadata', 'The document version is equivalent to the document content version. When set to true the document version will be increased when the document metadata is updated.', 'updateContentVersion', 'default', 'false', 'boolean', NULL, 1);
  276 +(102, 'tweaks', 'Update Document Version (Content) on Editing Metadata', 'The document version is equivalent to the document content version. When set to true the document version will be increased when the document metadata is updated.', 'updateContentVersion', 'default', 'false', 'boolean', NULL, 1),
  277 +(103, 'tweaks', 'Always Force Original Filename on Checkin', 'When set to true, the checkbox for "Force Original Filename" will be hidden on check-in. This ensures that the filename will always stay the same.', 'disableForceFilenameOption', 'default', 'false', 'boolean', NULL, 1);
277 278 /*!40000 ALTER TABLE `config_settings` ENABLE KEYS */;
278 279 UNLOCK TABLES;
279 280  
... ...
sql/mysql/upgrade/3.5.3/length_config_setting.sql
1 1 INSERT INTO `config_settings` (group_name, display_name, description, item, value, default_value, type, options, can_edit) VALUES
2 2  
3   -('browse', 'Truncate Document and Folder Titles in Browse View', 'Defines the length of the document or folder title displayed in the
4   -browse view.', 'titleCharLength', 'default', '40', 'numeric_string', NULL, 1),
  3 +('browse', 'Truncate Document and Folder Titles in Browse View', 'Defines the maximum number of characters to display for a document or folder title in the browse view. The maximum allowable number of characters is 255.', 'titleCharLength', 'default', '40', 'numeric_string', NULL, 1),
5 4  
6 5 ('import', 'Disable Bulk Import', 'Disable the bulk import plugin', 'disableBulkImport', 'default', 'false', 'string', NULL, 1),
7 6  
8   -('session', 'Enable version check', 'Compares the system version with the database version to determine if a database upgrade is needed.',
9   -'dbversioncompare', 'default', 'true', 'boolean', NULL, 0),
  7 +('session', 'Enable version check', 'Compares the system version with the database version to determine if a database upgrade is needed.', 'dbversioncompare', 'default', 'true', 'boolean', NULL, 0),
10 8  
11   -('tweaks', 'Update Document Version (Content) on Editing Metadata', 'The document version is equivalent to the document content version. When set to
12   -true the document version will be increased when the document metadata is updated.', 'updateContentVersion', 'default', 'false', 'boolean', NULL, 1);
13 9 \ No newline at end of file
  10 +('tweaks', 'Update Document Version (Content) on Editing Metadata', 'The document version is equivalent to the document content version. When set to true the document version will be increased when the document metadata is updated.', 'updateContentVersion', 'default', 'false', 'boolean', NULL, 1),
  11 +
  12 +('tweaks', 'Always Force Original Filename on Checkin', 'When set to true, the checkbox for "Force Original Filename" will be hidden on check-in. This ensures that the filename will always stay the same.', 'disableForceFilenameOption', 'default', 'false', 'boolean', NULL, 1);
  13 +
  14 +UPDATE config_settings SET default_value = 'Add Company Name' WHERE group_name = 'ui' AND item = 'companyLogoTitle';
14 15 \ No newline at end of file
... ...
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 "> - {i18n}Filename:{/i18n} {$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  
... ...