From 9650f889b3dd6c2a51fc41dcb41cfdbb8fce9317 Mon Sep 17 00:00:00 2001 From: kevin_fourie Date: Sun, 28 Sep 2008 20:25:05 +0000 Subject: [PATCH] Merged in from STABLE trunk... --- plugins/ktcore/KTDocumentActions.php | 50 +++++++++++++++++++++++++++++++++++--------------- plugins/ktstandard/documentpreview/resources/container.css | 2 +- plugins/ktstandard/documentpreview/resources/preview.js | 2 +- plugins/rssplugin/loadFeed.inc.php | 2 +- plugins/rssplugin/rss2array.inc.php | 20 ++++++++++---------- plugins/rssplugin/templates/RSSPlugin/addfeed.smarty | 13 ------------- plugins/rssplugin/templates/RSSPlugin/editfeed.smarty | 13 ------------- search2.php | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- search2/indexing/indexerCore.inc.php | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------ search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php | 5 +---- search2/search/expr.inc.php | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------------------- search2/search/exprConstants.inc.php | 34 +++++++++++++++++++++++++--------- search2/search/fields/CreatedByField.inc.php | 37 ++++++++++++++++++++++++++++--------- search2/search/fields/FullPathField.inc.php | 37 ++++++++++++++++++++++++++++--------- search2/search/fields/TitleField.inc.php | 38 ++++++++++++++++++++++++++++---------- search2/search/search.inc.php | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- sql/mysql/install/data.sql | 7 ++++--- sql/mysql/upgrade/3.5.3/length_config_setting.sql | 13 +++++++------ templates/ktcore/search2/search_results.smarty | 68 ++++++++++++++++++++++++++++++++++++++++++++++---------------------- 19 files changed, 842 insertions(+), 367 deletions(-) diff --git a/plugins/ktcore/KTDocumentActions.php b/plugins/ktcore/KTDocumentActions.php index 06b3b68..5967bd1 100644 --- a/plugins/ktcore/KTDocumentActions.php +++ b/plugins/ktcore/KTDocumentActions.php @@ -599,7 +599,8 @@ class KTDocumentCheckInAction extends KTDocumentAction { $major_inc = sprintf('%d.%d', $this->oDocument->getMajorVersionNumber()+1, 0); $minor_inc = sprintf('%d.%d', $this->oDocument->getMajorVersionNumber(), $this->oDocument->getMinorVersionNumber()+1); - $oForm->setWidgets(array( + // Set the widgets for the form + $aWidgets = array( array('ktcore.widgets.file', array( 'label' => _kt('File'), '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 %s'), htmlentities($this->oDocument->getFilename(),ENT_QUOTES,'UTF-8')), @@ -618,14 +619,10 @@ class KTDocumentCheckInAction extends KTDocumentAction { 'description' => _kt('Please describe the changes you made to the document. Bear in mind that you can use a maximum of 250 characters.'), 'name' => 'reason', )), - array('ktcore.widgets.boolean',array( - 'label' => _kt('Force Original Filename'), - 'description' => sprintf(_kt('If this is checked, the uploaded document must have the same filename as the original: %s'), htmlentities($this->oDocument->getFilename(),ENT_QUOTES,'UTF-8')), - 'name' => 'forcefilename', - 'value' => true, - )), - )); - $oForm->setValidators(array( + ); + + // Set the validators for the widgets + $aValidators = array( array('ktcore.validators.string', array( 'test' => 'reason', 'max_length' => 250, @@ -639,12 +636,27 @@ class KTDocumentCheckInAction extends KTDocumentAction { 'test' => 'file', 'output' => 'file', )), - array('ktcore.validators.boolean', array( + ); + + // Add the "Force Original Filename" option if applicable + global $default; + if(!$default->disableForceFilenameOption){ + $aWidgets[] = array('ktcore.widgets.boolean',array( + 'label' => _kt('Force Original Filename'), + 'description' => sprintf(_kt('If this is checked, the uploaded document must have the same filename as the original: %s'), htmlentities($this->oDocument->getFilename(),ENT_QUOTES,'UTF-8')), + 'name' => 'forcefilename', + 'value' => true, + )); + + $aValidators[] = array('ktcore.validators.boolean', array( 'test' => 'forcefilename', 'output' => 'forcefilename', - )), - )); + )); + } + // Add widgets and validators to the form + $oForm->setWidgets($aWidgets); + $oForm->setValidators($aValidators); return $oForm; } @@ -669,8 +681,16 @@ class KTDocumentCheckInAction extends KTDocumentAction { $extra_errors = array(); - if ($data['forcefilename'] && ($data['file']['name'] != $this->oDocument->getFilename())) { - $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')); + // If the filename is different to the original check if "Force Original Filename" is set and return an error if it is. + $docFileName = $this->oDocument->getFilename(); + if($data['file']['name'] != $docFileName){ + global $default; + + if($default->disableForceFilenameOption){ + $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')); + }else if ($data['forcefilename']) { + $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')); + } } if (!empty($res['errors']) || !empty($extra_errors)) { @@ -679,7 +699,7 @@ class KTDocumentCheckInAction extends KTDocumentAction { $sReason = $data['reason']; - $sCurrentFilename = $this->oDocument->getFileName(); + $sCurrentFilename = $docFileName; $sNewFilename = $data['file']['name']; $aOptions = array(); diff --git a/plugins/ktstandard/documentpreview/resources/container.css b/plugins/ktstandard/documentpreview/resources/container.css index c59baa8..099ae4d 100644 --- a/plugins/ktstandard/documentpreview/resources/container.css +++ b/plugins/ktstandard/documentpreview/resources/container.css @@ -28,7 +28,7 @@ padding-bottom: 0px; cursor: pointer; height: 250px; - overflow: hidden; + overflow: auto; clear: both; } diff --git a/plugins/ktstandard/documentpreview/resources/preview.js b/plugins/ktstandard/documentpreview/resources/preview.js index f65a54c..f9703a1 100644 --- a/plugins/ktstandard/documentpreview/resources/preview.js +++ b/plugins/ktstandard/documentpreview/resources/preview.js @@ -15,7 +15,7 @@ var showInfo = function(iDocId, sUrl, sDir, loading){ modal: true, plain: false, width: 500, - height: 300, + height: 360, minWidth: 300, minHeight: 250 }); diff --git a/plugins/rssplugin/loadFeed.inc.php b/plugins/rssplugin/loadFeed.inc.php index 1fc89d2..12dd838 100644 --- a/plugins/rssplugin/loadFeed.inc.php +++ b/plugins/rssplugin/loadFeed.inc.php @@ -48,7 +48,7 @@ } // Check if the feed matches a url - if(!preg_match("/^http:\/\/([^\/]+)(.*)$/", $feed, $matches)){ + if(!preg_match("/^http[s]?:\/\/([^\/]+)(.*)$/", $feed, $matches)){ // If not, it is an internal feed $aRSSArray = KTrss::getInternalFeed($user); }else{ diff --git a/plugins/rssplugin/rss2array.inc.php b/plugins/rssplugin/rss2array.inc.php index 2d76129..5213541 100644 --- a/plugins/rssplugin/rss2array.inc.php +++ b/plugins/rssplugin/rss2array.inc.php @@ -7,31 +7,31 @@ * Document Management Made Simple * Copyright (C) 2008 KnowledgeTree Inc. * Portions copyright The Jam Warehouse Software (Pty) Limited - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the * Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, * California 94120-7775, or email info@knowledgetree.com. - * + * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. - * + * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by - * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * KnowledgeTree" logo and retain the original copyright notice. If the display of the * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices - * must display the words "Powered by KnowledgeTree" and retain the original + * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. * Contributor( s): ______________________________________ * @@ -62,7 +62,7 @@ # if the URL looks ok # - if(preg_match("/^http:\/\/([^\/]+)(.*)$/", $url, $matches)){ + if(preg_match("/^http[s]?:\/\/([^\/]+)(.*)$/", $url, $matches)){ $host = $matches[1]; $uri = $matches[2]; diff --git a/plugins/rssplugin/templates/RSSPlugin/addfeed.smarty b/plugins/rssplugin/templates/RSSPlugin/addfeed.smarty index 265e9c0..d3c5508 100644 --- a/plugins/rssplugin/templates/RSSPlugin/addfeed.smarty +++ b/plugins/rssplugin/templates/RSSPlugin/addfeed.smarty @@ -1,16 +1,3 @@ -{$context->oPage->requireJSResource("thirdpartyjs/tinymce/jscripts/tiny_mce/tiny_mce.js")} -{capture assign=sJS} -{literal} -tinyMCE.init({ - mode : "textareas", - theme : "simple", -}); -{/literal} -{/capture} -{$context->oPage->requireJSStandalone($sJS)} - - -

{i18n}New RSS Feed{/i18n}

{i18n}Create a rss feed which will be displayed on the dashboard{/i18n}

diff --git a/plugins/rssplugin/templates/RSSPlugin/editfeed.smarty b/plugins/rssplugin/templates/RSSPlugin/editfeed.smarty index 30c22ae..8ae155a 100644 --- a/plugins/rssplugin/templates/RSSPlugin/editfeed.smarty +++ b/plugins/rssplugin/templates/RSSPlugin/editfeed.smarty @@ -1,16 +1,3 @@ -{$context->oPage->requireJSResource("thirdpartyjs/tinymce/jscripts/tiny_mce/tiny_mce.js")} -{capture assign=sJS} -{literal} -tinyMCE.init({ - mode : "textareas", - theme : "simple", -}); -{/literal} -{/capture} -{$context->oPage->requireJSStandalone($sJS)} - - -

{i18n}Edit RSS Feed{/i18n}

{i18n}Edit a RSS feed{/i18n}

diff --git a/search2.php b/search2.php index a2e90fc..63b5378 100644 --- a/search2.php +++ b/search2.php @@ -35,15 +35,20 @@ * Contributor( s): ______________________________________ */ +// TODO: do we have to serialise/unserialise the results. this is not optimal!!! + session_start(); require_once("config/dmsDefaults.php"); +require_once(KT_DIR . '/search2/indexing/indexerCore.inc.php'); + require_once(KT_LIB_DIR . "/unitmanagement/Unit.inc"); require_once(KT_LIB_DIR . "/templating/templating.inc.php"); require_once(KT_LIB_DIR . "/dispatcher.inc.php"); require_once(KT_LIB_DIR . "/widgets/forms.inc.php"); require_once(KT_LIB_DIR . "/actions/bulkaction.php"); -require_once(KT_DIR . '/search2/search/search.inc.php'); + +require_once(KT_LIB_DIR . '/browse/DocumentCollection.inc.php'); require_once(KT_LIB_DIR . '/documentmanagement/Document.inc'); require_once(KT_LIB_DIR . '/browse/PartialQuery.inc.php'); @@ -117,7 +122,6 @@ function search2QuerySort($sSortColumn, $sSortOrder) return; } - $results = unserialize($_SESSION['search2_results']); usort($results, 'search2queryCompare'); @@ -132,38 +136,70 @@ function search2QuerySort($sSortColumn, $sSortOrder) */ class Search2Query extends PartialQuery { - function getFolderCount() { return 0; } + function _count($type) + { + $count = 0; + $results = unserialize($_SESSION['search2_results']); + + switch ($type) + { + case 'Document': + return count($results['docs']) + count($results['shortdocs']); + case 'Folder': + return count($results['folders']) + count($results['shortfolders']); + default: + return 0; + } + } + + function getFolderCount() + { + return $this->_count('Folder'); + } function getDocumentCount() { - $results = $_SESSION['search2_results']; - if(isset($results) && !empty($results)){ - return count(unserialize($results)); + return $this->_count('Document'); + } + + function getItems($type, $iStart, $iSize, $sSortColumn, $sSortOrder) + { + // TODO: quick hack. do this more optimally!!!! + $results = unserialize($_SESSION['search2_results']); + + switch ($type) + { + case 'Document': + $type = 'docs'; + break; + case 'Folder': + $type = 'folders'; + break; } - return 0; + + $resultArray = $results[$type]; + foreach($results['short' . $type] as $rec) + { + $resultArray[] = $rec; + } + + $resultArray = array_slice($resultArray, $iStart, $iSize); + $results = array(); + foreach($resultArray as $rec) + { + $results[] = array('id'=>$rec->Id); + } + + return $results; } function getFolders($iBatchSize, $iBatchStart, $sSortColumn, $sSortOrder, $sJoinClause = null, $aJoinParams = null) { - return array(); + return $this->getItems('Folder', $iBatchStart, $iBatchSize, $sSortColumn, $sSortOrder); } function getDocuments($iBatchSize, $iBatchStart, $sSortColumn, $sSortOrder, $sJoinClause = null, $aJoinParams = null) { - search2QuerySort($_GET['sort_on'], $_GET['sort_order']); - $results = unserialize($_SESSION['search2_results']); - - $batch = array(); - - $no_results = count($results); - for($i=0;$i<$no_results;$i++) - { - if ($i < $iBatchStart) continue; - if ($i > $iBatchStart + $iBatchSize) continue; - - $batch[] = array('id'=>$results[$i]->DocumentID); - } - - return $batch; + return $this->getItems('Document', $iBatchStart, $iBatchSize, $sSortColumn, $sSortOrder); } } @@ -208,10 +244,12 @@ class SearchDispatcher extends KTStandardDispatcher { { $expr = parseExpression($query); - $result = $expr->evaluate(); - usort($result, 'rank_compare'); + $results = $expr->evaluate(); + $results = resolveSearchShortcuts($results); - $_SESSION['search2_results'] = serialize($result); + usort($result['docs'], 'rank_compare'); + + $_SESSION['search2_results'] = serialize($results); $_SESSION['search2_query'] = $query; $_SESSION['search2_sort'] = 'rank'; @@ -410,6 +448,15 @@ class SearchDispatcher extends KTStandardDispatcher { $results = unserialize($_SESSION['search2_results']); + // NOTE: sorting results (when it is implemented) might have to be done per section, as it is done with the browse view + + $resultArray = $results['docs']; + foreach($results['folders'] as $f) $resultArray[] = $f; + foreach($results['shortdocs'] as $d) $resultArray[] = $d; + foreach($results['shortfolders'] as $f) $resultArray[] = $f; + + $results = $resultArray; + if (!is_array($results) || count($results) == 0) { $results=array(); diff --git a/search2/indexing/indexerCore.inc.php b/search2/indexing/indexerCore.inc.php index 84c9527..bcf91aa 100755 --- a/search2/indexing/indexerCore.inc.php +++ b/search2/indexing/indexerCore.inc.php @@ -41,17 +41,142 @@ define('SEARCH2_INDEXER_DIR',realpath(dirname(__FILE__)) . '/'); require_once('indexing/extractorCore.inc.php'); require_once(KT_DIR . '/plugins/ktcore/scheduler/schedulerUtil.php'); - class IndexerInconsistencyException extends Exception {}; +// TODO: Query Result Items code should be moved into the Search section. It has less to do with indexing... + class QueryResultItem { - protected $document_id; + protected $id; protected $title; protected $rank; protected $text; - protected $filesize; protected $fullpath; + + public function __construct($id, $title, $rank, $text, $fullpath) + { + $this->id = $id; + $this->title = $title; + $this->rank = $rank; + $this->text = $text; + $this->fullpath = $fullpath; + } + + public function getId() { return $this->id; } + + public function getIsProxy() { return $this instanceof ProxyResultItem; } + public function getIsFolder() { return substr(get_class($this), 0, 6) == 'Folder' ; } + public function getIsDocument() { return substr(get_class($this), 0, 8) == 'Document' ; } + + public function setRank($value) + { + $this->rank = number_format($value,2,'.',','); + } + + public function getIsLive() + { + return true; + } + + public function setTitle($value) + { + $this->title = $value; + } + + public function setText($value) + { + $this->text = $value; + } + + public function getRelevance() { return (float) $this->rank; } + public function getRank() { return $this->getRelevance(); } + public function getText() { return (string) $this->text; } + public function getTitle() { return (string) $this->title; } + public function getFullPath() { return (string) $this->fullpath; } + + protected function __get($property) + { + if (empty($property)) + { + return ''; + } + + $method = 'get' . $property; + if (method_exists($this, $method)) + { + return $this->$method(); + } + return $this->getUnknown(); + } + + protected function getUnknown() + { + return _kt('n/a'); + } + + protected function __set($property, $value) + { + if (empty($property)) + { + return ''; + } + + $method = 'set' . $property; + if (method_exists($this, $method)) + { + return $this->$method($value); + } + throw new Exception("Unknown property '$property' to set on QueryResultItem"); + } +} + +class ProxyResultItem extends QueryResultItem +{ + protected $proxy; + protected $proxyId; + + public function __construct($proxyId, $proxy) + { + parent::__construct($proxyId, $proxy->getTitle, $proxy->getRank(), $proxy->getText(), $proxy->getFullPath()); + $this->proxyId = $proxyId; + $this->proxy = $proxy; + } + + public function getId() { return $this->proxyId; } + public function getTitle() { return $this->proxy->getTitle(); } + public function getRealId() { return $this->proxy->getId(); } + + protected function __get($property) + { + $method = 'get' . $property; + + if (method_exists($this, $method)) + { + return $this->$method(); + } + else + { + return $this->proxy->$method(); + } + } + + protected function __set($property, $value) + { + $method = 'set' . $property; + if (method_exists($this, $method)) + { + return $this->$method($value); + } + else + { + return $this->proxy->$method($value); + } + } +} + +class DocumentResultItem extends QueryResultItem +{ + protected $filesize; protected $live; protected $version; protected $mimeType; @@ -78,17 +203,14 @@ class QueryResultItem protected $mimeDisplay; protected $oemDocumentNo; - public function __construct($document_id, $rank=null, $title=null, $text=null) + public function __construct($document_id, $rank=null, $title=null, $text=null, $fullpath = null) { - $this->document_id=(int) $document_id; - $this->rank= $rank; - $this->title=$title; - $this->text = $text; + parent::__construct($document_id, $title, $rank, $text, $fullpath); $this->live = true; $this->loadDocumentInfo(); } - protected function __isset($property) + /*protected function __isset($property) { switch($property) { @@ -101,8 +223,9 @@ class QueryResultItem throw new Exception("Unknown property '$property' to get on QueryResultItem"); } return true; // should not be reached - } + }*/ + // TODO: this is bad. must refactor to do the query on the group of documents. public function loadDocumentInfo() { global $default; @@ -127,7 +250,7 @@ class QueryResultItem LEFT JOIN users cbu ON d.creator_id=cbu.id LEFT JOIN users ou ON d.owner_id=ou.id WHERE - d.id=$this->document_id"; + d.id=$this->id"; $result = DBUtil::getOneResult($sql); @@ -186,107 +309,116 @@ class QueryResultItem $this->mimeType = $result['mimetype']; $this->mimeIconPath = $result['mime_icon_path']; + if (empty($this->mimeIconPath)) + { + $this->mimeIconPath = 'unspecified_type'; + } $this->mimeDisplay = $result['mime_display']; $this->storagePath = $result['storage_path']; $this->status = Document::getStatusString($result['status_id']); } - protected function __get($property) - { - switch($property) - { - case null: return ''; - case 'DocumentID': return (int) $this->document_id; - case 'Relevance': - case 'Rank': return (float) $this->rank; - case 'Text': return (string) $this->text; - case 'Title': return (string) $this->title; - case 'FullPath': return (string) $this->fullpath; - case 'IsLive': return (bool) $this->live; - case 'Filesize': return $this->filesize; - case 'Version': return (string) $this->version; - case 'Filename': return (string)$this->filename; - case 'FolderId': return (int)$this->folderId; - case 'OemDocumentNo': return (string) $this->oemDocumentNo; - case 'Document': - if (is_null($this->document)) + public function getDocumentID() { return $this->getId(); } + public function getIsLive() { return (bool) $this->live; } + public function getFilesize() { return $this->filesize; } + public function getVersion() { return (string) $this->version; } + public function getFilename() { return (string)$this->filename; } + public function getFolderId() { return (int)$this->folderId; } + public function getOemDocumentNo() { return (string) $this->oemDocumentNo; } + public function getDocument() { if (is_null($this->document)) { - $this->document = Document::get($this->document_id); + $this->document = Document::get($this->id); } - return $this->document; - case 'IsAvailable': - return $this->Document->isLive(); - case 'CheckedOutUser': - case 'CheckedOutBy': - return (string) $this->checkedOutUser; - case 'WorkflowOnly': - case 'Workflow': - return (string)$this->workflow; - case 'WorkflowStateOnly': - case 'WorkflowState': - return (string)$this->workflowState; - case 'WorkflowAndState': - if (is_null($this->workflow)) + return $this->document; } + public function getIsAvailable() { return $this->Document->isLive(); } + public function getCheckedOutUser() { return (string) $this->checkedOutUser; } + public function getCheckedOutByr() { return $this->getCheckedOutUser(); } + public function getWorkflowOnly() { return (string)$this->workflow; } + public function getWorkflow() { return $this->getWorkflow(); } + public function getWorkflowStateOnly() { return (string)$this->workflowState; } + public function getWorkflowState() { return $this->getWorkflowStateOnly(); } + public function getWorkflowAndState() { if (is_null($this->workflow)) { return ''; } - return "$this->workflow - $this->workflowState"; - case 'MimeType': - return (string) $this->mimeType; - case 'MimeIconPath': - return (string) $this->mimeIconPath; - case 'MimeDisplay': - return (string) $this->mimeDisplay; - case 'DateCheckedOut': - return (string) $this->dateCheckedout; - case 'ModifiedBy': - return (string) $this->modifiedBy; - case 'DateModified': - return (string) $this->dateModified; - case 'CreatedBy': - return (string) $this->createdBy; - case 'DateCreated': - return (string) $this->dateCreated; - case 'Owner': - case 'OwnedBy': - return (string) $this->owner; - case 'IsImmutable': - case 'Immutable': - return (bool) $this->immutable; - case 'Status': - return $this->status; - case 'StoragePath': - return $this->storagePath; - case 'DocumentType': - return $this->documentType; - case 'Permissions': - return 'not available'; - case 'CanBeReadByUser': - if (!$this->live) + return "$this->workflow - $this->workflowState"; } + public function getMimeType() { return (string) $this->mimeType; } + public function getMimeIconPath() { return (string) $this->mimeIconPath; } + public function getMimeDisplay() { return (string) $this->mimeDisplay; } + public function getDateCheckedOut() { return (string) $this->dateCheckedout; } + public function getModifiedBy() { return (string) $this->modifiedBy; } + public function getDateModified() { return (string) $this->dateModified; } + public function getCreatedBy() { return (string) $this->createdBy; } + public function getDateCreated() { return (string) $this->dateCreated; } + public function getOwner() { return (string) $this->owner; } + public function getOwnedBy() { return $this->getOwner(); } + public function getIsImmutable() { return (bool) $this->immutable; } + public function getImmutable() { return $this->getIsImmutable(); } + public function getStatus() { return $this->status; } + public function getStoragePath() { return $this->storagePath; } + public function getDocumentType() { return $this->documentType; } + public function getPermissions() { return 'not available'; } + public function getCanBeReadByUser() { if (!$this->live) return false; if (Permission::userHasDocumentReadPermission($this->Document)) return true; if (Permission::adminIsInAdminMode()) return true; - return false; - default: - throw new Exception("Unknown property '$property' to get on QueryResultItem"); - } - return ''; // Should not be reached + return false; } + + +} + +class FolderResultItem extends QueryResultItem +{ + protected $folder; + protected $createdBy; + protected $parentId; + + public function __construct($folder_id, $rank=null, $title=null, $text=null, $fullpath = null) + { + parent::__construct($folder_id, $title, $rank, $text, $fullpath); + $this->loadDocumentInfo(); } - protected function __set($property, $value) + public function getFolderID() { return $this->getId(); } + public function getParentID() { return $this->parentId; } + public function getCreatedBy() { return $this->createdBy; } + public function getMimeIconPath() { return 'folder'; } + public function getFolder() { + return $this->folder; } + + public function loadDocumentInfo() { - switch($property) + global $default; + $this->folder = $folder = Folder::get($this->getFolderID()); + if (PEAR::isError($folder)) { - case 'Rank': $this->rank = number_format($value,2,'.',','); break; - case 'Title': $this->title = $value; break; - case 'Text': $this->text = $value; break; - default: - throw new Exception("Unknown property '$property' to set on QueryResultItem"); + throw new Exception('Database exception! There appears to be an error in the system: ' .$result->getMessage()); } + $this->title = $folder->getName(); + $this->fullpath = '/' . $folder->getFullPath(); + $this->parentId = $folder->getParentId(); + + $user = User::get($folder->getCreatorID()); + $this->createdBy = (PEAR::isError($user))?_kt('Unknown'):$user->getName(); } + +} + +class DocumentShortcutResultItem extends ProxyResultItem +{ + public function getDocumentID() { return $this->getId(); } + public function getMimeIconPath() { return $this->proxy->getMimeIconPath() . '_shortcut'; } + +} + +class FolderShortcutResultItem extends ProxyResultItem +{ + public function getFolderID() { return $this->getId(); } + public function getMimeIconPath() { return 'folder_shortcut'; } + } function MatchResultCompare($a, $b) diff --git a/search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php b/search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php index d95a5cb..b117085 100755 --- a/search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php +++ b/search2/indexing/indexers/JavaXMLRPCLuceneIndexer.inc.php @@ -187,10 +187,7 @@ class JavaXMLRPCLuceneIndexer extends Indexer { try { - $item = new QueryResultItem($document_id); - $item->Title = $hit->Title; - $item->Text = $hit->Content; - $item->Rank = $hit->Rank; + $item = new DocumentResultItem($document_id, $hit->Rank, $hit->Title, $hit->Content); if ($item->CanBeReadByUser) { diff --git a/search2/search/expr.inc.php b/search2/search/expr.inc.php index 6f7a293..6881d6b 100755 --- a/search2/search/expr.inc.php +++ b/search2/search/expr.inc.php @@ -43,6 +43,8 @@ * */ +// TODO: search expression evaluation needs some optimisation + require_once('indexing/indexerCore.inc.php'); require_once('search/fieldRegistry.inc.php'); require_once('search/exprConstants.inc.php'); @@ -157,11 +159,28 @@ class Expr protected $expr_id; + protected $context; + public function __construct() { $this->expr_id = Expr::$node_id++; } + public function appliesToContext() + { + return ExprContext::DOCUMENT; + } + + public function setContext($context) + { + $this->context = $context; + } + + public function getContext() + { + return $this->context; + } + public function getExprId() { return $this->expr_id; @@ -363,7 +382,7 @@ class FieldExpr extends Expr */ public function __toString() { - return $this->alias; + return $this->display; } public function toViz(&$str, $phase) @@ -406,6 +425,14 @@ class FieldExpr extends Expr } } +class ExprContext +{ + const DOCUMENT = 1; + const FOLDER = 2; + const DOCUMENT_AND_FOLDER = 3; +} + + class DBFieldExpr extends FieldExpr { /** @@ -966,24 +993,43 @@ class SQLQueryBuilder implements QueryBuilder private $sql; private $db; private $metadata; + private $context; - public function __construct() + public function __construct($context) { - $this->used_tables = array( - 'documents'=>1, - 'document_metadata_version'=>1, - 'document_content_version'=>0, - 'tag_words'=>0, - 'document_fields_link'=>0 - ); - - $this->aliases = array( - 'documents'=>'d', - 'document_metadata_version'=>'dmv', - 'document_content_version'=>'dcv', - 'tag_words'=>'tw', - 'document_fields_link'=>'pdfl' - ); + $this->context = $context; + + switch ($context) + { + case ExprContext::DOCUMENT: + $this->used_tables = array( + 'documents'=>1, + 'document_metadata_version'=>1, + 'document_content_version'=>0, + 'tag_words'=>0, + 'document_fields_link'=>0 + ); + + $this->aliases = array( + 'documents'=>'d', + 'document_metadata_version'=>'dmv', + 'document_content_version'=>'dcv', + 'tag_words'=>'tw', + 'document_fields_link'=>'pdfl' + ); + break; + case ExprContext::FOLDER: + $this->used_tables = array( + 'folders'=>1, + ); + + $this->aliases = array( + 'folders'=>'f', + ); + break; + default: + throw new Exception('This was not expected - Context = ' . $context); + } $this->sql = ''; $this->db = array(); @@ -1013,8 +1059,15 @@ class SQLQueryBuilder implements QueryBuilder } elseif ($expr->isDBExpr()) { - $this->db[] = & $parent; - $this->used_tables[$expr->getTable()]++; + if (($this->context & $expr->appliesToContext()) == $this->context) + { + $this->db[] = & $parent; + $tablename = $expr->getTable(); + if (array_key_exists($tablename, $this->used_tables)) + { + $this->used_tables[$tablename]++; + } + } } elseif ($expr->isOpExpr()) { @@ -1042,12 +1095,16 @@ class SQLQueryBuilder implements QueryBuilder if ($field->isMetadataField()) { - $this->metadata[] = $expr->getParent(); + $this->metadata[] = $expr->getParent(); } elseif ($field->isDBExpr()) { - $this->db[] = $expr->getParent(); - $this->used_tables[$field->getTable()]++; + $this->db[] = $expr->getParent(); + $tablename = $field->getTable(); + if (array_key_exists($tablename, $this->used_tables)) + { + $this->used_tables[$tablename]++; + } } } } @@ -1076,29 +1133,40 @@ class SQLQueryBuilder implements QueryBuilder $left = $expr->left(); $right = $expr->right(); $isNot = $expr->not(); - if ($left->isMetadataField()) + if ($left->isMetadataField() ) { - $offset = $this->resolveMetadataOffset($expr) + 1; - - $fieldset = $left->getField(); - $query = '('; - - if ($isNot) - { - $query .= "df$offset.name IS NULL OR "; - } - - $query .= '(' . "df$offset.name='$fieldset' AND " . $right->getSQL($left, "dfl$offset.value", $expr->op(), $isNot) . ')'; - - - $query .= ')'; - + if ($this->context == ExprContext::DOCUMENT) + { + $offset = $this->resolveMetadataOffset($expr) + 1; + + $fieldset = $left->getField(); + $query = '('; + + if ($isNot) + { + $query .= "df$offset.name IS NULL OR "; + } + + $query .= '(' . "df$offset.name='$fieldset' AND " . $right->getSQL($left, "dfl$offset.value", $expr->op(), $isNot) . ')'; + + $query .= ')'; + } + else + { + $query = 'false'; + } } else { - $fieldname = $this->getFieldnameFromExpr($expr); - - $query = $right->getSQL($left, $left->modifyName($fieldname), $expr->op(), $isNot); + if ($this->context == ExprContext::FOLDER && $left->getContext() != ExprContext::FOLDER) + { + $query = 'false'; + } + else + { + $fieldname = $this->getFieldnameFromExpr($expr); + $query = $right->getSQL($left, $left->modifyName($fieldname), $expr->op(), $isNot); + } } return $query; } @@ -1107,17 +1175,30 @@ class SQLQueryBuilder implements QueryBuilder { if (count($this->metadata) + count($this->db) == 0) { - throw new Exception('nothing to do'); + // return empty result set + return 'select 1 from documents where false'; } - // we are doing this because content table is dependant on metadata table - if ($this->used_tables['document_content_version'] > 0) $this->used_tables['document_metadata_version']++; - $sql = 'SELECT ' . "\n"; - $sql .= + if ($this->context == ExprContext::DOCUMENT) + { + // we are doing this because content table is dependant on metadata table + if ($this->used_tables['document_content_version'] > 0) + { + $this->used_tables['document_metadata_version']++; + } + + $sql .= ' DISTINCT d.id, dmv.name as title'; + } + else + { + $sql .= + ' DISTINCT f.id, f.name as title'; + } + $offset=0; foreach($this->db as $expr) @@ -1133,26 +1214,36 @@ class SQLQueryBuilder implements QueryBuilder } $sql .= - "\n" . 'FROM ' ."\n" . - ' documents d ' ."\n"; + "\n" . 'FROM ' ."\n"; - if ($this->used_tables['document_metadata_version'] > 0) - { - $sql .= ' INNER JOIN document_metadata_version dmv ON d.metadata_version_id=dmv.id' . "\n"; - } - if ($this->used_tables['document_content_version'] > 0) - { - $sql .= ' INNER JOIN document_content_version dcv ON dmv.content_version_id=dcv.id ' . "\n"; - } - if ($this->used_tables['document_fields_link'] > 0) + if ($this->context == ExprContext::DOCUMENT) { - $sql .= ' LEFT JOIN document_fields_link pdfl ON dmv.id=pdfl.metadata_version_id ' . "\n"; - } + $primaryAlias = 'd'; + $sql .= ' documents d ' ."\n"; - if ($this->used_tables['tag_words'] > 0) + if ($this->used_tables['document_metadata_version'] > 0) + { + $sql .= ' INNER JOIN document_metadata_version dmv ON d.metadata_version_id=dmv.id' . "\n"; + } + if ($this->used_tables['document_content_version'] > 0) + { + $sql .= ' INNER JOIN document_content_version dcv ON dmv.content_version_id=dcv.id ' . "\n"; + } + if ($this->used_tables['document_fields_link'] > 0) + { + $sql .= ' LEFT JOIN document_fields_link pdfl ON dmv.id=pdfl.metadata_version_id ' . "\n"; + } + + if ($this->used_tables['tag_words'] > 0) + { + $sql .= ' LEFT OUTER JOIN document_tags dt ON dt.document_id=d.id ' . "\n" . + ' LEFT OUTER JOIN tag_words tw ON dt.tag_id = tw.id ' . "\n"; + } + } + else { - $sql .= ' LEFT OUTER JOIN document_tags dt ON dt.document_id=d.id ' . "\n" . - ' LEFT OUTER JOIN tag_words tw ON dt.tag_id = tw.id ' . "\n"; + $primaryAlias = 'f'; + $sql .= ' folders f ' ."\n"; } $offset = 0; @@ -1171,17 +1262,18 @@ class SQLQueryBuilder implements QueryBuilder $offset++; } - - - $offset=0; - foreach($this->metadata as $expr) + if ($this->context == ExprContext::DOCUMENT) { - $offset++; - $field = $expr->left(); + $offset=0; + foreach($this->metadata as $expr) + { + $offset++; + $field = $expr->left(); - $fieldid = $field->getFieldId(); - $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"; - $sql .= " LEFT JOIN document_fields df$offset ON df$offset.id=dfl$offset.document_field_id" . "\n"; + $fieldid = $field->getFieldId(); + $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"; + $sql .= " LEFT JOIN document_fields df$offset ON df$offset.id=dfl$offset.document_field_id" . "\n"; + } } // Add permissions sql for read access @@ -1189,15 +1281,16 @@ class SQLQueryBuilder implements QueryBuilder $permId = $oPermission->getID(); $oUser = User::get($_SESSION['userID']); $aPermissionDescriptors = KTPermissionUtil::getPermissionDescriptorsForUser($oUser); - $sPermissionDescriptors = (!empty($aPermissionDescriptors)) ? implode(',', $aPermissionDescriptors) : ''; + $sPermissionDescriptors = empty($aPermissionDescriptors)? -1: implode(',', $aPermissionDescriptors); - $sql .= 'LEFT JOIN folders f ON d.folder_id = f.id '. "\n"; - - $sql .= 'INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id '. "\n"; + $sql .= "INNER JOIN permission_lookups AS PL ON $primaryAlias.permission_lookup_id = PL.id\n"; $sql .= 'INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = '.$permId. " \n"; + $sql .= "WHERE PLA.permission_descriptor_id IN ($sPermissionDescriptors) AND "; - $sql .= "WHERE PLA.permission_descriptor_id IN ($sPermissionDescriptors) AND dmv.status_id=1 AND d.status_id=1 AND \n "; - + if ($this->context == ExprContext::DOCUMENT) + { + $sql .= "dmv.status_id=1 AND d.status_id=1 AND \n "; + } return $sql; } @@ -1222,8 +1315,6 @@ class SQLQueryBuilder implements QueryBuilder private function resolveJoinOffset($expr) { - - $offset=0; foreach($this->db as $item) { @@ -1242,11 +1333,18 @@ class SQLQueryBuilder implements QueryBuilder $right = $expr->right(); if (DefaultOpCollection::isBoolean($expr)) { - $query = '(' . $this->buildCoreSQLExpr($left) . ' ' . $expr->op() . ' ' . $this->buildCoreSQLExpr($right) . ')'; + $query = '(' . $this->buildCoreSQLExpr($left) . ' ' . $expr->op() . ' ' . $this->buildCoreSQLExpr($right) . ')'; } else { - $query = $this->getSQLEvalExpr($expr); + if (($this->context & $expr->appliesToContext()) == $this->context) + { + $query = $this->getSQLEvalExpr($expr); + } + else + { + $query = 'false'; + } } return $query; @@ -1259,7 +1357,11 @@ class SQLQueryBuilder implements QueryBuilder $sql = $this->buildCoreSQL(); - $sql .= $this->buildCoreSQLExpr($expr); + $expr = $this->buildCoreSQLExpr($expr); + if ($expr != 'false') + { + $sql .= $expr; + } return $sql; } @@ -1299,19 +1401,22 @@ class SQLQueryBuilder implements QueryBuilder $sql .= $value->getSQL($field, $left->modifyName($fieldname), $expr->op(), $expr->not()); } - $moffset=0; - foreach($this->metadata as $expr) + if ($this->context == ExprContext::DOCUMENT) { - $moffset++; - if ($offset++) + $moffset=0; + foreach($this->metadata as $expr) { - $sql .= " $op\n " ; - } + $moffset++; + if ($offset++) + { + $sql .= " $op\n " ; + } - $field = $expr->left(); - $value = $expr->right(); + $field = $expr->left(); + $value = $expr->right(); - $sql .= $value->getSQL($field, "dfl$moffset.value", $expr->getOp()); + $sql .= $value->getSQL($field, "dfl$moffset.value", $expr->getOp()); + } } return $sql; @@ -1331,7 +1436,6 @@ class SQLQueryBuilder implements QueryBuilder if (substr($col, 0, 4) == 'expr' && is_numeric(substr($col, 4))) { - $exprno = substr($col, 4); if ($exprno <= count($this->db)) { @@ -1379,8 +1483,6 @@ class SQLQueryBuilder implements QueryBuilder } return '(' . implode(') AND (', $text) . ')'; } - - } @@ -1444,6 +1546,13 @@ class OpExpr extends Expr $this->has_text=$value; } + public function setContext($context) + { + parent::setContext($context); + $this->left()->setContext($context); + $this->right()->setContext($context); + } + public function getHasDb() { return $this->has_db; @@ -1619,7 +1728,6 @@ class OpExpr extends Expr $point = null; - if ($left_op_match && $right_op_match) { $point = 'point'; } $left_op_match_flex = $left_op_match || ($left->isOpExpr()); @@ -1682,6 +1790,11 @@ class OpExpr extends Expr return $this->getHasDb() && $this->getHasText(); } + public function appliesToContext() + { + return $this->left()->appliesToContext() | $this->right()->appliesToContext(); + } + /** * Enter description here... * @@ -1759,13 +1872,14 @@ class OpExpr extends Expr */ public function __toString() { - $expr = $this->left_expr . ' ' . $this->op .' ' . $this->right_expr; + // _kt may not translate well here. + $expr = $this->left_expr . ' ' . _kt($this->op) .' ' . $this->right_expr; if (is_null($this->parent)) { if ($this->not()) { - $expr = "NOT $expr"; + $expr = _kt('NOT') . $expr; } return $expr; } @@ -1805,7 +1919,7 @@ class OpExpr extends Expr * @param array $rightres * @return array */ - protected static function intersect($leftres, $rightres) + protected static function _intersect($leftres, $rightres) { if (empty($leftres) || empty($rightres)) { @@ -1814,7 +1928,7 @@ class OpExpr extends Expr $result = array(); foreach($leftres as $item) { - $document_id = $item->DocumentID; + $document_id = $item->Id; if (!$item->IsLive) { @@ -1831,6 +1945,22 @@ class OpExpr extends Expr return $result; } + protected static function intersect($leftres, $rightres) + { + return array( + 'docs'=>self::_intersect($leftres['docs'],$rightres['docs']), + 'folders'=>self::_intersect($leftres['folders'],$rightres['folders']) + ); + } + + protected static function union($leftres, $rightres) + { + return array( + 'docs'=>self::_union($leftres['docs'],$rightres['docs']), + 'folders'=>self::_union($leftres['folders'],$rightres['folders']) + ); + } + /** * The objective of this function is to merge the results so that there is a union of the results, * but there should be no duplicates. @@ -1839,7 +1969,7 @@ class OpExpr extends Expr * @param array $rightres * @return array */ - protected static function union($leftres, $rightres) + protected static function _union($leftres, $rightres) { if (empty($leftres)) { @@ -1855,15 +1985,15 @@ class OpExpr extends Expr { if ($item->IsLive) { - $result[$item->DocumentID] = $item; + $result[$item->Id] = $item; } } foreach($rightres as $item) { - if (!array_key_exists($item->DocumentID, $result) || $item->Rank > $result[$item->DocumentID]->Rank) + if (!array_key_exists($item->Id, $result) || $item->Rank > $result[$item->Id]->Rank) { - $result[$item->DocumentID] = $item; + $result[$item->Id] = $item; } } return $result; @@ -1996,7 +2126,7 @@ class OpExpr extends Expr { if (empty($group)) { return array(); } - $exprbuilder = new SQLQueryBuilder(); + $exprbuilder = new SQLQueryBuilder($this->getContext()); if (count($group) == 1) { @@ -2020,11 +2150,18 @@ class OpExpr extends Expr foreach($rs as $item) { - $document_id = $item['id']; + $id = $item['id']; $rank = $exprbuilder->getRanking($item); - if (!array_key_exists($document_id, $results) || $rank > $results[$document_id]->Rank) + if (!array_key_exists($id, $results) || $rank > $results[$id]->Rank) { - $results[$document_id] = new QueryResultItem($document_id, $rank, $item['title'], $exprbuilder->getResultText($item)); + if ($this->context == ExprContext::DOCUMENT) + { + $results[$id] = new DocumentResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item)); + } + else + { + $results[$id] = new FolderResultItem($id, $rank, $item['title'], $exprbuilder->getResultText($item)); + } } } @@ -2034,7 +2171,10 @@ class OpExpr extends Expr private function exec_text_query($op, $group) { - if (empty($group)) { return array(); } + if (($this->getContext() != ExprContext::DOCUMENT) || empty($group)) + { + return array(); + } $exprbuilder = new TextQueryBuilder(); @@ -2060,12 +2200,21 @@ class OpExpr extends Expr } return $results; - - } - public function evaluate() + public function evaluate($context = ExprContext::DOCUMENT_AND_FOLDER) { + if ($context == ExprContext::DOCUMENT_AND_FOLDER) + { + $docs = $this->evaluate(ExprContext::DOCUMENT); + $folders = $this->evaluate(ExprContext::FOLDER); + + return array( + 'docs' => $docs['docs'], + 'folders' => $folders['folders']); + } + $this->setContext($context); + $left = $this->left(); $right = $this->right(); $op = $this->op(); @@ -2075,12 +2224,12 @@ class OpExpr extends Expr { $point = 'point'; } + $resultContext = ($this->getContext() == ExprContext::DOCUMENT)?'docs':'folders'; if ($point == 'merge') { - - $leftres = $left->evaluate(); - $rightres = $right->evaluate(); + $leftres = $left->evaluate($context); + $rightres = $right->evaluate($context); switch ($op) { case ExprOp::OP_AND: @@ -2099,31 +2248,33 @@ class OpExpr extends Expr { if ($this->isDBonly()) { - $result = $this->exec_db_query($op, array($this)); + $result[$resultContext] = $this->exec_db_query($op, array($this)); } elseif ($this->isTextOnly()) { - $result = $this->exec_text_query($op, array($this)); + $result[$resultContext] = $this->exec_text_query($op, array($this)); } elseif (in_array($op, array(ExprOp::OP_OR, ExprOp::OP_AND))) { + // do we get to this??? + // TODO: remove me please.... the simpleQuery stuff should go??? $db_group = array(); $text_group = array(); $this->explore($left, $right, $db_group, 'db'); $this->explore($left, $right, $text_group, 'text'); - $db_result = $this->exec_db_query($op, $db_group); - $text_result = $this->exec_text_query($op, $text_group); + $db_result[$resultContext] = $this->exec_db_query($op, $db_group); + $text_result[$resultContext] = $this->exec_text_query($op, $text_group); switch ($op) { case ExprOp::OP_AND: if ($this->debug) print "\n\npoint: intersect\n\n"; - $result = OpExpr::intersect($db_result, $text_result); + $result[$resultContext] = OpExpr::intersect($db_result, $text_result); break; case ExprOp::OP_OR: if ($this->debug) print "\n\nmerge: union\n\n"; - $result = OpExpr::union($db_result, $text_result); + $result[$resultContext] = OpExpr::union($db_result, $text_result); break; default: throw new Exception('how did this happen??'); @@ -2141,9 +2292,9 @@ class OpExpr extends Expr } $permResults = array(); - foreach($result as $idx=>$item) + foreach($result[$resultContext] as $idx=>$item) { - $permResults[$idx] = $item; + $permResults[$resultContext][$idx] = $item; } return $permResults; diff --git a/search2/search/exprConstants.inc.php b/search2/search/exprConstants.inc.php index 34ed08d..b67efdb 100755 --- a/search2/search/exprConstants.inc.php +++ b/search2/search/exprConstants.inc.php @@ -6,31 +6,31 @@ * Document Management Made Simple * Copyright (C) 2008 KnowledgeTree Inc. * Portions copyright The Jam Warehouse Software (Pty) Limited - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the * Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, * California 94120-7775, or email info@knowledgetree.com. - * + * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. - * + * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by - * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * KnowledgeTree" logo and retain the original copyright notice. If the display of the * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices - * must display the words "Powered by KnowledgeTree" and retain the original + * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. * Contributor( s): ______________________________________ * @@ -51,6 +51,22 @@ class ExprOp const OP_OR = 'OR'; const IS_NOT = 'is not'; + + private static + function init() + { + // this isn't really called, but used to help with translation. + _kt('is'); + _kt('contains'); + _kt('between'); + _kt('start with'); + _kt('ends with'); + _kt('like'); + _kt('AND'); + _kt('OR'); + _kt('is not'); + } + } diff --git a/search2/search/fields/CreatedByField.inc.php b/search2/search/fields/CreatedByField.inc.php index 3d872c5..ab930a3 100755 --- a/search2/search/fields/CreatedByField.inc.php +++ b/search2/search/fields/CreatedByField.inc.php @@ -7,31 +7,31 @@ * Document Management Made Simple * Copyright (C) 2008 KnowledgeTree Inc. * Portions copyright The Jam Warehouse Software (Pty) Limited - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the * Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, * California 94120-7775, or email info@knowledgetree.com. - * + * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. - * + * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by - * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * KnowledgeTree" logo and retain the original copyright notice. If the display of the * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices - * must display the words "Powered by KnowledgeTree" and retain the original + * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. * Contributor( s): ______________________________________ * @@ -47,6 +47,25 @@ class CreatedByField extends DBFieldExpr $this->matchField('name'); } + public function appliesToContext() + { + return ExprContext::DOCUMENT | ExprContext::FOLDER; + } + + public function setContext($context) + { + parent::setContext($context); + switch($context) + { + case ExprContext::DOCUMENT: + $this->table = 'documents'; + break; + case ExprContext::FOLDER: + $this->table = 'folders'; + break; + } + } + public function getInputRequirements() { return array('value'=>array('type'=>FieldInputType::USER_LIST)); diff --git a/search2/search/fields/FullPathField.inc.php b/search2/search/fields/FullPathField.inc.php index 28665bc..b758b06 100644 --- a/search2/search/fields/FullPathField.inc.php +++ b/search2/search/fields/FullPathField.inc.php @@ -7,31 +7,31 @@ * Document Management Made Simple * Copyright (C) 2008 KnowledgeTree Inc. * Portions copyright The Jam Warehouse Software (Pty) Limited - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the * Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, * California 94120-7775, or email info@knowledgetree.com. - * + * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. - * + * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by - * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * KnowledgeTree" logo and retain the original copyright notice. If the display of the * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices - * must display the words "Powered by KnowledgeTree" and retain the original + * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. * Contributor( s): ______________________________________ * @@ -47,6 +47,25 @@ class FullPathField extends DBFieldExpr $this->setAlias('FullPath'); } + public function setContext($context) + { + parent::setContext($context); + switch($context) + { + case ExprContext::DOCUMENT: + $this->table = 'documents'; + break; + case ExprContext::FOLDER: + $this->table = 'folders'; + break; + } + } + + public function appliesToContext($context) + { + return ExprContext::DOCUMENT | ExprContext::FOLDER; + } + public function getInputRequirements() { return array('value'=>array('type'=>FieldInputType::TEXT)); diff --git a/search2/search/fields/TitleField.inc.php b/search2/search/fields/TitleField.inc.php index 6cda879..abddf9f 100755 --- a/search2/search/fields/TitleField.inc.php +++ b/search2/search/fields/TitleField.inc.php @@ -7,31 +7,31 @@ * Document Management Made Simple * Copyright (C) 2008 KnowledgeTree Inc. * Portions copyright The Jam Warehouse Software (Pty) Limited - * + * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 3 as published by the * Free Software Foundation. - * + * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, * California 94120-7775, or email info@knowledgetree.com. - * + * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU General Public License version 3. - * + * * In accordance with Section 7(b) of the GNU General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by - * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * KnowledgeTree" logo and retain the original copyright notice. If the display of the * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices - * must display the words "Powered by KnowledgeTree" and retain the original + * must display the words "Powered by KnowledgeTree" and retain the original * copyright notice. * Contributor( s): ______________________________________ * @@ -39,13 +39,31 @@ class TitleField extends DBFieldExpr { - public function __construct() { parent::__construct('name', 'document_metadata_version', _kt('Title')); $this->setAlias('Title'); } + public function setContext($context) + { + parent::setContext($context); + switch($context) + { + case ExprContext::DOCUMENT: + $this->table = 'document_metadata_version'; + break; + case ExprContext::FOLDER: + $this->table = 'folders'; + break; + } + } + + public function appliesToContext() + { + return ExprContext::DOCUMENT | ExprContext::FOLDER; + } + public function getInputRequirements() { return array('value'=>array('type'=>FieldInputType::TEXT)); diff --git a/search2/search/search.inc.php b/search2/search/search.inc.php index a36db8d..f9a6e44 100755 --- a/search2/search/search.inc.php +++ b/search2/search/search.inc.php @@ -42,6 +42,8 @@ require_once('search/fieldRegistry.inc.php'); require_once('search/expr.inc.php'); require_once(KT_LIB_DIR . '/security/Permission.inc'); +// TODO: move standalone functions into a class.... what was I thinking? + function rank_compare($a, $b) { if ($a->Rank == $b->Rank) @@ -66,6 +68,8 @@ function searchfix($str) return str_replace(array("\n","\r"), array('',''), addslashes($str)); } +// TODO: replace manual json construction with json_encode(). + class SearchHelper { public static function correctPath($path) @@ -366,8 +370,6 @@ class SearchHelper dtfl.document_type_id=$documentTypeID ORDER BY df.name"; - - } else { @@ -566,7 +568,8 @@ function processSearchExpression($query) { $expr = parseExpression($query); - $rs = $expr->evaluate(); + $rs = $expr->evaluate(ExprContext::DOCUMENT); + $rs = $rs['docs']; usort($rs, 'rank_compare'); $results = array(); @@ -629,4 +632,57 @@ function processSearchExpression($query) } } +function resolveSearchShortcuts($result) +{ + $oPermission =& KTPermission::getByName('ktcore.permissions.read'); + $permId = $oPermission->getID(); + + $oUser = User::get($_SESSION['userID']); + $aPermissionDescriptors = KTPermissionUtil::getPermissionDescriptorsForUser($oUser); + $sPermissionDescriptors = empty($aPermissionDescriptors)? -1: implode(',', $aPermissionDescriptors); + + $documentIds = implode(',',array_keys($result['docs'])); + $linkedDocuments = array(); + if (!empty($documentIds)) + { + $sql = "SELECT d.id, d.linked_document_id from documents d "; + $sql .= 'INNER JOIN permission_lookups AS PL ON d.permission_lookup_id = PL.id '. "\n"; + $sql .= 'INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = '.$permId. " \n"; + $sql .= " WHERE d.linked_document_id in ($documentIds) AND PLA.permission_descriptor_id IN ($sPermissionDescriptors)"; + + $rs = DBUtil::getResultArray($sql); + + foreach($rs as $row) + { + $id = $row['id']; + $linked_id = $row['linked_document_id']; + + $result['shortdocs'][$id] = new DocumentShortcutResultItem($id, $result['docs'][$linked_id]); + } + } + + $folderIds = implode(',',array_keys($result['folders'])); + $linkedFolders = array(); + + if (!empty($folderIds)) + { + + $sql = "SELECT f.id, f.linked_folder_id from folders f "; + $sql .= 'INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id '. "\n"; + $sql .= 'INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = '.$permId. " \n"; + $sql .= " WHERE f.linked_folder_id in ($folderIds) AND PLA.permission_descriptor_id IN ($sPermissionDescriptors)"; + + $rs = DBUtil::getResultArray($sql); + + foreach($rs as $row) + { + $id = $row['id']; + $linked_id = $row['linked_folder_id']; + + $result['shortfolders'][$id] = new FolderShortcutResultItem($id, $result['folders'][$linked_id]); + } + } + return $result; +} + ?> diff --git a/sql/mysql/install/data.sql b/sql/mysql/install/data.sql index e215509..1eef936 100644 --- a/sql/mysql/install/data.sql +++ b/sql/mysql/install/data.sql @@ -199,7 +199,7 @@ INSERT INTO `config_settings` VALUES (25, 'session', 'Anonymous Login', 'Defines whether to allow anonymous users to log in automatically. Default is \'False\'.
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), (26, 'ui', 'Company Logo', 'Specifies the path (relative to the KnowledgeTree directory) to the custom logo for the KnowledgeTree user interface.
The logo must be 50px tall, and on a white background.', 'companyLogo', 'default', '${rootUrl}/resources/companylogo.png', 'string', NULL, 1), (27, 'ui', 'Company Logo Width', 'Defines the width, in pixels, of your custom logo.', 'companyLogoWidth', 'default', '313px', 'string', NULL, 1), -(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), +(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), (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), (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), (31, 'ui', 'Fake Mimetype', 'Defines whether browsers may provide the option to \'open\' a document from download. Default is \'False\'.
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 (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), (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), (98, 'ldapAuthentication', 'Create Groups Automatically', 'Defines whether to allow LDAP groups to be created automatically. Default is \'False\'.', 'autoGroupCreation', 'default', 'false', 'boolean', 'string', 1), -(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), +(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), (100, 'import', 'Disable Bulk Import', 'Disable the bulk import plugin', 'disableBulkImport', 'default', 'false', 'string', NULL, 1), (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), -(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); +(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), +(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); /*!40000 ALTER TABLE `config_settings` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/mysql/upgrade/3.5.3/length_config_setting.sql b/sql/mysql/upgrade/3.5.3/length_config_setting.sql index a06683f..fcaa544 100644 --- a/sql/mysql/upgrade/3.5.3/length_config_setting.sql +++ b/sql/mysql/upgrade/3.5.3/length_config_setting.sql @@ -1,12 +1,13 @@ INSERT INTO `config_settings` (group_name, display_name, description, item, value, default_value, type, options, can_edit) VALUES -('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', NULL, 1), +('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), ('import', 'Disable Bulk Import', 'Disable the bulk import plugin', 'disableBulkImport', 'default', 'false', 'string', NULL, 1), -('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), +('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), -('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); \ No newline at end of file +('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), + +('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); + +UPDATE config_settings SET default_value = 'Add Company Name' WHERE group_name = 'ui' AND item = 'companyLogoTitle'; \ No newline at end of file diff --git a/templates/ktcore/search2/search_results.smarty b/templates/ktcore/search2/search_results.smarty index 2d4d2ef..46cbd23 100755 --- a/templates/ktcore/search2/search_results.smarty +++ b/templates/ktcore/search2/search_results.smarty @@ -46,7 +46,6 @@ function saveSearch() url='{/literal}{$rootUrl}{literal}/search2/ajax/saveExpr.php?txtName='+ escape(txtName.value) + '&txtQuery=' + escape('{/literal}{$txtQuery|escape:'quotes'}{literal}'); - Ext.Ajax.request( { url: url, @@ -132,57 +131,82 @@ function onShowAll(showall) {assign var=cbid value=0} - {foreach item=document from=$results} + {i18n arg_count=$numResults}Search results found: #count#{/i18n} +
+
+ + {foreach item=hit from=$results} + {if $hit->IsDocument} + {i18n}Document ID:{/i18n} {$hit->Id} +    + {i18n}Version:{/i18n} {$hit->Version} + {else} + {i18n}Folder ID:{/i18n} {$hit->Id} + {/if} - - + + {if $hit->IsDocument} + {/if} -- libgit2 0.21.4
-   {$document->Title|truncate:80} - {if $document->Title != $document->Filename} + IsDocument}name="selection_d[]"{else}name="selection_f[]"{/if} id="cb{$cbid}" value="{$hit->Id}"> + + + {if $hit->IsDocument} +   {$hit->Title|truncate:80} + {if $hit->Title != $hit->Filename}    - - Filename: {$document->Filename|truncate:40} + - {i18n}Filename:{/i18n} {$hit->Filename|truncate:40} {/if} - {if $document->IsAvailable} + {if $hit->IsAvailable}    - + {/if} - {if !$document->IsAvailable} + {if !$hit->IsAvailable}    * {i18n}NOT AVAILABLE{/i18n} * {/if} + {else} +   {$hit->Title|truncate:80} + + {/if} + - {i18n}Document ID:{/i18n} {$document->DocumentID} -    - {i18n}Version:{/i18n} {$document->Version}
{$document->Text}
{$document->FullPath|truncate:40}/{$document->Title|truncate:40} - {$document->Filesize} - +
{$hit->Text}
+ {if $hit->IsDocument} + {$hit->FullPath|truncate:40}/{$hit->Title|truncate:40} - {$hit->Filesize} + {else} + {$hit->FullPath|truncate:40} + {/if} - {i18n}Created By:{/i18n} {$document->CreatedBy} - {i18n}on{/i18n} {$document->DateCreated} + {i18n}Created By:{/i18n} {$hit->CreatedBy} + {if $hit->IsDocument} + {i18n}on{/i18n} {$hit->DateCreated} + {/if}
{if $workflow != ''} - {i18n}Workflow:{/i18n} $document->Workflow} + {i18n}Workflow:{/i18n} $hit->Workflow} {/if} - {if $document->CheckedOutUser != ''} - {i18n}Checked out by:{/i18n} {$document->CheckedOutUser} - {i18n}on{/i18n} {$document->DateCheckedOut} + {if $hit->CheckedOutUser != ''} + {i18n}Checked out by:{/i18n} {$hit->CheckedOutUser} + {i18n}on{/i18n} {$hit->DateCheckedOut} {else} - {i18n}Modified by:{/i18n} {$document->ModifiedBy} - {i18n}on{/i18n} {$document->DateModified} + {i18n}Modified by:{/i18n} {$hit->ModifiedBy} + {i18n}on{/i18n} {$hit->DateModified} {/if}