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
{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 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
-
+
{/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} | + {if $hit->IsDocument} + {i18n}Document ID:{/i18n} {$hit->Id} + + {i18n}Version:{/i18n} {$hit->Version} + {else} + {i18n}Folder ID:{/i18n} {$hit->Id} + {/if}
| {$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} | |
| {if $workflow != ''} - {i18n}Workflow:{/i18n} $document->Workflow} + {i18n}Workflow:{/i18n} $hit->Workflow} {/if} | |