From ff00441f68d4082337dc7ce8025f2a5bb6169e19 Mon Sep 17 00:00:00 2001 From: kevin_fourie Date: Wed, 20 Jun 2007 10:19:38 +0000 Subject: [PATCH] Merged in from DEV trunk... --- ktapi/KTAPIConstants.inc.php | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ktapi/KTAPIDocument.inc.php | 1168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ktapi/KTAPIFolder.inc.php | 586 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ktapi/KTAPISession.inc.php | 343 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ktapi/ktapi.inc.php | 2067 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ lib/documentmanagement/Document.inc | 6 +++++- lib/foldermanagement/folderutil.inc.php | 14 +++++++++----- lib/templating/kt3template.inc.php | 1 + tests/runtests.php | 4 ++++ thirdparty/simpletest/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE | 26 ++++++++++++++++++++++++-- thirdparty/simpletest/simpletest/LICENSE | 4 ++-- thirdparty/simpletest/simpletest/README | 2 +- thirdparty/simpletest/simpletest/VERSION | 2 +- thirdparty/simpletest/simpletest/browser.php | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- thirdparty/simpletest/simpletest/collector.php | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------------------------------------- thirdparty/simpletest/simpletest/compatibility.php | 27 ++++++++------------------- thirdparty/simpletest/simpletest/dumper.php | 42 ------------------------------------------ thirdparty/simpletest/simpletest/encoding.php | 2 +- thirdparty/simpletest/simpletest/errors.php | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------- thirdparty/simpletest/simpletest/exceptions.php | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ thirdparty/simpletest/simpletest/expectation.php | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- thirdparty/simpletest/simpletest/extensions/pear_test_case.php | 16 +++++++++------- thirdparty/simpletest/simpletest/extensions/phpunit_test_case.php | 2 +- thirdparty/simpletest/simpletest/mock_objects.php | 102 +++++++++++++++++++++++------------------------------------------------------------------------------- thirdparty/simpletest/simpletest/parser.php | 16 +++++++++------- thirdparty/simpletest/simpletest/reflection_php5.php | 39 ++++++++++++++++++++++----------------- thirdparty/simpletest/simpletest/remote.php | 6 ++++-- thirdparty/simpletest/simpletest/reporter.php | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- thirdparty/simpletest/simpletest/scorer.php | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- thirdparty/simpletest/simpletest/shell_tester.php | 31 +++++++++++++++++++++++++++++-- thirdparty/simpletest/simpletest/simpletest.php | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- thirdparty/simpletest/simpletest/test_case.php | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------------------------------------------------------------- thirdparty/simpletest/simpletest/unit_tester.php | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------- thirdparty/simpletest/simpletest/url.php | 4 ++-- thirdparty/simpletest/simpletest/web_tester.php | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------- thirdparty/simpletest/simpletest/xml.php | 51 ++++++++++++++++++++++++++++++++++++++++++--------- 36 files changed, 3639 insertions(+), 2630 deletions(-) create mode 100644 ktapi/KTAPIConstants.inc.php create mode 100644 ktapi/KTAPIDocument.inc.php create mode 100644 ktapi/KTAPIFolder.inc.php create mode 100644 ktapi/KTAPISession.inc.php create mode 100644 thirdparty/simpletest/simpletest/exceptions.php diff --git a/ktapi/KTAPIConstants.inc.php b/ktapi/KTAPIConstants.inc.php new file mode 100644 index 0000000..fe555c8 --- /dev/null +++ b/ktapi/KTAPIConstants.inc.php @@ -0,0 +1,72 @@ + \ No newline at end of file diff --git a/ktapi/KTAPIDocument.inc.php b/ktapi/KTAPIDocument.inc.php new file mode 100644 index 0000000..a0ba1cd --- /dev/null +++ b/ktapi/KTAPIDocument.inc.php @@ -0,0 +1,1168 @@ +documentid; + } + + /** + * This is used to get a document based on document id. + * + * @static + * @access public + * @param KTAPI $ktapi + * @param int $documentid + * @return KTAPI_Document + */ + function &get(&$ktapi, $documentid) + { + assert(!is_null($ktapi)); + assert(is_a($ktapi, 'KTAPI')); + assert(is_numeric($documentid)); + + $documentid += 0; + + $document = &Document::get($documentid); + if (is_null($document) || PEAR::isError($document)) + { + return new KTAPI_Error(KTAPI_ERROR_DOCUMENT_INVALID,$document ); + } + + $user = $ktapi->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ); + + if (is_null($user) || PEAR::isError($user)) + { + return $user; + } + + $folderid = $document->getParentID(); + + if (!is_null($folderid)) + { + $ktapi_folder = &KTAPI_Folder::get($ktapi, $folderid); + } + else + { + $ktapi_folder = null; + } + // We don't do any checks on this folder as it could possibly be deleted, and is not required right now. + + return new KTAPI_Document($ktapi, $ktapi_folder, $document); + } + + function is_deleted() + { + return ($this->document->getStatusID() == 3); + } + + /** + * This is the constructor for the KTAPI_Folder. + * + * @access private + * @param KTAPI $ktapi + * @param Document $document + * @return KTAPI_Document + */ + function KTAPI_Document(&$ktapi, &$ktapi_folder, &$document) + { + assert(is_a($ktapi,'KTAPI')); + assert(is_null($ktapi_folder) || is_a($ktapi_folder,'KTAPI_Folder')); + + $this->ktapi = &$ktapi; + $this->ktapi_folder = &$ktapi_folder; + $this->document = &$document; + $this->documentid = $document->getId(); + } + + /** + * This checks a document into the repository + * + * @param string $filename + * @param string $reason + * @param string $tempfilename + * @param bool $major_update + */ + function checkin($filename, $reason, $tempfilename, $major_update=false) + { + if (!is_file($tempfilename)) + { + return new PEAR_Error('File does not exist.'); + } + + $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($user)) + { + return $user; + } + + if (!$this->document->getIsCheckedOut()) + { + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT); + } + + $options = array('major_update'=>$major_update); + + $currentfilename = $this->document->getFileName(); + if ($filename != $currentfilename) + { + $options['newfilename'] = $filename; + } + + DBUtil::startTransaction(); + $result = KTDocumentUtil::checkin($this->document, $tempfilename, $reason, $user, $options); + + if (PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$result); + } + DBUtil::commit(); + + $tempfilename=addslashes($tempfilename); + $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'"; + $result = DBUtil::runQuery($sql); + if (PEAR::isError($result)) + { + return $result; + } + + } + + + function is_checked_out() + { + return ($this->document->getIsCheckedOut()); + } + + /** + * This reverses the checkout process. + * + * @param string $reason + */ + function undo_checkout($reason) + { + $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($user)) + { + return $user; + } + + if (!$this->document->getIsCheckedOut()) + { + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT); + } + + DBUtil::startTransaction(); + + $this->document->setIsCheckedOut(0); + $this->document->setCheckedOutUserID(-1); + $res = $this->document->update(); + if (($res === false) || PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res); + } + + $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.force_checkin'); + + $res = $oDocumentTransaction->create(); + if (($res === false) || PEAR::isError($res)) { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res); + } + DBUtil::commit(); + } + + /** + * This returns a URL to the file that can be downloaded. + * + * @param string $reason + */ + function checkout($reason) + { + $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($user)) + { + return $user; + } + + if ($this->document->getIsCheckedOut()) + { + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); + } + + DBUtil::startTransaction(); + $res = KTDocumentUtil::checkout($this->document, $reason, $user); + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res); + } + + DBUtil::commit(); + } + + /** + * This deletes a document from the folder. + * + * @param string $reason + */ + function delete($reason) + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DELETE); + + if (PEAR::isError($user)) + { + return $user; + } + + if ($this->document->getIsCheckedOut()) + { + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); + } + + DBUtil::startTransaction(); + $res = KTDocumentUtil::delete($this->document, $reason); + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res); + } + + DBUtil::commit(); + } + + /** + * This changes the owner of the file. + * + * @param string $ktapi_newuser + */ + function change_owner($newusername, $reason='Changing of owner.') + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_CHANGE_OWNERSHIP); + + if (PEAR::isError($user)) + { + return $user; + } + + DBUtil::startTransaction(); + + $user = &User::getByUserName($newusername); + if (is_null($user) || PEAR::isError($user)) + { + return new KTAPI_Error('User could not be found',$user); + } + + $newuserid = $user->getId(); + + $this->document->setOwnerID($newuserid); + + $res = $this->document->update(); + + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR ,$res ); + } + + $res = KTPermissionUtil::updatePermissionLookup($this->document); + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res ); + } + + $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.permissions_change'); + + $res = $oDocumentTransaction->create(); + if (($res === false) || PEAR::isError($res)) { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res ); + } + + DBUtil::commit(); + } + + /** + * This copies the document to another folder. + * + * @param KTAPI_Folder $ktapi_target_folder + * @param string $reason + * @param string $newname + * @param string $newfilename + */ + function copy(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null) + { + assert(!is_null($ktapi_target_folder)); + assert(is_a($ktapi_target_folder,'KTAPI_Folder')); + + if (empty($newname)) + { + $newname=null; + } + if (empty($newfilename)) + { + $newfilename=null; + } + + $user = $this->ktapi->get_user(); + + if ($this->document->getIsCheckedOut()) + { + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); + } + + $target_folder = &$ktapi_target_folder->get_folder(); + + $result = $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($result)) + { + return $result; + } + + $name = $this->document->getName(); + $clash = KTDocumentUtil::nameExists($target_folder, $name); + if ($clash && !is_null($newname)) + { + $name = $newname; + $clash = KTDocumentUtil::nameExists($target_folder, $name); + } + if ($clash) + { + return new PEAR_Error('A document with this title already exists in your chosen folder. Please choose a different folder, or specify a new title for the copied document.'); + } + + $filename=$this->document->getFilename(); + $clash = KTDocumentUtil::fileExists($target_folder, $filename); + + if ($clash && !is_null($newname)) + { + $filename = $newfilename; + $clash = KTDocumentUtil::fileExists($target_folder, $filename); + } + if ($clash) + { + return new PEAR_Error('A document with this filename already exists in your chosen folder. Please choose a different folder, or specify a new filename for the copied document.'); + } + + DBUtil::startTransaction(); + + $new_document = KTDocumentUtil::copy($this->document, $target_folder, $reason); + if (PEAR::isError($new_document)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$new_document ); + } + + $new_document->setName($name); + $new_document->setFilename($filename); + + $res = $new_document->update(); + + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res ); + } + + DBUtil::commit(); + + // FIXME do we need to refactor all trigger usage into the util function? + $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); + $aTriggers = $oKTTriggerRegistry->getTriggers('copyDocument', 'postValidate'); + foreach ($aTriggers as $aTrigger) { + $sTrigger = $aTrigger[0]; + $oTrigger = new $sTrigger; + $aInfo = array( + 'document' => $new_document, + 'old_folder' => $this->folder->get_folder(), + 'new_folder' => $target_folder, + ); + $oTrigger->setInfo($aInfo); + $ret = $oTrigger->postValidate(); + } + } + + /** + * This moves the document to another folder. + * + * @param KTAPI_Folder $ktapi_target_folder + * @param string $reason + * @param string $newname + * @param string $newfilename + */ + function move(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null) + { + assert(!is_null($ktapi_target_folder)); + assert(is_a($ktapi_target_folder,'KTAPI_Folder')); + + if (empty($newname)) + { + $newname=null; + } + if (empty($newfilename)) + { + $newfilename=null; + } + + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DOCUMENT_MOVE); + + if (PEAR::isError($user)) + { + return $user; + } + + if ($this->document->getIsCheckedOut()) + { + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); + } + + $target_folder = $ktapi_target_folder->get_folder(); + + $result= $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($result)) + { + return $result; + } + + if (!KTDocumentUtil::canBeMoved($this->document)) + { + return new PEAR_Error('Document cannot be moved.'); + } + + $name = $this->document->getName(); + $clash = KTDocumentUtil::nameExists($target_folder, $name); + if ($clash && !is_null($newname)) + { + $name = $newname; + $clash = KTDocumentUtil::nameExists($target_folder, $name); + } + if ($clash) + { + return new PEAR_Error('A document with this title already exists in your chosen folder. Please choose a different folder, or specify a new title for the moved document.'); + } + + $filename=$this->document->getFilename(); + $clash = KTDocumentUtil::fileExists($target_folder, $filename); + + if ($clash && !is_null($newname)) + { + $filename = $newfilename; + $clash = KTDocumentUtil::fileExists($target_folder, $filename); + } + if ($clash) + { + return new PEAR_Error('A document with this filename already exists in your chosen folder. Please choose a different folder, or specify a new filename for the moved document.'); + } + + DBUtil::startTransaction(); + + $res = KTDocumentUtil::move($this->document, $target_folder, $user, $reason); + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res ); + } + + $this->document->setName($name); + $this->document->setFilename($filename); + + $res = $this->document->update(); + + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res ); + } + + DBUtil::commit(); + } + + /** + * This changes the filename of the document. + * + * @param string $newname + */ + function renameFile($newname) + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($user)) + { + return $user; + } + + DBUtil::startTransaction(); + $res = KTDocumentUtil::rename($this->document, $newname, $user); + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res ); + } + DBUtil::commit(); + } + + /** + * This changes the document type of the document. + * + * @param string $newname + */ + function change_document_type($documenttype) + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($user)) + { + return $user; + } + + $doctypeid = KTAPI::get_documenttypeid($documenttype); + + if ($this->document->getDocumentTypeId() != $doctypeid) + { + DBUtil::startTransaction(); + $this->document->setDocumentTypeId($doctypeid); + $res = $this->document->update(); + + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res ); + } + DBUtil::commit(); + } + } + + /** + * This changes the title of the document. + * + * @param string $newname + */ + function rename($newname) + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($user)) + { + return $user; + } + + if ($this->document->getName() != $newname) + { + + DBUtil::startTransaction(); + $this->document->setName($newname); + $res = $this->document->update(); + + if (PEAR::isError($res)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res); + } + DBUtil::commit(); + } + } + + /** + * This flags the document as 'archived'. + * + * @param string $reason + */ + function archive($reason) + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($user)) + { + return $user; + } + + list($permission, $user) = $perm_and_user; + + DBUtil::startTransaction(); + $this->document->setStatusID(ARCHIVED); + $res = $this->document->update(); + if (($res === false) || PEAR::isError($res)) { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res); + } + + $oDocumentTransaction = & new DocumentTransaction($this->document, sprintf(_kt('Document archived: %s'), $reason), 'ktcore.transactions.update'); + $oDocumentTransaction->create(); + + DBUtil::commit(); + + $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); + $aTriggers = $oKTTriggerRegistry->getTriggers('archive', 'postValidate'); + foreach ($aTriggers as $aTrigger) + { + $sTrigger = $aTrigger[0]; + $oTrigger = new $sTrigger; + $aInfo = array( + 'document' => $this->document, + ); + $oTrigger->setInfo($aInfo); + $ret = $oTrigger->postValidate(); + } + } + + /** + * This starts a workflow on a document. + * + * @param string $workflow + */ + function start_workflow($workflow) + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); + + if (PEAR::isError($user)) + { + return $user; + } + + $workflowid = $this->document->getWorkflowId(); + + if (!empty($workflowid)) + { + return new PEAR_Error('A workflow is already defined.'); + } + + $workflow = KTWorkflow::getByName($workflow); + if (is_null($workflow) || PEAR::isError($workflow)) + { + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $workflow); + } + + DBUtil::startTransaction(); + $result = KTWorkflowUtil::startWorkflowOnDocument($workflow, $this->document); + if (is_null($result) || PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $result); + } + DBUtil::commit(); + } + + /** + * This deletes the workflow on the document. + * + */ + function delete_workflow() + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); + + if (PEAR::isError($user)) + { + return $user; + } + + $workflowid=$this->document->getWorkflowId(); + if (!empty($workflowid)) + { + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); + } + + DBUtil::startTransaction(); + $result = KTWorkflowUtil::startWorkflowOnDocument(null, $this->document); + if (is_null($result) || PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID,$result); + } + DBUtil::commit(); + } + + /** + * This performs a transition on the workflow + * + * @param string $transition + * @param string $reason + */ + function perform_workflow_transition($transition, $reason) + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); + + if (PEAR::isError($user)) + { + return $user; + } + + $workflowid=$this->document->getWorkflowId(); + if (empty($workflowid)) + { + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); + } + + $transition = &KTWorkflowTransition::getByName($transition); + if (is_null($transition) || PEAR::isError($transition)) + { + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $transition); + } + + DBUtil::startTransaction(); + $result = KTWorkflowUtil::performTransitionOnDocument($transition, $this->document, $user, $reason); + if (is_null($result) || PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $transition); + } + DBUtil::commit(); + } + + + + /** + * This returns all metadata for the document. + * + * @return array + */ + function get_metadata() + { + $doctypeid = $this->document->getDocumentTypeID(); + $fieldsets = (array) KTMetadataUtil::fieldsetsForDocument($this->document, $doctypeid); + + $results = array(); + + foreach ($fieldsets as $fieldset) + { + if ($fieldset->getIsConditional()) { /* this is not implemented...*/ continue; } + + $fields = $fieldset->getFields(); + $result = array('fieldset' => $fieldset->getName(), + 'description' => $fieldset->getDescription()); + + $fieldsresult = array(); + + foreach ($fields as $field) + { + $value = 'n/a'; + + $fieldvalue = DocumentFieldLink::getByDocumentAndField($this->document, $field); + if (!is_null($fieldvalue) && (!PEAR::isError($fieldvalue))) + { + $value = $fieldvalue->getValue(); + } + + $controltype = 'string'; + if ($field->getHasLookup()) + { + $controltype = 'lookup'; + if ($field->getHasLookupTree()) + { + $controltype = 'tree'; + } + } + + switch ($controltype) + { + case 'lookup': + $selection = KTAPI::get_metadata_lookup($field->getId()); + break; + case 'tree': + $selection = KTAPI::get_metadata_tree($field->getId()); + break; + default: + $selection= array(); + } + + + $fieldsresult[] = array( + 'name' => $field->getName(), + 'required' => $field->getIsMandatory(), + 'value' => $value, + 'description' => $field->getDescription(), + 'control_type' => $controltype, + 'selection' => $selection + + ); + + } + $result['fields'] = $fieldsresult; + $results [] = $result; + } + + return $results; + } + + /** + * This updates the metadata on the file. This includes the 'title'. + * + * @param array This is an array containing the metadata to be associated with the file. + */ + function update_metadata($metadata) + { + $packed = array(); + + foreach($metadata as $fieldset_metadata) + { + $fieldsetname=$fieldset_metadata['fieldset']; + $fieldset = KTFieldset::getByName($fieldsetname); + if (is_null($fieldset) || PEAR::isError($fieldset)) + { + // exit graciously + continue; + } + + foreach($fieldset_metadata['fields'] as $fieldinfo) + { + $fieldname = $fieldinfo['name']; + $field = DocumentField::getByFieldsetAndName($fieldset, $fieldname); + if (is_null($field) || PEAR::isError($fieldset)) + { + // exit graciously + continue; + } + $value = $fieldinfo['value']; + + $packed[] = array($field, $value); + } + } + + DBUtil::startTransaction(); + $result = KTDocumentUtil::saveMetadata($this->document, $packed); + + if (is_null($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR); + } + if (PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(sprintf(_kt("Unexpected validation failure: %s."), $result->getMessage())); + } + DBUtil::commit(); + } + + + /** + * This returns a workflow transition + * + * @return array + */ + function get_workflow_transitions() + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); + + if (PEAR::isError($user)) + { + return $user; + } + + $workflowid=$this->document->getWorkflowId(); + if (empty($workflowid)) + { + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); + } + + $result = array(); + + $transitions = KTWorkflowUtil::getTransitionsForDocumentUser($this->document, $user); + if (is_null($transitions) || PEAR::isError($transitions)) + { + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $transitions); + } + foreach($transitions as $transition) + { + $result[] = $transition->getName(); + } + + return $result; + } + + /** + * This returns the current workflow state + * + * @return string + */ + function get_workflow_state() + { + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); + + if (PEAR::isError($user)) + { + return $user; + } + + $workflowid=$this->document->getWorkflowId(); + if (empty($workflowid)) + { + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); + } + + $result = array(); + + $state = KTWorkflowUtil::getWorkflowStateForDocument($this->document); + if (is_null($state) || PEAR::isError($state)) + { + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); + } + + $statename = $state->getName(); + + return $statename; + + } + + /** + * This returns detailed information on the document. + * + * @return array + */ + function get_detail() + { + $detail = array(); + $document = $this->document; + + $detail['title'] = $document->getName(); + + $documenttypeid=$document->getDocumentTypeID(); + if (is_numeric($documenttypeid)) + { + $documenttype = DocumentType::get($documenttypeid); + + $documenttype=$documenttype->getName(); + } + else + { + $documenttype = '* unknown *'; + } + $detail['document_type'] = $documenttype; + + $detail['version'] = $document->getVersion(); + $detail['filename'] = $document->getFilename(); + + $detail['created_date'] = $document->getCreatedDateTime(); + + $userid = $document->getCreatorID(); + if (is_numeric($userid)) + { + $user = User::get($userid); + $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName(); + } + else + { + $username='n/a'; + } + $detail['created_by'] = $username; + $detail['updated_date'] = $document->getLastModifiedDate(); + + $userid = $document->getModifiedUserId(); + if (is_numeric($userid)) + { + $user = User::get($userid); + $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName(); + } + else + { + $username='n/a'; + } + $detail['updated_by'] = $username; + $detail['document_id'] = (int) $document->getId(); + $detail['folder_id'] = (int) $document->getFolderID(); + + $workflowid = $document->getWorkflowId(); + if (is_numeric($workflowid)) + { + $workflow = KTWorkflow::get($workflowid); + $workflowname=(is_null($workflow) || PEAR::isError($workflow))?'* unknown *':$workflow->getName(); + } + else + { + $workflowname='n/a'; + } + $detail['workflow'] = $workflowname; + + $stateid = $document->getWorkflowStateId(); + if (is_numeric($stateid)) + { + $state = KTWorkflowState::get($stateid); + $workflowstate=(is_null($state) || PEAR::isError($state))?'* unknown *':$state->getName(); + } + else + { + $workflowstate = 'n/a'; + } + $detail['workflow_state']=$workflowstate; + + $userid = $document->getCheckedOutUserID(); + + if (is_numeric($userid)) + { + $user = User::get($userid); + $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName(); + } + else + { + $username = 'n/a'; + } + $detail['checkout_by'] = $username; + + $detail['full_path'] = $this->ktapi_folder->get_full_path() . '/' . $this->get_title(); + + return $detail; + } + + function get_title() + { + return $this->document->getDescription(); + } + + /** + * This does a download of a version of the document. + * + * @param string $version + */ + function download($version=null) + { + $storage =& KTStorageManagerUtil::getSingleton(); + $options = array(); + + + $oDocumentTransaction = & new DocumentTransaction($this->document, 'Document downloaded', 'ktcore.transactions.download', $aOptions); + $oDocumentTransaction->create(); + } + + /** + * This returns the transaction history for the document. + * + * @return array + */ + function get_transaction_history() + { + $sQuery = 'SELECT DTT.name AS transaction_name, U.name AS username, DT.version AS version, DT.comment AS comment, DT.datetime AS datetime ' . + 'FROM ' . KTUtil::getTableName('document_transactions') . ' AS DT INNER JOIN ' . KTUtil::getTableName('users') . ' AS U ON DT.user_id = U.id ' . + 'INNER JOIN ' . KTUtil::getTableName('transaction_types') . ' AS DTT ON DTT.namespace = DT.transaction_namespace ' . + 'WHERE DT.document_id = ? ORDER BY DT.datetime DESC'; + $aParams = array($this->documentid); + + $transactions = DBUtil::getResultArray(array($sQuery, $aParams)); + if (is_null($transactions) || PEAR::isError($transactions)) + { + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $transactions ); + } + + return $transactions; + } + + /** + * This returns the version history on the document. + * + * @return array + */ + function get_version_history() + { + $metadata_versions = KTDocumentMetadataVersion::getByDocument($this->document); + + $versions = array(); + foreach ($metadata_versions as $version) + { + $document = &Document::get($this->documentid, $version->getId()); + + $version = array(); + + $userid = $document->getModifiedUserId(); + $user = User::get($userid); + + $version['user'] = $user->getName(); + $version['metadata_version'] = $document->getMetadataVersion(); + $version['content_version'] = $document->getVersion(); + + $versions[] = $version; + } + return $versions; + } + + /** + * This expunges a document from the system. + * + * @access public + */ + function expunge() + { + if ($this->document->getStatusID() != 3) + { + return new PEAR_Error('You should not purge this'); + } + DBUtil::startTransaction(); + + $transaction = & new DocumentTransaction($this->document, "Document expunged", 'ktcore.transactions.expunge'); + + $transaction->create(); + + $this->document->delete(); + + $this->document->cleanupDocumentData($this->documentid); + + $storage =& KTStorageManagerUtil::getSingleton(); + + $result= $storage->expunge($this->document); + + DBUtil::commit(); + } + + /** + * This expunges a document from the system. + * + * @access public + */ + function restore() + { + DBUtil::startTransaction(); + + $storage =& KTStorageManagerUtil::getSingleton(); + + $folder = Folder::get($this->document->getRestoreFolderId()); + if (PEAR::isError($folder)) + { + $this->document->setFolderId(1); + $folder = Folder::get(1); + } + else + { + $this->document->setFolderId($this->document->getRestoreFolderId()); + } + + $storage->restore($this->document); + + $this->document->setStatusId(LIVE); + $this->document->setPermissionObjectId($folder->getPermissionObjectId()); + $res = $this->document->update(); + + $res = KTPermissionUtil::updatePermissionLookup($this->document); + + $user = $this->ktapi->get_user(); + + $oTransaction = new DocumentTransaction($this->document, 'Restored from deleted state by ' . $user->getName(), 'ktcore.transactions.update'); + $oTransaction->create(); + + DBUtil::commit(); + } +} +?> \ No newline at end of file diff --git a/ktapi/KTAPIFolder.inc.php b/ktapi/KTAPIFolder.inc.php new file mode 100644 index 0000000..86c9777 --- /dev/null +++ b/ktapi/KTAPIFolder.inc.php @@ -0,0 +1,586 @@ +can_user_access_object_requiring_permission($folder, KTAPI_PERMISSION_READ); + + if (is_null($user) || PEAR::isError($user)) + { + return $user; + } + + return new KTAPI_Folder($ktapi, $folder); + } + + /** + * This is the constructor for the KTAPI_Folder. + * + * @access private + * @param KTAPI $ktapi + * @param Folder $folder + * @return KTAPI_Folder + */ + function KTAPI_Folder(&$ktapi, &$folder) + { + $this->ktapi = &$ktapi; + $this->folder = &$folder; + $this->folderid = $folder->getId(); + } + + /** + * This returns a reference to the internal folder object. + * + * @access protected + * @return Folder + */ + function &get_folder() + { + return $this->folder; + } + + + /** + * This returns detailed information on the document. + * + * @return array + */ + function get_detail() + { + $detail = array( + 'id'=>(int) $this->folderid, + 'folder_name'=>$this->get_folder_name(), + 'parent_id'=>(int) $this->get_parent_folder_id(), + 'full_path'=>$this->get_full_path(), + ); + + return $detail; + } + + function get_parent_folder_id() + { + return (int) $this->folder->getParentID(); + } + + function get_folder_name() + { + return $this->folder->getFolderName($this->folderid); + } + + + /** + * This returns the folderid. + * + * @return int + */ + function get_folderid() + { + return (int) $this->folderid; + } + + /** + * This can resolve a folder relative to the current directy by name + * + * @access public + * @param string $foldername + * @return KTAPI_Folder + */ + function &get_folder_by_name($foldername) + { + $foldername=trim($foldername); + if (empty($foldername)) + { + return new PEAR_Error('A valid folder name must be specified.'); + } + + $split = explode('/', $foldername); + + $folderid=$this->folderid; + foreach($split as $foldername) + { + if (empty($foldername)) + { + continue; + } + $sql = "SELECT id FROM folders WHERE name='$foldername' and parent_id=$folderid"; + $row = DBUtil::getOneResult($sql); + if (is_null($row) || PEAR::isError($row)) + { + return new KTAPI_Error(KTAPI_ERROR_FOLDER_INVALID,$row); + } + $folderid = $row['id']; + } + + return KTAPI_Folder::get($this->ktapi, $folderid); + } + + function get_full_path() + { + $path = $this->folder->getFullPath() . '/' . $this->folder->getName(); + + return $path; + } + + /** + * This gets a document by filename or name. + * + * @access private + * @param string $documentname + * @param string $function + * @return KTAPI_Document + */ + function &_get_document_by_name($documentname, $function='getByNameAndFolder') + { + $documentname=trim($documentname); + if (empty($documentname)) + { + return new PEAR_Error('A valid document name must be specified.'); + } + + $foldername = dirname($documentname); + $documentname = basename($documentname); + + $ktapi_folder = $this; + + if (!empty($foldername) && ($foldername != '.')) + { + $ktapi_folder = $this->get_folder_by_name($foldername); + } + + if (is_null($ktapi_folder) || PEAR::isError($ktapi_folder)) + { + return new KTAPI_Error(KTAPI_ERROR_FOLDER_INVALID, $ktapi_folder); + } + + //$folder = $ktapi_folder->get_folder(); + $folderid = $ktapi_folder->folderid; + + $document = Document::$function($documentname, $folderid); + if (is_null($document) || PEAR::isError($document)) + { + return new KTAPI_Error(KTAPI_ERROR_DOCUMENT_INVALID, $document); + } + + $user = $this->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ); + if (PEAR::isError($user)) + { + return $user; + } + + return new KTAPI_Document($this->ktapi, $ktapi_folder, $document); + } + + /** + * This can resolve a document relative to the current directy by name. + * + * @access public + * @param string $documentname + * @return KTAPI_Document + */ + function &get_document_by_name($documentname) + { + return $this->_get_document_by_name($documentname,'getByNameAndFolder'); + } + + /** + * This can resolve a document relative to the current directy by filename . + * + * @access public + * @param string $documentname + * @return KTAPI_Document + */ + function &get_document_by_filename($documentname) + { + return $this->_get_document_by_name($documentname,'getByFilenameAndFolder'); + } + + function get_listing($depth=1, $what='DF') + { + if ($depth < 1) + { + return array(); + } + $permission = &KTPermission::getByName(KTAPI_PERMISSION_READ); + $permissionid= $permission->getId(); + + $user = $this->ktapi->get_user(); + $descriptors=KTPermissionUtil::getPermissionDescriptorsForUser($user); + if (is_null($descriptors) || PEAR::isError($descriptors)) + { + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR . ': problem with descriptors for user', $descriptors); + } + if (count($descriptors == 0)) + { + $descriptors=array(0); + } + + $aPermissionDescriptors = implode(',',$descriptors); + + $sql = ''; + if (strpos($what,'D') !== false) + { + $sql .= "SELECT + d.id, + 'D' as item_type, + dmv.name as title, + ifnull(uc.name, 'n/a') AS creator, + ifnull(cou.name, 'n/a') AS checkedoutby, + ifnull(mu.name, 'n/a') AS modifiedby, + dcv.filename, + dcv.size, + dcv.major_version, + dcv.minor_version, + dcv.storage_path, + ifnull(mt.mimetypes, 'unknown') as mime_type, + ifnull(mt.icon_path, 'unknown') as mime_icon_path, + ifnull(mt.friendly_name, 'unknown') as mime_display + FROM + documents d + INNER JOIN permission_lookups AS PL ON d.permission_lookup_id = PL.id + INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid + INNER JOIN document_metadata_version AS dmv ON d.metadata_version_id=dmv.id + INNER JOIN document_content_version AS dcv ON dmv.content_version_id=dcv.id + LEFT OUTER JOIN mime_types mt ON dcv.mime_id = mt.id + LEFT OUTER JOIN users AS uc ON d.creator_id=uc.id + LEFT OUTER JOIN users AS cou ON d.checked_out_user_id=cou.id + LEFT OUTER JOIN users AS mu ON d.modified_user_id=mu.id + WHERE + d.folder_id=$this->folderid + AND d.status_id = 1 + AND PLA.permission_descriptor_id IN ($aPermissionDescriptors)"; + } + + if (strpos($what,'F') !== false) + { + if (strpos($what,'D') !== false) + { + $sql .= ' UNION '; + } + + $sql .= " + SELECT + f.id, + 'F' as item_type, + f.name as title, + ifnull(uc.name, 'n/a') AS creator, + 'n/a' checkedoutby, + 'n/a' AS modifiedby, + f.name as filename, + 'n/a' as size, + 'n/a' as major_version, + 'n/a' as minor_version, + 'n/a' as storage_path, + 'folder' as mime_type, + 'folder' as mime_icon_path, + 'Folder' as mime_display + FROM + folders f + INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id + INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid + LEFT OUTER JOIN users AS uc ON f.creator_id=uc.id + + WHERE + f.parent_id=$this->folderid + + AND PLA.permission_descriptor_id IN ($aPermissionDescriptors) + ORDER BY item_type DESC, title, filename + "; + } + + $contents = DBUtil::getResultArray($sql); + if (is_null($contents) || PEAR::isError($contents)) + { + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR , $contents); + } + + $num_items = count($contents); + for($i=0;$i<$num_items;$i++) + { + $contents[$i]['id'] = (int) $contents[$i]['id']; + if ($contents[$i]['item_type'] == 'D') + { + $contents[$i]['items'] = array(); + } + else + { + if ($depth-1 > 0) + { + $folder = &$this->ktapi->get_folder_by_id($item['id']); + $contents[$i]['items'] = $folder->get_listing($depth-1); + } + else + { + $contents[$i]['items'] = array(); + } + } + } + + return $contents; + } + + /** + * This adds a document to the current folder. + * + * @access public + * @param string $title This is the title for the file in the repository. + * @param string $filename This is the filename in the system for the file. + * @param string $documenttype This is the name or id of the document type. It first looks by name, then by id. + * @param string $tempfilename This is a reference to the file that is accessible locally on the file system. + * @return KTAPI_Document + */ + function &add_document($title, $filename, $documenttype, $tempfilename) + { + if (!is_file($tempfilename)) + { + return new PEAR_Error('File does not exist.'); + } + + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_WRITE); + if (PEAR::isError($user)) + { + return $user; + } + + $filename = basename($filename); + $documenttypeid = KTAPI::get_documenttypeid($documenttype); + + $options = array( + 'contents' => new KTFSFileLike($tempfilename), + 'novalidate' => true, + 'documenttype' => DocumentType::get($documenttypeid), + 'description' => $title, + 'metadata'=>array(), + 'cleanup_initial_file' => true + ); + + DBUtil::startTransaction(); + $document =& KTDocumentUtil::add($this->folder, $filename, $user, $options); + + if (!is_a($document,'Document')) + { + DBUtil::rollback(); + return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR . ' : ' . $document->getMessage()); + } + DBUtil::commit(); + + $tempfilename=addslashes($tempfilename); + $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'"; + $result = DBUtil::runQuery($sql); + if (PEAR::isError($result)) + { + return $result; + } + + return new KTAPI_Document($this->ktapi, $this, $document); + } + + /** + * This adds a subfolder folder to the current folder. + * + * @access public + * @param string $foldername + * @return KTAPI_Folder + */ + function &add_folder($foldername) + { + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_ADD_FOLDER); + + if (PEAR::isError($user)) + { + return $user; + } + + DBUtil::startTransaction(); + $result = KTFolderUtil::add($this->folder, $foldername, $user); + + if (PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result); + } + DBUtil::commit(); + $folderid = $result->getId(); + + return $this->ktapi->get_folder_by_id($folderid); + } + + /** + * This deletes the current folder. + * + * @param string $reason + */ + function delete($reason) + { + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_DELETE); + if (PEAR::isError($user)) + { + return $user; + } + + if ($this->folderid == 1) + { + return new PEAR_Error('Cannot delete root folder!'); + } + + DBUtil::startTransaction(); + $result = KTFolderUtil::delete($this->folder, $user, $reason); + + if (PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result); + } + DBUtil::commit(); + } + + /** + * This renames the folder + * + * @param string $newname + */ + function rename($newname) + { + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_RENAME_FOLDER); + if (PEAR::isError($user)) + { + return $user; + } + + DBUtil::startTransaction(); + $result = KTFolderUtil::rename($this->folder, $newname, $user); + + if (PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result); + } + DBUtil::commit(); + } + + /** + * This moves the folder to another location. + * + * @param KTAPI_Folder $ktapi_target_folder + * @param string $reason + */ + function move($ktapi_target_folder, $reason='') + { + assert(!is_null($ktapi_target_folder)); + assert(is_a($ktapi_target_folder,'KTAPI_Folder')); + + $user = $this->ktapi->get_user(); + + $target_folder = $ktapi_target_folder->get_folder(); + + $result = $this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE); + if (PEAR::isError($result)) + { + return $result; + } + + DBUtil::startTransaction(); + $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason); + + if (PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result); + } + DBUtil::commit(); + } + + /** + * This copies a folder to another location. + * + * @param KTAPI_Folder $ktapi_target_folder + * @param string $reason + */ + function copy($ktapi_target_folder, $reason='') + { + assert(!is_null($ktapi_target_folder)); + assert(is_a($ktapi_target_folder,'KTAPI_Folder')); + + $user = $this->ktapi->get_user(); + + $target_folder = $ktapi_target_folder->get_folder(); + + $result =$this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE); + + if (PEAR::isError($result)) + { + return $result; + } + + DBUtil::startTransaction(); + $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason); + + if (PEAR::isError($result)) + { + DBUtil::rollback(); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result); + } + DBUtil::commit(); + } + + /** + * This returns all permissions linked to the folder. + * + * @access public + * @return array + */ + function get_permissions() + { + return new PEAR_Error('TODO'); + } + + /** + * This returns a transaction history listing. + * + * @access public + * @return array + */ + function get_transaction_history() + { + return new PEAR_Error('TODO'); + } +} + +?> \ No newline at end of file diff --git a/ktapi/KTAPISession.inc.php b/ktapi/KTAPISession.inc.php new file mode 100644 index 0000000..1b18c3c --- /dev/null +++ b/ktapi/KTAPISession.inc.php @@ -0,0 +1,343 @@ +ktapi=&$ktapi; + $this->user=&$user; + $this->active = false; + } + + /** + * return the session string + * + * @return string + */ + function get_session() + { + die('get_session() should be overloaded!'); + } + + /** + * Return the session id + * + * @return int + */ + function get_sessionid() + { + die('get_sessionid() should be overloaded!'); + } + + /** + * Return the user + * + * @return User + */ + function &get_user() + { + return $this->user; + } + + function logout() + { + $this->active=false; + // don't need to do anything really + } + + function is_active() + { + return $this->active; + } + +} + + +class KTAPI_UserSession extends KTAPI_Session +{ + var $ip = null; + + function KTAPI_UserSession(&$ktapi, &$user, $session, $sessionid, $ip) + { + parent::KTAPI_Session($ktapi, $user); + + $this->ktapi = &$ktapi; + $this->user = &$user; + $this->session = $session; + $this->sessionid = $sessionid; + $this->ip = $ip; + + // TODO: get documenttransaction to not look at the session variable! + $_SESSION["userID"] = $user->getId(); + $_SESSION["sessionID"] = $this->sessionid; + $this->active = true; + } + + /** + * This returns the session string + * + * @return string + */ + function get_session() + { + return $this->session; + } + + /** + * This returns the sessionid in the database. + * + * @return int + */ + function get_sessionid() + { + return $this->sessionid; + } + + /** + * This resolves the user's ip + * + * @access private + * @return string + */ + function resolveIP() + { + if (getenv("REMOTE_ADDR")) + { + $ip = getenv("REMOTE_ADDR"); + } + elseif (getenv("HTTP_X_FORWARDED_FOR")) + { + $forwardedip = getenv("HTTP_X_FORWARDED_FOR"); + list($ip,$ip2,$ip3,$ip4)= split (",", $forwardedip); + } + elseif (getenv("HTTP_CLIENT_IP")) + { + $ip = getenv("HTTP_CLIENT_IP"); + } + + if ($ip == '') + { + $ip = '127.0.0.1'; + } + + return $ip; + } + + /** + * + * @access protected + * @static + * @param User $user + */ + function _check_session(&$user) + { + $user_id = $user->getId(); + + $sql = "SELECT count(*) >= u.max_sessions as over_limit FROM active_sessions ass INNER JOIN users u ON ass.user_id=u.id WHERE ass.user_id = $user_id"; + $row = DBUtil::getOneResult($sql); + if (PEAR::isError($row)) + { + return $row; + } + if (is_null($row)) + { + return new PEAR_Error('No record found for user?'); + } + if ($row['over_limit'] == 1) + { + return new PEAR_Error('Session limit exceeded. Logout of any active sessions.'); + } + + $session = session_id(); + + $sessionid = DBUtil::autoInsert('active_sessions', + array( + 'user_id' => $user_id, + 'session_id' => session_id(), + 'lastused' => date('Y-m-d H:i:s'), + 'ip' => $ip + )); + if (PEAR::isError($sessionid) ) + { + return $sessionid; + } + + return array($session,$sessionid); + } + + + /** + * This returns a session object based on authentication credentials. + * + * @access public + * @static + * @param string $username + * @param string $password + * @return KTAPI_Session + */ + function &start_session(&$ktapi, $username, $password, $ip=null) + { + $this->active=false; + if ( empty($username) ) + { + return new PEAR_Error(_kt('The username is empty.')); + } + + $user =& User::getByUsername($username); + if (PEAR::isError($user) || ($user === false)) + { + return new KTAPI_Error(_kt("The user '$username' cound not be found."),$user); + } + + if ( empty($password) ) + { + return new PEAR_Error(_kt('The password is empty.')); + } + + $authenticated = KTAuthenticationUtil::checkPassword($user, $password); + + if (PEAR::isError($authenticated) || $authenticated === false) + { + return new KTAPI_Error(_kt("The password is invalid."),$authenticated); + } + + if (is_null($ip)) + { + $ip = '127.0.0.1'; + //$ip = KTAPI_Session::resolveIP(); + } + + list($session,$sessionid) = KTAPI_UserSession::_check_session($user); + if (PEAR::isError($sessionid)) + { + return $sessionid; + } + + $session = &new KTAPI_UserSession($ktapi, $user, $session, $sessionid, $ip); + + return $session; + } + + /** + * This returns an active session. + * + * @param KTAPI $ktapi + * @param string $session + * @param string $ip + * @return KTAPI_Session + */ + function &get_active_session(&$ktapi, $session, $ip) + { + $sql = "SELECT id, user_id FROM active_sessions WHERE session_id='$session'"; + if (!empty($ip)) + { + $sql .= " AND ip='$ip'"; + } + + $row = DBUtil::getOneResult($sql); + if (is_null($row) || PEAR::isError($row)) + { + return new KTAPI_Error(KTAPI_ERROR_SESSION_INVALID, $row); + } + + $sessionid = $row['id']; + $userid = $row['user_id']; + + $user = &User::get($userid); + if (is_null($user) || PEAR::isError($user)) + { + return new KTAPI_Error(KTAPI_ERROR_USER_INVALID, $user); + } + + + + $now=date('Y-m-d H:i:s'); + $sql = "UPDATE active_sessions SET last_used='$now' WHERE id=$sessionid"; + DBUtil::runQuery($sql); + + $session = &new KTAPI_UserSession($ktapi, $user, $session, $sessionid, $ip); + return $session; + } + + /** + * This closes the current session. + * + */ + function logout() + { + $sql = "DELETE FROM active_sessions WHERE id=$this->sessionid"; + $result = DBUtil::runQuery($sql); + if (PEAR::isError($result)) + { + return $result; + } + + $this->user = null; + $this->session = ''; + $this->sessionid = -1; + $this->active=false; + } + +} + +class KTAPI_AnonymousSession extends KTAPI_UserSession +{ + function &start_session(&$ktapi, $ip=null) + { + $user =& User::get(-2); + if (is_null($user) || PEAR::isError($user) || ($user === false) || !$user->isAnonymous()) + { + return new KTAPI_Error(_kt("The anonymous user could not be found."), $user); + } + + $authenticated = true; + + $config = &KTConfig::getSingleton(); + $allow_anonymous = $config->get('session/allowAnonymousLogin', false); + + if (!$allow_anonymous) + { + return new PEAR_Error(_kt('Anonymous user not allowed')); + } + + if (is_null($ip)) + { + $ip = '127.0.0.1'; + //$ip = KTAPI_Session::resolveIP(); + } + + list($session,$sessionid) = KTAPI_UserSession::_check_session($user); + if (PEAR::isError($sessionid)) + { + return $sessionid; + } + + $session = &new KTAPI_AnonymousSession($ktapi, $user, $session, $sessionid, $ip); + + return $session; + } +} + + + +class KTAPI_SystemSession extends KTAPI_Session +{ + function KTAPI_SystemSession(&$ktapi, &$user) + { + parent::KTAPI_Session($ktapi, $user); + $this->active=true; + } +} + +?> \ No newline at end of file diff --git a/ktapi/ktapi.inc.php b/ktapi/ktapi.inc.php index 10b7d13..842a4e5 100644 --- a/ktapi/ktapi.inc.php +++ b/ktapi/ktapi.inc.php @@ -30,2026 +30,44 @@ * */ +session_start(); require_once('../config/dmsDefaults.php'); require_once(KT_LIB_DIR . '/filelike/fsfilelike.inc.php'); require_once(KT_LIB_DIR . '/foldermanagement/folderutil.inc.php'); -// Generic error messages used in the API. There may be some others specific to functionality -// directly in the code. -// TODO: Check that they are all relevant. - -define('KTAPI_ERROR_SESSION_INVALID', 'The session could not be resolved.'); -define('KTAPI_ERROR_PERMISSION_INVALID', 'The permission could not be resolved.'); -define('KTAPI_ERROR_FOLDER_INVALID', 'The folder could not be resolved.'); -define('KTAPI_ERROR_DOCUMENT_INVALID', 'The document could not be resolved.'); -define('KTAPI_ERROR_USER_INVALID', 'The user could not be resolved.'); -define('KTAPI_ERROR_KTAPI_INVALID', 'The ktapi could not be resolved.'); -define('KTAPI_ERROR_INSUFFICIENT_PERMISSIONS', 'The user does not have sufficient permissions to access the resource.'); -define('KTAPI_ERROR_INTERNAL_ERROR', 'An internal error occurred. Please review the logs.'); -define('KTAPI_ERROR_DOCUMENT_TYPE_INVALID', 'The document type could not be resolved.'); -define('KTAPI_ERROR_DOCUMENT_CHECKED_OUT', 'The document is checked out.'); -define('KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT', 'The document is not checked out.'); -define('KTAPI_ERROR_WORKFLOW_INVALID', 'The workflow could not be resolved.'); -define('KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS', 'The workflow is not in progress.'); - -// Mapping of permissions to actions. -// TODO: Check that they are all correct. -// Note, currently, all core actions have permissions that are defined in the plugins. -// As the permissions are currently associated with actions which are quite closely linked -// to the web interface, it is not the nicest way to do things. They should be associated at -// a lower level, such as in the api. probably, better, would be at some stage to assocate -// the permissions to the action/transaction in the database so administrators can really customise -// as required. - -define('KTAPI_PERMISSION_DELETE', 'ktcore.permissions.delete'); -define('KTAPI_PERMISSION_READ', 'ktcore.permissions.read'); -define('KTAPI_PERMISSION_WRITE', 'ktcore.permissions.write'); -define('KTAPI_PERMISSION_ADD_FOLDER', 'ktcore.permissions.addFolder'); -define('KTAPI_PERMISSION_RENAME_FOLDER', 'ktcore.permissions.folder_rename'); -define('KTAPI_PERMISSION_CHANGE_OWNERSHIP', 'ktcore.permissions.security'); -define('KTAPI_PERMISSION_DOCUMENT_MOVE', 'ktcore.permissions.write'); -define('KTAPI_PERMISSION_WORKFLOW', 'ktcore.permissions.workflow'); - -// - -class KTAPI_Session -{ - var $ktapi; - var $user = null; - var $session = ''; - var $sessionid = -1; - var $ip = null; - - function KTAPI_Session(&$ktapi, &$user, $session, $sessionid, $ip) - { - assert(!is_null($ktapi)); - assert(is_a($ktapi,'KTAPI')); - assert(!is_null($user)); - assert(is_a($user,'User')); - - $this->ktapi = &$ktapi; - $this->user = &$user; - $this->session = $session; - $this->sessionid = $sessionid; - $this->ip = $ip; - - // TODO: get documenttransaction to not look at the session variable! - $_SESSION["userID"] = $user->getId(); - $_SESSION["sessionID"] = $this->sessionid; - } - - /** - * This returns the session string - * - * @return string - */ - function get_session() - { - return $this->session; - } - - /** - * This returns the sessionid in the database. - * - * @return int - */ - function get_sessionid() - { - return $this->sessionid; - } - - /** - * This returns a user object for the use rassociated with the session. - * - * @return User - */ - function &get_user() - { - return $this->user; - } - - /** - * This resolves the user's ip - * - * @access private - * @return string - */ - function resolveIP() - { - if (getenv("REMOTE_ADDR")) - { - $ip = getenv("REMOTE_ADDR"); - } - elseif (getenv("HTTP_X_FORWARDED_FOR")) - { - $forwardedip = getenv("HTTP_X_FORWARDED_FOR"); - list($ip,$ip2,$ip3,$ip4)= split (",", $forwardedip); - } - elseif (getenv("HTTP_CLIENT_IP")) - { - $ip = getenv("HTTP_CLIENT_IP"); - } - - if ($ip == '') - { - $ip = '127.0.0.1'; - } - - return $ip; - } - - /** - * This returns a session object based on authentication credentials. - * - * @access private - * @param string $username - * @param string $password - * @return KTAPI_Session - */ - function &start_session(&$ktapi, $username, $password, $ip=null) - { - - if ( empty($username) ) - { - return new PEAR_Error(_kt('The username is empty.')); - } - - $user =& User::getByUsername($username); - if (PEAR::isError($user) || ($user === false)) - { - return new PEAR_Error(_kt("The user '$username' cound not be found.")); - } - - if ($user->isAnonymous()) - { - $authenticated = true; - - $config = &KTConfig::getSingleton(); - $allow_anonymous = $config->get('session/allowAnonymousLogin', false); - - if (!$allow_anonymous) - { - return new PEAR_Error(_kt('Anonymous user not allowed')); - } - - } - else - { - - if ( empty($password) ) - { - return new PEAR_Error(_kt('The password is empty.')); - } - - $authenticated = KTAuthenticationUtil::checkPassword($user, $password); - - if (PEAR::isError($authenticated) || $authenticated === false) - { - return new PEAR_Error(_kt("The password is invalid.")); - } - } - - - - - if (is_null($ip)) - { - $ip = '127.0.0.1'; - //$ip = KTAPI_Session::resolveIP(); - } - - session_start(); - - $user_id = $user->getId(); - - $sql = "SELECT count(*) >= u.max_sessions as over_limit FROM active_sessions ass INNER JOIN users u ON ass.user_id=u.id WHERE ass.user_id = $user_id"; - $row = DBUtil::getOneResult($sql); - if (PEAR::isError($row)) - { - return $row; - } - if (is_null($row)) - { - return new PEAR_Error('No record found for user?'); - } - if ($row['over_limit'] == 1) - { - return new PEAR_Error('Session limit exceeded. Logout of any active sessions.'); - } - - $session = session_id(); - - $sessionid = DBUtil::autoInsert('active_sessions', - array( - 'user_id' => $user_id, - 'session_id' => session_id(), - 'lastused' => date('Y-m-d H:i:s'), - 'ip' => $ip - )); - if (PEAR::isError($sessionid) ) - { - return $sessionid; - } - - $session = &new KTAPI_Session($ktapi, $user, $session, $sessionid, $ip); - - return $session; - } - - /** - * This returns an active session. - * - * @param KTAPI $ktapi - * @param string $session - * @param string $ip - * @return KTAPI_Session - */ - function &get_active_session(&$ktapi, $session, $ip) - { - $sql = "SELECT id, user_id FROM active_sessions WHERE session_id='$session'"; - if (!empty($ip)) - { - $sql .= " AND ip='$ip'"; - } - - $row = DBUtil::getOneResult($sql); - if (is_null($row) || PEAR::isError($row)) - { - return new PEAR_Error(KTAPI_ERROR_SESSION_INVALID); - } - - $sessionid = $row['id']; - $userid = $row['user_id']; - - $user = &User::get($userid); - if (is_null($user) || PEAR::isError($user)) - { - return new PEAR_Error(KTAPI_ERROR_USER_INVALID); - } - - - - $now=date('Y-m-d H:i:s'); - $sql = "UPDATE active_sessions SET last_used='$now' WHERE id=$sessionid"; - DBUtil::runQuery($sql); - - $session = &new KTAPI_Session($ktapi, $user, $session, $sessionid, $ip); - return $session; - } - - /** - * This closes the current session. - * - */ - function logout() - { - $sql = "DELETE FROM active_sessions WHERE id=$this->sessionid"; - $result = DBUtil::runQuery($sql); - if (PEAR::isError($result)) - { - return $result; - } - - $this->user = null; - $this->session = ''; - $this->sessionid = -1; - } - -} - -class KTAPI_FolderItem -{ - /** - * This is a reference to the core KTAPI controller - * - * @access protected - * @var KTAPI - */ - var $ktapi; - - function &can_user_access_object_requiring_permission(&$object, $permission) - { - return $this->ktapi->can_user_access_object_requiring_permission($object, $permission); - } -} - - -class KTAPI_Folder extends KTAPI_FolderItem -{ - /** - * This is a reference to a base Folder object. - * - * @access private - * @var Folder - */ - var $folder; - - /** - * This is the id of the folder on the database. - * - * @access private - * @var int - */ - var $folderid; - - /** - * This is used to get a folder based on a folder id. - * - * @access private - * @param KTAPI $ktapi - * @param int $folderid - * @return KTAPI_Folder - */ - function &get(&$ktapi, $folderid) - { - assert(!is_null($ktapi)); - assert(is_a($ktapi, 'KTAPI')); - assert(is_numeric($folderid)); - - $folderid += 0; - - $folder = &Folder::get($folderid); - if (is_null($folder) || PEAR::isError($folder)) - { - return new PEAR_Error(KTAPI_ERROR_FOLDER_INVALID); - } - - $user = $ktapi->can_user_access_object_requiring_permission($folder, KTAPI_PERMISSION_READ); - - if (is_null($user) || PEAR::isError($user)) - { - return $user; - } - - return new KTAPI_Folder($ktapi, $folder); - } - - /** - * This is the constructor for the KTAPI_Folder. - * - * @access private - * @param KTAPI $ktapi - * @param Folder $folder - * @return KTAPI_Folder - */ - function KTAPI_Folder(&$ktapi, &$folder) - { - $this->ktapi = &$ktapi; - $this->folder = &$folder; - $this->folderid = $folder->getId(); - } - - /** - * This returns a reference to the internal folder object. - * - * @access protected - * @return Folder - */ - function &get_folder() - { - return $this->folder; - } - - - /** - * This returns detailed information on the document. - * - * @return array - */ - function get_detail() - { - $detail = array( - 'id'=>(int) $this->folderid, - 'folder_name'=>$this->get_folder_name(), - 'parent_id'=>(int) $this->get_parent_folder_id(), - 'full_path'=>$this->get_full_path(), - ); - - return $detail; - } - - function get_parent_folder_id() - { - return (int) $this->folder->getParentID(); - } - - function get_folder_name() - { - return $this->folder->getFolderName($this->folderid); - } - - - /** - * This returns the folderid. - * - * @return int - */ - function get_folderid() - { - return (int) $this->folderid; - } - - /** - * This can resolve a folder relative to the current directy by name - * - * @access public - * @param string $foldername - * @return KTAPI_Folder - */ - function &get_folder_by_name($foldername) - { - $foldername=trim($foldername); - if (empty($foldername)) - { - return new PEAR_Error('A valid folder name must be specified.'); - } - - $split = explode('/', $foldername); - - $folderid=$this->folderid; - foreach($split as $foldername) - { - $sql = "SELECT id FROM folders WHERE name='$foldername' and parent_id=$folderid"; - $row = DBUtil::getOneResult($sql); - if (is_null($row) || PEAR::isError($row)) - { - return new PEAR_Error(KTAPI_ERROR_FOLDER_INVALID); - } - $folderid = $row['id']; - } - - return KTAPI_Folder::get($this->ktapi, $folderid); - } - - function get_full_path() - { - $path = $this->folder->getFullPath() . '/' . $this->folder->getName(); - - return $path; - } - - /** - * This gets a document by filename or name. - * - * @access private - * @param string $documentname - * @param string $function - * @return KTAPI_Document - */ - function &_get_document_by_name($documentname, $function='getByNameAndFolder') - { - $documentname=trim($documentname); - if (empty($documentname)) - { - return new PEAR_Error('A valid document name must be specified.'); - } - - $foldername = dirname($documentname); - $documentname = basename($documentname); - - $ktapi_folder = $this; - - if (!empty($foldername) && ($foldername != '.')) - { - $ktapi_folder = $this->get_folder_by_name($foldername); - } - - if (is_null($ktapi_folder) || PEAR::isError($ktapi_folder)) - { - return new PEAR_Error(KTAPI_ERROR_FOLDER_INVALID); - } - - //$folder = $ktapi_folder->get_folder(); - $folderid = $ktapi_folder->folderid; - - $document = Document::$function($documentname, $folderid); - if (is_null($document) || PEAR::isError($document)) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_INVALID); - } - - $user = $this->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ); - if (PEAR::isError($user)) - { - return $user; - } - - return new KTAPI_Document($this->ktapi, $ktapi_folder, $document); - } - - /** - * This can resolve a document relative to the current directy by name. - * - * @access public - * @param string $documentname - * @return KTAPI_Document - */ - function &get_document_by_name($documentname) - { - return $this->_get_document_by_name($documentname,'getByNameAndFolder'); - } - - /** - * This can resolve a document relative to the current directy by filename . - * - * @access public - * @param string $documentname - * @return KTAPI_Document - */ - function &get_document_by_filename($documentname) - { - return $this->_get_document_by_name($documentname,'getByFilenameAndFolder'); - } - - function get_listing($depth=1, $what='DF') - { - if ($depth < 1) - { - return array(); - } - $permission = &KTPermission::getByName(KTAPI_PERMISSION_READ); - $permissionid= $permission->getId(); - - $user = $this->ktapi->get_user(); - $aPermissionDescriptors = implode(',',KTPermissionUtil::getPermissionDescriptorsForUser($user)); - - $sql = ''; - if (strpos($what,'D') !== false) - { - $sql .= "SELECT - d.id, - 'D' as item_type, - dmv.name as title, - ifnull(uc.name, 'n/a') AS creator, - ifnull(cou.name, 'n/a') AS checkedoutby, - ifnull(mu.name, 'n/a') AS modifiedby, - dcv.filename, - dcv.size, - dcv.major_version, - dcv.minor_version, - dcv.storage_path, - ifnull(mt.mimetypes, 'unknown') as mime_type, - ifnull(mt.icon_path, 'unknown') as mime_icon_path, - ifnull(mt.friendly_name, 'unknown') as mime_display - FROM - documents d - INNER JOIN permission_lookups AS PL ON d.permission_lookup_id = PL.id - INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid - INNER JOIN document_metadata_version AS dmv ON d.metadata_version_id=dmv.id - INNER JOIN document_content_version AS dcv ON dmv.content_version_id=dcv.id - LEFT OUTER JOIN mime_types mt ON dcv.mime_id = mt.id - LEFT OUTER JOIN users AS uc ON d.creator_id=uc.id - LEFT OUTER JOIN users AS cou ON d.checked_out_user_id=cou.id - LEFT OUTER JOIN users AS mu ON d.modified_user_id=mu.id - WHERE - d.folder_id=$this->folderid - AND d.status_id = 1 - AND PLA.permission_descriptor_id IN ($aPermissionDescriptors)"; - } - - if (strpos($what,'F') !== false) - { - if (strpos($what,'D') !== false) - { - $sql .= ' UNION '; - } - - $sql .= " - SELECT - f.id, - 'F' as item_type, - f.name as title, - ifnull(uc.name, 'n/a') AS creator, - 'n/a' checkedoutby, - 'n/a' AS modifiedby, - f.name as filename, - 'n/a' as size, - 'n/a' as major_version, - 'n/a' as minor_version, - 'n/a' as storage_path, - 'folder' as mime_type, - 'folder' as mime_icon_path, - 'Folder' as mime_display - FROM - folders f - INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id - INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid - LEFT OUTER JOIN users AS uc ON f.creator_id=uc.id - - WHERE - f.parent_id=$this->folderid - - AND PLA.permission_descriptor_id IN ($aPermissionDescriptors) - ORDER BY item_type DESC, title, filename - "; - } - - $contents = DBUtil::getResultArray($sql); - if (is_null($contents) || PEAR::isError($contents)) - { - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - $num_items = count($contents); - for($i=0;$i<$num_items;$i++) - { - $contents[$i]['id'] = (int) $contents[$i]['id']; - if ($contents[$i]['item_type'] == 'D') - { - $contents[$i]['items'] = array(); - } - else - { - if ($depth-1 > 0) - { - $folder = &$this->ktapi->get_folder_by_id($item['id']); - $contents[$i]['items'] = $folder->get_listing($depth-1); - } - else - { - $contents[$i]['items'] = array(); - } - } - } - - return $contents; - } - - /** - * This adds a document to the current folder. - * - * @access public - * @param string $title This is the title for the file in the repository. - * @param string $filename This is the filename in the system for the file. - * @param string $documenttype This is the name or id of the document type. It first looks by name, then by id. - * @param string $tempfilename This is a reference to the file that is accessible locally on the file system. - * @return KTAPI_Document - */ - function &add_document($title, $filename, $documenttype, $tempfilename) - { - if (!is_file($tempfilename)) - { - return new PEAR_Error('File does not exist.'); - } - - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_WRITE); - if (PEAR::isError($user)) - { - return $user; - } - - $filename = basename($filename); - $documenttypeid = KTAPI::get_documenttypeid($documenttype); - - $options = array( - 'contents' => new KTFSFileLike($tempfilename), - 'novalidate' => true, - 'documenttype' => DocumentType::get($documenttypeid), - 'description' => $title, - 'metadata'=>array(), - 'cleanup_initial_file' => true - ); - - DBUtil::startTransaction(); - $document =& KTDocumentUtil::add($this->folder, $filename, $user, $options); - - if (!is_a($document,'Document')) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - - $tempfilename=addslashes($tempfilename); - $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'"; - $result = DBUtil::runQuery($sql); - if (PEAR::isError($result)) - { - return $result; - } - - return new KTAPI_Document($this->ktapi, $this, $document); - } - - /** - * This adds a subfolder folder to the current folder. - * - * @access public - * @param string $foldername - * @return KTAPI_Folder - */ - function &add_folder($foldername) - { - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_ADD_FOLDER); - - if (PEAR::isError($user)) - { - return $user; - } - - DBUtil::startTransaction(); - $result = KTFolderUtil::add($this->folder, $foldername, $user); - - if (PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - $folderid = $result->getId(); - - return $this->ktapi->get_folder_by_id($folderid); - } - - /** - * This deletes the current folder. - * - * @param string $reason - */ - function delete($reason) - { - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_DELETE); - if (PEAR::isError($user)) - { - return $user; - } - - if ($this->folderid == 1) - { - return new PEAR_Error('Cannot delete root folder!'); - } - - DBUtil::startTransaction(); - $result = KTFolderUtil::delete($this->folder, $user, $reason); - - if (PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - - /** - * This renames the folder - * - * @param string $newname - */ - function rename($newname) - { - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_RENAME_FOLDER); - if (PEAR::isError($user)) - { - return $user; - } - - DBUtil::startTransaction(); - $result = KTFolderUtil::rename($this->folder, $newname, $user); - - if (PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - - /** - * This moves the folder to another location. - * - * @param KTAPI_Folder $ktapi_target_folder - * @param string $reason - */ - function move($ktapi_target_folder, $reason='') - { - assert(!is_null($ktapi_target_folder)); - assert(is_a($ktapi_target_folder,'KTAPI_Folder')); - - $user = $this->ktapi->get_user(); - - $target_folder = $ktapi_target_folder->get_folder(); - - $result = $this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE); - if (PEAR::isError($result)) - { - return $result; - } - - DBUtil::startTransaction(); - $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason); - - if (PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - - /** - * This copies a folder to another location. - * - * @param KTAPI_Folder $ktapi_target_folder - * @param string $reason - */ - function copy($ktapi_target_folder, $reason='') - { - assert(!is_null($ktapi_target_folder)); - assert(is_a($ktapi_target_folder,'KTAPI_Folder')); - - $user = $this->ktapi->get_user(); - - $target_folder = $ktapi_target_folder->get_folder(); - - $result =$this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($result)) - { - return $result; - } - - DBUtil::startTransaction(); - $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason); - - if (PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - - /** - * This returns all permissions linked to the folder. - * - * @access public - * @return array - */ - function get_permissions() - { - return new PEAR_Error('TODO'); - } - - /** - * This returns a transaction history listing. - * - * @access public - * @return array - */ - function get_transaction_history() - { - return new PEAR_Error('TODO'); - } -} - -class KTAPI_Document extends KTAPI_FolderItem -{ - /** - * This is a reference to the internal document object. - * - * @var Document - */ - var $document; - /** - * This is the id of the document. - * - * @var int - */ - var $documentid; - /** - * This is a reference to the parent folder. - * - * @var KTAPI_Folder - */ - var $ktapi_folder; - - /** - * This is used to get a document based on document id. - * - * @static - * @access public - * @param KTAPI $ktapi - * @param int $documentid - * @return KTAPI_Document - */ - function &get(&$ktapi, $documentid) - { - assert(!is_null($ktapi)); - assert(is_a($ktapi, 'KTAPI')); - assert(is_numeric($documentid)); - - $documentid += 0; - - $document = &Document::get($documentid); - if (is_null($document) || PEAR::isError($document)) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_INVALID); - } - - $user = $ktapi->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ); - - if (is_null($user) || PEAR::isError($user)) - { - return $user; - } - - $folderid = $document->getParentID(); - - if (!is_null($folderid)) - { - $ktapi_folder = &KTAPI_Folder::get($ktapi, $folderid); - } - else - { - $ktapi_folder = null; - } - // We don't do any checks on this folder as it could possibly be deleted, and is not required right now. - - return new KTAPI_Document($ktapi, $ktapi_folder, $document); - } - - /** - * This is the constructor for the KTAPI_Folder. - * - * @access private - * @param KTAPI $ktapi - * @param Document $document - * @return KTAPI_Document - */ - function KTAPI_Document(&$ktapi, &$ktapi_folder, &$document) - { - assert(is_a($ktapi,'KTAPI')); - assert(is_null($ktapi_folder) || is_a($ktapi_folder,'KTAPI_Folder')); - - $this->ktapi = &$ktapi; - $this->ktapi_folder = &$ktapi_folder; - $this->document = &$document; - $this->documentid = $document->getId(); - } - - /** - * This checks a document into the repository - * - * @param string $filename - * @param string $reason - * @param string $tempfilename - * @param bool $major_update - */ - function checkin($filename, $reason, $tempfilename, $major_update=false) - { - if (!is_file($tempfilename)) - { - return new PEAR_Error('File does not exist.'); - } - - $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($user)) - { - return $user; - } - - if (!$this->document->getIsCheckedOut()) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT); - } - - $options = array('major_update'=>$major_update); - - $currentfilename = $this->document->getFileName(); - if ($filename != $currentfilename) - { - $options['newfilename'] = $filename; - } - - DBUtil::startTransaction(); - $result = KTDocumentUtil::checkin($this->document, $tempfilename, $reason, $user, $options); - - if (PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - - $tempfilename=addslashes($tempfilename); - $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'"; - $result = DBUtil::runQuery($sql); - if (PEAR::isError($result)) - { - return $result; - } - - } - - /** - * This reverses the checkout process. - * - * @param string $reason - */ - function undo_checkout($reason) - { - $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($user)) - { - return $user; - } - - if (!$this->document->getIsCheckedOut()) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT); - } - - DBUtil::startTransaction(); - - $this->document->setIsCheckedOut(0); - $this->document->setCheckedOutUserID(-1); - $res = $this->document->update(); - if (($res === false) || PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.force_checkin'); - - $res = $oDocumentTransaction->create(); - if (($res === false) || PEAR::isError($res)) { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - - /** - * This returns a URL to the file that can be downloaded. - * - * @param string $reason - */ - function checkout($reason) - { - $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($user)) - { - return $user; - } - - if ($this->document->getIsCheckedOut()) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); - } - - DBUtil::startTransaction(); - $res = KTDocumentUtil::checkout($this->document, $reason, $user); - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - DBUtil::commit(); - } - - /** - * This deletes a document from the folder. - * - * @param string $reason - */ - function delete($reason) - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DELETE); - - if (PEAR::isError($user)) - { - return $user; - } - - if ($this->document->getIsCheckedOut()) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); - } - - DBUtil::startTransaction(); - $res = KTDocumentUtil::delete($this->document, $reason); - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - DBUtil::commit(); - } - - /** - * This changes the owner of the file. - * - * @param string $ktapi_newuser - */ - function change_owner($newusername, $reason='Changing of owner.') - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_CHANGE_OWNERSHIP); - - if (PEAR::isError($user)) - { - return $user; - } - - DBUtil::startTransaction(); - - $user = &User::getByUserName($newusername); - if (is_null($user) || PEAR::isError($user)) - { - return new PEAR_Error('User could not be found'); - } - - $newuserid = $user->getId(); - - $this->document->setOwnerID($newuserid); - - $res = $this->document->update(); - - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - $res = KTPermissionUtil::updatePermissionLookup($this->document); - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.permissions_change'); - - $res = $oDocumentTransaction->create(); - if (($res === false) || PEAR::isError($res)) { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - DBUtil::commit(); - } - - /** - * This copies the document to another folder. - * - * @param KTAPI_Folder $ktapi_target_folder - * @param string $reason - * @param string $newname - * @param string $newfilename - */ - function copy(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null) - { - assert(!is_null($ktapi_target_folder)); - assert(is_a($ktapi_target_folder,'KTAPI_Folder')); - - if (empty($newname)) - { - $newname=null; - } - if (empty($newfilename)) - { - $newfilename=null; - } - - $user = $this->ktapi->get_user(); - - if ($this->document->getIsCheckedOut()) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); - } - - $target_folder = &$ktapi_target_folder->get_folder(); - - $result = $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($result)) - { - return $result; - } - - $name = $this->document->getName(); - $clash = KTDocumentUtil::nameExists($target_folder, $name); - if ($clash && !is_null($newname)) - { - $name = $newname; - $clash = KTDocumentUtil::nameExists($target_folder, $name); - } - if ($clash) - { - return new PEAR_Error('A document with this title already exists in your chosen folder. Please choose a different folder, or specify a new title for the copied document.'); - } - - $filename=$this->document->getFilename(); - $clash = KTDocumentUtil::fileExists($target_folder, $filename); - - if ($clash && !is_null($newname)) - { - $filename = $newfilename; - $clash = KTDocumentUtil::fileExists($target_folder, $filename); - } - if ($clash) - { - return new PEAR_Error('A document with this filename already exists in your chosen folder. Please choose a different folder, or specify a new filename for the copied document.'); - } - - DBUtil::startTransaction(); - - $new_document = KTDocumentUtil::copy($this->document, $target_folder, $reason); - if (PEAR::isError($new_document)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - $new_document->setName($name); - $new_document->setFilename($filename); - - $res = $new_document->update(); - - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - DBUtil::commit(); - - // FIXME do we need to refactor all trigger usage into the util function? - $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); - $aTriggers = $oKTTriggerRegistry->getTriggers('copyDocument', 'postValidate'); - foreach ($aTriggers as $aTrigger) { - $sTrigger = $aTrigger[0]; - $oTrigger = new $sTrigger; - $aInfo = array( - 'document' => $new_document, - 'old_folder' => $this->folder->get_folder(), - 'new_folder' => $target_folder, - ); - $oTrigger->setInfo($aInfo); - $ret = $oTrigger->postValidate(); - } - } - - /** - * This moves the document to another folder. - * - * @param KTAPI_Folder $ktapi_target_folder - * @param string $reason - * @param string $newname - * @param string $newfilename - */ - function move(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null) - { - assert(!is_null($ktapi_target_folder)); - assert(is_a($ktapi_target_folder,'KTAPI_Folder')); - - if (empty($newname)) - { - $newname=null; - } - if (empty($newfilename)) - { - $newfilename=null; - } - - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DOCUMENT_MOVE); - - if (PEAR::isError($user)) - { - return $user; - } - - if ($this->document->getIsCheckedOut()) - { - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT); - } - - $target_folder = $ktapi_target_folder->get_folder(); - - $result= $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($result)) - { - return $result; - } - - if (!KTDocumentUtil::canBeMoved($this->document)) - { - return new PEAR_Error('Document cannot be moved.'); - } - - $name = $this->document->getName(); - $clash = KTDocumentUtil::nameExists($target_folder, $name); - if ($clash && !is_null($newname)) - { - $name = $newname; - $clash = KTDocumentUtil::nameExists($target_folder, $name); - } - if ($clash) - { - return new PEAR_Error('A document with this title already exists in your chosen folder. Please choose a different folder, or specify a new title for the moved document.'); - } - - $filename=$this->document->getFilename(); - $clash = KTDocumentUtil::fileExists($target_folder, $filename); - - if ($clash && !is_null($newname)) - { - $filename = $newfilename; - $clash = KTDocumentUtil::fileExists($target_folder, $filename); - } - if ($clash) - { - return new PEAR_Error('A document with this filename already exists in your chosen folder. Please choose a different folder, or specify a new filename for the moved document.'); - } - - DBUtil::startTransaction(); - - $res = KTDocumentUtil::move($this->document, $target_folder, $user, $reason); - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - $this->document->setName($name); - $this->document->setFilename($filename); - - $res = $this->document->update(); - - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - DBUtil::commit(); - } - - /** - * This changes the filename of the document. - * - * @param string $newname - */ - function renameFile($newname) - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($user)) - { - return $user; - } - - DBUtil::startTransaction(); - $res = KTDocumentUtil::rename($this->document, $newname, $user); - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - - /** - * This changes the document type of the document. - * - * @param string $newname - */ - function change_document_type($documenttype) - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($user)) - { - return $user; - } - - $doctypeid = KTAPI::get_documenttypeid($documenttype); - - if ($this->document->getDocumentTypeId() != $doctypeid) - { - DBUtil::startTransaction(); - $this->document->setDocumentTypeId($doctypeid); - $res = $this->document->update(); - - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - } - - /** - * This changes the title of the document. - * - * @param string $newname - */ - function rename($newname) - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($user)) - { - return $user; - } - - if ($this->document->getName() != $newname) - { - - DBUtil::startTransaction(); - $this->document->setName($newname); - $res = $this->document->update(); - - if (PEAR::isError($res)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - DBUtil::commit(); - } - } - - /** - * This flags the document as 'archived'. - * - * @param string $reason - */ - function archive($reason) - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE); - - if (PEAR::isError($user)) - { - return $user; - } - - list($permission, $user) = $perm_and_user; - - DBUtil::startTransaction(); - $this->document->setStatusID(ARCHIVED); - $res = $this->document->update(); - if (($res === false) || PEAR::isError($res)) { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - $oDocumentTransaction = & new DocumentTransaction($this->document, sprintf(_kt('Document archived: %s'), $reason), 'ktcore.transactions.update'); - $oDocumentTransaction->create(); - - DBUtil::commit(); - - $oKTTriggerRegistry = KTTriggerRegistry::getSingleton(); - $aTriggers = $oKTTriggerRegistry->getTriggers('archive', 'postValidate'); - foreach ($aTriggers as $aTrigger) - { - $sTrigger = $aTrigger[0]; - $oTrigger = new $sTrigger; - $aInfo = array( - 'document' => $this->document, - ); - $oTrigger->setInfo($aInfo); - $ret = $oTrigger->postValidate(); - } - } - - /** - * This starts a workflow on a document. - * - * @param string $workflow - */ - function start_workflow($workflow) - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); - - if (PEAR::isError($user)) - { - return $user; - } - - $workflowid = $this->document->getWorkflowId(); - - if (!empty($workflowid)) - { - return new PEAR_Error('A workflow is already defined.'); - } - - $workflow = KTWorkflow::getByName($workflow); - if (is_null($workflow) || PEAR::isError($workflow)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); - } - - DBUtil::startTransaction(); - $result = KTWorkflowUtil::startWorkflowOnDocument($workflow, $this->document); - if (is_null($result) || PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); - } - DBUtil::commit(); - } - - /** - * This deletes the workflow on the document. - * - */ - function delete_workflow() - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); - - if (PEAR::isError($user)) - { - return $user; - } - - $workflowid=$this->document->getWorkflowId(); - if (!empty($workflowid)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); - } - - DBUtil::startTransaction(); - $result = KTWorkflowUtil::startWorkflowOnDocument(null, $this->document); - if (is_null($result) || PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); - } - DBUtil::commit(); - } - - /** - * This performs a transition on the workflow - * - * @param string $transition - * @param string $reason - */ - function perform_workflow_transition($transition, $reason) - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); - - if (PEAR::isError($user)) - { - return $user; - } - - $workflowid=$this->document->getWorkflowId(); - if (empty($workflowid)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); - } - - $transition = &KTWorkflowTransition::getByName($transition); - if (is_null($transition) || PEAR::isError($transition)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); - } - - DBUtil::startTransaction(); - $result = KTWorkflowUtil::performTransitionOnDocument($transition, $this->document, $user, $reason); - if (is_null($result) || PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); - } - DBUtil::commit(); - } - - - - /** - * This returns all metadata for the document. - * - * @return array - */ - function get_metadata() - { - $doctypeid = $this->document->getDocumentTypeID(); - $fieldsets = (array) KTMetadataUtil::fieldsetsForDocument($this->document, $doctypeid); - - $results = array(); - - foreach ($fieldsets as $fieldset) - { - if ($fieldset->getIsConditional()) { /* this is not implemented...*/ continue; } - - $fields = $fieldset->getFields(); - $result = array('fieldset' => $fieldset->getName(), - 'description' => $fieldset->getDescription()); - - $fieldsresult = array(); - - foreach ($fields as $field) - { - $value = 'n/a'; - - $fieldvalue = DocumentFieldLink::getByDocumentAndField($this->document, $field); - if (!is_null($fieldvalue) && (!PEAR::isError($fieldvalue))) - { - $value = $fieldvalue->getValue(); - } - - $controltype = 'string'; - if ($field->getHasLookup()) - { - $controltype = 'lookup'; - if ($field->getHasLookupTree()) - { - $controltype = 'tree'; - } - } - - switch ($controltype) - { - case 'lookup': - $selection = KTAPI::get_metadata_lookup($field->getId()); - break; - case 'tree': - $selection = KTAPI::get_metadata_tree($field->getId()); - break; - default: - $selection= array(); - } - - - $fieldsresult[] = array( - 'name' => $field->getName(), - 'required' => $field->getIsMandatory(), - 'value' => $value, - 'description' => $field->getDescription(), - 'control_type' => $controltype, - 'selection' => $selection - - ); - - } - $result['fields'] = $fieldsresult; - $results [] = $result; - } - - return $results; - } - - /** - * This updates the metadata on the file. This includes the 'title'. - * - * @param array This is an array containing the metadata to be associated with the file. - */ - function update_metadata($metadata) - { - $packed = array(); - - foreach($metadata as $fieldset_metadata) - { - $fieldsetname=$fieldset_metadata['fieldset']; - $fieldset = KTFieldset::getByName($fieldsetname); - if (is_null($fieldset) || PEAR::isError($fieldset)) - { - // exit graciously - continue; - } - - foreach($fieldset_metadata['fields'] as $fieldinfo) - { - $fieldname = $fieldinfo['name']; - $field = DocumentField::getByFieldsetAndName($fieldset, $fieldname); - if (is_null($field) || PEAR::isError($fieldset)) - { - // exit graciously - continue; - } - $value = $fieldinfo['value']; - - $packed[] = array($field, $value); - } - } - - DBUtil::startTransaction(); - $result = KTDocumentUtil::saveMetadata($this->document, $packed); - - if (is_null($result)) - { - DBUtil::rollback(); - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - if (PEAR::isError($result)) - { - DBUtil::rollback(); - return new PEAR_Error(sprintf(_kt("Unexpected validation failure: %s."), $result->getMessage())); - } - DBUtil::commit(); - } - - - /** - * This returns a workflow transition - * - * @return array - */ - function get_workflow_transitions() - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); - - if (PEAR::isError($user)) - { - return $user; - } - - $workflowid=$this->document->getWorkflowId(); - if (empty($workflowid)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); - } - - $result = array(); - - $transitions = KTWorkflowUtil::getTransitionsForDocumentUser($this->document, $user); - if (is_null($transitions) || PEAR::isError($transitions)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); - } - foreach($transitions as $transition) - { - $result[] = $transition->getName(); - } - - return $result; - } - - /** - * This returns the current workflow state - * - * @return string - */ - function get_workflow_state() - { - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW); - - if (PEAR::isError($user)) - { - return $user; - } - - $workflowid=$this->document->getWorkflowId(); - if (empty($workflowid)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS); - } - - $result = array(); - - $state = KTWorkflowUtil::getWorkflowStateForDocument($this->document); - if (is_null($state) || PEAR::isError($state)) - { - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID); - } - - $statename = $state->getName(); - - return $statename; - - } - - /** - * This returns detailed information on the document. - * - * @return array - */ - function get_detail() - { - $detail = array(); - $document = $this->document; - - $detail['title'] = $document->getName(); - - $documenttypeid=$document->getDocumentTypeID(); - if (is_numeric($documenttypeid)) - { - $documenttype = DocumentType::get($documenttypeid); - - $documenttype=$documenttype->getName(); - } - else - { - $documenttype = '* unknown *'; - } - $detail['document_type'] = $documenttype; - - $detail['version'] = $document->getVersion(); - $detail['filename'] = $document->getFilename(); - - $detail['created_date'] = $document->getCreatedDateTime(); - - $userid = $document->getCreatorID(); - if (is_numeric($userid)) - { - $user = User::get($userid); - $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName(); - } - else - { - $username='n/a'; - } - $detail['created_by'] = $username; - $detail['updated_date'] = $document->getLastModifiedDate(); - - $userid = $document->getModifiedUserId(); - if (is_numeric($userid)) - { - $user = User::get($userid); - $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName(); - } - else - { - $username='n/a'; - } - $detail['updated_by'] = $username; - $detail['document_id'] = (int) $document->getId(); - $detail['folder_id'] = (int) $document->getFolderID(); - - $workflowid = $document->getWorkflowId(); - if (is_numeric($workflowid)) - { - $workflow = KTWorkflow::get($workflowid); - $workflowname=(is_null($workflow) || PEAR::isError($workflow))?'* unknown *':$workflow->getName(); - } - else - { - $workflowname='n/a'; - } - $detail['workflow'] = $workflowname; - - $stateid = $document->getWorkflowStateId(); - if (is_numeric($stateid)) - { - $state = KTWorkflowState::get($stateid); - $workflowstate=(is_null($state) || PEAR::isError($state))?'* unknown *':$state->getName(); - } - else - { - $workflowstate = 'n/a'; - } - $detail['workflow_state']=$workflowstate; - - $userid = $document->getCheckedOutUserID(); - - if (is_numeric($userid)) - { - $user = User::get($userid); - $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName(); - } - else - { - $username = 'n/a'; - } - $detail['checkout_by'] = $username; - - $detail['full_path'] = $this->ktapi_folder->get_full_path() . '/' . $this->get_title(); - - return $detail; - } - - function get_title() - { - return $this->document->getDescription(); - } - - /** - * This does a download of a version of the document. - * - * @param string $version - */ - function download($version=null) - { - $storage =& KTStorageManagerUtil::getSingleton(); - $options = array(); +require_once('KTAPIConstants.inc.php'); +require_once('KTAPISession.inc.php'); +require_once('KTAPIFolder.inc.php'); +require_once('KTAPIDocument.inc.php'); - - $oDocumentTransaction = & new DocumentTransaction($this->document, 'Document downloaded', 'ktcore.transactions.download', $aOptions); - $oDocumentTransaction->create(); - } - +class KTAPI_FolderItem +{ /** - * This returns the transaction history for the document. + * This is a reference to the core KTAPI controller * - * @return array + * @access protected + * @var KTAPI */ - function get_transaction_history() - { - $sQuery = 'SELECT DTT.name AS transaction_name, U.name AS username, DT.version AS version, DT.comment AS comment, DT.datetime AS datetime ' . - 'FROM ' . KTUtil::getTableName('document_transactions') . ' AS DT INNER JOIN ' . KTUtil::getTableName('users') . ' AS U ON DT.user_id = U.id ' . - 'INNER JOIN ' . KTUtil::getTableName('transaction_types') . ' AS DTT ON DTT.namespace = DT.transaction_namespace ' . - 'WHERE DT.document_id = ? ORDER BY DT.datetime DESC'; - $aParams = array($this->documentid); - - $transactions = DBUtil::getResultArray(array($sQuery, $aParams)); - if (is_null($transactions) || PEAR::isError($transactions)) - { - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); - } - - return $transactions; - } + var $ktapi; - /** - * This returns the version history on the document. - * - * @return array - */ - function get_version_history() - { - $metadata_versions = KTDocumentMetadataVersion::getByDocument($this->document); - - $versions = array(); - foreach ($metadata_versions as $version) - { - $document = &Document::get($this->documentid, $version->getId()); - - $version = array(); - - $userid = $document->getModifiedUserId(); - $user = User::get($userid); - - $version['user'] = $user->getName(); - $version['metadata_version'] = $document->getMetadataVersion(); - $version['content_version'] = $document->getVersion(); - - $versions[] = $version; - } - return $versions; + function &can_user_access_object_requiring_permission(&$object, $permission) + { + return $this->ktapi->can_user_access_object_requiring_permission($object, $permission); } +} - /** - * This expunges a document from the system. - * - * @access public - */ - function expunge() +class KTAPI_Error extends PEAR_Error +{ + function KTAPI_Error($msg, $obj) { - if ($this->document->getStatusID() != 3) + if (PEAR::isError($obj)) { - return new PEAR_Error('You should not purge this'); + parent::PEAR_Error($msg . ' - ' . $obj->getMessage()); } - DBUtil::startTransaction(); - - $transaction = & new DocumentTransaction($this->document, "Document expunged", 'ktcore.transactions.expunge'); - - $transaction->create(); - - $this->document->delete(); - - $this->document->cleanupDocumentData($this->documentid); - - $storage =& KTStorageManagerUtil::getSingleton(); - - $result= $storage->expunge($this->document); - - DBUtil::commit(); - } - - /** - * This expunges a document from the system. - * - * @access public - */ - function restore() - { - DBUtil::startTransaction(); - - $storage =& KTStorageManagerUtil::getSingleton(); - - $folder = Folder::get($this->document->getRestoreFolderId()); - if (PEAR::isError($folder)) - { - $this->document->setFolderId(1); - $folder = Folder::get(1); - } else { - $this->document->setFolderId($this->document->getRestoreFolderId()); + parent::PEAR_Error($msg); } - - $storage->restore($this->document); - - $this->document->setStatusId(LIVE); - $this->document->setPermissionObjectId($folder->getPermissionObjectId()); - $res = $this->document->update(); - - $res = KTPermissionUtil::updatePermissionLookup($this->document); - - $user = $this->ktapi->get_user(); - - $oTransaction = new DocumentTransaction($this->document, 'Restored from deleted state by ' . $user->getName(), 'ktcore.transactions.update'); - $oTransaction->create(); - - DBUtil::commit(); } } @@ -2163,7 +181,7 @@ class KTAPI return new PEAR_Error('A session is currently active.'); } - $session = &KTAPI_Session::get_active_session($this, $session, $ip); + $session = &KTAPI_UserSession::get_active_session($this, $session, $ip); if (is_null($session) || PEAR::isError($session)) { @@ -2189,7 +207,7 @@ class KTAPI return new PEAR_Error('A session is currently active.'); } - $session = &KTAPI_Session::start_session($this, $username, $password, $ip); + $session = &KTAPI_UserSession::start_session($this, $username, $password, $ip); if (is_null($session)) { return new PEAR_Error('Session is null.'); @@ -2203,6 +221,19 @@ class KTAPI return $session; } + + function & start_system_session() + { + $user = User::get(1); + + $session = & new KTAPI_SystemSession($this, $user); + $this->session = &$session; + + return $session; + } + + + /** * Starts an anonymous session. * @@ -2211,7 +242,23 @@ class KTAPI */ function &start_anonymous_session($ip=null) { - return $this->start_session('anonymous','',$ip); + if (!is_null($this->session)) + { + return new PEAR_Error('A session is currently active.'); + } + + $session = &KTAPI_AnonymousSession::start_session($this, $ip); + if (is_null($session)) + { + return new PEAR_Error('Session is null.'); + } + if (PEAR::isError($session)) + { + return new PEAR_Error('Session is invalid. ' . $session->getMessage()); + } + $this->session = &$session; + + return $session; } @@ -2288,7 +335,7 @@ class KTAPI $rows = DBUtil::getResultArray($sql); if (is_null($rows) || PEAR::isError($rows)) { - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows); } $result = array(); @@ -2313,7 +360,7 @@ class KTAPI $rows = DBUtil::getResultArray($sql); if (is_null($rows) || PEAR::isError($rows)) { - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows); } return $rows; } @@ -2332,7 +379,7 @@ class KTAPI $rows = DBUtil::getResultArray($sql); if (is_null($rows) || PEAR::isError($rows)) { - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows); } $results=array(); foreach($rows as $row) @@ -2398,7 +445,7 @@ class KTAPI $rows=DBUtil::getResultArray($sql); if (is_null($rows) || PEAR::isError($rows)) { - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR); + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows); } $results=array(); foreach($rows as $row) diff --git a/lib/documentmanagement/Document.inc b/lib/documentmanagement/Document.inc index e7b48f5..541223e 100644 --- a/lib/documentmanagement/Document.inc +++ b/lib/documentmanagement/Document.inc @@ -230,7 +230,11 @@ class Document { $this->_oDocumentMetadataVersion = KTDocumentMetadataVersion::get($iMetadataVersionId); $this->iCurrentMetadataVersionId = $iMetadataVersionId; } - if (PEAR::isError($this->_oDocumentMetadataVersion)) { var_dump($this->_oDocumentMetadataVersion); return $this->_oDocumentMetadataVersion; } + if (PEAR::isError($this->_oDocumentMetadataVersion)) + { + // var_dump($this->_oDocumentMetadataVersion); + return $this->_oDocumentMetadataVersion; + } $this->_oDocumentContentVersion = KTDocumentContentVersion::get($this->_oDocumentMetadataVersion->getContentVersionId()); if (PEAR::isError($this->_oDocumentContentVersion)) { return $this->_oDocumentContentVersion; } diff --git a/lib/foldermanagement/folderutil.inc.php b/lib/foldermanagement/folderutil.inc.php index d8fd6f8..aa3f3ab 100644 --- a/lib/foldermanagement/folderutil.inc.php +++ b/lib/foldermanagement/folderutil.inc.php @@ -68,15 +68,19 @@ class KTFolderUtil { } function add ($oParentFolder, $sFolderName, $oUser) { - $oFolder = KTFolderUtil::_add($oParentFolder, $sFolderName, $oUser); + + + $folderid=$oParentFolder->getId(); + // check for conflicts first + if (Folder::folderExistsName($sFolderName,$folderid)) { + return PEAR::raiseError(sprintf(_kt('The folder %s already exists.'), $sFolderName)); + } + + $oFolder = KTFolderUtil::_add($oParentFolder, $sFolderName, $oUser); if (PEAR::isError($oFolder)) { return $oFolder; } - // check for conflicts first - if (Folder::folderExistsName(KTUtil::getId($oParentFolder), $sFolderName)) { - return PEAR::raiseError(sprintf(_kt('The folder %s already exists.'), $sFolderName)); - } $oTransaction = KTFolderTransaction::createFromArray(array( 'folderid' => $oFolder->getId(), diff --git a/lib/templating/kt3template.inc.php b/lib/templating/kt3template.inc.php index 8b2eb1f..543354c 100644 --- a/lib/templating/kt3template.inc.php +++ b/lib/templating/kt3template.inc.php @@ -39,6 +39,7 @@ * */ +require_once(KT_LIB_DIR . "/plugins/pluginregistry.inc.php"); require_once(KT_LIB_DIR . "/templating/templating.inc.php"); require_once(KT_LIB_DIR . "/session/control.inc"); diff --git a/tests/runtests.php b/tests/runtests.php index 6661201..01a740e 100644 --- a/tests/runtests.php +++ b/tests/runtests.php @@ -5,6 +5,9 @@ require_once('test.php'); class UnitTests extends GroupTest { function UnitTests() { $this->GroupTest('Unit tests'); + $this->addTestFile('api/authentication.php'); + $this->addTestFile('api/document.php'); + $this->addTestFile('api/folder.php'); $this->addTestFile('SQLFile/test_sqlfile.php'); $this->addTestFile('cache/testCache.php'); $this->addTestFile('config/testConfig.php'); @@ -29,3 +32,4 @@ if (SimpleReporter::inCli()) { } $test->run(new HtmlReporter()); +?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE b/thirdparty/simpletest/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE index 2c9ae6f..806a7a7 100644 --- a/thirdparty/simpletest/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE +++ b/thirdparty/simpletest/simpletest/HELP_MY_TESTS_DONT_WORK_ANYMORE @@ -5,6 +5,28 @@ written with earlier versions will fail with the newest ones. The most dramatic changes are in the alpha releases. Here is a list of possible problems and their fixes... +No method _getTest() on mocks +----------------------------- +This has finally been removed. It was a pretty esoteric +flex point anyway. It was there to allow the mocks to +work with other test tools, but no one does this anyway. + +No method assertError(), assertNoErrors(), swallowErrors() +---------------------------------------------------------- +These have been deprecated in 1.0.1beta in favour of +expectError() and expectException(). assertNoErrors() is +redundant if you use expectError() as failures are now reporterted +immediately. + +No method TestCase::signal() +---------------------------- +This has been deprecated in favour of triggering an error or +throwing an exception. Deprecated as of 1.0.1beta. + +No method TestCase::sendMessage() +--------------------------------- +This has been deprecated as of 1.0.1beta. + Failure to connect now emits failures ------------------------------------- It used to be that you would have to use the @@ -46,7 +68,7 @@ No method addPartialMockCode() ------------------------------ The ability to insert arbitrary partial mock code has been removed. This was a low value feature -causing needless complications.It was removed +causing needless complications. It was removed in the 1.0.1beta release. No method setMockBaseClass() @@ -139,7 +161,7 @@ My custom test case ignored by tally() The _assertTrue method has had it's signature changed due to a bug in the PHP 5.0.1 release. You must now use getTest() from within that method to get the test case. Mock compatibility with other -unit testers is now deprecated as of 1.0.1alpha as PEAR::PHUnit2 +unit testers is now deprecated as of 1.0.1alpha as PEAR::PHPUnit2 should soon have mock support of it's own. Broken code extending SimpleRunner diff --git a/thirdparty/simpletest/simpletest/LICENSE b/thirdparty/simpletest/simpletest/LICENSE index 4a1f720..09f465a 100644 --- a/thirdparty/simpletest/simpletest/LICENSE +++ b/thirdparty/simpletest/simpletest/LICENSE @@ -2,7 +2,7 @@ Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. diff --git a/thirdparty/simpletest/simpletest/README b/thirdparty/simpletest/simpletest/README index c2ff9b7..b8d1979 100644 --- a/thirdparty/simpletest/simpletest/README +++ b/thirdparty/simpletest/simpletest/README @@ -1,7 +1,7 @@ SimpleTest ========== You probably got this package from... -http://sourceforge.net/projects/simpletest/ +http://simpletest.sourceforge.net/projects/simpletest/ If there is no licence agreement with this package please download a version from the location above. You must read and accept that diff --git a/thirdparty/simpletest/simpletest/VERSION b/thirdparty/simpletest/simpletest/VERSION index 9980c7d..fff5748 100644 --- a/thirdparty/simpletest/simpletest/VERSION +++ b/thirdparty/simpletest/simpletest/VERSION @@ -1 +1 @@ -1.0.1alpha3 \ No newline at end of file +1.0.1beta \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/browser.php b/thirdparty/simpletest/simpletest/browser.php index 462262a..6b41d0b 100644 --- a/thirdparty/simpletest/simpletest/browser.php +++ b/thirdparty/simpletest/simpletest/browser.php @@ -894,6 +894,17 @@ $form->submitButton(new SimpleById($id), $additional)); return ($success ? $this->getContent() : $success); } + + /** + * Tests to see if a submit button exists with this + * label. + * @param string $label Button label. + * @return boolean True if present. + * @access public + */ + function isSubmit($label) { + return (boolean)$this->_page->getFormBySubmit(new SimpleByLabel($label)); + } /** * Clicks the submit image by some kind of label. Usually @@ -962,6 +973,17 @@ $form->submitImage(new SimpleById($id), $x, $y, $additional)); return ($success ? $this->getContent() : $success); } + + /** + * Tests to see if an image exists with this + * title or alt text. + * @param string $label Image text. + * @return boolean True if present. + * @access public + */ + function isImage($label) { + return (boolean)$this->_page->getFormByImage(new SimpleByLabel($label)); + } /** * Submits a form by the ID. @@ -981,16 +1003,16 @@ } /** - * Follows a link by label. Will click the first link + * Finds a URL by label. Will find the first link * found with this link text by default, or a later * one if an index is given. The match ignores case and * white space issues. * @param string $label Text between the anchor tags. * @param integer $index Link position counting from zero. - * @return string/boolean Page on success. + * @return string/boolean URL on success. * @access public */ - function clickLink($label, $index = 0) { + function getLink($label, $index = 0) { $urls = $this->_page->getUrlsByLabel($label); if (count($urls) == 0) { return false; @@ -998,18 +1020,36 @@ if (count($urls) < $index + 1) { return false; } - $this->_load($urls[$index], new SimpleGetEncoding()); - return $this->getContent(); + return $urls[$index]; } /** - * Tests to see if a link is present by label. - * @param string $label Text of value attribute. - * @return boolean True if link present. + * Follows a link by label. Will click the first link + * found with this link text by default, or a later + * one if an index is given. The match ignores case and + * white space issues. + * @param string $label Text between the anchor tags. + * @param integer $index Link position counting from zero. + * @return string/boolean Page on success. + * @access public + */ + function clickLink($label, $index = 0) { + $url = $this->getLink($label, $index); + if ($url === false) { + return false; + } + $this->_load($url, new SimpleGetEncoding()); + return $this->getContent(); + } + + /** + * Finds a link by id attribute. + * @param string $id ID attribute value. + * @return string/boolean URL on success. * @access public */ - function isLink($label) { - return (count($this->_page->getUrlsByLabel($label)) > 0); + function getLinkById($id) { + return $this->_page->getUrlById($id); } /** @@ -1019,7 +1059,7 @@ * @access public */ function clickLinkById($id) { - if (! ($url = $this->_page->getUrlById($id))) { + if (! ($url = $this->getLinkById($id))) { return false; } $this->_load($url, new SimpleGetEncoding()); @@ -1027,16 +1067,6 @@ } /** - * Tests to see if a link is present by ID attribute. - * @param string $id Text of id attribute. - * @return boolean True if link present. - * @access public - */ - function isLinkById($id) { - return (boolean)$this->_page->getUrlById($id); - } - - /** * Clicks a visible text item. Will first try buttons, * then links and then images. * @param string $label Visible text or alt text. @@ -1053,5 +1083,15 @@ } return $raw; } + + /** + * Tests to see if a click target exists. + * @param string $label Visible text or alt text. + * @return boolean True if target present. + * @access public + */ + function isClickable($label) { + return $this->isSubmit($label) || ($this->getLink($label) !== false) || $this->isImage($label); + } } ?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/collector.php b/thirdparty/simpletest/simpletest/collector.php index 34229d8..439f6b5 100644 --- a/thirdparty/simpletest/simpletest/collector.php +++ b/thirdparty/simpletest/simpletest/collector.php @@ -1,115 +1,107 @@ - * @package SimpleTest - * @subpackage UnitTester - * @version $Id$ - */ - -/** - * The basic collector for {@link GroupTest} - * - * @see collect(), GroupTest::collect() - * @package SimpleTest - * @subpackage UnitTester - */ -class SimpleCollector { - /** - * Strips off any kind of slash at the end so as to normalise the path + * This file contains the following classes: {@link SimpleCollector}, + * {@link SimplePatternCollector}. * - * @param string $path Path to normalise. + * @author Travis Swicegood + * @package SimpleTest + * @subpackage UnitTester + * @version $Id$ */ - function _removeTrailingSlash($path) { - return preg_replace('|[\\/]$|', '', $path); - - /** - * @internal - * Try benchmarking the following. It's more code, but by not using the - * regex, it may be faster? Also, shouldn't be looking for - * DIRECTORY_SEPERATOR instead of a manual "/"? - */ - if (substr($path, -1) == DIRECTORY_SEPERATOR) { - return substr($path, 0, -1); - } else { - return $path; - } - } - + /** - * Scans the directory and adds what it can. - * @param object $test Group test with {@link GroupTest::addTestFile()} method. - * @param string $path Directory to scan. - * @see _attemptToAdd() + * The basic collector for {@link GroupTest} + * + * @see collect(), GroupTest::collect() + * @package SimpleTest + * @subpackage UnitTester */ - function collect(&$test, $path) { - $path = $this->_removeTrailingSlash($path); - if ($handle = opendir($path)) { - while (($entry = readdir($handle)) !== false) { - $this->_handle($test, $path . DIRECTORY_SEPARATOR . $entry); + class SimpleCollector { + + /** + * Strips off any kind of slash at the end so as to normalise the path. + * @param string $path Path to normalise. + * @return string Path without trailing slash. + */ + function _removeTrailingSlash($path) { + if (substr($path, -1) == DIRECTORY_SEPARATOR) { + return substr($path, 0, -1); + } elseif (substr($path, -1) == '/') { + return substr($path, 0, -1); + } else { + return $path; } - closedir($handle); } - } - /** - * This method determines what should be done with a given file and adds - * it via {@link GroupTest::addTestFile()} if necessary. - * - * This method should be overriden to provide custom matching criteria, - * such as pattern matching, recursive matching, etc. For an example, see - * {@link SimplePatternCollector::_handle()}. - * - * @param object $test Group test with {@link GroupTest::addTestFile()} method. - * @param string $filename A filename as generated by {@link collect()} - * @see collect() - * @access protected - */ - function _handle(&$test, $file) { - if (!is_dir($file)) { - $test->addTestFile($file); + /** + * Scans the directory and adds what it can. + * @param object $test Group test with {@link GroupTest::addTestFile()} method. + * @param string $path Directory to scan. + * @see _attemptToAdd() + */ + function collect(&$test, $path) { + $path = $this->_removeTrailingSlash($path); + if ($handle = opendir($path)) { + while (($entry = readdir($handle)) !== false) { + $this->_handle($test, $path . DIRECTORY_SEPARATOR . $entry); + } + closedir($handle); + } } - } -} - -/** - * An extension to {@link SimpleCollector} that only adds files matching a - * given pattern. - * - * @package SimpleTest - * @subpackage UnitTester - * @see SimpleCollector - */ -class SimplePatternCollector extends SimpleCollector { - var $_pattern; + /** + * This method determines what should be done with a given file and adds + * it via {@link GroupTest::addTestFile()} if necessary. + * + * This method should be overriden to provide custom matching criteria, + * such as pattern matching, recursive matching, etc. For an example, see + * {@link SimplePatternCollector::_handle()}. + * + * @param object $test Group test with {@link GroupTest::addTestFile()} method. + * @param string $filename A filename as generated by {@link collect()} + * @see collect() + * @access protected + */ + function _handle(&$test, $file) { + if (! is_dir($file)) { + $test->addTestFile($file); + } + } + } /** + * An extension to {@link SimpleCollector} that only adds files matching a + * given pattern. * - * @param string $pattern Perl compatible regex to test name against - * See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE} - * for full documentation of valid pattern.s + * @package SimpleTest + * @subpackage UnitTester + * @see SimpleCollector */ - function SimplePatternCollector($pattern = '/php$/i') { - $this->_pattern = $pattern; - } + class SimplePatternCollector extends SimpleCollector { + var $_pattern; + /** + * + * @param string $pattern Perl compatible regex to test name against + * See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE} + * for full documentation of valid pattern.s + */ + function SimplePatternCollector($pattern = '/php$/i') { + $this->_pattern = $pattern; + } - /** - * Attempts to add files that match a given pattern. - * - * @see SimpleCollector::_handle() - * @param object $test Group test with {@link GroupTest::addTestFile()} method. - * @param string $path Directory to scan. - * @access protected - */ - function _handle(&$test, $filename) { - if (preg_match($this->_pattern, $filename)) { - parent::_handle($test, $filename); + /** + * Attempts to add files that match a given pattern. + * + * @see SimpleCollector::_handle() + * @param object $test Group test with {@link GroupTest::addTestFile()} method. + * @param string $path Directory to scan. + * @access protected + */ + function _handle(&$test, $filename) { + if (preg_match($this->_pattern, $filename)) { + parent::_handle($test, $filename); + } } } -} ?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/compatibility.php b/thirdparty/simpletest/simpletest/compatibility.php index 67b806a..eefb602 100644 --- a/thirdparty/simpletest/simpletest/compatibility.php +++ b/thirdparty/simpletest/simpletest/compatibility.php @@ -70,6 +70,9 @@ if (is_array($first) && is_array($second)) { return SimpleTestCompatibility::_isArrayOfIdenticalTypes($first, $second); } + if ($first !== $second) { + return false; + } return true; } @@ -105,8 +108,7 @@ * @static */ function isReference(&$first, &$second) { - if (version_compare(phpversion(), '5', '>=') - && is_object($first)) { + if (version_compare(phpversion(), '5', '>=') && is_object($first)) { return ($first === $second); } if (is_object($first) && is_object($second)) { @@ -133,9 +135,6 @@ * @static */ function isA($object, $class) { - if (function_exists('is_a')) { - return is_a($object, $class); - } if (version_compare(phpversion(), '5') >= 0) { if (! class_exists($class, false)) { if (function_exists('interface_exists')) { @@ -147,6 +146,9 @@ eval("\$is_a = \$object instanceof $class;"); return $is_a; } + if (function_exists('is_a')) { + return is_a($object, $class); + } return ((strtolower($class) == get_class($object)) or (is_subclass_of($object, $class))); } @@ -167,18 +169,5 @@ set_socket_timeout($handle, $timeout, 0); } } - - /** - * Gets the current stack trace topmost first. - * @return array List of stack frames. - * @access public - * @static - */ - function getStackTrace() { - if (function_exists('debug_backtrace')) { - return array_reverse(debug_backtrace()); - } - return array(); - } } -?> +?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/dumper.php b/thirdparty/simpletest/simpletest/dumper.php index 63af5ea..ea21e64 100644 --- a/thirdparty/simpletest/simpletest/dumper.php +++ b/thirdparty/simpletest/simpletest/dumper.php @@ -356,47 +356,5 @@ ob_end_clean(); return $formatted; } - - /** - * Extracts the last assertion that was not within - * Simpletest itself. The name must start with "assert". - * @param array $stack List of stack frames. - * @access public - * @static - */ - function getFormattedAssertionLine($stack) { - foreach ($stack as $frame) { - if (isset($frame['file'])) { - if (strpos($frame['file'], SIMPLE_TEST) !== false) { - if (dirname($frame['file']) . '/' == SIMPLE_TEST) { - continue; - } - } - } - if (SimpleDumper::_stackFrameIsAnAssertion($frame)) { - return ' at [' . $frame['file'] . ' line ' . $frame['line'] . ']'; - } - } - return ''; - } - - /** - * Tries to determine if the method call is an assertion. - * @param array $frame PHP stack frame. - * @access private - * @static - */ - function _stackFrameIsAnAssertion($frame) { - if (($frame['function'] == 'fail') || ($frame['function'] == 'pass')) { - return true; - } - if (strncmp($frame['function'], 'assert', 6) == 0) { - return true; - } - if (strncmp($frame['function'], 'expect', 6) == 0) { - return true; - } - return false; - } } ?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/encoding.php b/thirdparty/simpletest/simpletest/encoding.php index a1a0d76..bffa071 100644 --- a/thirdparty/simpletest/simpletest/encoding.php +++ b/thirdparty/simpletest/simpletest/encoding.php @@ -37,7 +37,7 @@ * @access public */ function asRequest() { - return $this->_key . '=' . urlencode($this->_value); + return urlencode($this->_key) . '=' . urlencode($this->_value); } /** diff --git a/thirdparty/simpletest/simpletest/errors.php b/thirdparty/simpletest/simpletest/errors.php index 99b5096..76745f8 100644 --- a/thirdparty/simpletest/simpletest/errors.php +++ b/thirdparty/simpletest/simpletest/errors.php @@ -15,6 +15,8 @@ * Includes SimpleTest files. */ require_once(dirname(__FILE__) . '/invoker.php'); + require_once(dirname(__FILE__) . '/test_case.php'); + require_once(dirname(__FILE__) . '/expectation.php'); /** * Extension that traps errors into an error queue. @@ -39,13 +41,15 @@ * @access public */ function invoke($method) { - set_error_handler('simpleTestErrorHandler'); + $context = &SimpleTest::getContext(); + $queue = &$context->get('SimpleErrorQueue'); + $queue->setTestCase($this->GetTestCase()); + set_error_handler('SimpleTestErrorHandler'); parent::invoke($method); - $queue = &SimpleErrorQueue::instance(); - while (list($severity, $message, $file, $line, $globals) = $queue->extract()) { + while (list($severity, $message, $file, $line) = $queue->extract()) { $severity = SimpleErrorQueue::getSeverityAsString($severity); - $test_case = &$this->getTestCase(); - $test_case->error($severity, $message, $file, $line); + $test = &$this->getTestCase(); + $test->error($severity, $message, $file, $line); } restore_error_handler(); } @@ -59,28 +63,63 @@ */ class SimpleErrorQueue { var $_queue; + var $_expectation_queue; + var $_test; /** * Starts with an empty queue. - * @access public */ function SimpleErrorQueue() { $this->clear(); } /** + * Sets the currently running test case. + * @param SimpleTestCase $test Test case to send messages to. + * @access public + */ + function setTestCase(&$test) { + $this->_test = &$test; + } + + /** * Adds an error to the front of the queue. - * @param $severity PHP error code. - * @param $message Text of error. - * @param $filename File error occoured in. - * @param $line Line number of error. - * @param $super_globals Hash of PHP super global arrays. + * @param integer $severity PHP error code. + * @param string $content Text of error. + * @param string $filename File error occoured in. + * @param integer $line Line number of error. * @access public */ - function add($severity, $message, $filename, $line, $super_globals) { - array_push( - $this->_queue, - array($severity, $message, $filename, $line, $super_globals)); + function add($severity, $content, $filename, $line) { + $content = str_replace('%', '%%', $content); + if (count($this->_expectation_queue)) { + $this->_testLatestError($severity, $content, $filename, $line); + } else { + array_push( + $this->_queue, + array($severity, $content, $filename, $line)); + } + } + + /** + * Tests the error against the most recent expected + * error. + * @param integer $severity PHP error code. + * @param string $content Text of error. + * @param string $filename File error occoured in. + * @param integer $line Line number of error. + * @access private + */ + function _testLatestError($severity, $content, $filename, $line) { + list($expected, $message) = array_shift($this->_expectation_queue); + $severity = $this->getSeverityAsString($severity); + $is_match = $this->_test->assert( + $expected, + $content, + sprintf($message, "%s -> PHP error [$content] severity [$severity] in [$filename] line [$line]")); + if (! $is_match) { + $this->_test->error($severity, $content, $filename, $line); + } } /** @@ -105,32 +144,52 @@ */ function clear() { $this->_queue = array(); + $this->_expectation_queue = array(); } /** - * Tests to see if the queue is empty. - * @return True if empty. + * @deprecated */ - function isEmpty() { - return (count($this->_queue) == 0); + function assertNoErrors($message) { + return $this->_test->assert( + new TrueExpectation(), + count($this->_queue) == 0, + sprintf($message, 'Should be no errors')); } /** - * Global access to a single error queue. - * @return Global error queue object. - * @access public - * @static + * @deprecated */ - function &instance() { - static $queue = false; - if (! $queue) { - $queue = new SimpleErrorQueue(); + function assertError($expected, $message) { + if (count($this->_queue) == 0) { + $this->_test->fail(sprintf($message, 'Expected error not found')); + return false; } - return $queue; + list($severity, $content, $file, $line) = $this->extract(); + $severity = $this->getSeverityAsString($severity); + return $this->_test->assert( + $expected, + $content, + sprintf($message, "Expected PHP error [$content] severity [$severity] in [$file] line [$line]")); + } + + /** + * Sets up an expectation of an error. If this is + * not fulfilled at the end of the test, a failure + * will occour. If the error does happen, then this + * will cancel it out and send a pass message. + * @param SimpleExpectation $expected Expected error match. + * @param string $message Message to display. + * @access public + */ + function expectError($expected, $message) { + array_push( + $this->_expectation_queue, + array($expected, $message)); } /** - * Converst an error code into it's string + * Converts an error code into it's string * representation. * @param $severity PHP integer error code. * @return String version of error code. @@ -167,16 +226,17 @@ * @static * @access public */ - function simpleTestErrorHandler($severity, $message, $filename, $line, $super_globals) { + function SimpleTestErrorHandler($severity, $message, $filename, $line, $super_globals) { if ($severity = $severity & error_reporting()) { restore_error_handler(); if (ini_get('log_errors')) { $label = SimpleErrorQueue::getSeverityAsString($severity); error_log("$label: $message in $filename on line $line"); } - $queue = &SimpleErrorQueue::instance(); - $queue->add($severity, $message, $filename, $line, $super_globals); - set_error_handler('simpleTestErrorHandler'); + $context = &SimpleTest::getContext(); + $queue = &$context->get('SimpleErrorQueue'); + $queue->add($severity, $message, $filename, $line); + set_error_handler('SimpleTestErrorHandler'); } } ?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/exceptions.php b/thirdparty/simpletest/simpletest/exceptions.php new file mode 100644 index 0000000..2cde258 --- /dev/null +++ b/thirdparty/simpletest/simpletest/exceptions.php @@ -0,0 +1,191 @@ +SimpleInvokerDecorator($invoker); + } + + /** + * Invokes a test method whilst trapping expected + * exceptions. Any left over unthrown exceptions + * are then reported as failures. + * @param string $method Test method to call. + */ + function invoke($method) { + $trap = SimpleTest::getContext()->get('SimpleExceptionTrap'); + $trap->clear(); + try { + parent::invoke($method); + } catch (Exception $exception) { + if (! $trap->isExpected($this->getTestCase(), $exception)) { + $this->getTestCase()->exception($exception); + } + $trap->clear(); + } + if ($message = $trap->getOutstanding()) { + $this->getTestCase()->fail($message); + } + } + } + + /** + * Tests exceptions either by type or the exact + * exception. This could be improved to accept + * a pattern expectation to test the error + * message, but that will have to come later. + * @package SimpleTest + * @subpackage UnitTester + */ + class ExceptionExpectation extends SimpleExpectation { + private $expected; + + /** + * Sets up the conditions to test against. + * If the expected value is a string, then + * it will act as a test of the class name. + * An exception as the comparison will + * trigger an identical match. Writing this + * down now makes it look doubly dumb. I hope + * come up with a better scheme later. + * @param mixed $expected A class name or an actual + * exception to compare with. + * @param string $message Message to display. + */ + function __construct($expected, $message = '%s') { + $this->expected = $expected; + parent::__construct($message); + } + + /** + * Carry out the test. + * @param Exception $compare Value to check. + * @return boolean True if matched. + */ + function test($compare) { + if (is_string($this->expected)) { + return ($compare instanceof $this->expected); + } + if (get_class($compare) != get_class($this->expected)) { + return false; + } + return $compare->getMessage() == $this->expected->getMessage(); + } + + /** + * Create the message to display describing the test. + * @param Exception $compare Exception to match. + * @return string Final message. + */ + function testMessage($compare) { + if (is_string($this->expected)) { + return "Exception [" . $this->describeException($compare) . + "] should be type [" . $this->expected . "]"; + } + return "Exception [" . $this->describeException($compare) . + "] should match [" . + $this->describeException($this->expected) . "]"; + } + + /** + * Summary of an Exception object. + * @param Exception $compare Exception to describe. + * @return string Text description. + */ + protected function describeException($exception) { + return get_class($exception) . ": " . $exception->getMessage(); + } + } + + /** + * Stores expected exceptions for when they + * get thrown. Saves the irritating try...catch + * block. + * @package SimpleTest + * @subpackage UnitTester + */ + class SimpleExceptionTrap { + private $expected; + private $message; + + /** + * Clears down the queue ready for action. + */ + function __construct() { + $this->clear(); + } + + /** + * Sets up an expectation of an exception. + * This has the effect of intercepting an + * exception that matches. + * @param SimpleExpectation $expected Expected exception to match. + * @param string $message Message to display. + * @access public + */ + function expectException($expected = false, $message = '%s') { + if ($expected === false) { + $expected = new AnythingExpectation(); + } + if (! SimpleExpectation::isExpectation($expected)) { + $expected = new ExceptionExpectation($expected); + } + $this->expected = $expected; + $this->message = $message; + } + + /** + * Compares the expected exception with any + * in the queue. Issues a pass or fail and + * returns the state of the test. + * @param SimpleTestCase $test Test case to send messages to. + * @param Exception $exception Exception to compare. + * @return boolean False on no match. + */ + function isExpected($test, $exception) { + if ($this->expected) { + return $test->assert($this->expected, $exception, $this->message); + } + return false; + } + + /** + * Tests for any left over exception. + * @return string/false The failure message or false if none. + */ + function getOutstanding() { + return sprintf($this->message, 'Failed to trap exception'); + } + + /** + * Discards the contents of the error queue. + */ + function clear() { + $this->expected = false; + $this->message = false; + } + } +?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/expectation.php b/thirdparty/simpletest/simpletest/expectation.php index 7cb4cf8..a4e9de2 100644 --- a/thirdparty/simpletest/simpletest/expectation.php +++ b/thirdparty/simpletest/simpletest/expectation.php @@ -30,7 +30,6 @@ * @param string $message Customised message on failure. */ function SimpleExpectation($message = '%s') { - $this->_dumper = &new SimpleDumper(); $this->_message = $message; } @@ -58,12 +57,14 @@ /** * Overlays the generated message onto the stored user * message. An additional message can be interjected. - * @param mixed $compare Comparison value. - * @return string Description of success - * or failure. + * @param mixed $compare Comparison value. + * @param SimpleDumper $dumper For formatting the results. + * @return string Description of success + * or failure. * @access public */ - function overlayMessage($compare) { + function overlayMessage($compare, $dumper) { + $this->_dumper = $dumper; return sprintf($this->_message, $this->testMessage($compare)); } @@ -91,6 +92,96 @@ SimpleTestCompatibility::isA($expectation, 'SimpleExpectation'); } } + + /** + * A wildcard expectation always matches. + * @package SimpleTest + * @subpackage MockObjects + */ + class AnythingExpectation extends SimpleExpectation { + + /** + * Tests the expectation. Always true. + * @param mixed $compare Ignored. + * @return boolean True. + * @access public + */ + function test($compare) { + return true; + } + + /** + * Returns a human readable test message. + * @param mixed $compare Comparison value. + * @return string Description of success + * or failure. + * @access public + */ + function testMessage($compare) { + $dumper = &$this->_getDumper(); + return 'Anything always matches [' . $dumper->describeValue($compare) . ']'; + } + } + + /** + * An expectation that passes on boolean true. + * @package SimpleTest + * @subpackage MockObjects + */ + class TrueExpectation extends SimpleExpectation { + + /** + * Tests the expectation. + * @param mixed $compare Should be true. + * @return boolean True on match. + * @access public + */ + function test($compare) { + return (boolean)$compare; + } + + /** + * Returns a human readable test message. + * @param mixed $compare Comparison value. + * @return string Description of success + * or failure. + * @access public + */ + function testMessage($compare) { + $dumper = &$this->_getDumper(); + return 'Expected true, got [' . $dumper->describeValue($compare) . ']'; + } + } + + /** + * An expectation that passes on boolean false. + * @package SimpleTest + * @subpackage MockObjects + */ + class FalseExpectation extends SimpleExpectation { + + /** + * Tests the expectation. + * @param mixed $compare Should be false. + * @return boolean True on match. + * @access public + */ + function test($compare) { + return ! (boolean)$compare; + } + + /** + * Returns a human readable test message. + * @param mixed $compare Comparison value. + * @return string Description of success + * or failure. + * @access public + */ + function testMessage($compare) { + $dumper = &$this->_getDumper(); + return 'Expected false, got [' . $dumper->describeValue($compare) . ']'; + } + } /** * Test for equality. @@ -471,8 +562,8 @@ /** * Describes a pattern match including the string * found and it's position. - * @package SimpleTest - * @subpackage UnitTester + * @package SimpleTest + * @subpackage UnitTester * @param string $pattern Regex to match against. * @param string $subject Subject to search. * @access protected @@ -480,7 +571,7 @@ function _describePatternMatch($pattern, $subject) { preg_match($pattern, $subject, $matches); $position = strpos($subject, $matches[0]); - $dumper = &$this->_getDumper(); + $dumper = $this->_getDumper(); return "Pattern [$pattern] detected at character [$position] in [" . $dumper->describeValue($subject) . "] as [" . $matches[0] . "] in region [" . @@ -717,4 +808,4 @@ "] should contain method [$method]"; } } -?> +?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/extensions/pear_test_case.php b/thirdparty/simpletest/simpletest/extensions/pear_test_case.php index 7c91fb0..4f9f4ad 100644 --- a/thirdparty/simpletest/simpletest/extensions/pear_test_case.php +++ b/thirdparty/simpletest/simpletest/extensions/pear_test_case.php @@ -58,7 +58,7 @@ * @public */ function assertNotNull($value, $message = "%s") { - parent::assertTrue(isset($value), $message); + parent::assert(new TrueExpectation(), isset($value), $message); } /** @@ -68,7 +68,7 @@ * @public */ function assertNull($value, $message = "%s") { - parent::assertTrue(!isset($value), $message); + parent::assert(new TrueExpectation(), !isset($value), $message); } /** @@ -86,7 +86,8 @@ "[" . $dumper->describeValue($first) . "] and [" . $dumper->describeValue($second) . "] should reference the same object"); - return $this->assertTrue( + return $this->assert( + new TrueExpectation(), SimpleTestCompatibility::isReference($first, $second), $message); } @@ -106,7 +107,8 @@ "[" . $dumper->describeValue($first) . "] and [" . $dumper->describeValue($second) . "] should not be the same object"); - return $this->assertFalse( + return $this->assert( + new falseExpectation(), SimpleTestCompatibility::isReference($first, $second), $message); } @@ -119,7 +121,7 @@ * @public */ function assertTrue($condition, $message = "%s") { - parent::assertTrue($condition, $message); + parent::assert(new TrueExpectation(), $condition, $message); } /** @@ -130,7 +132,7 @@ * @public */ function assertFalse($condition, $message = "%s") { - parent::assertTrue(!$condition, $message); + parent::assert(new FalseExpectation(), $condition, $message); } /** @@ -152,7 +154,7 @@ * @public */ function assertType($value, $type, $message = "%s") { - parent::assertTrue(gettype($value) == strtolower($type), $message); + parent::assert(new TrueExpectation(), gettype($value) == strtolower($type), $message); } /** diff --git a/thirdparty/simpletest/simpletest/extensions/phpunit_test_case.php b/thirdparty/simpletest/simpletest/extensions/phpunit_test_case.php index 7646b9c..1628ed7 100644 --- a/thirdparty/simpletest/simpletest/extensions/phpunit_test_case.php +++ b/thirdparty/simpletest/simpletest/extensions/phpunit_test_case.php @@ -38,7 +38,7 @@ * @public */ function assert($condition, $message = false) { - parent::assertTrue($condition, $message); + parent::assert(new TrueExpectation(), $condition, $message); } /** diff --git a/thirdparty/simpletest/simpletest/mock_objects.php b/thirdparty/simpletest/simpletest/mock_objects.php index ccb16df..aefdd31 100644 --- a/thirdparty/simpletest/simpletest/mock_objects.php +++ b/thirdparty/simpletest/simpletest/mock_objects.php @@ -27,36 +27,6 @@ } /** - * A wildcard expectation always matches. - * @package SimpleTest - * @subpackage MockObjects - */ - class AnythingExpectation extends SimpleExpectation { - - /** - * Tests the expectation. Always true. - * @param mixed $compare Ignored. - * @return boolean True. - * @access public - */ - function test($compare) { - return true; - } - - /** - * Returns a human readable test message. - * @param mixed $compare Comparison value. - * @return string Description of success - * or failure. - * @access public - */ - function testMessage($compare) { - $dumper = &$this->_getDumper(); - return 'Anything always matches [' . $dumper->describeValue($compare) . ']'; - } - } - - /** * Parameter comparison assertion. * @package SimpleTest * @subpackage MockObjects @@ -70,8 +40,6 @@ * those that are wildcarded. * If the value is not an array * then it is considered to match any. - * @param mixed $wildcard Any parameter matching this - * will always match. * @param string $message Customised message on failure. * @access public */ @@ -151,7 +119,7 @@ $comparison = $this->_coerceToExpectation($expected[$i]); if (! $comparison->test($parameters[$i])) { $messages[] = "parameter " . ($i + 1) . " with [" . - $comparison->overlayMessage($parameters[$i]) . "]"; + $comparison->overlayMessage($parameters[$i], $this->_getDumper()) . "]"; } } return "Parameter expectation differs at " . implode(" and ", $messages); @@ -473,7 +441,8 @@ * @access protected */ function &_getCurrentTestCase() { - return SimpleTest::getCurrent(); + $context = &SimpleTest::getContext(); + return $context->getTest(); } /** @@ -809,20 +778,17 @@ * test method has finished. Totals up the call * counts and triggers a test assertion if a test * is present for expected call counts. - * @param string $method Current method name. + * @param string $test_method Current method name. + * @param SimpleTestCase $test Test to send message to. * @access public */ - function atTestEnd($method) { + function atTestEnd($test_method, &$test) { foreach ($this->_expected_counts as $method => $expectation) { - $this->_assertTrue( - $expectation->test($this->getCallCount($method)), - $expectation->overlayMessage($this->getCallCount($method))); + $test->assert($expectation, $this->getCallCount($method)); } foreach ($this->_max_counts as $method => $expectation) { if ($expectation->test($this->getCallCount($method))) { - $this->_assertTrue( - true, - $expectation->overlayMessage($this->getCallCount($method))); + $test->assert($expectation, $this->getCallCount($method)); } } } @@ -880,39 +846,24 @@ * @access private */ function _checkExpectations($method, $args, $timing) { + $test = &$this->_getCurrentTestCase(); if (isset($this->_max_counts[$method])) { if (! $this->_max_counts[$method]->test($timing + 1)) { - $this->_assertTrue( - false, - $this->_max_counts[$method]->overlayMessage($timing + 1)); + $test->assert($this->_max_counts[$method], $timing + 1); } } if (isset($this->_expected_args_at[$timing][$method])) { - $this->_assertTrue( - $this->_expected_args_at[$timing][$method]->test($args), - "Mock method [$method] at [$timing] -> " . - $this->_expected_args_at[$timing][$method]->overlayMessage($args)); + $test->assert( + $this->_expected_args_at[$timing][$method], + $args, + "Mock method [$method] at [$timing] -> %s"); } elseif (isset($this->_expected_args[$method])) { - $this->_assertTrue( - $this->_expected_args[$method]->test($args), - "Mock method [$method] -> " . $this->_expected_args[$method]->overlayMessage($args)); + $test->assert( + $this->_expected_args[$method], + $args, + "Mock method [$method] -> %s"); } } - - /** - * Triggers an assertion on the held test case. - * Should be overridden when using another test - * framework other than the SimpleTest one if the - * assertion method has a different name. - * @param boolean $assertion True will pass. - * @param string $message Message that will go with - * the test event. - * @access protected - */ - function _assertTrue($assertion, $message) { - $test = &$this->_getCurrentTestCase(); - $test->assertTrue($assertion, $message); - } } /** @@ -928,7 +879,7 @@ * @access public */ function Mock() { - trigger_error('Mock factory methods are class only.'); + trigger_error('Mock factory methods are static.'); } /** @@ -970,19 +921,12 @@ /** * Uses a stack trace to find the line of an assertion. - * @param array $stack Stack frames top most first. Only - * needed if not using the PHP - * backtrace function. - * @return string Location of first expect* - * method embedded in format string. * @access public * @static */ - function getExpectationLine($stack = false) { - if ($stack === false) { - $stack = SimpleTestCompatibility::getStackTrace(); - } - return SimpleDumper::getFormattedAssertionLine($stack); + function getExpectationLine() { + $trace = new SimpleStackTrace(array('expect')); + return $trace->traceMethod(); } } @@ -1051,7 +995,7 @@ } $mock_reflection = new SimpleReflection($this->_mock_class); if ($mock_reflection->classExistsSansAutoload()) { - trigger_error("Partial mock class [$mock_class] already exists"); + trigger_error('Partial mock class [' . $this->_mock_class . '] already exists'); return false; } return eval($this->_extendClassCode($methods)); diff --git a/thirdparty/simpletest/simpletest/parser.php b/thirdparty/simpletest/simpletest/parser.php index 97db5ca..06977f8 100644 --- a/thirdparty/simpletest/simpletest/parser.php +++ b/thirdparty/simpletest/simpletest/parser.php @@ -645,13 +645,15 @@ * @access public */ function acceptAttributeToken($token, $event) { - if ($event == LEXER_UNMATCHED) { - $this->_attributes[$this->_current_attribute] .= - SimpleHtmlSaxParser::decodeHtml($token); - } - if ($event == LEXER_SPECIAL) { - $this->_attributes[$this->_current_attribute] .= - preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token)); + if ($this->_current_attribute) { + if ($event == LEXER_UNMATCHED) { + $this->_attributes[$this->_current_attribute] .= + SimpleHtmlSaxParser::decodeHtml($token); + } + if ($event == LEXER_SPECIAL) { + $this->_attributes[$this->_current_attribute] .= + preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token)); + } } return true; } diff --git a/thirdparty/simpletest/simpletest/reflection_php5.php b/thirdparty/simpletest/simpletest/reflection_php5.php index b816ef3..7e023f3 100644 --- a/thirdparty/simpletest/simpletest/reflection_php5.php +++ b/thirdparty/simpletest/simpletest/reflection_php5.php @@ -8,8 +8,8 @@ /** * Version specific reflection API. - * @package SimpleTest - * @subpackage UnitTester + * @package SimpleTest + * @subpackage UnitTester */ class SimpleReflection { var $_interface; @@ -193,12 +193,17 @@ * @access public */ function getSignature($name) { - if ($name == '__get') { - return 'function __get($key)'; - } if ($name == '__set') { return 'function __set($key, $value)'; } + if ($name == '__call') { + return 'function __call($method, $arguments)'; + } + if (version_compare(phpversion(), '5.1.0', '>=')) { + if (in_array($name, array('__get', '__isset', $name == '__unset'))) { + return "function {$name}(\$key)"; + } + } if (! is_callable(array($this->_interface, $name))) { return "function $name()"; } @@ -217,31 +222,31 @@ * @access private */ function _getFullSignature($name) { - $interface = new ReflectionClass($this->_interface); - $method = $interface->getMethod($name); - $reference = $method->returnsReference() ? '&' : ''; - return "function $reference$name(" . - implode(', ', $this->_getParameterSignatures($method)) . - ")"; + $interface = new ReflectionClass($this->_interface); + $method = $interface->getMethod($name); + $reference = $method->returnsReference() ? '&' : ''; + return "function $reference$name(" . + implode(', ', $this->_getParameterSignatures($method)) . + ")"; } /** * Gets the source code for each parameter. * @param ReflectionMethod $method Method object from - * reflection API + * reflection API * @return array List of strings, each * a snippet of code. * @access private */ function _getParameterSignatures($method) { - $signatures = array(); + $signatures = array(); foreach ($method->getParameters() as $parameter) { $type = $parameter->getClass(); $signatures[] = - (! is_null($type) ? $type->getName() . ' ' : '') . - ($parameter->isPassedByReference() ? '&' : '') . - '$' . $this->_suppressSpurious($parameter->getName()) . - ($this->_isOptional($parameter) ? ' = null' : ''); + (! is_null($type) ? $type->getName() . ' ' : '') . + ($parameter->isPassedByReference() ? '&' : '') . + '$' . $this->_suppressSpurious($parameter->getName()) . + ($this->_isOptional($parameter) ? ' = null' : ''); } return $signatures; } diff --git a/thirdparty/simpletest/simpletest/remote.php b/thirdparty/simpletest/simpletest/remote.php index cb6b9f2..c7e02b1 100644 --- a/thirdparty/simpletest/simpletest/remote.php +++ b/thirdparty/simpletest/simpletest/remote.php @@ -75,7 +75,8 @@ * @access protected */ function &_createBrowser() { - return new SimpleBrowser(); + $browser = &new SimpleBrowser(); + return $browser; } /** @@ -85,7 +86,8 @@ * @access protected */ function &_createParser(&$reporter) { - return new SimpleTestXmlParser($reporter); + $parser = &new SimpleTestXmlParser($reporter); + return $parser; } /** diff --git a/thirdparty/simpletest/simpletest/reporter.php b/thirdparty/simpletest/simpletest/reporter.php index 3be2a15..492faab 100644 --- a/thirdparty/simpletest/simpletest/reporter.php +++ b/thirdparty/simpletest/simpletest/reporter.php @@ -40,6 +40,7 @@ */ function paintHeader($test_name) { $this->sendNoCacheHeaders(); + print ""; print "\n\n$test_name\n"; print "\n"; @@ -74,7 +75,9 @@ * @access protected */ function _getCss() { - return ".fail { color: red; } pre { background-color: lightgray; }"; + return ".fail { background-color: inherit; color: red; }" . + ".pass { background-color: inherit; color: green; }" . + " pre { background-color: lightgray; color: inherit; }"; } /** @@ -115,10 +118,9 @@ } /** - * Paints a PHP error or exception. + * Paints a PHP error. * @param string $message Message is ignored. * @access public - * @abstract */ function paintError($message) { parent::paintError($message); @@ -130,6 +132,38 @@ } /** + * Paints a PHP exception. + * @param Exception $exception Exception to display. + * @access public + */ + function paintException($exception) { + parent::paintException($exception); + print "Exception: "; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print implode(" -> ", $breadcrumb); + $message = 'Unexpected exception of type [' . get_class($exception) . + '] with message ['. $exception->getMessage() . + '] in ['. $exception->getFile() . + ' line ' . $exception->getLine() . ']'; + print " -> " . $this->_htmlEntities($message) . "
\n"; + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + parent::paintSkip($message); + print "Skipped: "; + $breadcrumb = $this->getTestList(); + array_shift($breadcrumb); + print implode(" -> ", $breadcrumb); + print " -> " . $this->_htmlEntities($message) . "
\n"; + } + + /** * Paints formatted text such as dumped variables. * @param string $message Text to show. * @access public @@ -218,7 +252,7 @@ /** * Paints a PHP error or exception. - * @param string $message Message is ignored. + * @param string $message Message to be shown. * @access public * @abstract */ @@ -228,6 +262,31 @@ } /** + * Paints a PHP error or exception. + * @param Exception $exception Exception to describe. + * @access public + * @abstract + */ + function paintException($exception) { + parent::paintException($exception); + $message = 'Unexpected exception of type [' . get_class($exception) . + '] with message ['. $exception->getMessage() . + '] in ['. $exception->getFile() . + ' line ' . $exception->getLine() . ']'; + print "Exception " . $this->getExceptionCount() . "!\n$message\n"; + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + parent::paintSkip($message); + print "Skip: $message\n"; + } + + /** * Paints formatted text such as dumped variables. * @param string $message Text to show. * @access public diff --git a/thirdparty/simpletest/simpletest/scorer.php b/thirdparty/simpletest/simpletest/scorer.php index 62d232d..279d714 100644 --- a/thirdparty/simpletest/simpletest/scorer.php +++ b/thirdparty/simpletest/simpletest/scorer.php @@ -149,8 +149,7 @@ } /** - * Deals with PHP 4 throwing an error or PHP 5 - * throwing an exception. + * Deals with PHP 4 throwing an error. * @param string $message Text of error formatted by * the test case. * @access public @@ -160,6 +159,23 @@ } /** + * Deals with PHP 5 throwing an exception. + * @param Exception $exception The actual exception thrown. + * @access public + */ + function paintException($exception) { + $this->_exceptions++; + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + } + + /** * Accessor for the number of passes so far. * @return integer Number of passes. * @access public @@ -237,6 +253,16 @@ $this->_size = null; $this->_progress = 0; } + + /** + * Gets the formatter for variables and other small + * generic data items. + * @return SimpleDumper Formatter. + * @access public + */ + function getDumper() { + return new SimpleDumper(); + } /** * Paints the start of a group test. Will also paint @@ -393,7 +419,7 @@ var $_reporter; /** - * Mediates between teh reporter and the test case. + * Mediates between the reporter and the test case. * @param SimpleScorer $reporter Reporter to receive events. */ function SimpleReporterDecorator(&$reporter) { @@ -443,6 +469,16 @@ function &createInvoker(&$invoker) { return $this->_reporter->createInvoker($invoker); } + + /** + * Gets the formatter for variables and other small + * generic data items. + * @return SimpleDumper Formatter. + * @access public + */ + function getDumper() { + return $this->_reporter->getDumper(); + } /** * Paints the start of a group test. @@ -529,6 +565,24 @@ /** * Chains to the wrapped reporter. + * @param Exception $exception Exception to show. + * @access public + */ + function paintException($exception) { + $this->_reporter->paintException($exception); + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + $this->_reporter->paintSkip($message); + } + + /** + * Chains to the wrapped reporter. * @param string $message Text to display. * @access public */ @@ -635,6 +689,16 @@ } return $invoker; } + + /** + * Gets the formatter for variables and other small + * generic data items. + * @return SimpleDumper Formatter. + * @access public + */ + function getDumper() { + return new SimpleDumper(); + } /** * Paints the start of a group test. @@ -736,6 +800,28 @@ $this->_reporters[$i]->paintError($message); } } + + /** + * Chains to the wrapped reporter. + * @param Exception $exception Exception to display. + * @access public + */ + function paintException($exception) { + for ($i = 0; $i < count($this->_reporters); $i++) { + $this->_reporters[$i]->paintException($exception); + } + } + + /** + * Prints the message for skipping tests. + * @param string $message Text of skip condition. + * @access public + */ + function paintSkip($message) { + for ($i = 0; $i < count($this->_reporters); $i++) { + $this->_reporters[$i]->paintSkip($message); + } + } /** * Chains to the wrapped reporter. diff --git a/thirdparty/simpletest/simpletest/shell_tester.php b/thirdparty/simpletest/simpletest/shell_tester.php index 1e513ef..d47d2d6 100644 --- a/thirdparty/simpletest/simpletest/shell_tester.php +++ b/thirdparty/simpletest/simpletest/shell_tester.php @@ -63,7 +63,7 @@ /** * Test case for testing of command line scripts and - * utilities. Usually scripts taht are external to the + * utilities. Usually scripts that are external to the * PHP code, but support it in some way. * @package SimpleTest * @subpackage UnitTester @@ -127,6 +127,33 @@ $shell = &$this->_getShell(); return $shell->getOutputAsList(); } + + /** + * Called from within the test methods to register + * passes and failures. + * @param boolean $result Pass on true. + * @param string $message Message to display describing + * the test state. + * @return boolean True on pass + * @access public + */ + function assertTrue($result, $message = false) { + return $this->assert(new TrueExpectation(), $result, $message); + } + + /** + * Will be true on false and vice versa. False + * is the PHP definition of false, so that null, + * empty strings, zero and an empty array all count + * as false. + * @param boolean $result Pass on false. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertFalse($result, $message = '%s') { + return $this->assert(new FalseExpectation(), $result, $message); + } /** * Will trigger a pass if the two parameters have @@ -303,4 +330,4 @@ return $shell; } } -?> \ No newline at end of file +?> diff --git a/thirdparty/simpletest/simpletest/simpletest.php b/thirdparty/simpletest/simpletest/simpletest.php index 0a1693a..6464bdc 100644 --- a/thirdparty/simpletest/simpletest/simpletest.php +++ b/thirdparty/simpletest/simpletest/simpletest.php @@ -17,10 +17,8 @@ /**#@-*/ /** - * Static global directives and options. I hate this - * class. It's a mixture of reference hacks, configuration - * and previous design screw-ups that I have to maintain - * to keep backward compatibility. + * Registry and test context. Includes a few + * global options that I'm slowly getting rid of. * @package SimpleTest */ class SimpleTest { @@ -59,8 +57,8 @@ * missing abstract declarations. This cannot * be done whilst loading classes wiithout forcing * a particular order on the class declarations and - * the ignore() calls. It's nice to havethe ignore() - * calls at the top of teh file. + * the ignore() calls. It's just nice to have the ignore() + * calls at the top of the file before the actual declarations. * @param array $classes Class names of interest. * @static * @access public @@ -157,30 +155,6 @@ } /** - * Sets the current test case instance. This - * global instance can be used by the mock objects - * to send message to the test cases. - * @param SimpleTestCase $test Test case to register. - * @access public - * @static - */ - function setCurrent(&$test) { - $registry = &SimpleTest::_getRegistry(); - $registry['CurrentTestCase'] = &$test; - } - - /** - * Accessor for current test instance. - * @return SimpleTEstCase Currently running test. - * @access public - * @static - */ - function &getCurrent() { - $registry = &SimpleTest::_getRegistry(); - return $registry['CurrentTestCase']; - } - - /** * Accessor for global registry of options. * @return hash All stored values. * @access private @@ -195,6 +169,21 @@ } /** + * Accessor for the context of the current + * test run. + * @return SimpleTestContext Current test run. + * @access public + * @static + */ + function &getContext() { + static $context = false; + if (! $context) { + $context = new SimpleTestContext(); + } + return $context; + } + + /** * Constant default values. * @return hash All registry defaults. * @access private @@ -212,6 +201,168 @@ } /** + * Container for all components for a specific + * test run. Makes things like error queues + * available to PHP event handlers, and also + * gets around some nasty reference issues in + * the mocks. + * @package SimpleTest + */ + class SimpleTestContext { + var $_test; + var $_reporter; + var $_resources; + + /** + * Clears down the current context. + * @access public + */ + function clear() { + $this->_resources = array(); + } + + /** + * Sets the current test case instance. This + * global instance can be used by the mock objects + * to send message to the test cases. + * @param SimpleTestCase $test Test case to register. + * @access public + */ + function setTest(&$test) { + $this->clear(); + $this->_test = &$test; + } + + /** + * Accessor for currently running test case. + * @return SimpleTestCase Current test. + * @acess pubic + */ + function &getTest() { + return $this->_test; + } + + /** + * Sets the current reporter. This + * global instance can be used by the mock objects + * to send messages. + * @param SimpleReporter $reporter Reporter to register. + * @access public + */ + function setReporter(&$reporter) { + $this->clear(); + $this->_reporter = &$reporter; + } + + /** + * Accessor for current reporter. + * @return SimpleReporter Current reporter. + * @acess pubic + */ + function &getReporter() { + return $this->_reporter; + } + + /** + * Accessor for the Singleton resource. + * @return object Global resource. + * @access public + * @static + */ + function &get($resource) { + if (! isset($this->_resources[$resource])) { + $this->_resources[$resource] = &new $resource(); + } + return $this->_resources[$resource]; + } + } + + /** + * Interrogates the stack trace to recover the + * failure point. + * @package SimpleTest + * @subpackage UnitTester + */ + class SimpleStackTrace { + var $_prefixes; + + /** + * Stashes the list of target prefixes. + * @param array $prefixes List of method prefixes + * to search for. + */ + function SimpleStackTrace($prefixes) { + $this->_prefixes = $prefixes; + } + + /** + * Extracts the last method name that was not within + * Simpletest itself. Captures a stack trace if none given. + * @param array $stack List of stack frames. + * @return string Snippet of test report with line + * number and file. + * @access public + */ + function traceMethod($stack = false) { + $stack = $stack ? $stack : $this->_captureTrace(); + foreach ($stack as $frame) { + if ($this->_frameLiesWithinSimpleTestFolder($frame)) { + continue; + } + if ($this->_frameMatchesPrefix($frame)) { + return ' at [' . $frame['file'] . ' line ' . $frame['line'] . ']'; + } + } + return ''; + } + + /** + * Test to see if error is generated by SimpleTest itself. + * @param array $frame PHP stack frame. + * @return boolean True if a SimpleTest file. + * @access private + */ + function _frameLiesWithinSimpleTestFolder($frame) { + if (isset($frame['file'])) { + $path = substr(SIMPLE_TEST, 0, -1); + if (strpos($frame['file'], $path) === 0) { + if (dirname($frame['file']) == $path) { + return true; + } + } + } + return false; + } + + /** + * Tries to determine if the method call is an assert, etc. + * @param array $frame PHP stack frame. + * @return boolean True if matches a target. + * @access private + */ + function _frameMatchesPrefix($frame) { + foreach ($this->_prefixes as $prefix) { + if (strncmp($frame['function'], $prefix, strlen($prefix)) == 0) { + return true; + } + } + return false; + } + + /** + * Grabs a current stack trace. + * @return array Fulle trace. + * @access private + */ + function _captureTrace() { + if (function_exists('debug_backtrace')) { + return array_reverse(debug_backtrace()); + } + return array(); + } + } + + /** * @deprecated */ class SimpleTestOptions extends SimpleTest { @@ -279,4 +430,4 @@ return Simpletest::getDefaultProxyPassword(); } } -?> \ No newline at end of file +?> diff --git a/thirdparty/simpletest/simpletest/test_case.php b/thirdparty/simpletest/simpletest/test_case.php index c2f56f0..62d0349 100644 --- a/thirdparty/simpletest/simpletest/test_case.php +++ b/thirdparty/simpletest/simpletest/test_case.php @@ -24,10 +24,8 @@ require_once(dirname(__FILE__) . '/reflection_php4.php'); } if (! defined('SIMPLE_TEST')) { - /** - * @ignore - */ - define('SIMPLE_TEST', dirname(__FILE__) . '/'); + /** @ignore */ + define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR); } /**#@-*/ @@ -43,6 +41,7 @@ var $_label = false; var $_reporter; var $_observers; + var $_should_skip = false; /** * Sets up the test with no display. @@ -66,6 +65,41 @@ } /** + * This is a placeholder for skipping tests. In this + * method you place skipIf() and skipUnless() calls to + * set the skipping state. + * @access public + */ + function skip() { + } + + /** + * Will issue a message to the reporter and tell the test + * case to skip if the incoming flag is true. + * @param string $should_skip Condition causing the tests to be skipped. + * @param string $message Text of skip condition. + * @access public + */ + function skipIf($should_skip, $message = '%s') { + if ($should_skip && ! $this->_should_skip) { + $this->_should_skip = true; + $message = sprintf($message, 'Skipping [' . get_class($this) . ']'); + $this->_reporter->paintSkip($message . $this->getAssertionLine()); + } + } + + /** + * Will issue a message to the reporter and tell the test + * case to skip if the incoming flag is false. + * @param string $shouldnt_skip Condition causing the tests to be run. + * @param string $message Text of skip condition. + * @access public + */ + function skipUnless($shouldnt_skip, $message = false) { + $this->skipIf(! $shouldnt_skip, $message); + } + + /** * Used to invoke the single tests. * @return SimpleInvoker Individual test runner. * @access public @@ -83,21 +117,27 @@ * starting with the string "test" unless a method * is specified. * @param SimpleReporter $reporter Current test reporter. + * @return boolean True if all tests passed. * @access public */ function run(&$reporter) { - SimpleTest::setCurrent($this); + $context = &SimpleTest::getContext(); + $context->setTest($this); + $context->setReporter($reporter); $this->_reporter = &$reporter; - $this->_reporter->paintCaseStart($this->getLabel()); - foreach ($this->getTests() as $method) { - if ($this->_reporter->shouldInvoke($this->getLabel(), $method)) { - $invoker = &$this->_reporter->createInvoker($this->createInvoker()); - $invoker->before($method); - $invoker->invoke($method); - $invoker->after($method); + $reporter->paintCaseStart($this->getLabel()); + $this->skip(); + if (! $this->_should_skip) { + foreach ($this->getTests() as $method) { + if ($reporter->shouldInvoke($this->getLabel(), $method)) { + $invoker = &$this->_reporter->createInvoker($this->createInvoker()); + $invoker->before($method); + $invoker->invoke($method); + $invoker->after($method); + } } } - $this->_reporter->paintCaseEnd($this->getLabel()); + $reporter->paintCaseEnd($this->getLabel()); unset($this->_reporter); return $reporter->getStatus(); } @@ -169,7 +209,7 @@ */ function after($method) { for ($i = 0; $i < count($this->_observers); $i++) { - $this->_observers[$i]->atTestEnd($method); + $this->_observers[$i]->atTestEnd($method, $this); } $this->_reporter->paintMethodEnd($method); } @@ -185,9 +225,7 @@ } /** - * Sends a pass event with a message. - * @param string $message Message to send. - * @access public + * @deprecated */ function pass($message = "Pass") { if (! isset($this->_reporter)) { @@ -226,7 +264,7 @@ trigger_error('Can only make assertions within test methods'); } $this->_reporter->paintError( - "Unexpected PHP error [$message] severity [$severity] in [$file] line [$line]"); + "Unexpected PHP error [$message] severity [$severity] in [$file line $line]"); } /** @@ -236,21 +274,11 @@ * @access public */ function exception($exception) { - $this->_reporter->paintError( - 'Unexpected exception of type [' . get_class($exception) . - '] with message ['. $exception->getMessage() . - '] in ['. $exception->getFile() . - '] line [' . $exception->getLine() . ']'); + $this->_reporter->paintException($exception); } /** - * Sends a user defined event to the test reporter. - * This is for small scale extension where - * both the test case and either the reporter or - * display are subclassed. - * @param string $type Type of event. - * @param mixed $payload Object or message to deliver. - * @access public + * @deprecated */ function signal($type, &$payload) { if (! isset($this->_reporter)) { @@ -260,15 +288,6 @@ } /** - * Cancels any outstanding errors. - * @access public - */ - function swallowErrors() { - $queue = &SimpleErrorQueue::instance(); - $queue->clear(); - } - - /** * Runs an expectation directly, for extending the * tests with new expectation classes. * @param SimpleExpectation $expectation Expectation subclass. @@ -278,9 +297,15 @@ * @access public */ function assert(&$expectation, $compare, $message = '%s') { - return $this->assertTrue( - $expectation->test($compare), - sprintf($message, $expectation->overlayMessage($compare))); + if ($expectation->test($compare)) { + return $this->pass(sprintf( + $message, + $expectation->overlayMessage($compare, $this->_reporter->getDumper()))); + } else { + return $this->fail(sprintf( + $message, + $expectation->overlayMessage($compare, $this->_reporter->getDumper()))); + } } /** @@ -291,57 +316,14 @@ } /** - * Called from within the test methods to register - * passes and failures. - * @param boolean $result Pass on true. - * @param string $message Message to display describing - * the test state. - * @return boolean True on pass - * @access public - */ - function assertTrue($result, $message = false) { - if (! $message) { - $message = 'True assertion got ' . ($result ? 'True' : 'False'); - } - if ($result) { - return $this->pass($message); - } else { - return $this->fail($message); - } - } - - /** - * Will be true on false and vice versa. False - * is the PHP definition of false, so that null, - * empty strings, zero and an empty array all count - * as false. - * @param boolean $result Pass on false. - * @param string $message Message to display. - * @return boolean True on pass - * @access public - */ - function assertFalse($result, $message = false) { - if (! $message) { - $message = 'False assertion got ' . ($result ? 'True' : 'False'); - } - return $this->assertTrue(! $result, $message); - } - - /** * Uses a stack trace to find the line of an assertion. - * @param string $format String formatting. - * @param array $stack Stack frames top most first. Only - * needed if not using the PHP - * backtrace function. * @return string Line number of first assert* * method embedded in format string. * @access public */ - function getAssertionLine($stack = false) { - if ($stack === false) { - $stack = SimpleTestCompatibility::getStackTrace(); - } - return SimpleDumper::getFormattedAssertionLine($stack); + function getAssertionLine() { + $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip')); + return $trace->traceMethod(); } /** @@ -354,7 +336,8 @@ * @access public */ function dump($variable, $message = false) { - $formatted = SimpleDumper::dump($variable); + $dumper = $this->_reporter->getDumper(); + $formatted = $dumper->dump($variable); if ($message) { $formatted = $message . "\n" . $formatted; } @@ -363,10 +346,7 @@ } /** - * Dispatches a text message straight to the - * test suite. Useful for status bar displays. - * @param string $message Message to show. - * @access public + * @deprecated */ function sendMessage($message) { $this->_reporter->PaintMessage($message); @@ -390,7 +370,7 @@ * @package SimpleTest * @subpackage UnitTester */ - class GroupTest { + class TestSuite { var $_label; var $_test_cases; var $_old_track_errors; @@ -402,7 +382,7 @@ * of the test. * @access public */ - function GroupTest($label = false) { + function TestSuite($label = false) { $this->_label = $label ? $label : get_class($this); $this->_test_cases = array(); $this->_old_track_errors = ini_get('track_errors'); @@ -440,7 +420,7 @@ * @access public */ function addTestClass($class) { - if ($this->_getBaseTestCase($class) == 'grouptest') { + if ($this->_getBaseTestCase($class) == 'testsuite' || $this->_getBaseTestCase($class) == 'grouptest') { $this->_test_cases[] = &new $class(); } else { $this->_test_cases[] = $class; @@ -457,12 +437,12 @@ function addTestFile($test_file) { $existing_classes = get_declared_classes(); if ($error = $this->_requireWithError($test_file)) { - $this->addTestCase(new BadGroupTest($test_file, $error)); + $this->addTestCase(new BadTestSuite($test_file, $error)); return; } $classes = $this->_selectRunnableTests($existing_classes, get_declared_classes()); if (count($classes) == 0) { - $this->addTestCase(new BadGroupTest($test_file, "No runnable test cases in [$test_file]")); + $this->addTestCase(new BadTestSuite($test_file, "No runnable test cases in [$test_file]")); return; } $group = &$this->_createGroupFromClasses($test_file, $classes); @@ -482,11 +462,14 @@ $error = isset($php_errormsg) ? $php_errormsg : false; $this->_disableErrorReporting(); $self_inflicted_errors = array( - 'Assigning the return value of new by reference is deprecated', - 'var: Deprecated. Please use the public/private/protected modifiers'); - if (in_array($error, $self_inflicted_errors)) { - return false; - } + '/Assigning the return value of new by reference/i', + '/var: Deprecated/i', + '/Non-static method/i'); + foreach ($self_inflicted_errors as $pattern) { + if (preg_match($pattern, $error)) { + return false; + } + } return $error; } @@ -548,13 +531,13 @@ * Builds a group test from a class list. * @param string $title Title of new group. * @param array $classes Test classes. - * @return GroupTest Group loaded with the new + * @return TestSuite Group loaded with the new * test cases. * @access private */ function &_createGroupFromClasses($title, $classes) { SimpleTest::ignoreParentsIfIgnored($classes); - $group = &new GroupTest($title); + $group = &new TestSuite($title); foreach ($classes as $class) { if (! SimpleTest::isIgnored($class)) { $group->addTestClass($class); @@ -572,7 +555,7 @@ function _getBaseTestCase($class) { while ($class = get_parent_class($class)) { $class = strtolower($class); - if ($class == "simpletestcase" || $class == "grouptest") { + if ($class == 'simpletestcase' || $class == 'testsuite' || $class == 'grouptest') { return $class; } } @@ -603,6 +586,7 @@ $class = $this->_test_cases[$i]; $test = &new $class(); $test->run($reporter); + unset($test); } else { $this->_test_cases[$i]->run($reporter); } @@ -628,6 +612,11 @@ return $count; } } + + /** + * @deprecated + */ + class GroupTest extends TestSuite { } /** * This is a failing group test for when a test suite hasn't @@ -635,7 +624,7 @@ * @package SimpleTest * @subpackage UnitTester */ - class BadGroupTest { + class BadTestSuite { var $_label; var $_error; @@ -645,7 +634,7 @@ * of the test. * @access public */ - function BadGroupTest($label, $error) { + function BadTestSuite($label, $error) { $this->_label = $label; $this->_error = $error; } @@ -666,7 +655,7 @@ */ function run(&$reporter) { $reporter->paintGroupStart($this->getLabel(), $this->getSize()); - $reporter->paintFail('Bad GroupTest [' . $this->getLabel() . + $reporter->paintFail('Bad TestSuite [' . $this->getLabel() . '] with error [' . $this->_error . ']'); $reporter->paintGroupEnd($this->getLabel()); return $reporter->getStatus(); @@ -681,4 +670,9 @@ return 0; } } -?> + + /** + * @deprecated + */ + class BadGroupTest extends BadTestSuite { } +?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/unit_tester.php b/thirdparty/simpletest/simpletest/unit_tester.php index bbfe02b..6efaf60 100644 --- a/thirdparty/simpletest/simpletest/unit_tester.php +++ b/thirdparty/simpletest/simpletest/unit_tester.php @@ -37,17 +37,44 @@ } /** + * Called from within the test methods to register + * passes and failures. + * @param boolean $result Pass on true. + * @param string $message Message to display describing + * the test state. + * @return boolean True on pass + * @access public + */ + function assertTrue($result, $message = false) { + return $this->assert(new TrueExpectation(), $result, $message); + } + + /** + * Will be true on false and vice versa. False + * is the PHP definition of false, so that null, + * empty strings, zero and an empty array all count + * as false. + * @param boolean $result Pass on false. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertFalse($result, $message = '%s') { + return $this->assert(new FalseExpectation(), $result, $message); + } + + /** * Will be true if the value is null. * @param null $value Supposedly null value. * @param string $message Message to display. * @return boolean True on pass * @access public */ - function assertNull($value, $message = "%s") { + function assertNull($value, $message = '%s') { $dumper = &new SimpleDumper(); $message = sprintf( $message, - "[" . $dumper->describeValue($value) . "] should be null"); + '[' . $dumper->describeValue($value) . '] should be null'); return $this->assertTrue(! isset($value), $message); } @@ -58,11 +85,11 @@ * @return boolean True on pass. * @access public */ - function assertNotNull($value, $message = "%s") { + function assertNotNull($value, $message = '%s') { $dumper = &new SimpleDumper(); $message = sprintf( $message, - "[" . $dumper->describeValue($value) . "] should not be null"); + '[' . $dumper->describeValue($value) . '] should not be null'); return $this->assertTrue(isset($value), $message); } @@ -76,7 +103,7 @@ * @return boolean True on pass. * @access public */ - function assertIsA($object, $type, $message = "%s") { + function assertIsA($object, $type, $message = '%s') { return $this->assert( new IsAExpectation($type), $object, @@ -93,7 +120,7 @@ * @return boolean True on pass. * @access public */ - function assertNotA($object, $type, $message = "%s") { + function assertNotA($object, $type, $message = '%s') { return $this->assert( new NotAExpectation($type), $object, @@ -109,7 +136,7 @@ * @return boolean True on pass * @access public */ - function assertEqual($first, $second, $message = "%s") { + function assertEqual($first, $second, $message = '%s') { return $this->assert( new EqualExpectation($first), $second, @@ -125,7 +152,7 @@ * @return boolean True on pass * @access public */ - function assertNotEqual($first, $second, $message = "%s") { + function assertNotEqual($first, $second, $message = '%s') { return $this->assert( new NotEqualExpectation($first), $second, @@ -142,7 +169,7 @@ * @return boolean True on pass * @access public */ - function assertWithinMargin($first, $second, $margin, $message = "%s") { + function assertWithinMargin($first, $second, $margin, $message = '%s') { return $this->assert( new WithinMarginExpectation($first, $margin), $second, @@ -159,7 +186,7 @@ * @return boolean True on pass * @access public */ - function assertOutsideMargin($first, $second, $margin, $message = "%s") { + function assertOutsideMargin($first, $second, $margin, $message = '%s') { return $this->assert( new OutsideMarginExpectation($first, $margin), $second, @@ -175,7 +202,7 @@ * @return boolean True on pass * @access public */ - function assertIdentical($first, $second, $message = "%s") { + function assertIdentical($first, $second, $message = '%s') { return $this->assert( new IdenticalExpectation($first), $second, @@ -191,7 +218,7 @@ * @return boolean True on pass * @access public */ - function assertNotIdentical($first, $second, $message = "%s") { + function assertNotIdentical($first, $second, $message = '%s') { return $this->assert( new NotIdenticalExpectation($first), $second, @@ -207,13 +234,13 @@ * @return boolean True on pass * @access public */ - function assertReference(&$first, &$second, $message = "%s") { + function assertReference(&$first, &$second, $message = '%s') { $dumper = &new SimpleDumper(); $message = sprintf( $message, - "[" . $dumper->describeValue($first) . - "] and [" . $dumper->describeValue($second) . - "] should reference the same object"); + '[' . $dumper->describeValue($first) . + '] and [' . $dumper->describeValue($second) . + '] should reference the same object'); return $this->assertTrue( SimpleTestCompatibility::isReference($first, $second), $message); @@ -229,13 +256,13 @@ * @return boolean True on pass * @access public */ - function assertClone(&$first, &$second, $message = "%s") { + function assertClone(&$first, &$second, $message = '%s') { $dumper = &new SimpleDumper(); $message = sprintf( $message, - "[" . $dumper->describeValue($first) . - "] and [" . $dumper->describeValue($second) . - "] should not be the same object"); + '[' . $dumper->describeValue($first) . + '] and [' . $dumper->describeValue($second) . + '] should not be the same object'); $identical = &new IdenticalExpectation($first); return $this->assertTrue( $identical->test($second) && @@ -268,7 +295,7 @@ * @return boolean True on pass * @access public */ - function assertPattern($pattern, $subject, $message = "%s") { + function assertPattern($pattern, $subject, $message = '%s') { return $this->assert( new PatternExpectation($pattern), $subject, @@ -278,7 +305,7 @@ /** * @deprecated */ - function assertWantedPattern($pattern, $subject, $message = "%s") { + function assertWantedPattern($pattern, $subject, $message = '%s') { return $this->assertPattern($pattern, $subject, $message); } @@ -292,7 +319,7 @@ * @return boolean True on pass * @access public */ - function assertNoPattern($pattern, $subject, $message = "%s") { + function assertNoPattern($pattern, $subject, $message = '%s') { return $this->assert( new NoPatternExpectation($pattern), $subject, @@ -302,50 +329,63 @@ /** * @deprecated */ - function assertNoUnwantedPattern($pattern, $subject, $message = "%s") { + function assertNoUnwantedPattern($pattern, $subject, $message = '%s') { return $this->assertNoPattern($pattern, $subject, $message); } /** - * Confirms that no errors have occoured so - * far in the test method. - * @param string $message Message to display. - * @return boolean True on pass + * @deprecated + */ + function swallowErrors() { + $context = &SimpleTest::getContext(); + $queue = &$context->get('SimpleErrorQueue'); + $queue->clear(); + } + + /** + * @deprecated + */ + function assertNoErrors($message = '%s') { + $context = &SimpleTest::getContext(); + $queue = &$context->get('SimpleErrorQueue'); + return $queue->assertNoErrors($message); + } + + /** + * @deprecated + */ + function assertError($expected = false, $message = '%s') { + $context = &SimpleTest::getContext(); + $queue = &$context->get('SimpleErrorQueue'); + return $queue->assertError($this->_coerceExpectation($expected), $message); + } + + /** + * Prepares for an error. If the error mismatches it + * passes through, otherwise it is swallowed. Any + * left over errors trigger failures. + * @param SimpleExpectation/string $expected The error to match. + * @param string $message Message on failure. * @access public */ - function assertNoErrors($message = "%s") { - $queue = &SimpleErrorQueue::instance(); - return $this->assertTrue( - $queue->isEmpty(), - sprintf($message, "Should be no errors")); + function expectError($expected = false, $message = '%s') { + $context = &SimpleTest::getContext(); + $queue = &$context->get('SimpleErrorQueue'); + $queue->expectError($this->_coerceExpectation($expected), $message); } /** - * Confirms that an error has occoured and - * optionally that the error text matches exactly. - * @param string $expected Expected error text or - * false for no check. - * @param string $message Message to display. - * @return boolean True on pass + * Prepares for an exception. If the error mismatches it + * passes through, otherwise it is swallowed. Any + * left over errors trigger failures. + * @param SimpleExpectation/Exception $expected The error to match. + * @param string $message Message on failure. * @access public */ - function assertError($expected = false, $message = "%s") { - $queue = &SimpleErrorQueue::instance(); - if ($queue->isEmpty()) { - $this->fail(sprintf($message, "Expected error not found")); - return; - } - list($severity, $content, $file, $line, $globals) = $queue->extract(); - $severity = SimpleErrorQueue::getSeverityAsString($severity); - if (! $expected) { - return $this->pass( - "Captured a PHP error of [$content] severity [$severity] in [$file] line [$line] -> %s"); - } - $expected = $this->_coerceToExpectation($expected); - return $this->assert( - $expected, - $content, - "Expected PHP error [$content] severity [$severity] in [$file] line [$line] -> %s"); + function expectException($expected = false, $message = '%s') { + $context = &SimpleTest::getContext(); + $queue = &$context->get('SimpleExceptionTrap'); + $queue->expectException($expected, $message . $this->getAssertionLine()); } /** @@ -356,18 +396,24 @@ * @return SimpleExpectation Expectation object. * @access private */ - function _coerceToExpectation($expected) { + function _coerceExpectation($expected) { + if ($expected == false) { + return new AnythingExpectation(); + } if (SimpleTestCompatibility::isA($expected, 'SimpleExpectation')) { return $expected; } + if(is_string($expected)) { + $expected = str_replace('%', '%%', $expected); + } return new EqualExpectation($expected); } /** * @deprecated */ - function assertErrorPattern($pattern, $message = "%s") { + function assertErrorPattern($pattern, $message = '%s') { return $this->assertError(new PatternExpectation($pattern), $message); } } -?> +?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/url.php b/thirdparty/simpletest/simpletest/url.php index 9a68156..b66b31f 100644 --- a/thirdparty/simpletest/simpletest/url.php +++ b/thirdparty/simpletest/simpletest/url.php @@ -507,8 +507,8 @@ * @access public */ function normalisePath($path) { - $path = preg_replace('|/[^/]+/\.\./|', '/', $path); - return preg_replace('|/\./|', '/', $path); + $path = preg_replace('|/\./|', '/', $path); + return preg_replace('|/[^/]+/\.\./|', '/', $path); } /** diff --git a/thirdparty/simpletest/simpletest/web_tester.php b/thirdparty/simpletest/simpletest/web_tester.php index c98507d..be424bb 100644 --- a/thirdparty/simpletest/simpletest/web_tester.php +++ b/thirdparty/simpletest/simpletest/web_tester.php @@ -120,8 +120,8 @@ } else { return "Field expectation [" . $dumper->describeValue($this->_value) . "] fails with [" . - $this->_dumper->describeValue($compare) . "] " . - $this->_dumper->describeDifference($this->_value, $compare); + $dumper->describeValue($compare) . "] " . + $dumper->describeDifference($this->_value, $compare); } } } @@ -242,7 +242,7 @@ */ function testMessage($compare) { if (SimpleExpectation::isExpectation($this->_expected_value)) { - $message = $this->_expected_value->testMessage($compare); + $message = $this->_expected_value->overlayMessage($compare, $this->_getDumper()); } else { $message = $this->_expected_header . ($this->_expected_value ? ': ' . $this->_expected_value : ''); @@ -784,7 +784,7 @@ * @param string $expiry Expiry date. * @access public */ - function setCookie($name, $value, $host = false, $path = "/", $expiry = false) { + function setCookie($name, $value, $host = false, $path = '/', $expiry = false) { $this->_browser->setCookie($name, $value, $host, $path, $expiry); } @@ -841,6 +841,18 @@ } /** + * Checks for a click target. + * @param string $label Visible text or alt text. + * @return boolean True if click target. + * @access public + */ + function assertClickable($label, $message = '%s') { + return $this->assertTrue( + $this->_browser->isClickable($label), + sprintf($message, "Click target [$label] should exist")); + } + + /** * Clicks the submit button by label. The owning * form will be submitted by this. * @param string $label Button label. An unlabeled @@ -881,6 +893,18 @@ } /** + * Checks for a valid button label. + * @param string $label Visible text. + * @return boolean True if click target. + * @access public + */ + function assertSubmit($label, $message = '%s') { + return $this->assertTrue( + $this->_browser->isSubmit($label), + sprintf($message, "Submit button [$label] should exist")); + } + + /** * Clicks the submit image by some kind of label. Usually * the alt tag or the nearest equivalent. The owning * form will be submitted by this. Clicking outside of @@ -934,6 +958,18 @@ } /** + * Checks for a valid image with atht alt text or title. + * @param string $label Visible text. + * @return boolean True if click target. + * @access public + */ + function assertImage($label, $message = '%s') { + return $this->assertTrue( + $this->_browser->isImage($label), + sprintf($message, "Image with text [$label] should exist")); + } + + /** * Submits a form by the ID. * @param string $id Form ID. No button information * is submitted this way. @@ -969,52 +1005,24 @@ } /** - * Will trigger a pass if the two parameters have - * the same value only. Otherwise a fail. This - * is for testing hand extracted text, etc. - * @param mixed $first Value to compare. - * @param mixed $second Value to compare. - * @param string $message Message to display. - * @return boolean True on pass - * @access public - */ - function assertEqual($first, $second, $message = "%s") { - return $this->assert( - new EqualExpectation($first), - $second, - $message); - } - - /** - * Will trigger a pass if the two parameters have - * a different value. Otherwise a fail. This - * is for testing hand extracted text, etc. - * @param mixed $first Value to compare. - * @param mixed $second Value to compare. - * @param string $message Message to display. - * @return boolean True on pass - * @access public - */ - function assertNotEqual($first, $second, $message = "%s") { - return $this->assert( - new NotEqualExpectation($first), - $second, - $message); - } - - /** * Tests for the presence of a link label. Match is * case insensitive with normalised space. * @param string $label Text between the anchor tags. + * @param mixed $expected Expected URL or expectation object. * @param string $message Message to display. Default * can be embedded with %s. * @return boolean True if link present. * @access public */ - function assertLink($label, $message = "%s") { - return $this->assertTrue( - $this->_browser->isLink($label), - sprintf($message, "Link [$label] should exist")); + function assertLink($label, $expected = true, $message = '%s') { + $url = $this->_browser->getLink($label); + if ($expected === true) { + return $this->assertTrue($url !== false, sprintf($message, "Link [$label] should exist")); + } + if (! SimpleExpectation::isExpectation($expected)) { + $expected = new IdenticalExpectation($expected); + } + return $this->assert($expected, $url->asString(), sprintf($message, "Link [$label] should match")); } /** @@ -1027,24 +1035,30 @@ * @return boolean True if link missing. * @access public */ - function assertNoLink($label, $message = "%s") { - return $this->assertFalse( - $this->_browser->isLink($label), + function assertNoLink($label, $message = '%s') { + return $this->assertTrue( + $this->_browser->getLink($label) === false, sprintf($message, "Link [$label] should not exist")); } /** * Tests for the presence of a link id attribute. * @param string $id Id attribute value. + * @param mixed $expected Expected URL or expectation object. * @param string $message Message to display. Default * can be embedded with %s. * @return boolean True if link present. * @access public */ - function assertLinkById($id, $message = "%s") { - return $this->assertTrue( - $this->_browser->isLinkById($id), - sprintf($message, "Link ID [$id] should exist")); + function assertLinkById($id, $expected = true, $message = '%s') { + $url = $this->_browser->getLinkById($id); + if ($expected === true) { + return $this->assertTrue($url !== false, sprintf($message, "Link ID [$id] should exist")); + } + if (! SimpleExpectation::isExpectation($expected)) { + $expected = new IdenticalExpectation($expected); + } + return $this->assert($expected, $url->asString(), sprintf($message, "Link ID [$id] should match")); } /** @@ -1056,9 +1070,9 @@ * @return boolean True if link missing. * @access public */ - function assertNoLinkById($id, $message = "%s") { - return $this->assertFalse( - $this->_browser->isLinkById($id), + function assertNoLinkById($id, $message = '%s') { + return $this->assertTrue( + $this->_browser->getLinkById($id) === false, sprintf($message, "Link ID [$id] should not exist")); } @@ -1313,9 +1327,9 @@ /** * Tests the text between the title tags. - * @param string $title Expected title. - * @param string $message Message to display. - * @return boolean True if pass. + * @param string/SimpleExpectation $title Expected title. + * @param string $message Message to display. + * @return boolean True if pass. * @access public */ function assertTitle($title = false, $message = '%s') { @@ -1451,5 +1465,77 @@ $this->getCookie($name) === false, sprintf($message, "Not expecting cookie [$name]")); } + + /** + * Called from within the test methods to register + * passes and failures. + * @param boolean $result Pass on true. + * @param string $message Message to display describing + * the test state. + * @return boolean True on pass + * @access public + */ + function assertTrue($result, $message = false) { + return $this->assert(new TrueExpectation(), $result, $message); + } + + /** + * Will be true on false and vice versa. False + * is the PHP definition of false, so that null, + * empty strings, zero and an empty array all count + * as false. + * @param boolean $result Pass on false. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertFalse($result, $message = '%s') { + return $this->assert(new FalseExpectation(), $result, $message); + } + + /** + * Will trigger a pass if the two parameters have + * the same value only. Otherwise a fail. This + * is for testing hand extracted text, etc. + * @param mixed $first Value to compare. + * @param mixed $second Value to compare. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertEqual($first, $second, $message = '%s') { + return $this->assert( + new EqualExpectation($first), + $second, + $message); + } + + /** + * Will trigger a pass if the two parameters have + * a different value. Otherwise a fail. This + * is for testing hand extracted text, etc. + * @param mixed $first Value to compare. + * @param mixed $second Value to compare. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assertNotEqual($first, $second, $message = '%s') { + return $this->assert( + new NotEqualExpectation($first), + $second, + $message); + } + + /** + * Uses a stack trace to find the line of an assertion. + * @return string Line number of first assert* + * method embedded in format string. + * @access public + */ + function getAssertionLine() { + $trace = new SimpleStackTrace(array('assert', 'click', 'pass', 'fail')); + return $trace->traceMethod(); + } } ?> \ No newline at end of file diff --git a/thirdparty/simpletest/simpletest/xml.php b/thirdparty/simpletest/simpletest/xml.php index c6158cf..0de9a5e 100644 --- a/thirdparty/simpletest/simpletest/xml.php +++ b/thirdparty/simpletest/simpletest/xml.php @@ -23,7 +23,9 @@ var $_namespace; /** - * Does nothing yet. + * Sets up indentation and namespace. + * @param string $namespace Namespace to add to each tag. + * @param string $indent Indenting to add on each nesting. * @access public */ function XmlReporter($namespace = false, $indent = ' ') { @@ -140,8 +142,8 @@ } /** - * Increments the pass count. - * @param string $message Message is ignored. + * Paints pass as XML. + * @param string $message Message to encode. * @access public */ function paintPass($message) { @@ -153,8 +155,8 @@ } /** - * Increments the fail count. - * @param string $message Message is ignored. + * Paints failure as XML. + * @param string $message Message to encode. * @access public */ function paintFail($message) { @@ -166,10 +168,9 @@ } /** - * Paints a PHP error or exception. - * @param string $message Message is ignored. + * Paints error as XML. + * @param string $message Message to encode. * @access public - * @abstract */ function paintError($message) { parent::paintError($message); @@ -180,6 +181,36 @@ } /** + * Paints exception as XML. + * @param Exception $exception Exception to encode. + * @access public + */ + function paintException($exception) { + parent::paintException($exception); + print $this->_getIndent(1); + print "<" . $this->_namespace . "exception>"; + $message = 'Unexpected exception of type [' . get_class($exception) . + '] with message ['. $exception->getMessage() . + '] in ['. $exception->getFile() . + ' line ' . $exception->getLine() . ']'; + print $this->toParsedXml($message); + print "_namespace . "exception>\n"; + } + + /** + * Paints the skipping message and tag. + * @param string $message Text to display in skip tag. + * @access public + */ + function paintSkip($message) { + parent::paintSkip($message); + print $this->_getIndent(1); + print "<" . $this->_namespace . "skip>"; + print $this->toParsedXml($message); + print "_namespace . "skip>\n"; + } + + /** * Paints a simple supplementary message. * @param string $message Text to display. * @access public @@ -531,7 +562,7 @@ */ function _isLeaf($tag) { return in_array($tag, array( - 'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'MESSAGE', 'FORMATTED', 'SIGNAL')); + 'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'SKIP', 'MESSAGE', 'FORMATTED', 'SIGNAL')); } /** @@ -578,6 +609,8 @@ $this->_listener->paintFail($this->_content); } elseif ($tag == 'EXCEPTION') { $this->_listener->paintError($this->_content); + } elseif ($tag == 'SKIP') { + $this->_listener->paintSkip($this->_content); } elseif ($tag == 'SIGNAL') { $this->_listener->paintSignal( $this->_attributes['TYPE'], -- libgit2 0.21.4