diff --git a/LetoDMS_Core/Core/inc.ClassDMS.php b/LetoDMS_Core/Core/inc.ClassDMS.php
index 734029939..be5cb1159 100644
--- a/LetoDMS_Core/Core/inc.ClassDMS.php
+++ b/LetoDMS_Core/Core/inc.ClassDMS.php
@@ -126,6 +126,47 @@ class LetoDMS_Core_DMS {
*/
public $version;
+ /**
+ * @var array $callbacks list of methods called when certain operations,
+ * like removing a document, are executed. Set a callback with
+ * {LetoDMS_Core_DMS::setCallback}.
+ * The key of the array is the internal callback function name. Each
+ * array element is an array with two elements: the function name
+ * and the parameter passed to the function.
+ *
+ * Currently implemented callbacks are:
+ *
+ * onPreRemoveDocument($user_param, $document);
+ * called before deleting a document. If this function returns false
+ * the document will not be deleted.
+ *
+ * onPostRemoveDocument($user_param, $document_id);
+ * called after the successful deletion of a document.
+ *
+ * @access public
+ */
+ public $callbacks;
+
+
+ /**
+ * Checks if two objects are equal by comparing its ID
+ *
+ * The regular php check done by '==' compares all attributes of
+ * two objects, which isn't required. The method will first check
+ * if the objects are instances of the same class.
+ *
+ * @param object $object1
+ * @param object $object2
+ * @return boolean true if objects are equal, otherwise false
+ */
+ static function checkIfEqual($object1, $object2) { /* {{{ */
+ if(get_class($object1) != get_class($object2))
+ return false;
+ if($object1->getID() != $object2->getID())
+ return false;
+ return true;
+ } /* }}} */
+
/**
* Filter objects out which are not accessible in a given mode by a user.
*
@@ -507,9 +548,11 @@ class LetoDMS_Core_DMS {
* 0x1 = documents only
* 0x2 = folders only
* 0x3 = both
+ * @param expirationstartdate array search for documents expiring after this date
+ * @param expirationenddate array search for documents expiring before this date
* @return array containing the elements total and docs
*/
- function search($query, $limit=0, $offset=0, $logicalmode='AND', $searchin=array(), $startFolder=null, $owner=null, $status = array(), $creationstartdate=array(), $creationenddate=array(), $modificationstartdate=array(), $modificationenddate=array(), $categories=array(), $attributes=array(), $mode=0x3) { /* {{{ */
+ function search($query, $limit=0, $offset=0, $logicalmode='AND', $searchin=array(), $startFolder=null, $owner=null, $status = array(), $creationstartdate=array(), $creationenddate=array(), $modificationstartdate=array(), $modificationenddate=array(), $categories=array(), $attributes=array(), $mode=0x3, $expirationstartdate=array(), $expirationenddate=array()) { /* {{{ */
// Split the search string into constituent keywords.
$tkeys=array();
if (strlen($query)>0) {
@@ -742,6 +785,23 @@ class LetoDMS_Core_DMS {
$searchCreateDate .= "`tblDocumentContent`.`date` <= ".$stopdate;
}
}
+ $searchExpirationDate = '';
+ if ($expirationstartdate) {
+ $startdate = LetoDMS_Core_DMS::makeTimeStamp($expirationstartdate['hour'], $expirationstartdate['minute'], $expirationstartdate['second'], $expirationstartdate['year'], $expirationstartdate["month"], $expirationstartdate["day"]);
+ if ($startdate) {
+ if($searchExpirationDate)
+ $searchExpirationDate .= " AND ";
+ $searchExpirationDate .= "`tblDocuments`.`expires` >= ".$startdate;
+ }
+ }
+ if ($expirationenddate) {
+ $stopdate = LetoDMS_Core_DMS::makeTimeStamp($expirationenddate['hour'], $expirationenddate['minute'], $expirationenddate['second'], $expirationenddate["year"], $expirationenddate["month"], $expirationenddate["day"]);
+ if ($stopdate) {
+ if($searchExpirationDate)
+ $searchExpirationDate .= " AND ";
+ $searchExpirationDate .= "`tblDocuments`.`expires` <= ".$stopdate;
+ }
+ }
// ---------------------- Suche starten ----------------------------------
@@ -781,6 +841,9 @@ class LetoDMS_Core_DMS {
if (strlen($searchCreateDate)>0) {
$searchQuery .= " AND (".$searchCreateDate.")";
}
+ if (strlen($searchExpirationDate)>0) {
+ $searchQuery .= " AND (".$searchExpirationDate.")";
+ }
if ($searchAttributes) {
$searchQuery .= " AND (".implode(" AND ", $searchAttributes).")";
}
@@ -791,7 +854,7 @@ class LetoDMS_Core_DMS {
}
// Count the number of rows that the search will produce.
- $resArr = $this->db->getResultArray("SELECT COUNT(*) AS num ".$searchQuery." GROUP BY `tblDocuments`.`id`");
+ $resArr = $this->db->getResultArray("SELECT COUNT(*) as num FROM (SELECT DISTINCT `tblDocuments`.id ".$searchQuery.") a");
$totalDocs = 0;
if (is_numeric($resArr[0]["num"]) && $resArr[0]["num"]>0) {
$totalDocs = (integer)$resArr[0]["num"];
@@ -808,19 +871,24 @@ class LetoDMS_Core_DMS {
// calculate the remaining entrŅes of the current page
// If page is not full yet, get remaining entries
- $remain = $limit - count($folderresult['folders']);
- if($remain) {
- if($remain == $limit)
- $offset -= $totalFolders;
- else
- $offset = 0;
- if($limit)
- $searchQuery .= " LIMIT ".$offset.",".$remain;
+ if($limit) {
+ $remain = $limit - count($folderresult['folders']);
+ if($remain) {
+ if($remain == $limit)
+ $offset -= $totalFolders;
+ else
+ $offset = 0;
+ if($limit)
+ $searchQuery .= " LIMIT ".$offset.",".$remain;
+ // Send the complete search query to the database.
+ $resArr = $this->db->getResultArray($searchQuery);
+ } else {
+ $resArr = array();
+ }
+ } else {
// Send the complete search query to the database.
$resArr = $this->db->getResultArray($searchQuery);
- } else {
- $resArr = array();
}
// ------------------- Ausgabe der Ergebnisse ----------------------------
@@ -1000,7 +1068,7 @@ class LetoDMS_Core_DMS {
$users = array();
for ($i = 0; $i < count($resArr); $i++) {
- $user = new LetoDMS_Core_User($resArr[$i]["id"], $resArr[$i]["login"], $resArr[$i]["pwd"], $resArr[$i]["fullName"], $resArr[$i]["email"], (isset($resArr["language"])?$resArr["language"]:NULL), (isset($resArr["theme"])?$resArr["theme"]:NULL), $resArr[$i]["comment"], $resArr[$i]["role"], $resArr[$i]["hidden"], $resArr[$i]["disabled"], $resArr[$i]["pwdExpiration"], $resArr[$i]["loginfailures"], $resArr["quota"]);
+ $user = new LetoDMS_Core_User($resArr[$i]["id"], $resArr[$i]["login"], $resArr[$i]["pwd"], $resArr[$i]["fullName"], $resArr[$i]["email"], (isset($resArr["language"])?$resArr["language"]:NULL), (isset($resArr["theme"])?$resArr["theme"]:NULL), $resArr[$i]["comment"], $resArr[$i]["role"], $resArr[$i]["hidden"], $resArr[$i]["disabled"], $resArr[$i]["pwdExpiration"], $resArr[$i]["loginfailures"], $resArr[$i]["quota"]);
$user->setDMS($this);
$users[$i] = $user;
}
@@ -1483,6 +1551,252 @@ class LetoDMS_Core_DMS {
return $this->getAttributeDefinition($this->db->getInsertID());
} /* }}} */
+ /**
+ * Return list of all workflows
+ *
+ * @return array of instances of LetoDMS_Core_Workflow or false
+ */
+ function getAllWorkflows() { /* {{{ */
+ $queryStr = "SELECT * FROM tblWorkflows ORDER BY name";
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false)
+ return false;
+
+ $queryStr = "SELECT * FROM tblWorkflowStates ORDER BY name";
+ $ressArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($ressArr) && $ressArr == false)
+ return false;
+
+ for ($i = 0; $i < count($ressArr); $i++) {
+ $wkfstates[$ressArr[$i]["id"]] = new LetoDMS_Core_Workflow_State($ressArr[$i]["id"], $ressArr[$i]["name"], $ressArr[$i]["maxtime"], $ressArr[$i]["precondfunc"], $ressArr[$i]["documentstatus"]);
+ }
+
+ $workflows = array();
+ for ($i = 0; $i < count($resArr); $i++) {
+ $workflow = new LetoDMS_Core_Workflow($resArr[$i]["id"], $resArr[$i]["name"], $wkfstates[$resArr[$i]["initstate"]]);
+ $workflow->setDMS($this);
+ $workflows[$i] = $workflow;
+ }
+
+ return $workflows;
+ } /* }}} */
+
+ /**
+ * Return workflow by its Id
+ *
+ * @return object of instances of LetoDMS_Core_Workflow or false
+ */
+ function getWorkflow($id) { /* {{{ */
+ $queryStr = "SELECT * FROM tblWorkflows WHERE id=".intval($id);
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false)
+ return false;
+
+ if(!$resArr)
+ return false;
+
+ $initstate = $this->getWorkflowState($resArr[0]['initstate']);
+
+ $workflow = new LetoDMS_Core_Workflow($resArr[0]["id"], $resArr[0]["name"], $initstate);
+ $workflow->setDMS($this);
+
+ return $workflow;
+ } /* }}} */
+
+ /**
+ * Return workflow by its name
+ *
+ * @return object of instances of LetoDMS_Core_Workflow or false
+ */
+ function getWorkflowByName($name) { /* {{{ */
+ $queryStr = "SELECT * FROM tblWorkflows WHERE name=".$this->db->qstr($name);
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false)
+ return false;
+
+ if(!$resArr)
+ return false;
+
+ $initstate = $this->getWorkflowState($resArr[0]['initstate']);
+
+ $workflow = new LetoDMS_Core_Workflow($resArr[0]["id"], $resArr[0]["name"], $initstate);
+ $workflow->setDMS($this);
+
+ return $workflow;
+ } /* }}} */
+
+ function addWorkflow($name, $initstate) { /* {{{ */
+ $db = $this->db;
+ if (is_object($this->getWorkflowByName($name))) {
+ return false;
+ }
+ $queryStr = "INSERT INTO tblWorkflows (name, initstate) VALUES (".$db->qstr($name).", ".$initstate->getID().")";
+ $res = $db->getResult($queryStr);
+ if (!$res)
+ return false;
+
+ return $this->getWorkflow($db->getInsertID());
+ } /* }}} */
+
+ /**
+ * Return a workflow state by its id
+ *
+ * This function retrieves a workflow state from the database by its id.
+ *
+ * @param integer $id internal id of workflow state
+ * @return object instance of LetoDMS_Core_Workflow_State or false
+ */
+ function getWorkflowState($id) { /* {{{ */
+ if (!is_numeric($id))
+ return false;
+
+ $queryStr = "SELECT * FROM tblWorkflowStates WHERE id = " . (int) $id;
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false) return false;
+ if (count($resArr) != 1) return false;
+
+ $resArr = $resArr[0];
+
+ $state = new LetoDMS_Core_Workflow_State($resArr["id"], $resArr["name"], $resArr["maxtime"], $resArr["precondfunc"], $resArr["documentstatus"]);
+ $state->setDMS($this);
+ return $state;
+ } /* }}} */
+
+ /**
+ * Return workflow state by its name
+ *
+ * @return object of instances of LetoDMS_Core_Workflow_State or false
+ */
+ function getWorkflowStateByName($name) { /* {{{ */
+ $queryStr = "SELECT * FROM tblWorkflowStates WHERE name=".$this->db->qstr($name);
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false)
+ return false;
+
+ if(!$resArr)
+ return false;
+
+ $resArr = $resArr[0];
+
+ $state = new LetoDMS_Core_Workflow_State($resArr["id"], $resArr["name"], $resArr["maxtime"], $resArr["precondfunc"], $resArr["documentstatus"]);
+ $state->setDMS($this);
+
+ return $state;
+ } /* }}} */
+
+ /**
+ * Return list of all workflow states
+ *
+ * @return array of instances of LetoDMS_Core_Workflow_State or false
+ */
+ function getAllWorkflowStates() { /* {{{ */
+ $queryStr = "SELECT * FROM tblWorkflowStates ORDER BY name";
+ $ressArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($ressArr) && $ressArr == false)
+ return false;
+
+ $wkfstates = array();
+ for ($i = 0; $i < count($ressArr); $i++) {
+ $wkfstate = new LetoDMS_Core_Workflow_State($ressArr[$i]["id"], $ressArr[$i]["name"], $ressArr[$i]["maxtime"], $ressArr[$i]["precondfunc"], $ressArr[$i]["documentstatus"]);
+ $wkfstate->setDMS($this);
+ $wkfstates[$i] = $wkfstate;
+ }
+
+ return $wkfstates;
+ } /* }}} */
+
+ function addWorkflowState($name, $docstatus) { /* {{{ */
+ $db = $this->db;
+ if (is_object($this->getWorkflowStateByName($name))) {
+ return false;
+ }
+ $queryStr = "INSERT INTO tblWorkflowStates (name, documentstatus) VALUES (".$db->qstr($name).", ".(int) $docstatus.")";
+ $res = $db->getResult($queryStr);
+ if (!$res)
+ return false;
+
+ return $this->getWorkflowState($db->getInsertID());
+ } /* }}} */
+
+ /**
+ * Return a workflow action by its id
+ *
+ * This function retrieves a workflow action from the database by its id.
+ *
+ * @param integer $id internal id of workflow state
+ * @return object instance of LetoDMS_Core_Workflow_State or false
+ */
+ function getWorkflowAction($id) { /* {{{ */
+ if (!is_numeric($id))
+ return false;
+
+ $queryStr = "SELECT * FROM tblWorkflowActions WHERE id = " . (int) $id;
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false) return false;
+ if (count($resArr) != 1) return false;
+
+ $resArr = $resArr[0];
+
+ $action = new LetoDMS_Core_Workflow_Action($resArr["id"], $resArr["name"]);
+ $action->setDMS($this);
+ return $action;
+ } /* }}} */
+
+ /**
+ * Return list of workflow action
+ *
+ * @return array list of instances of LetoDMS_Core_Workflow_Action or false
+ */
+ function getAllWorkflowActions() { /* {{{ */
+ $queryStr = "SELECT * FROM tblWorkflowActions";
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false)
+ return false;
+
+ $wkfactions = array();
+ for ($i = 0; $i < count($resArr); $i++) {
+ $action = new LetoDMS_Core_Workflow_Action($resArr[$i]["id"], $resArr[$i]["name"]);
+ $action->setDMS($this);
+ $wkfactions[$i] = $action;
+ }
+
+ return $wkfactions;
+ } /* }}} */
+
+ /**
+ * Return a workflow transition by its id
+ *
+ * This function retrieves a workflow transition from the database by its id.
+ *
+ * @param integer $id internal id of workflow transition
+ * @return object instance of LetoDMS_Core_Workflow_Transition or false
+ */
+ function getWorkflowTransition($id) { /* {{{ */
+ if (!is_numeric($id))
+ return false;
+
+ $queryStr = "SELECT * FROM tblWorkflowTransitions WHERE id = " . (int) $id;
+ $resArr = $this->db->getResultArray($queryStr);
+
+ if (is_bool($resArr) && $resArr == false) return false;
+ if (count($resArr) != 1) return false;
+
+ $resArr = $resArr[0];
+
+ $transition = new LetoDMS_Core_Workflow_Transition($resArr["id"], $this->getWorkflow($resArr["workflow"]), $this->getWorkflowState($resArr["state"]), $this->getWorkflowAction($resArr["action"]), $this->getWorkflowState($resArr["nextstate"]), $resArr["maxtime"]);
+ $transition->setDMS($this);
+ return $transition;
+ } /* }}} */
+
/**
* Returns document content which is not linked to a document
*
@@ -1532,5 +1846,19 @@ class LetoDMS_Core_DMS {
return $versions;
} /* }}} */
+
+ /**
+ * Set a callback function
+ *
+ * @param string $name internal name of callback
+ * @param mixed $func function name as expected by {call_user_method}
+ * @param mixed $params parameter passed as the first argument to the
+ * callback
+ */
+ function setCallback($name, $func, $params=null) { /* {{{ */
+ if($name && $func)
+ $this->callbacks[$name] = array($func, $params);
+ } /* }}} */
+
}
?>
diff --git a/LetoDMS_Core/Core/inc.ClassDocument.php b/LetoDMS_Core/Core/inc.ClassDocument.php
index d0249b9bd..54f1740de 100644
--- a/LetoDMS_Core/Core/inc.ClassDocument.php
+++ b/LetoDMS_Core/Core/inc.ClassDocument.php
@@ -34,6 +34,12 @@ define("S_DRAFT_APP", 1);
*/
define("S_RELEASED", 2);
+/*
+ * Document is in workflow. A document is in workflow if a workflow
+ * has been started and has not reached a final state.
+ */
+define("S_IN_WORKFLOW", 3);
+
/*
* Document was rejected. A document is in rejected state when
* the review failed or approval was not given.
@@ -77,67 +83,67 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
/**
* @var string name of document
*/
- var $_name;
+ protected $_name;
/**
* @var string comment of document
*/
- var $_comment;
+ protected $_comment;
/**
* @var integer unix timestamp of creation date
*/
- var $_date;
+ protected $_date;
/**
* @var integer id of user who is the owner
*/
- var $_ownerID;
+ protected $_ownerID;
/**
* @var integer id of folder this document belongs to
*/
- var $_folderID;
+ protected $_folderID;
/**
* @var integer timestamp of expiration date
*/
- var $_expires;
+ protected $_expires;
/**
* @var boolean true if access is inherited, otherwise false
*/
- var $_inheritAccess;
+ protected $_inheritAccess;
/**
* @var integer default access if access rights are not inherited
*/
- var $_defaultAccess;
+ protected $_defaultAccess;
/**
* @var array list of notifications for users and groups
*/
- var $_notifyList;
+ public $_notifyList;
/**
* @var boolean true if document is locked, otherwise false
*/
- var $_locked;
+ protected $_locked;
/**
* @var string list of keywords
*/
- var $_keywords;
+ protected $_keywords;
/**
* @var array list of categories
*/
- var $_categories;
+ protected $_categories;
/**
* @var integer position of document within the parent folder
*/
- var $_sequence;
+ protected $_sequence;
function LetoDMS_Core_Document($id, $name, $comment, $date, $expires, $ownerID, $folderID, $inheritAccess, $defaultAccess, $locked, $keywords, $sequence) { /* {{{ */
parent::__construct($id);
@@ -509,13 +515,19 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
return false;
} /* }}} */
- // return true if status has changed (to reload page)
+ /**
+ * Check if the document has expired and set the status accordingly
+ * It will also recalculate the status if the current status is
+ * set to S_EXPIRED but the document isn't actually expired.
+ *
+ * @return boolean true if status has changed
+ */
function verifyLastestContentExpriry(){ /* {{{ */
$lc=$this->getLatestContent();
if($lc) {
$st=$lc->getStatus();
- if (($st["status"]==S_DRAFT_REV || $st["status"]==S_DRAFT_APP) && $this->hasExpired()){
+ if (($st["status"]==S_DRAFT_REV || $st["status"]==S_DRAFT_APP || $st["status"]==S_IN_WORKFLOW) && $this->hasExpired()){
$lc->setStatus(S_EXPIRED,"", $this->getOwner());
return true;
}
@@ -1082,7 +1094,7 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
* must be the id of the attribute definition.
* @return bool/array false in case of an error or a result set
*/
- function addContent($comment, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $reviewers=array(), $approvers=array(), $version=0, $attributes=array()) { /* {{{ */
+ function addContent($comment, $user, $tmpFile, $orgFileName, $fileType, $mimeType, $reviewers=array(), $approvers=array(), $version=0, $attributes=array(), $workflow=null) { /* {{{ */
$db = $this->_dms->getDB();
// the doc path is id/version.filetype
@@ -1128,6 +1140,8 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
unset($this->_content);
unset($this->_latestContent);
$content = new LetoDMS_Core_DocumentContent($contentID, $this, $version, $comment, $date, $user->getID(), $dir, $orgFileName, $fileType, $mimeType);
+ if($workflow)
+ $content->setWorkflow($workflow);
$docResultSet = new LetoDMS_Core_AddContentResultSet($content);
if($attributes) {
@@ -1142,8 +1156,8 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
}
// TODO - verify
- if ($this->_dms->enableConverting && in_array($docResultSet->_content->getFileType(), array_keys($this->_dms->convertFileTypes)))
- $docResultSet->_content->convert(); // Even if if fails, do not return false
+ if ($this->_dms->enableConverting && in_array($docResultSet->getContent()->getFileType(), array_keys($this->_dms->convertFileTypes)))
+ $docResultSet->getContent()->convert(); // Even if if fails, do not return false
$queryStr = "INSERT INTO `tblDocumentStatus` (`documentID`, `version`) ".
"VALUES (". $this->_id .", ". (int) $version .")";
@@ -1164,7 +1178,7 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
if (isset($reviewers[$i])) {
foreach ($reviewers[$i] as $reviewerID) {
$reviewer=($i=="i" ?$this->_dms->getUser($reviewerID) : $this->_dms->getGroup($reviewerID));
- $res = ($i=="i" ? $docResultSet->_content->addIndReviewer($reviewer, $user, true) : $docResultSet->_content->addGrpReviewer($reviewer, $user, true));
+ $res = ($i=="i" ? $docResultSet->getContent()->addIndReviewer($reviewer, $user, true) : $docResultSet->getContent()->addGrpReviewer($reviewer, $user, true));
$docResultSet->addReviewer($reviewer, $i, $res);
// If no error is returned, or if the error is just due to email
// failure, mark the state as "pending review".
@@ -1182,7 +1196,7 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
if (isset($approvers[$i])) {
foreach ($approvers[$i] as $approverID) {
$approver=($i=="i" ? $this->_dms->getUser($approverID) : $this->_dms->getGroup($approverID));
- $res=($i=="i" ? $docResultSet->_content->addIndApprover($approver, $user, !$pendingReview) : $docResultSet->_content->addGrpApprover($approver, $user, !$pendingReview));
+ $res=($i=="i" ? $docResultSet->getContent()->addIndApprover($approver, $user, !$pendingReview) : $docResultSet->getContent()->addGrpApprover($approver, $user, !$pendingReview));
$docResultSet->addApprover($approver, $i, $res);
if ($res==0 || $res=-3 || $res=-4) {
$pendingApproval=true;
@@ -1197,13 +1211,16 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
$status = S_DRAFT_REV;
$comment = "";
}
- else if ($pendingApproval) {
+ elseif ($pendingApproval) {
$status = S_DRAFT_APP;
$comment = "";
}
- else {
+ elseif($workflow) {
+ $status = S_IN_WORKFLOW;
+ $comment = ", workflow: ".$workflow->getName();
+ } else {
$status = S_RELEASED;
- $comment="";
+ $comment = "";
}
$queryStr = "INSERT INTO `tblDocumentStatusLog` (`statusID`, `status`, `comment`, `date`, `userID`) ".
"VALUES ('". $statusID ."', '". $status."', 'New document content submitted". $comment ."', CURRENT_TIMESTAMP, '". $user->getID() ."')";
@@ -1368,6 +1385,12 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
return false;
}
+ $queryStr = "DELETE FROM `tblWorkflowDocumentContent` WHERE `document` = '". $this->getID() ."' AND `version` = '" . $version->_version."'";
+ if (!$db->getResult($queryStr)) {
+ $db->rollbackTransaction();
+ return false;
+ }
+
$db->commitTransaction();
return true;
} /* }}} */
@@ -1509,11 +1532,27 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
/**
* Remove a document completly
*
+ * This methods calls the callback 'onPreRemoveDocument' before removing
+ * the document. The current document will be passed as the second
+ * parameter to the callback function. After successful deletion the
+ * 'onPostRemoveDocument' callback will be used. The current document id
+ * will be passed as the second parameter. If onPreRemoveDocument fails
+ * the whole function will fail and the document will not be deleted.
+ * The return value of 'onPostRemoveDocument' will be disregarded.
+ *
* @return boolean true on success, otherwise false
*/
function remove() { /* {{{ */
$db = $this->_dms->getDB();
+ /* Check if 'onPreRemoveDocument' callback is set */
+ if(isset($this->_dms->callbacks['onPreRemoveDocument'])) {
+ $callback = $this->_dms->callbacks['onPreRemoveDocument'];
+ if(!call_user_func($callback[0], $callback[1], $this)) {
+ return false;
+ }
+ }
+
$res = $this->getContent();
if (is_bool($res) && !$res) return false;
@@ -1592,6 +1631,14 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
}
$db->commitTransaction();
+
+ /* Check if 'onPostRemoveDocument' callback is set */
+ if(isset($this->_dms->callbacks['onPostRemoveDocument'])) {
+ $callback = $this->_dms->callbacks['onPostRemoveDocument'];
+ if(!call_user_func($callback[0], $callback[1], $this->_id)) {
+ }
+ }
+
return true;
} /* }}} */
@@ -1770,9 +1817,23 @@ class LetoDMS_Core_Document extends LetoDMS_Core_Object { /* {{{ */
*/
class LetoDMS_Core_DocumentContent extends LetoDMS_Core_Object { /* {{{ */
- // if status is released and there are reviewers set status draft_rev
- // if status is released or draft_rev and there are approves set status draft_app
- // if status is draft and there are no approver and no reviewers set status to release
+ /**
+ * Recalculate the status of a document
+ * The methods checks the review and approval status and sets the
+ * status of the document accordingly.
+ * If status is S_RELEASED and there are reviewers set status S_DRAFT_REV
+ * If status is S_RELEASED or S_DRAFT_REV and there are approvers set
+ * status S_DRAFT_APP
+ * If status is draft and there are no approver and no reviewers set
+ * status to S_RELEASED
+ * The status of a document with the current status S_OBSOLETE, S_REJECTED,
+ * or S_EXPIRED will not be changed unless the parameter
+ * $ignorecurrentstatus is set to true.
+ *
+ * @param boolean $ignorecurrentstatus ignore the current status and
+ * recalculate a new status in any case
+ * @param object $user the user initiating this method
+ */
function verifyStatus($ignorecurrentstatus=false, $user=null) { /* {{{ */
unset($this->_status);
@@ -1805,6 +1866,7 @@ class LetoDMS_Core_DocumentContent extends LetoDMS_Core_Object { /* {{{ */
if ($pendingReview) $this->setStatus(S_DRAFT_REV,"",$user);
else if ($pendingApproval) $this->setStatus(S_DRAFT_APP,"",$user);
+ else if ($this->getWorkflow()) $this->setStatus(S_IN_WORKFLOW,"",$user);
else $this->setStatus(S_RELEASED,"",$user);
} /* }}} */
@@ -1825,6 +1887,8 @@ class LetoDMS_Core_DocumentContent extends LetoDMS_Core_Object { /* {{{ */
} else {
$this->_fileSize = $fileSize;
}
+ $this->_workflow = null;
+ $this->_workflowState = null;
} /* }}} */
function getVersion() { return $this->_version; }
@@ -1845,17 +1909,16 @@ class LetoDMS_Core_DocumentContent extends LetoDMS_Core_Object { /* {{{ */
function getPath() { return $this->_document->getDir() . $this->_version . $this->_fileType; }
- function getFileSize() {
+ function getFileSize() { /* {{{ */
return $this->_fileSize;
// return LetoDMS_Core_File::fileSize($this->_dms->contentDir . $this->_document->getDir() . $this->getFileName());
- }
+ } /* }}} */
- function setFileSize() {
+ function setFileSize() { /* {{{ */
$filesize = LetoDMS_Core_File::fileSize($this->_dms->contentDir . $this->_document->getDir() . $this->getFileName());
- if(!$filesize);
+ if($filesize === false);
return false;
-echo $filesize;
$db = $this->_document->_dms->getDB();
$queryStr = "UPDATE tblDocumentContent SET fileSize = ".$filesize." where `document` = " . $this->_document->getID() . " AND `version` = " . $this->_version;
if (!$db->getResult($queryStr))
@@ -1863,7 +1926,7 @@ echo $filesize;
$this->_fileSize = $filesize;
return true;
- }
+ } /* }}} */
function setComment($newComment) { /* {{{ */
$db = $this->_document->_dms->getDB();
@@ -2001,6 +2064,36 @@ echo $filesize;
return $this->_status;
} /* }}} */
+ /**
+ * Get current and former states of the document content
+ *
+ * @param integer $limit if not set all log entries will be returned
+ * @return array list of status changes
+ */
+ function getStatusLog($limit=0) { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if (!is_numeric($limit)) return false;
+
+ $queryStr=
+ "SELECT `tblDocumentStatus`.*, `tblDocumentStatusLog`.`status`, ".
+ "`tblDocumentStatusLog`.`comment`, `tblDocumentStatusLog`.`date`, ".
+ "`tblDocumentStatusLog`.`userID` ".
+ "FROM `tblDocumentStatus` ".
+ "LEFT JOIN `tblDocumentStatusLog` USING (`statusID`) ".
+ "WHERE `tblDocumentStatus`.`documentID` = '". $this->_document->getID() ."' ".
+ "AND `tblDocumentStatus`.`version` = '". $this->_version ."' ".
+ "ORDER BY `tblDocumentStatusLog`.`statusLogID` DESC ";
+ if($limit)
+ $queryStr .= "LIMIT ".(int) $limit;
+
+ $res = $db->getResultArray($queryStr);
+ if (is_bool($res) && !$res)
+ return false;
+
+ return $res;
+ } /* }}} */
+
/**
* Set the status of the content
* Setting the status means to add another entry into the table
@@ -2022,7 +2115,7 @@ echo $filesize;
// If the supplied value lies outside of the accepted range, return an
// error.
- if ($status < -3 || $status > 2) {
+ if ($status < -3 || $status > 3) {
return false;
}
@@ -2043,6 +2136,40 @@ echo $filesize;
return true;
} /* }}} */
+ /**
+ * Returns the access mode similar to a document
+ * There is no real access mode for document content, so this is more
+ * like a virtual access mode, derived from the status or workflow
+ * of the document content. The idea is to return an access mode
+ * M_NONE if the user is still in a workflow or under review/approval.
+ * In such a case only those user involved in the workflow/review/approval
+ * process should be allowed to see the document. This method could
+ * be called by any function that returns the content e.g. getLatestContent()
+ * It may as well be used by LetoDMS_Core_Document::getAccessMode() to
+ * prevent access on the whole document if there is just one version.
+ * The return value is planed to be either M_NONE or M_READ.
+ *
+ * @param object $user
+ * @return integer mode
+ */
+ function getAccessMode($u) { /* {{{ */
+ if(!$this->_workflow)
+ $this->getWorkflow();
+
+ if($this->_workflow) {
+ if (!$this->_workflowState)
+ $this->getWorkflowState();
+ $transitions = $this->_workflow->getNextTransitions($this->_workflowState);
+ foreach($transitions as $transition) {
+ if($this->triggerWorkflowTransitionIsAllowed($u, $transition))
+ return M_READ;
+ }
+ return M_NONE;
+ }
+
+ return M_READ;
+ } /* }}} */
+
/**
* Get the current review status of the document content
* The review status is a list of reviewers and its current status
@@ -2652,6 +2779,659 @@ echo $filesize;
return 0;
} /* }}} */
+ /**
+ * Set state of workflow assigned to the document content
+ *
+ * @param object $state
+ */
+ function setWorkflowState($state) { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if($this->_workflow) {
+ $queryStr = "UPDATE tblWorkflowDocumentContent set state=". $state->getID() ." WHERE workflow=". intval($this->_workflow->getID()). " AND document=". intval($this->_document->getID()) ." AND version=". intval($this->_version) ."";
+ if (!$db->getResult($queryStr)) {
+ return false;
+ }
+ $this->_workflowState = $state;
+ return true;
+ }
+ return false;
+ } /* }}} */
+
+ /**
+ * Get state of workflow assigned to the document content
+ *
+ * @return object/boolean an object of class LetoDMS_Core_Workflow_State
+ * or false in case of error, e.g. the version has not a workflow
+ */
+ function getWorkflowState() { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if(!$this->_workflow)
+ $this->getWorkflow();
+
+ if(!$this->_workflow)
+ return false;
+
+ if (!$this->_workflowState) {
+ $queryStr=
+ "SELECT b.* FROM tblWorkflowDocumentContent a LEFT JOIN tblWorkflowStates b ON a.state = b.id WHERE workflow=". intval($this->_workflow->getID())
+ ." AND a.version='".$this->_version
+ ."' AND a.document = '". $this->_document->getID() ."' ";
+ $recs = $db->getResultArray($queryStr);
+ if (is_bool($recs) && !$recs)
+ return false;
+ $this->_workflowState = new LetoDMS_Core_Workflow_State($recs[0]['id'], $recs[0]['name'], $recs[0]['maxtime'], $recs[0]['precondfunc'], $recs[0]['documentstatus']);
+ $this->_workflowState->setDMS($this->_document->_dms);
+ }
+ return $this->_workflowState;
+ } /* }}} */
+
+ /**
+ * Assign a workflow to a document
+ *
+ * @param object $workflow
+ */
+ function setWorkflow($workflow, $user) { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ $this->getWorkflow();
+ if($workflow && is_object($workflow)) {
+ $initstate = $workflow->getInitState();
+ $queryStr = "INSERT INTO tblWorkflowDocumentContent (workflow, document, version, state, date) VALUES (". $workflow->getID(). ", ". $this->_document->getID() .", ". $this->_version .", ".$initstate->getID().", CURRENT_TIMESTAMP)";
+ if (!$db->getResult($queryStr)) {
+ return false;
+ }
+ $this->_workflow = $workflow;
+ $this->setStatus(S_IN_WORKFLOW, "Added workflow '".$workflow->getName()."'", $user);
+ return true;
+ }
+ return true;
+ } /* }}} */
+
+ /**
+ * Get workflow assigned to the document content
+ *
+ * The method returns the last sub workflow if one was assigned.
+ *
+ * @return object/boolean an object of class LetoDMS_Core_Workflow
+ * or false in case of error, e.g. the version has not a workflow
+ */
+ function getWorkflow() { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if (!isset($this->_workflow)) {
+ $queryStr=
+ "SELECT b.* FROM tblWorkflowDocumentContent a LEFT JOIN tblWorkflows b ON a.workflow = b.id WHERE a.`version`='".$this->_version
+ ."' AND a.`document` = '". $this->_document->getID() ."' "
+ ."ORDER BY `date` DESC LIMIT 1";
+ $recs = $db->getResultArray($queryStr);
+ if (is_bool($recs) && !$recs)
+ return false;
+ if(!$recs)
+ return false;
+ $this->_workflow = new LetoDMS_Core_Workflow($recs[0]['id'], $recs[0]['name'], $this->_document->_dms->getWorkflowState($recs[0]['initstate']));
+ $this->_workflow->setDMS($this->_document->_dms);
+ }
+ return $this->_workflow;
+ } /* }}} */
+
+ /**
+ * Restart workflow from its initial state
+ *
+ * @return boolean true if workflow could be restarted
+ * or false in case of error
+ */
+ function rewindWorkflow() { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ $this->getWorkflow();
+
+ if (!isset($this->_workflow)) {
+ return true;
+ }
+
+ $db->startTransaction();
+ $queryStr = "DELETE from tblWorkflowLog WHERE `document` = ". $this->_document->getID() ." AND `version` = ".$this->_version." AND `workflow` = ".$this->_workflow->getID();
+ if (!$db->getResult($queryStr)) {
+ $db->rollbackTransaction();
+ return false;
+ }
+
+ $this->setWorkflowState($this->_workflow->getInitState());
+ $db->commitTransaction();
+
+ return true;
+ } /* }}} */
+
+ /**
+ * Remove workflow
+ *
+ * Fully removing a workflow including entries in the workflow log is
+ * only allowed if the workflow is still its initial state.
+ * At a later point of time only unlinking the document from the
+ * workflow is allowed. It will keep any log entries.
+ * A workflow is unlinked from a document when enterNextState()
+ * succeeds.
+ *
+ * @param object $user user doing initiating the removal
+ * @param boolean $unlink if true, just unlink the workflow from the
+ * document but do not remove the workflow log. The $unlink
+ * flag has been added to detach the workflow from the document
+ * when it has reached a valid end state
+ (see LetoDMS_Core_DocumentContent::enterNextState())
+ * @return boolean true if workflow could be removed
+ * or false in case of error
+ */
+ function removeWorkflow($user, $unlink=false) { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ $this->getWorkflow();
+
+ if (!isset($this->_workflow)) {
+ return true;
+ }
+
+ if(LetoDMS_Core_DMS::checkIfEqual($this->_workflow->getInitState(), $this->getWorkflowState()) || $unlink == true) {
+ $db->startTransaction();
+ $queryStr=
+ "DELETE FROM tblWorkflowDocumentContent WHERE "
+ ."`version`='".$this->_version."' "
+ ." AND `document` = '". $this->_document->getID() ."' "
+ ." AND `workflow` = '". $this->_workflow->getID() ."' ";
+ if (!$db->getResult($queryStr)) {
+ $db->rollbackTransaction();
+ return false;
+ }
+ if(!$unlink) {
+ $queryStr=
+ "DELETE FROM tblWorkflowLog WHERE "
+ ."`version`='".$this->_version."' "
+ ." AND `document` = '". $this->_document->getID() ."' "
+ ." AND `workflow` = '". $this->_workflow->getID() ."' ";
+ if (!$db->getResult($queryStr)) {
+ $db->rollbackTransaction();
+ return false;
+ }
+ }
+ $this->_workflow = null;
+ $this->_workflowState = null;
+ $this->verifyStatus(false, $user);
+ $db->commitTransaction();
+ }
+
+ return true;
+ } /* }}} */
+
+ /**
+ * Run a sub workflow
+ *
+ * @param object $subworkflow
+ */
+ function getParentWorkflow() { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ /* document content must be in a workflow */
+ $this->getWorkflow();
+ if(!$this->_workflow)
+ return false;
+
+ $queryStr=
+ "SELECT * FROM tblWorkflowDocumentContent WHERE "
+ ."`version`='".$this->_version."' "
+ ." AND `document` = '". $this->_document->getID() ."' "
+ ." AND `workflow` = '". $this->_workflow->getID() ."' ";
+ $recs = $db->getResultArray($queryStr);
+ if (is_bool($recs) && !$recs)
+ return false;
+ if(!$recs)
+ return false;
+
+ if($recs[0]['parentworkflow'])
+ return $this->_document->_dms->getWorkflow($recs[0]['parentworkflow']);
+
+ return false;
+ } /* }}} */
+
+ /**
+ * Run a sub workflow
+ *
+ * @param object $subworkflow
+ */
+ function runSubWorkflow($subworkflow) { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ /* document content must be in a workflow */
+ $this->getWorkflow();
+ if(!$this->_workflow)
+ return false;
+
+ /* The current workflow state must match the sub workflows initial state */
+ if($subworkflow->getInitState()->getID() != $this->_workflowState->getID())
+ return false;
+
+ if($subworkflow) {
+ $initstate = $subworkflow->getInitState();
+ $queryStr = "INSERT INTO tblWorkflowDocumentContent (parentworkflow, workflow, document, version, state, date) VALUES (". $this->_workflow->getID(). ", ". $subworkflow->getID(). ", ". $this->_document->getID() .", ". $this->_version .", ".$initstate->getID().", CURRENT_TIMESTAMP)";
+ if (!$db->getResult($queryStr)) {
+ return false;
+ }
+ $this->_workflow = $subworkflow;
+ return true;
+ }
+ return true;
+ } /* }}} */
+
+ /**
+ * Return from sub workflow to parent workflow.
+ * The method will trigger the given transition
+ *
+ * FIXME: Needs much better checking if this is allowed
+ *
+ * @param object $user intiating the return
+ * @param object $transtion to trigger
+ * @param string comment for the transition trigger
+ */
+ function returnFromSubWorkflow($user, $transition=null, $comment='') { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ /* document content must be in a workflow */
+ $this->getWorkflow();
+ if(!$this->_workflow)
+ return false;
+
+ if (isset($this->_workflow)) {
+ $db->startTransaction();
+
+ $queryStr=
+ "SELECT * FROM tblWorkflowDocumentContent WHERE workflow=". intval($this->_workflow->getID())
+ . " AND `version`='".$this->_version
+ ."' AND `document` = '". $this->_document->getID() ."' ";
+ echo $queryStr;
+ $recs = $db->getResultArray($queryStr);
+ if (is_bool($recs) && !$recs) {
+ $db->rollbackTransaction();
+ return false;
+ }
+ if(!$recs) {
+ $db->rollbackTransaction();
+ return false;
+ }
+
+ $queryStr = "DELETE FROM `tblWorkflowDocumentContent` WHERE `workflow` =". intval($this->_workflow->getID())." AND `document` = '". $this->_document->getID() ."' AND `version` = '" . $this->_version."'";
+ echo $queryStr;
+ if (!$db->getResult($queryStr)) {
+ $db->rollbackTransaction();
+ return false;
+ }
+
+ $this->_workflow = $this->_document->_dms->getWorkflow($recs[0]['parentworkflow']);
+ $this->_workflow->setDMS($this->_document->_dms);
+
+ if($transition) {
+ echo "Trigger transition";
+ if(false === $this->triggerWorkflowTransition($user, $transition, $comment)) {
+ $db->rollbackTransaction();
+ return false;
+ }
+ }
+
+ $db->commitTransaction();
+ }
+ return $this->_workflow;
+ } /* }}} */
+
+ /**
+ * Check if the user is allowed to trigger the transition
+ * A user is allowed if either the user itself or
+ * a group of which the user is a member of is registered for
+ * triggering a transition. This method does not change the workflow
+ * state of the document content.
+ *
+ * @param object $user
+ * @return boolean true if user may trigger transaction
+ */
+ function triggerWorkflowTransitionIsAllowed($user, $transition) { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if(!$this->_workflow)
+ $this->getWorkflow();
+
+ if(!$this->_workflow)
+ return false;
+
+ if(!$this->_workflowState)
+ $this->getWorkflowState();
+
+ /* Check if the user has already triggered the transition */
+ $queryStr=
+ "SELECT * FROM tblWorkflowLog WHERE `version`='".$this->_version ."' AND `document` = '". $this->_document->getID() ."' AND `workflow` = ". $this->_workflow->getID(). " AND userid = ".$user->getID();
+ $queryStr .= " AND `transition` = ".$transition->getID();
+ $resArr = $db->getResultArray($queryStr);
+ if (is_bool($resArr) && !$resArr)
+ return false;
+
+ if(count($resArr))
+ return false;
+
+ /* Get all transition users allowed to trigger the transition */
+ $transusers = $transition->getUsers();
+ if($transusers) {
+ foreach($transusers as $transuser) {
+ if($user->getID() == $transuser->getUser()->getID())
+ return true;
+ }
+ }
+
+ /* Get all transition groups whose members are allowed to trigger
+ * the transition */
+ $transgroups = $transition->getGroups();
+ if($transgroups) {
+ foreach($transgroups as $transgroup) {
+ $group = $transgroup->getGroup();
+ if($group->isMember($user))
+ return true;
+ }
+ }
+
+ return false;
+ } /* }}} */
+
+ /**
+ * Check if all conditions are met to change the workflow state
+ * of a document content (run the transition).
+ * The conditions are met if all explicitly set users and a sufficient
+ * number of users of the groups have acknowledged the content.
+ *
+ * @return boolean true if transaction maybe executed
+ */
+ function executeWorkflowTransitionIsAllowed($transition) { /* {{{ */
+ if(!$this->_workflow)
+ $this->getWorkflow();
+
+ if(!$this->_workflow)
+ return false;
+
+ if(!$this->_workflowState)
+ $this->getWorkflowState();
+
+ /* Get the Log of transition triggers */
+ $entries = $this->getWorkflowLog($transition);
+ if(!$entries)
+ return false;
+
+ /* Get all transition users allowed to trigger the transition
+ * $allowedusers is a list of all users allowed to trigger the
+ * transition
+ */
+ $transusers = $transition->getUsers();
+ $allowedusers = array();
+ foreach($transusers as $transuser) {
+ $a = $transuser->getUser();
+ $allowedusers[$a->getID()] = $a;
+ }
+
+ /* Get all transition groups whose members are allowed to trigger
+ * the transition */
+ $transgroups = $transition->getGroups();
+ foreach($entries as $entry) {
+ $loguser = $entry->getUser();
+ /* Unset each allowed user if it was found in the log */
+ if(isset($allowedusers[$loguser->getID()]))
+ unset($allowedusers[$loguser->getID()]);
+ /* Also check groups if required. Count the group membership of
+ * each user in the log in the array $gg
+ */
+ if($transgroups) {
+ $loggroups = $loguser->getGroups();
+ foreach($loggroups as $loggroup) {
+ if(!isset($gg[$loggroup->getID()]))
+ $gg[$loggroup->getID()] = 1;
+ else
+ $gg[$loggroup->getID()]++;
+ }
+ }
+ }
+ /* If there are allowed users left, then there some users still
+ * need to trigger the transition.
+ */
+ if($allowedusers)
+ return false;
+
+ if($transgroups) {
+ foreach($transgroups as $transgroup) {
+ $group = $transgroup->getGroup();
+ $minusers = $transgroup->getNumOfUsers();
+ if(!isset($gg[$group->getID()]))
+ return false;
+ if($gg[$group->getID()] < $minusers)
+ return false;
+ }
+ }
+ return true;
+ } /* }}} */
+
+ /**
+ * Trigger transition
+ *
+ * This method will be deprecated
+ *
+ * The method will first check if the user is allowed to trigger the
+ * transition. If the user is allowed, an entry in the workflow log
+ * will be added, which is later used to check if the transition
+ * can actually be processed. The method will finally call
+ * executeWorkflowTransitionIsAllowed() which checks all log entries
+ * and does the transitions post function if all users and groups have
+ * triggered the transition. Finally enterNextState() is called which
+ * will try to enter the next state.
+ *
+ * @param object $user
+ * @param object $transition
+ * @param string $comment user comment
+ * @return boolean/object next state if transition could be triggered and
+ * then next state could be entered,
+ * true if the transition could just be triggered or
+ * false in case of an error
+ */
+ function triggerWorkflowTransition($user, $transition, $comment='') { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if(!$this->_workflow)
+ $this->getWorkflow();
+
+ if(!$this->_workflow)
+ return false;
+
+ if(!$this->_workflowState)
+ $this->getWorkflowState();
+
+ if(!$this->_workflowState)
+ return false;
+
+ /* Check if the user is allowed to trigger the transition.
+ */
+ if(!$this->triggerWorkflowTransitionIsAllowed($user, $transition))
+ return false;
+
+ $state = $this->_workflowState;
+ $queryStr = "INSERT INTO tblWorkflowLog (document, version, workflow, userid, transition, date, comment) VALUES (".$this->_document->getID().", ".$this->_version.", " . (int) $this->_workflow->getID() . ", " .(int) $user->getID(). ", ".(int) $transition->getID().", CURRENT_TIMESTAMP, ".$db->qstr($comment).")";
+ if (!$db->getResult($queryStr))
+ return false;
+
+ /* Check if this transition is processed. Run the post function in
+ * that case. A transition is processed when all users and groups
+ * have triggered it.
+ */
+ if($this->executeWorkflowTransitionIsAllowed($transition)) {
+ /* run post function of transition */
+// echo "run post function of transition ".$transition->getID()."
";
+ }
+
+ /* Go into the next state. This will only succeed if the pre condition
+ * function of that states succeeds.
+ */
+ $nextstate = $transition->getNextState();
+ if($this->enterNextState($user, $nextstate)) {
+ return $nextstate;
+ }
+ return true;
+
+ } /* }}} */
+
+ /**
+ * Enter next state of workflow if possible
+ *
+ * The method will check if one of the following states in the workflow
+ * can be reached.
+ * It does it by running
+ * the precondition function of that state. The precondition function
+ * gets a list of all transitions leading to the state. It will
+ * determine, whether the transitions has been triggered and if that
+ * is sufficient to enter the next state. If no pre condition function
+ * is set, then 1 of n transtions are enough to enter the next state.
+ *
+ * If moving in the next state is possible and this state has a
+ * corresponding document state, then the document state will be
+ * updated and the workflow will be detached from the document.
+ *
+ * @param object $user
+ * @param object $nextstate
+ * @return boolean true if the state could be reached
+ * false if not
+ */
+ function enterNextState($user, $nextstate) { /* {{{ */
+
+ /* run the pre condition of the next state. If it is not set
+ * the next state will be reached if one of the transitions
+ * leading to the given state can be processed.
+ */
+ if($nextstate->getPreCondFunc() == '') {
+ $transitions = $this->_workflow->getPreviousTransitions($nextstate);
+ foreach($transitions as $transition) {
+// echo "transition ".$transition->getID()." led to state ".$nextstate->getName()."
";
+ if($this->executeWorkflowTransitionIsAllowed($transition)) {
+// echo "stepping into next state
";
+ $this->setWorkflowState($nextstate);
+
+ /* Check if the new workflow state has a mapping into a
+ * document state. If yes, set the document state will
+ * be updated and the workflow will be removed from the
+ * document.
+ */
+ $docstate = $nextstate->getDocumentStatus();
+ if($docstate == S_RELEASED || $docstate == S_REJECTED) {
+ $this->setStatus($docstate, "Workflow has ended", $user);
+ /* Detach the workflow from the document, but keep the
+ * workflow log
+ */
+ $this->removeWorkflow($user, true);
+ return true ;
+ }
+
+ /* make sure the users and groups allowed to trigger the next
+ * transitions are also allowed to read the document
+ */
+ $transitions = $this->_workflow->getNextTransitions($nextstate);
+ foreach($transitions as $tran) {
+// echo "checking access for users/groups allowed to trigger transition ".$tran->getID()."
";
+ $transusers = $tran->getUsers();
+ foreach($transusers as $transuser) {
+ $u = $transuser->getUser();
+// echo $u->getFullName()."
";
+ if($this->_document->getAccessMode($u) < M_READ) {
+ $this->_document->addAccess(M_READ, $u->getID(), 1);
+// echo "granted read access
";
+ } else {
+// echo "has already access
";
+ }
+ }
+ $transgroups = $tran->getGroups();
+ foreach($transgroups as $transgroup) {
+ $g = $transgroup->getGroup();
+// echo $g->getName()."
";
+ if ($this->_document->getGroupAccessMode($g) < M_READ) {
+ $this->_document->addAccess(M_READ, $g->getID(), 0);
+// echo "granted read access
";
+ } else {
+// echo "has already access
";
+ }
+ }
+ }
+ return(true);
+ } else {
+// echo "transition not ready for process now
";
+ }
+ }
+ return false;
+ } else {
+ }
+
+ } /* }}} */
+
+ /**
+ * Get the so far logged operations on the document content within the
+ * workflow
+ *
+ * @return array list of operations
+ */
+ function getWorkflowLog($transition = null) { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if(!$this->_workflow)
+ $this->getWorkflow();
+
+ if(!$this->_workflow)
+ return false;
+
+ $queryStr=
+ "SELECT * FROM tblWorkflowLog WHERE `version`='".$this->_version ."' AND `document` = '". $this->_document->getID() ."' AND `workflow` = ". $this->_workflow->getID();
+ if($transition)
+ $queryStr .= " AND `transition` = ".$transition->getID();
+ $queryStr .= " ORDER BY `date`";
+ $resArr = $db->getResultArray($queryStr);
+ if (is_bool($resArr) && !$resArr)
+ return false;
+
+ $workflowlogs = array();
+ for ($i = 0; $i < count($resArr); $i++) {
+ $workflowlog = new LetoDMS_Core_Workflow_Log($resArr[$i]["id"], $this->_document->_dms->getDocument($resArr[$i]["document"]), $resArr[$i]["version"], $this->_workflow, $this->_document->_dms->getUser($resArr[$i]["userid"]), $this->_workflow->getTransition($resArr[$i]["transition"]), $resArr[$i]["date"], $resArr[$i]["comment"]);
+ $workflowlog->setDMS($this);
+ $workflowlogs[$i] = $workflowlog;
+ }
+
+ return $workflowlogs;
+ } /* }}} */
+
+ /**
+ * Get the latest logged transition for the document content within the
+ * workflow
+ *
+ * @return array list of operations
+ */
+ function getLastWorkflowTransition() { /* {{{ */
+ $db = $this->_document->_dms->getDB();
+
+ if(!$this->_workflow)
+ $this->getWorkflow();
+
+ if(!$this->_workflow)
+ return false;
+
+ $queryStr=
+ "SELECT * FROM tblWorkflowLog WHERE `version`='".$this->_version ."' AND `document` = '". $this->_document->getID() ."' AND `workflow` = ". $this->_workflow->getID();
+ $queryStr .= " ORDER BY `id` DESC LIMIT 1";
+ $resArr = $db->getResultArray($queryStr);
+ if (is_bool($resArr) && !$resArr)
+ return false;
+
+ $workflowlogs = array();
+ $i = 0;
+ $workflowlog = new LetoDMS_Core_Workflow_Log($resArr[$i]["id"], $this->_document->_dms->getDocument($resArr[$i]["document"]), $resArr[$i]["version"], $this->_workflow, $this->_document->_dms->getUser($resArr[$i]["userid"]), $this->_workflow->getTransition($resArr[$i]["transition"]), $resArr[$i]["date"], $resArr[$i]["comment"]);
+ $workflowlog->setDMS($this);
+
+ return $workflowlog;
+ } /* }}} */
+
} /* }}} */
@@ -2674,11 +3454,30 @@ echo $filesize;
* @version Release: @package_version@
*/
class LetoDMS_Core_DocumentLink { /* {{{ */
- var $_id;
- var $_document;
- var $_target;
- var $_userID;
- var $_public;
+ /**
+ * @var integer internal id of document link
+ */
+ protected $_id;
+
+ /**
+ * @var object reference to document this link belongs to
+ */
+ protected $_document;
+
+ /**
+ * @var object reference to target document this link points to
+ */
+ protected $_target;
+
+ /**
+ * @var integer id of user who is the owner of this link
+ */
+ protected $_userID;
+
+ /**
+ * @var integer 1 if this link is public, or 0 if is only visible to the owner
+ */
+ protected $_public;
function LetoDMS_Core_DocumentLink($id, $document, $target, $userID, $public) {
$this->_id = $id;
@@ -2726,16 +3525,58 @@ class LetoDMS_Core_DocumentLink { /* {{{ */
* @version Release: @package_version@
*/
class LetoDMS_Core_DocumentFile { /* {{{ */
- var $_id;
- var $_document;
- var $_userID;
- var $_comment;
- var $_date;
- var $_dir;
- var $_fileType;
- var $_mimeType;
- var $_orgFileName;
- var $_name;
+ /**
+ * @var integer internal id of document file
+ */
+ protected $_id;
+
+ /**
+ * @var object reference to document this file belongs to
+ */
+ protected $_document;
+
+ /**
+ * @var integer id of user who is the owner of this link
+ */
+ protected $_userID;
+
+ /**
+ * @var string comment for the attached file
+ */
+ protected $_comment;
+
+ /**
+ * @var string date when the file was attached
+ */
+ protected $_date;
+
+ /**
+ * @var string directory where the file is stored. This is the
+ * document id with a proceding '/'.
+ * FIXME: looks like this isn't used anymore. The file path is
+ * constructed by getPath()
+ */
+ protected $_dir;
+
+ /**
+ * @var string extension of the original file name with a leading '.'
+ */
+ protected $_fileType;
+
+ /**
+ * @var string mime type of the file
+ */
+ protected $_mimeType;
+
+ /**
+ * @var string name of the file that was originally uploaded
+ */
+ protected $_orgFileName;
+
+ /**
+ * @var string name of the file as given by the user
+ */
+ protected $_name;
function LetoDMS_Core_DocumentFile($id, $document, $userID, $comment, $date, $dir, $fileType, $mimeType, $orgFileName,$name) {
$this->_id = $id;
@@ -2793,24 +3634,23 @@ class LetoDMS_Core_DocumentFile { /* {{{ */
*/
class LetoDMS_Core_AddContentResultSet { /* {{{ */
- var $_indReviewers;
- var $_grpReviewers;
- var $_indApprovers;
- var $_grpApprovers;
- var $_content;
- var $_status;
-
- function LetoDMS_Core_AddContentResultSet($content) {
+ protected $_indReviewers;
+ protected $_grpReviewers;
+ protected $_indApprovers;
+ protected $_grpApprovers;
+ protected $_content;
+ protected $_status;
+ function LetoDMS_Core_AddContentResultSet($content) { /* {{{ */
$this->_content = $content;
$this->_indReviewers = null;
$this->_grpReviewers = null;
$this->_indApprovers = null;
$this->_grpApprovers = null;
$this->_status = null;
- }
+ } /* }}} */
- function addReviewer($reviewer, $type, $status) {
+ function addReviewer($reviewer, $type, $status) { /* {{{ */
if (!is_object($reviewer) || (strcasecmp($type, "i") && strcasecmp($type, "g")) && !is_integer($status)){
return false;
@@ -2834,9 +3674,9 @@ class LetoDMS_Core_AddContentResultSet { /* {{{ */
$this->_grpReviewers[$status][] = $reviewer;
}
return true;
- }
+ } /* }}} */
- function addApprover($approver, $type, $status) {
+ function addApprover($approver, $type, $status) { /* {{{ */
if (!is_object($approver) || (strcasecmp($type, "i") && strcasecmp($type, "g")) && !is_integer($status)){
return false;
@@ -2860,9 +3700,9 @@ class LetoDMS_Core_AddContentResultSet { /* {{{ */
$this->_grpApprovers[$status][] = $approver;
}
return true;
- }
+ } /* }}} */
- function setStatus($status) {
+ function setStatus($status) { /* {{{ */
if (!is_integer($status)) {
return false;
}
@@ -2871,13 +3711,17 @@ class LetoDMS_Core_AddContentResultSet { /* {{{ */
}
$this->_status = $status;
return true;
- }
+ } /* }}} */
- function getStatus() {
+ function getStatus() { /* {{{ */
return $this->_status;
- }
+ } /* }}} */
- function getReviewers($type) {
+ function getContent() { /* {{{ */
+ return $this->_content;
+ } /* }}} */
+
+ function getReviewers($type) { /* {{{ */
if (strcasecmp($type, "i") && strcasecmp($type, "g")) {
return false;
}
@@ -2887,9 +3731,9 @@ class LetoDMS_Core_AddContentResultSet { /* {{{ */
else {
return ($this->_grpReviewers == null ? array() : $this->_grpReviewers);
}
- }
+ } /* }}} */
- function getApprovers($type) {
+ function getApprovers($type) { /* {{{ */
if (strcasecmp($type, "i") && strcasecmp($type, "g")) {
return false;
}
@@ -2899,6 +3743,6 @@ class LetoDMS_Core_AddContentResultSet { /* {{{ */
else {
return ($this->_grpApprovers == null ? array() : $this->_grpApprovers);
}
- }
+ } /* }}} */
} /* }}} */
?>