function display_viewlet() { $oKTTemplating =& KTTemplating::getSingleton(); $oTemplate =& $oKTTemplating->loadTemplate("ktcore/document/viewlets/workflow"); if (is_null($oTemplate)) { return ""; } $oWorkflowState = KTWorkflowState::get($this->oDocument->getWorkflowStateId()); if (PEAR::isError($oWorkflowState)) { return ""; } $aDisplayTransitions = array(); $aTransitions = KTWorkflowUtil::getTransitionsForDocumentUser($this->oDocument, $this->oUser); if (empty($aTransitions)) { return ""; } // Check if the document has been checked out $bIsCheckedOut = $this->oDocument->getIsCheckedOut(); $iId = $this->oDocument->getId(); if ($bIsCheckedOut) { // If document is checked out, don't link into the workflow. $aDisplayTransitions = array(); } else { foreach ($aTransitions as $oTransition) { if (is_null($oTransition) || PEAR::isError($oTransition)) { continue; } $aDisplayTransitions[] = array('url' => KTUtil::ktLink('action.php', 'ktcore.actions.document.workflow', array("fDocumentId" => $iId, "action" => "quicktransition", "fTransitionId" => $oTransition->getId())), 'name' => $oTransition->getName()); } } //Retreive the comment for the previous transition $aCommentQuery = array("SELECT comment FROM document_transactions\n where transaction_namespace='ktcore.transactions.workflow_state_transition'\n AND document_id = ?\n ORDER BY id DESC LIMIT 1;"); $aCommentQuery[] = array($iId); $aTransitionComments = DBUtil::getResultArray($aCommentQuery); $oLatestTransitionComment = null; if (!empty($aTransitionComments)) { $aRow = $aTransitionComments[0]; $oLatestTransitionComment = $aRow['comment']; $iCommentPosition = strpos($oLatestTransitionComment, ':'); //comment found after first colon in string if ($iCommentPosition > 0) { $oLatestTransitionComment = substr($oLatestTransitionComment, $iCommentPosition + 2, strlen($oLatestTransitionComment) - $iCommentPosition); } else { $oLatestTransitionComment = null; } } $oTemplate->setData(array('context' => $this, 'bIsCheckedOut' => $bIsCheckedOut, 'transitions' => $aDisplayTransitions, 'state_name' => $oWorkflowState->getName(), 'comment' => $oLatestTransitionComment)); return $oTemplate->render(); }
function finalise() { $fWizardKey = KTUtil::arrayGet($_REQUEST, 'fWizardKey'); if (!empty($fWizardKey)) { $this->errorRedirectToMain(_kt("Could not create workflow.")); exit; } $wiz_data = $_SESSION['_wiz_data'][$fWizardKey]; // gather all our data. we're sure this is all good and healthy. $states = $wiz_data['states']; $transitions = $wiz_data['transitions']; $from = $wiz_data['from']; $to = $wiz_data['to']; $initial_state = $wiz_data['initial_state']; $workflow_name = $wiz_data['workflow_name']; $this->startTransaction(); // create the initial workflow $oWorkflow = KTWorkflow::createFromArray(array('name' => $workflow_name, 'humanname' => $workflow_name, 'enabled' => true)); if (PEAR::isError($oWorkflow)) { $this->errorRedirectToMain(sprintf(_kt("Failed to create workflow: %s"), $oWorkflow->getMessage())); } $iWorkflowId = $oWorkflow->getId(); // create the states. $aStates = array(); foreach ($states as $state_name) { $oState = KTWorkflowState::createFromArray(array('workflowid' => $iWorkflowId, 'name' => $state_name, 'humanname' => $state_name)); if (PEAR::isError($oState)) { $this->errorRedirectToMain(sprintf(_kt("Failed to create state: %s"), $oState->getMessage())); } $aStates[$state_name] = $oState; } // update the initial state on workflow $oInitialState = $aStates[$initial_state]; $oWorkflow->setStartStateId($oInitialState->getId()); $res = $oWorkflow->update(); if (PEAR::isError($res)) { $this->errorRedirectToMain(sprintf(_kt("Failed to update workflow: %s"), $res->getMessage())); } // next, we create and hook up the transitions. $aTransitions = array(); foreach ($transitions as $transition) { $dest_name = $to[$transition]; $oDestState = $aStates[$dest_name]; $oTransition = KTWorkflowTransition::createFromArray(array("WorkflowId" => $iWorkflowId, "Name" => $transition, "HumanName" => $transition, "TargetStateId" => $oDestState->getId(), "GuardPermissionId" => null, "GuardGroupId" => null, "GuardRoleId" => null, "GuardConditionId" => null)); if (PEAR::isError($oTransition)) { $this->errorRedirectToMain(sprintf(_kt("Failed to create transition: %s"), $oTransition->getMessage())); } // hook up source states. $state_ids = array(); $sources = (array) $from[$transition]; foreach ($sources as $state_name) { // must exist. $oState = $aStates[$state_name]; $state_ids[] = $oState->getId(); } $res = KTWorkflowAdminUtil::saveTransitionSources($oTransition, $state_ids); if (PEAR::isError($res)) { $this->errorRedirectToMain(sprintf(_kt("Failed to set transition origins: %s"), $res->getMessage())); } } $this->commitTransaction(); // finally, we want to redirect the user to the parent dispatcher somehow. // FIXME nbm: how do you recommend we do this? $base = $_SERVER['PHP_SELF']; $qs = sprintf("action=view&fWorkflowId=%d", $oWorkflow->getId()); $url = KTUtil::addQueryString($base, $qs); $this->addInfoMessage(_kt("Your new workflow has been created. You may want to configure security and notifications from the menu on the left.")); redirect($url); }
/** * This returns detailed information on the document. * * @author KnowledgeTree Team * @access public * @return array The document information */ function get_detail() { global $default; // make sure we ge tthe latest $this->clearCache(); $config = KTConfig::getSingleton(); $wsversion = $config->get('webservice/version', LATEST_WEBSERVICE_VERSION); $detail = array(); $document = $this->document; // get the document id $detail['document_id'] = (int) $document->getId(); $oem_document_no = null; if ($wsversion >= 2) { $oem_document_no = $document->getOemNo(); } if (empty($oem_document_no)) { $oem_document_no = 'n/a'; } $detail['custom_document_no'] = 'n/a'; $detail['oem_document_no'] = $oem_document_no; // get the title $detail['title'] = $document->getName(); // get the document type $documenttypeid = $document->getDocumentTypeID(); $documenttype = '* unknown *'; if (is_numeric($documenttypeid)) { $dt = DocumentType::get($documenttypeid); if (!is_null($dt) && !PEAR::isError($dt)) { $documenttype = $dt->getName(); } } $detail['document_type'] = $documenttype; // get the filename $detail['filename'] = $document->getFilename(); // get the filesize $detail['filesize'] = (int) $document->getFileSize(); // get the folder id $detail['folder_id'] = (int) $document->getFolderID(); // get the creator $userid = $document->getCreatorID(); $username = '******'; if (is_numeric($userid)) { $username = '******'; $user = User::get($userid); if (!is_null($user) && !PEAR::isError($user)) { $username = $user->getName(); } } $detail['created_by'] = $username; // get the creation date $detail['created_date'] = $document->getCreatedDateTime(); // get the checked out user $userid = $document->getCheckedOutUserID(); $username = '******'; if (is_numeric($userid)) { $username = '******'; $user = User::get($userid); if (!is_null($user) && !PEAR::isError($user)) { $username = $user->getName(); } } $detail['checked_out_by'] = $username; // get the checked out date list($major, $minor, $fix) = explode('.', $default->systemVersion); if ($major == 3 && $minor >= 5) { $detail['checked_out_date'] = $document->getCheckedOutDate(); } else { $detail['checked_out_date'] = $detail['modified_date']; } if (is_null($detail['checked_out_date'])) { $detail['checked_out_date'] = 'n/a'; } // get the modified user $userid = $document->getModifiedUserId(); $username = '******'; if (is_numeric($userid)) { $username = '******'; $user = User::get($userid); if (!is_null($user) && !PEAR::isError($user)) { $username = $user->getName(); } } $detail['modified_by'] = $detail['updated_by'] = $username; // get the modified date $detail['updated_date'] = $detail['modified_date'] = $document->getLastModifiedDate(); // get the owner $userid = $document->getOwnerID(); $username = '******'; if (is_numeric($userid)) { $username = '******'; $user = User::get($userid); if (!is_null($user) && !PEAR::isError($user)) { $username = $user->getName(); } } $detail['owned_by'] = $username; // get the version $detail['version'] = $document->getVersion(); if ($wsversion >= 2) { $detail['version'] = (double) $detail['version']; } //might be unset at the bottom in case of old webservice version //make sure we're using the real document for this one $this->document->switchToRealCore(); $detail['linked_document_id'] = $document->getLinkedDocumentId(); $this->document->switchToLinkedCore(); // check immutability $detail['is_immutable'] = (bool) $document->getImmutable(); // check permissions $detail['permissions'] = KTAPI_Document::get_permission_string($document); // get workflow name $workflowid = $document->getWorkflowId(); $workflowname = 'n/a'; if (is_numeric($workflowid)) { $workflow = KTWorkflow::get($workflowid); if (!is_null($workflow) && !PEAR::isError($workflow)) { $workflowname = $workflow->getName(); } } $detail['workflow'] = $workflowname; // get the workflow state $stateid = $document->getWorkflowStateId(); $workflowstate = 'n/a'; if (is_numeric($stateid)) { $state = KTWorkflowState::get($stateid); if (!is_null($state) && !PEAR::isError($state)) { $workflowstate = $state->getName(); } } $detail['workflow_state'] = $workflowstate; // get the full path $detail['full_path'] = '/' . $this->document->getFullPath(); // get mime info $mimetypeid = $document->getMimeTypeID(); $detail['mime_type'] = KTMime::getMimeTypeName($mimetypeid); $detail['mime_icon_path'] = KTMime::getIconPath($mimetypeid); $detail['mime_display'] = KTMime::getFriendlyNameForString($detail['mime_type']); // get the storage path $detail['storage_path'] = $document->getStoragePath(); if ($wsversion >= 2) { unset($detail['updated_by']); unset($detail['updated_date']); } if ($wsversion < 3) { unset($detail['linked_document_id']); } return $detail; }
/** * Performs a workflow transition on a document, changing it from * one workflow state to another, with potential side effects (user * scripts, and so forth). * * This function currently assumes that the user in question is * allowed to perform the transition and that all the guard * functionality on the transition has passed. */ function performTransitionOnDocument($oTransition, $oDocument, $oUser, $sComments) { $oWorkflow =& KTWorkflow::getByDocument($oDocument); if (empty($oWorkflow)) { return PEAR::raiseError(_kt("Document has no workflow")); } if (PEAR::isError($oWorkflow)) { return $oWorkflow; } $oSourceState =& KTWorkflowUtil::getWorkflowStateForDocument($oDocument); // walk the action triggers. $aActionTriggers = KTWorkflowUtil::getActionTriggersForTransition($oTransition); if (PEAR::isError($aActionTriggers)) { return $aActionTriggers; // error out? } foreach ($aActionTriggers as $oTrigger) { $res = $oTrigger->precheckTransition($oDocument, $oUser); if (PEAR::isError($res)) { return $res; } } $iPreviousMetadataVersion = $oDocument->getMetadataVersionId(); $oDocument->startNewMetadataVersion($oUser); KTDocumentUtil::copyMetadata($oDocument, $iPreviousMetadataVersion); $iStateId = $oTransition->getTargetStateId(); $oDocument->setWorkflowStateId($iStateId); $res = $oDocument->update(); if (PEAR::isError($res)) { return $res; } $oTargetState =& KTWorkflowState::get($iStateId); $sSourceState = $oSourceState->getName(); $sTargetState = $oTargetState->getName(); // create the document transaction record $sTransactionComments = sprintf(_kt("Workflow state changed from %s to %s"), $sSourceState, $sTargetState); if ($sComments) { $sTransactionComments .= _kt("; Reason given was: ") . $sComments; } $oDocumentTransaction = new DocumentTransaction($oDocument, $sTransactionComments, 'ktcore.transactions.workflow_state_transition'); $oDocumentTransaction->create(); // walk the action triggers. foreach ($aActionTriggers as $oTrigger) { $res = $oTrigger->performTransition($oDocument, $oUser); if (PEAR::isError($res)) { return $res; } } KTPermissionUtil::updatePermissionLookup($oDocument); KTWorkflowUtil::informUsersForState($oTargetState, KTWorkflowUtil::getInformedForState($oTargetState), $oDocument, $oUser, $sComments); return true; }
function &getByDocument($oDocument) { $oDocument =& KTUtil::getObject('Document', $oDocument); $iStateId = $oDocument->getWorkflowStateId(); if (PEAR::isError($iStateId)) { return $iStateId; } if (is_null($iStateId)) { return $iStateId; } return KTWorkflowState::get($iStateId); }
function getSourceStates($oTransition, $aOptions = null) { $bIds = KTUtil::arrayGet($aOptions, 'ids'); $sTable = KTUtil::getTableName('workflow_state_transitions'); $aQuery = array("SELECT state_id FROM {$sTable} WHERE transition_id = ?", array($oTransition->getId())); $aStateIds = DBUtil::getResultArrayKey($aQuery, 'state_id'); if (PEAR::isError($aStateIds)) { return $aStateIds; } if ($bIds) { return $aStateIds; } $aRet = array(); foreach ($aStateIds as $iId) { $aRet[] =& KTWorkflowState::get($iId); } return $aRet; }
function get_graph($oWorkflow) { $fontsize = 11.0; $fontname = "Times-Roman"; $opts = array('fontsize' => $fontsize, 'fontname' => $fontname); $graph = new Image_GraphViz(true, $opts); $graph->dotCommand = $this->dotCommand; // we need all states & transitions // FIXME do we want guards? // we want to enable link-editing, and indicate that transitions "converge" // so we use a temporary "node" for transitions // we also use a "fake" URL which we catch later // so we can give good "alt" tags. $states = KTWorkflowState::getByWorkflow($oWorkflow); $transitions = KTWorkflowTransition::getByWorkflow($oWorkflow); $this->state_names = array(); $this->transition_names = array(); $state_opts = array('shape' => 'box', 'fontsize' => $fontsize, 'fontname' => $fontname); $transition_opts = array('shape' => 'box', 'color' => '#ffffff', 'fontsize' => $fontsize, 'fontname' => $fontname); $finaltransition_opts = array('color' => '#333333'); $sourcetransition_opts = array('color' => '#999999'); // to make this a little more useful, we want to cascade our output from // start to end states - this will tend to give a better output. // // to do this, we need to order our nodes in terms of "nearness" to the // initial node. $processing_nodes = array(); $sorted_ids = array(); $availability = array(); $sources = array(); $destinations = array(); $states = KTUtil::keyArray($states); $transitions = KTUtil::keyArray($transitions); foreach ($transitions as $tid => $oTransition) { $sources[$tid] = KTWorkflowAdminUtil::getSourceStates($oTransition, array('ids' => true)); $destinations[$tid] = $oTransition->getTargetStateId(); foreach ($sources[$tid] as $sourcestateid) { $av = (array) KTUtil::arrayGet($availability, $sourcestateid, array()); $av[] = $tid; $availability[$sourcestateid] = $av; } } //var_dump($sources); exit(0); //var_dump($availability); exit(0); $processing = array($oWorkflow->getStartStateId()); while (!empty($processing)) { $active = array_shift($processing); if (!$processing_nodes[$active]) { // mark that we've seen this node $processing_nodes[$active] = true; $sorted[] = $active; // now add all reachable nodes to the *end* of the queue. foreach ((array) $availability[$active] as $tid) { $next = $destinations[$tid]; if (!$processing_nodes[$next]) { $processing[] = $next; } } } //var_dump($processing); } //var_dump($sorted); exit(0); foreach ($sorted as $sid) { $oState = $states[$sid]; $this->state_names[$oState->getId()] = $oState->getHumanName(); $local_opts = array('URL' => sprintf("s%d", $oState->getId()), 'label' => $oState->getHumanName(), 'color' => '#666666'); if ($oState->getId() == $oWorkflow->getStartStateId()) { $local_opts['color'] = '#000000'; $local_opts['style'] = 'filled'; $local_opts['fillcolor'] = '#cccccc'; } $graph->addNode(sprintf('state%d', $oState->getId()), KTUtil::meldOptions($state_opts, $local_opts)); } foreach ($transitions as $tid => $oTransition) { $name = sprintf('transition%d', $tid); $this->transition_names[$oTransition->getId()] = $oTransition->getHumanName(); // we "cheat" and use $graph->addNode($name, KTUtil::meldOptions($transition_opts, array('URL' => sprintf("t%d", $tid), 'label' => $oTransition->getHumanName()))); $dest = sprintf("state%d", $oTransition->getTargetStateId()); $graph->addEdge(array($name => $dest), $finaltransition_opts); foreach ($sources[$tid] as $source_id) { $source_name = sprintf("state%d", $source_id); $graph->addEdge(array($source_name => $name), $sourcetransition_opts); } } // some simple analysis $errors = array(); $info = array(); $sourceless_transitions = array(); foreach ($transitions as $tid => $oTransition) { if (empty($sources[$tid])) { $sourceless_transitions[] = $oTransition->getHumanName(); } } if (!empty($sourceless_transitions)) { $errors[] = sprintf(_kt("Some transitions have no source states: %s"), implode(', ', $sourceless_transitions)); } $unlinked_states = array(); foreach ($states as $sid => $oState) { if (!$processing_nodes[$sid]) { // quick sanity check $unlinked_states[] = $oState->getHumanName(); } } if (!empty($unlinked_states)) { $errors[] = sprintf(_kt("Some states cannot be reached from the initial state (<strong>%s</strong>): %s"), $states[$oWorkflow->getStartStateId()]->getHumanName(), implode(', ', $unlinked_states)); } $data = array('graph' => $graph, 'errors' => $errors, 'info' => $info); return $data; }
/** * Create a table of the document metadata. * Hard coded for the moment * * @return unknown */ function getMetadata() { /* Get document info */ // Filename $sFilenameLb = _kt('Document Filename: '); $sFilename = $this->_oDocument->getFileName(); // Mime type $sMimeTypeLb = _kt('File is a: '); $iMimeId = $this->_oDocument->getMimeTypeID(); $sMimeType = KTMime::getMimeTypeName($iMimeId); $sMimeType = KTMime::getFriendlyNameForString($sMimeType); // Version $sVersionLb = _kt('Document Version: '); $iVersion = $this->_oDocument->getVersion(); // Created by $sCreatedByLb = _kt('Created by: '); $iCreatorId = $this->_oDocument->getCreatorID(); $sCreated = $this->_oDocument->getCreatedDateTime(); $oCreator = User::get($iCreatorId); $sCreatedBy = $oCreator->getName() . ' (' . $sCreated . ')'; // Owned by $sOwnedByLb = _kt('Owned by: '); $iOwnedId = $this->_oDocument->getOwnerID(); $oOwner = User::get($iOwnedId); $sOwnedBy = $oOwner->getName(); // Last update by $iModifiedId = $this->_oDocument->getModifiedUserId(); $sLastUpdatedByLb = ''; $sLastUpdatedBy = ''; if (!empty($iModifiedId)) { $sLastUpdatedByLb = _kt('Last updated by: '); $sModified = $this->_oDocument->getLastModifiedDate(); $oModifier = User::get($iModifiedId); $sLastUpdatedBy = $oModifier->getName() . ' (' . $sModified . ')'; } // Document type $sDocTypeLb = _kt('Document Type: '); $iDocTypeId = $this->_oDocument->getDocumentTypeID(); $oDocType = DocumentType::get($iDocTypeId); $sDocType = $oDocType->getName(); // Workflow $iWFId = $this->_oDocument->getWorkflowId(); $sWF = ''; $sWFLb = ''; if (!empty($iWFId)) { $sWFLb = _kt('Workflow: '); $iWFStateId = $this->_oDocument->getWorkflowStateId(); $oWF = KTWorkflow::get($iWFId); $sWF = $oWF->getHumanName(); $oWFState = KTWorkflowState::get($iWFStateId); $sWF .= ' (' . $oWFState->getHumanName() . ')'; } // Checked out by $sCheckedLb = ''; $sCheckedOutBy = ''; if ($this->_oDocument->getIsCheckedOut()) { $sCheckedLb = _kt('Checked out by: '); $iCheckedID = $this->_oDocument->getCheckedOutUserID(); $oCheckedUser = User::get($iCheckedID); $sCheckedOutBy = $oCheckedUser->getName(); } // Id $sIdLb = _kt('Document ID: '); $sId = $this->_IDocId; /* Create table */ $sInfo = "<div style='float:left; width:405px;'>\n <table cellspacing='3px' cellpadding='3px' width='405px'>\n <tr><td>{$sFilenameLb}</td><td><b>{$sFilename}</b></td></tr>\n <tr><td>{$sMimeTypeLb}</td><td><b>{$sMimeType}</b></td></tr>\n <tr><td>{$sVersionLb}</td><td><b>{$iVersion}</b></td></tr>\n <tr><td>{$sCreatedByLb}</td><td><b>{$sCreatedBy}</b></td></tr>\n <tr><td>{$sOwnedByLb}</td><td><b>{$sOwnedBy}</b></td></tr>"; if (!empty($sLastUpdatedBy)) { $sInfo .= "<tr><td>{$sLastUpdatedByLb}</td><td><b>{$sLastUpdatedBy}</b></td></tr>"; } $sInfo .= "<tr><td>{$sDocTypeLb}</td><td><b>{$sDocType}</b></td></tr>"; if (!empty($sWF)) { $sInfo .= "<tr><td>{$sWFLb}</td><td><b>{$sWF}</b></td></tr>"; } if (!empty($sCheckedOutBy)) { $sInfo .= "<tr><td>{$sCheckedLb}</td><td><b>{$sCheckedOutBy}</b></td></tr>"; } $sInfo .= "<tr><td>{$sIdLb}</td><td><b>{$sId}</b></td></tr>"; $sInfo .= " </table></div>"; return $sInfo; }
function showDescription() { $oWorkflowState =& KTWorkflowState::get($this->getTargetStateId()); return sprintf(_kt("%s (to state %s)"), $this->getName(), $oWorkflowState->getName()); }