Commit ff00441f68d4082337dc7ce8025f2a5bb6169e19

Authored by kevin_fourie
1 parent 37c12a11

Merged in from DEV trunk...

"KTS-2113"
"Split KTAPI into seperate files based on classes"
Implemented.

Reviewed By: Kevin Fourie

"KTS-2114"
"kt3template.inc.php did not find the PluginRegistry."
Fixed. Added require_once statement.

Reviewed By: Kevin Fourie

"KTS-2116"
"Document object does var_dump() on pear error."
Fixed.

Reviewed By: Kevin Fourie

"KTS-2117"
"upgrade simpletest"
Upgraded.

Reviewed By: Kevin Fourie

"KTS-2118"
"FolderUtil allows duplicate folders to be added"
Fixed.

Reviewed By: Kevin Fourie

"KTS-2115"
"create unit tests for ktapi"
Fixed.

Reviewed By: Kevin Fourie

"KTS-2115"
"create unit tests for ktapi"
Implemented. Some basic unit tests for the KTAPI have been implemented.

Reviewed By: Kevin Fourie

"KTS-2119"
"Error message in upload.php: "get_session() should be overloaded!"
Fixed.

Reviewed By: Kevin Fourie



git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/STABLE/trunk@6814 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing 36 changed files with 3639 additions and 2630 deletions
ktapi/KTAPIConstants.inc.php 0 → 100644
  1 +<?
  2 +
  3 +/**
  4 + *
  5 + * The contents of this file are subject to the KnowledgeTree Public
  6 + * License Version 1.1.2 ("License"); You may not use this file except in
  7 + * compliance with the License. You may obtain a copy of the License at
  8 + * http://www.knowledgetree.com/KPL
  9 + *
  10 + * Software distributed under the License is distributed on an "AS IS"
  11 + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
  12 + * See the License for the specific language governing rights and
  13 + * limitations under the License.
  14 + *
  15 + * All copies of the Covered Code must include on each user interface screen:
  16 + * (i) the "Powered by KnowledgeTree" logo and
  17 + * (ii) the KnowledgeTree copyright notice
  18 + * in the same form as they appear in the distribution. See the License for
  19 + * requirements.
  20 + *
  21 + * The Original Code is: KnowledgeTree Open Source
  22 + *
  23 + * The Initial Developer of the Original Code is The Jam Warehouse Software
  24 + * (Pty) Ltd, trading as KnowledgeTree.
  25 + * Portions created by The Jam Warehouse Software (Pty) Ltd are Copyright
  26 + * (C) 2007 The Jam Warehouse Software (Pty) Ltd;
  27 + * All Rights Reserved.
  28 + * Contributor( s): ______________________________________
  29 + *
  30 + */
  31 +
  32 +require_once('../config/dmsDefaults.php');
  33 +require_once(KT_LIB_DIR . '/filelike/fsfilelike.inc.php');
  34 +require_once(KT_LIB_DIR . '/foldermanagement/folderutil.inc.php');
  35 +
  36 +// Generic error messages used in the API. There may be some others specific to functionality
  37 +// directly in the code.
  38 +// TODO: Check that they are all relevant.
  39 +
  40 +define('KTAPI_ERROR_SESSION_INVALID', 'The session could not be resolved.');
  41 +define('KTAPI_ERROR_PERMISSION_INVALID', 'The permission could not be resolved.');
  42 +define('KTAPI_ERROR_FOLDER_INVALID', 'The folder could not be resolved.');
  43 +define('KTAPI_ERROR_DOCUMENT_INVALID', 'The document could not be resolved.');
  44 +define('KTAPI_ERROR_USER_INVALID', 'The user could not be resolved.');
  45 +define('KTAPI_ERROR_KTAPI_INVALID', 'The ktapi could not be resolved.');
  46 +define('KTAPI_ERROR_INSUFFICIENT_PERMISSIONS', 'The user does not have sufficient permissions to access the resource.');
  47 +define('KTAPI_ERROR_INTERNAL_ERROR', 'An internal error occurred. Please review the logs.');
  48 +define('KTAPI_ERROR_DOCUMENT_TYPE_INVALID', 'The document type could not be resolved.');
  49 +define('KTAPI_ERROR_DOCUMENT_CHECKED_OUT', 'The document is checked out.');
  50 +define('KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT', 'The document is not checked out.');
  51 +define('KTAPI_ERROR_WORKFLOW_INVALID', 'The workflow could not be resolved.');
  52 +define('KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS', 'The workflow is not in progress.');
  53 +
  54 +// Mapping of permissions to actions.
  55 +// TODO: Check that they are all correct.
  56 +// Note, currently, all core actions have permissions that are defined in the plugins.
  57 +// As the permissions are currently associated with actions which are quite closely linked
  58 +// to the web interface, it is not the nicest way to do things. They should be associated at
  59 +// a lower level, such as in the api. probably, better, would be at some stage to assocate
  60 +// the permissions to the action/transaction in the database so administrators can really customise
  61 +// as required.
  62 +
  63 +define('KTAPI_PERMISSION_DELETE', 'ktcore.permissions.delete');
  64 +define('KTAPI_PERMISSION_READ', 'ktcore.permissions.read');
  65 +define('KTAPI_PERMISSION_WRITE', 'ktcore.permissions.write');
  66 +define('KTAPI_PERMISSION_ADD_FOLDER', 'ktcore.permissions.addFolder');
  67 +define('KTAPI_PERMISSION_RENAME_FOLDER', 'ktcore.permissions.folder_rename');
  68 +define('KTAPI_PERMISSION_CHANGE_OWNERSHIP', 'ktcore.permissions.security');
  69 +define('KTAPI_PERMISSION_DOCUMENT_MOVE', 'ktcore.permissions.write');
  70 +define('KTAPI_PERMISSION_WORKFLOW', 'ktcore.permissions.workflow');
  71 +
  72 +?>
0 73 \ No newline at end of file
... ...
ktapi/KTAPIDocument.inc.php 0 → 100644
  1 +<?
  2 +class KTAPI_Document extends KTAPI_FolderItem
  3 +{
  4 + /**
  5 + * This is a reference to the internal document object.
  6 + *
  7 + * @var Document
  8 + */
  9 + var $document;
  10 + /**
  11 + * This is the id of the document.
  12 + *
  13 + * @var int
  14 + */
  15 + var $documentid;
  16 + /**
  17 + * This is a reference to the parent folder.
  18 + *
  19 + * @var KTAPI_Folder
  20 + */
  21 + var $ktapi_folder;
  22 +
  23 + function get_documentid()
  24 + {
  25 + return $this->documentid;
  26 + }
  27 +
  28 + /**
  29 + * This is used to get a document based on document id.
  30 + *
  31 + * @static
  32 + * @access public
  33 + * @param KTAPI $ktapi
  34 + * @param int $documentid
  35 + * @return KTAPI_Document
  36 + */
  37 + function &get(&$ktapi, $documentid)
  38 + {
  39 + assert(!is_null($ktapi));
  40 + assert(is_a($ktapi, 'KTAPI'));
  41 + assert(is_numeric($documentid));
  42 +
  43 + $documentid += 0;
  44 +
  45 + $document = &Document::get($documentid);
  46 + if (is_null($document) || PEAR::isError($document))
  47 + {
  48 + return new KTAPI_Error(KTAPI_ERROR_DOCUMENT_INVALID,$document );
  49 + }
  50 +
  51 + $user = $ktapi->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ);
  52 +
  53 + if (is_null($user) || PEAR::isError($user))
  54 + {
  55 + return $user;
  56 + }
  57 +
  58 + $folderid = $document->getParentID();
  59 +
  60 + if (!is_null($folderid))
  61 + {
  62 + $ktapi_folder = &KTAPI_Folder::get($ktapi, $folderid);
  63 + }
  64 + else
  65 + {
  66 + $ktapi_folder = null;
  67 + }
  68 + // We don't do any checks on this folder as it could possibly be deleted, and is not required right now.
  69 +
  70 + return new KTAPI_Document($ktapi, $ktapi_folder, $document);
  71 + }
  72 +
  73 + function is_deleted()
  74 + {
  75 + return ($this->document->getStatusID() == 3);
  76 + }
  77 +
  78 + /**
  79 + * This is the constructor for the KTAPI_Folder.
  80 + *
  81 + * @access private
  82 + * @param KTAPI $ktapi
  83 + * @param Document $document
  84 + * @return KTAPI_Document
  85 + */
  86 + function KTAPI_Document(&$ktapi, &$ktapi_folder, &$document)
  87 + {
  88 + assert(is_a($ktapi,'KTAPI'));
  89 + assert(is_null($ktapi_folder) || is_a($ktapi_folder,'KTAPI_Folder'));
  90 +
  91 + $this->ktapi = &$ktapi;
  92 + $this->ktapi_folder = &$ktapi_folder;
  93 + $this->document = &$document;
  94 + $this->documentid = $document->getId();
  95 + }
  96 +
  97 + /**
  98 + * This checks a document into the repository
  99 + *
  100 + * @param string $filename
  101 + * @param string $reason
  102 + * @param string $tempfilename
  103 + * @param bool $major_update
  104 + */
  105 + function checkin($filename, $reason, $tempfilename, $major_update=false)
  106 + {
  107 + if (!is_file($tempfilename))
  108 + {
  109 + return new PEAR_Error('File does not exist.');
  110 + }
  111 +
  112 + $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE);
  113 +
  114 + if (PEAR::isError($user))
  115 + {
  116 + return $user;
  117 + }
  118 +
  119 + if (!$this->document->getIsCheckedOut())
  120 + {
  121 + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT);
  122 + }
  123 +
  124 + $options = array('major_update'=>$major_update);
  125 +
  126 + $currentfilename = $this->document->getFileName();
  127 + if ($filename != $currentfilename)
  128 + {
  129 + $options['newfilename'] = $filename;
  130 + }
  131 +
  132 + DBUtil::startTransaction();
  133 + $result = KTDocumentUtil::checkin($this->document, $tempfilename, $reason, $user, $options);
  134 +
  135 + if (PEAR::isError($result))
  136 + {
  137 + DBUtil::rollback();
  138 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$result);
  139 + }
  140 + DBUtil::commit();
  141 +
  142 + $tempfilename=addslashes($tempfilename);
  143 + $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'";
  144 + $result = DBUtil::runQuery($sql);
  145 + if (PEAR::isError($result))
  146 + {
  147 + return $result;
  148 + }
  149 +
  150 + }
  151 +
  152 +
  153 + function is_checked_out()
  154 + {
  155 + return ($this->document->getIsCheckedOut());
  156 + }
  157 +
  158 + /**
  159 + * This reverses the checkout process.
  160 + *
  161 + * @param string $reason
  162 + */
  163 + function undo_checkout($reason)
  164 + {
  165 + $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE);
  166 +
  167 + if (PEAR::isError($user))
  168 + {
  169 + return $user;
  170 + }
  171 +
  172 + if (!$this->document->getIsCheckedOut())
  173 + {
  174 + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT);
  175 + }
  176 +
  177 + DBUtil::startTransaction();
  178 +
  179 + $this->document->setIsCheckedOut(0);
  180 + $this->document->setCheckedOutUserID(-1);
  181 + $res = $this->document->update();
  182 + if (($res === false) || PEAR::isError($res))
  183 + {
  184 + DBUtil::rollback();
  185 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res);
  186 + }
  187 +
  188 + $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.force_checkin');
  189 +
  190 + $res = $oDocumentTransaction->create();
  191 + if (($res === false) || PEAR::isError($res)) {
  192 + DBUtil::rollback();
  193 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res);
  194 + }
  195 + DBUtil::commit();
  196 + }
  197 +
  198 + /**
  199 + * This returns a URL to the file that can be downloaded.
  200 + *
  201 + * @param string $reason
  202 + */
  203 + function checkout($reason)
  204 + {
  205 + $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE);
  206 +
  207 + if (PEAR::isError($user))
  208 + {
  209 + return $user;
  210 + }
  211 +
  212 + if ($this->document->getIsCheckedOut())
  213 + {
  214 + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
  215 + }
  216 +
  217 + DBUtil::startTransaction();
  218 + $res = KTDocumentUtil::checkout($this->document, $reason, $user);
  219 + if (PEAR::isError($res))
  220 + {
  221 + DBUtil::rollback();
  222 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res);
  223 + }
  224 +
  225 + DBUtil::commit();
  226 + }
  227 +
  228 + /**
  229 + * This deletes a document from the folder.
  230 + *
  231 + * @param string $reason
  232 + */
  233 + function delete($reason)
  234 + {
  235 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DELETE);
  236 +
  237 + if (PEAR::isError($user))
  238 + {
  239 + return $user;
  240 + }
  241 +
  242 + if ($this->document->getIsCheckedOut())
  243 + {
  244 + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
  245 + }
  246 +
  247 + DBUtil::startTransaction();
  248 + $res = KTDocumentUtil::delete($this->document, $reason);
  249 + if (PEAR::isError($res))
  250 + {
  251 + DBUtil::rollback();
  252 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res);
  253 + }
  254 +
  255 + DBUtil::commit();
  256 + }
  257 +
  258 + /**
  259 + * This changes the owner of the file.
  260 + *
  261 + * @param string $ktapi_newuser
  262 + */
  263 + function change_owner($newusername, $reason='Changing of owner.')
  264 + {
  265 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_CHANGE_OWNERSHIP);
  266 +
  267 + if (PEAR::isError($user))
  268 + {
  269 + return $user;
  270 + }
  271 +
  272 + DBUtil::startTransaction();
  273 +
  274 + $user = &User::getByUserName($newusername);
  275 + if (is_null($user) || PEAR::isError($user))
  276 + {
  277 + return new KTAPI_Error('User could not be found',$user);
  278 + }
  279 +
  280 + $newuserid = $user->getId();
  281 +
  282 + $this->document->setOwnerID($newuserid);
  283 +
  284 + $res = $this->document->update();
  285 +
  286 + if (PEAR::isError($res))
  287 + {
  288 + DBUtil::rollback();
  289 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR ,$res );
  290 + }
  291 +
  292 + $res = KTPermissionUtil::updatePermissionLookup($this->document);
  293 + if (PEAR::isError($res))
  294 + {
  295 + DBUtil::rollback();
  296 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res );
  297 + }
  298 +
  299 + $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.permissions_change');
  300 +
  301 + $res = $oDocumentTransaction->create();
  302 + if (($res === false) || PEAR::isError($res)) {
  303 + DBUtil::rollback();
  304 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res );
  305 + }
  306 +
  307 + DBUtil::commit();
  308 + }
  309 +
  310 + /**
  311 + * This copies the document to another folder.
  312 + *
  313 + * @param KTAPI_Folder $ktapi_target_folder
  314 + * @param string $reason
  315 + * @param string $newname
  316 + * @param string $newfilename
  317 + */
  318 + function copy(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null)
  319 + {
  320 + assert(!is_null($ktapi_target_folder));
  321 + assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
  322 +
  323 + if (empty($newname))
  324 + {
  325 + $newname=null;
  326 + }
  327 + if (empty($newfilename))
  328 + {
  329 + $newfilename=null;
  330 + }
  331 +
  332 + $user = $this->ktapi->get_user();
  333 +
  334 + if ($this->document->getIsCheckedOut())
  335 + {
  336 + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
  337 + }
  338 +
  339 + $target_folder = &$ktapi_target_folder->get_folder();
  340 +
  341 + $result = $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE);
  342 +
  343 + if (PEAR::isError($result))
  344 + {
  345 + return $result;
  346 + }
  347 +
  348 + $name = $this->document->getName();
  349 + $clash = KTDocumentUtil::nameExists($target_folder, $name);
  350 + if ($clash && !is_null($newname))
  351 + {
  352 + $name = $newname;
  353 + $clash = KTDocumentUtil::nameExists($target_folder, $name);
  354 + }
  355 + if ($clash)
  356 + {
  357 + 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.');
  358 + }
  359 +
  360 + $filename=$this->document->getFilename();
  361 + $clash = KTDocumentUtil::fileExists($target_folder, $filename);
  362 +
  363 + if ($clash && !is_null($newname))
  364 + {
  365 + $filename = $newfilename;
  366 + $clash = KTDocumentUtil::fileExists($target_folder, $filename);
  367 + }
  368 + if ($clash)
  369 + {
  370 + 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.');
  371 + }
  372 +
  373 + DBUtil::startTransaction();
  374 +
  375 + $new_document = KTDocumentUtil::copy($this->document, $target_folder, $reason);
  376 + if (PEAR::isError($new_document))
  377 + {
  378 + DBUtil::rollback();
  379 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$new_document );
  380 + }
  381 +
  382 + $new_document->setName($name);
  383 + $new_document->setFilename($filename);
  384 +
  385 + $res = $new_document->update();
  386 +
  387 + if (PEAR::isError($res))
  388 + {
  389 + DBUtil::rollback();
  390 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res );
  391 + }
  392 +
  393 + DBUtil::commit();
  394 +
  395 + // FIXME do we need to refactor all trigger usage into the util function?
  396 + $oKTTriggerRegistry = KTTriggerRegistry::getSingleton();
  397 + $aTriggers = $oKTTriggerRegistry->getTriggers('copyDocument', 'postValidate');
  398 + foreach ($aTriggers as $aTrigger) {
  399 + $sTrigger = $aTrigger[0];
  400 + $oTrigger = new $sTrigger;
  401 + $aInfo = array(
  402 + 'document' => $new_document,
  403 + 'old_folder' => $this->folder->get_folder(),
  404 + 'new_folder' => $target_folder,
  405 + );
  406 + $oTrigger->setInfo($aInfo);
  407 + $ret = $oTrigger->postValidate();
  408 + }
  409 + }
  410 +
  411 + /**
  412 + * This moves the document to another folder.
  413 + *
  414 + * @param KTAPI_Folder $ktapi_target_folder
  415 + * @param string $reason
  416 + * @param string $newname
  417 + * @param string $newfilename
  418 + */
  419 + function move(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null)
  420 + {
  421 + assert(!is_null($ktapi_target_folder));
  422 + assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
  423 +
  424 + if (empty($newname))
  425 + {
  426 + $newname=null;
  427 + }
  428 + if (empty($newfilename))
  429 + {
  430 + $newfilename=null;
  431 + }
  432 +
  433 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DOCUMENT_MOVE);
  434 +
  435 + if (PEAR::isError($user))
  436 + {
  437 + return $user;
  438 + }
  439 +
  440 + if ($this->document->getIsCheckedOut())
  441 + {
  442 + return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
  443 + }
  444 +
  445 + $target_folder = $ktapi_target_folder->get_folder();
  446 +
  447 + $result= $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE);
  448 +
  449 + if (PEAR::isError($result))
  450 + {
  451 + return $result;
  452 + }
  453 +
  454 + if (!KTDocumentUtil::canBeMoved($this->document))
  455 + {
  456 + return new PEAR_Error('Document cannot be moved.');
  457 + }
  458 +
  459 + $name = $this->document->getName();
  460 + $clash = KTDocumentUtil::nameExists($target_folder, $name);
  461 + if ($clash && !is_null($newname))
  462 + {
  463 + $name = $newname;
  464 + $clash = KTDocumentUtil::nameExists($target_folder, $name);
  465 + }
  466 + if ($clash)
  467 + {
  468 + 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.');
  469 + }
  470 +
  471 + $filename=$this->document->getFilename();
  472 + $clash = KTDocumentUtil::fileExists($target_folder, $filename);
  473 +
  474 + if ($clash && !is_null($newname))
  475 + {
  476 + $filename = $newfilename;
  477 + $clash = KTDocumentUtil::fileExists($target_folder, $filename);
  478 + }
  479 + if ($clash)
  480 + {
  481 + 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.');
  482 + }
  483 +
  484 + DBUtil::startTransaction();
  485 +
  486 + $res = KTDocumentUtil::move($this->document, $target_folder, $user, $reason);
  487 + if (PEAR::isError($res))
  488 + {
  489 + DBUtil::rollback();
  490 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res );
  491 + }
  492 +
  493 + $this->document->setName($name);
  494 + $this->document->setFilename($filename);
  495 +
  496 + $res = $this->document->update();
  497 +
  498 + if (PEAR::isError($res))
  499 + {
  500 + DBUtil::rollback();
  501 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res );
  502 + }
  503 +
  504 + DBUtil::commit();
  505 + }
  506 +
  507 + /**
  508 + * This changes the filename of the document.
  509 + *
  510 + * @param string $newname
  511 + */
  512 + function renameFile($newname)
  513 + {
  514 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
  515 +
  516 + if (PEAR::isError($user))
  517 + {
  518 + return $user;
  519 + }
  520 +
  521 + DBUtil::startTransaction();
  522 + $res = KTDocumentUtil::rename($this->document, $newname, $user);
  523 + if (PEAR::isError($res))
  524 + {
  525 + DBUtil::rollback();
  526 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res );
  527 + }
  528 + DBUtil::commit();
  529 + }
  530 +
  531 + /**
  532 + * This changes the document type of the document.
  533 + *
  534 + * @param string $newname
  535 + */
  536 + function change_document_type($documenttype)
  537 + {
  538 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
  539 +
  540 + if (PEAR::isError($user))
  541 + {
  542 + return $user;
  543 + }
  544 +
  545 + $doctypeid = KTAPI::get_documenttypeid($documenttype);
  546 +
  547 + if ($this->document->getDocumentTypeId() != $doctypeid)
  548 + {
  549 + DBUtil::startTransaction();
  550 + $this->document->setDocumentTypeId($doctypeid);
  551 + $res = $this->document->update();
  552 +
  553 + if (PEAR::isError($res))
  554 + {
  555 + DBUtil::rollback();
  556 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR,$res );
  557 + }
  558 + DBUtil::commit();
  559 + }
  560 + }
  561 +
  562 + /**
  563 + * This changes the title of the document.
  564 + *
  565 + * @param string $newname
  566 + */
  567 + function rename($newname)
  568 + {
  569 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
  570 +
  571 + if (PEAR::isError($user))
  572 + {
  573 + return $user;
  574 + }
  575 +
  576 + if ($this->document->getName() != $newname)
  577 + {
  578 +
  579 + DBUtil::startTransaction();
  580 + $this->document->setName($newname);
  581 + $res = $this->document->update();
  582 +
  583 + if (PEAR::isError($res))
  584 + {
  585 + DBUtil::rollback();
  586 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res);
  587 + }
  588 + DBUtil::commit();
  589 + }
  590 + }
  591 +
  592 + /**
  593 + * This flags the document as 'archived'.
  594 + *
  595 + * @param string $reason
  596 + */
  597 + function archive($reason)
  598 + {
  599 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
  600 +
  601 + if (PEAR::isError($user))
  602 + {
  603 + return $user;
  604 + }
  605 +
  606 + list($permission, $user) = $perm_and_user;
  607 +
  608 + DBUtil::startTransaction();
  609 + $this->document->setStatusID(ARCHIVED);
  610 + $res = $this->document->update();
  611 + if (($res === false) || PEAR::isError($res)) {
  612 + DBUtil::rollback();
  613 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $res);
  614 + }
  615 +
  616 + $oDocumentTransaction = & new DocumentTransaction($this->document, sprintf(_kt('Document archived: %s'), $reason), 'ktcore.transactions.update');
  617 + $oDocumentTransaction->create();
  618 +
  619 + DBUtil::commit();
  620 +
  621 + $oKTTriggerRegistry = KTTriggerRegistry::getSingleton();
  622 + $aTriggers = $oKTTriggerRegistry->getTriggers('archive', 'postValidate');
  623 + foreach ($aTriggers as $aTrigger)
  624 + {
  625 + $sTrigger = $aTrigger[0];
  626 + $oTrigger = new $sTrigger;
  627 + $aInfo = array(
  628 + 'document' => $this->document,
  629 + );
  630 + $oTrigger->setInfo($aInfo);
  631 + $ret = $oTrigger->postValidate();
  632 + }
  633 + }
  634 +
  635 + /**
  636 + * This starts a workflow on a document.
  637 + *
  638 + * @param string $workflow
  639 + */
  640 + function start_workflow($workflow)
  641 + {
  642 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
  643 +
  644 + if (PEAR::isError($user))
  645 + {
  646 + return $user;
  647 + }
  648 +
  649 + $workflowid = $this->document->getWorkflowId();
  650 +
  651 + if (!empty($workflowid))
  652 + {
  653 + return new PEAR_Error('A workflow is already defined.');
  654 + }
  655 +
  656 + $workflow = KTWorkflow::getByName($workflow);
  657 + if (is_null($workflow) || PEAR::isError($workflow))
  658 + {
  659 + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $workflow);
  660 + }
  661 +
  662 + DBUtil::startTransaction();
  663 + $result = KTWorkflowUtil::startWorkflowOnDocument($workflow, $this->document);
  664 + if (is_null($result) || PEAR::isError($result))
  665 + {
  666 + DBUtil::rollback();
  667 + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $result);
  668 + }
  669 + DBUtil::commit();
  670 + }
  671 +
  672 + /**
  673 + * This deletes the workflow on the document.
  674 + *
  675 + */
  676 + function delete_workflow()
  677 + {
  678 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
  679 +
  680 + if (PEAR::isError($user))
  681 + {
  682 + return $user;
  683 + }
  684 +
  685 + $workflowid=$this->document->getWorkflowId();
  686 + if (!empty($workflowid))
  687 + {
  688 + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
  689 + }
  690 +
  691 + DBUtil::startTransaction();
  692 + $result = KTWorkflowUtil::startWorkflowOnDocument(null, $this->document);
  693 + if (is_null($result) || PEAR::isError($result))
  694 + {
  695 + DBUtil::rollback();
  696 + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID,$result);
  697 + }
  698 + DBUtil::commit();
  699 + }
  700 +
  701 + /**
  702 + * This performs a transition on the workflow
  703 + *
  704 + * @param string $transition
  705 + * @param string $reason
  706 + */
  707 + function perform_workflow_transition($transition, $reason)
  708 + {
  709 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
  710 +
  711 + if (PEAR::isError($user))
  712 + {
  713 + return $user;
  714 + }
  715 +
  716 + $workflowid=$this->document->getWorkflowId();
  717 + if (empty($workflowid))
  718 + {
  719 + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
  720 + }
  721 +
  722 + $transition = &KTWorkflowTransition::getByName($transition);
  723 + if (is_null($transition) || PEAR::isError($transition))
  724 + {
  725 + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $transition);
  726 + }
  727 +
  728 + DBUtil::startTransaction();
  729 + $result = KTWorkflowUtil::performTransitionOnDocument($transition, $this->document, $user, $reason);
  730 + if (is_null($result) || PEAR::isError($result))
  731 + {
  732 + DBUtil::rollback();
  733 + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $transition);
  734 + }
  735 + DBUtil::commit();
  736 + }
  737 +
  738 +
  739 +
  740 + /**
  741 + * This returns all metadata for the document.
  742 + *
  743 + * @return array
  744 + */
  745 + function get_metadata()
  746 + {
  747 + $doctypeid = $this->document->getDocumentTypeID();
  748 + $fieldsets = (array) KTMetadataUtil::fieldsetsForDocument($this->document, $doctypeid);
  749 +
  750 + $results = array();
  751 +
  752 + foreach ($fieldsets as $fieldset)
  753 + {
  754 + if ($fieldset->getIsConditional()) { /* this is not implemented...*/ continue; }
  755 +
  756 + $fields = $fieldset->getFields();
  757 + $result = array('fieldset' => $fieldset->getName(),
  758 + 'description' => $fieldset->getDescription());
  759 +
  760 + $fieldsresult = array();
  761 +
  762 + foreach ($fields as $field)
  763 + {
  764 + $value = 'n/a';
  765 +
  766 + $fieldvalue = DocumentFieldLink::getByDocumentAndField($this->document, $field);
  767 + if (!is_null($fieldvalue) && (!PEAR::isError($fieldvalue)))
  768 + {
  769 + $value = $fieldvalue->getValue();
  770 + }
  771 +
  772 + $controltype = 'string';
  773 + if ($field->getHasLookup())
  774 + {
  775 + $controltype = 'lookup';
  776 + if ($field->getHasLookupTree())
  777 + {
  778 + $controltype = 'tree';
  779 + }
  780 + }
  781 +
  782 + switch ($controltype)
  783 + {
  784 + case 'lookup':
  785 + $selection = KTAPI::get_metadata_lookup($field->getId());
  786 + break;
  787 + case 'tree':
  788 + $selection = KTAPI::get_metadata_tree($field->getId());
  789 + break;
  790 + default:
  791 + $selection= array();
  792 + }
  793 +
  794 +
  795 + $fieldsresult[] = array(
  796 + 'name' => $field->getName(),
  797 + 'required' => $field->getIsMandatory(),
  798 + 'value' => $value,
  799 + 'description' => $field->getDescription(),
  800 + 'control_type' => $controltype,
  801 + 'selection' => $selection
  802 +
  803 + );
  804 +
  805 + }
  806 + $result['fields'] = $fieldsresult;
  807 + $results [] = $result;
  808 + }
  809 +
  810 + return $results;
  811 + }
  812 +
  813 + /**
  814 + * This updates the metadata on the file. This includes the 'title'.
  815 + *
  816 + * @param array This is an array containing the metadata to be associated with the file.
  817 + */
  818 + function update_metadata($metadata)
  819 + {
  820 + $packed = array();
  821 +
  822 + foreach($metadata as $fieldset_metadata)
  823 + {
  824 + $fieldsetname=$fieldset_metadata['fieldset'];
  825 + $fieldset = KTFieldset::getByName($fieldsetname);
  826 + if (is_null($fieldset) || PEAR::isError($fieldset))
  827 + {
  828 + // exit graciously
  829 + continue;
  830 + }
  831 +
  832 + foreach($fieldset_metadata['fields'] as $fieldinfo)
  833 + {
  834 + $fieldname = $fieldinfo['name'];
  835 + $field = DocumentField::getByFieldsetAndName($fieldset, $fieldname);
  836 + if (is_null($field) || PEAR::isError($fieldset))
  837 + {
  838 + // exit graciously
  839 + continue;
  840 + }
  841 + $value = $fieldinfo['value'];
  842 +
  843 + $packed[] = array($field, $value);
  844 + }
  845 + }
  846 +
  847 + DBUtil::startTransaction();
  848 + $result = KTDocumentUtil::saveMetadata($this->document, $packed);
  849 +
  850 + if (is_null($result))
  851 + {
  852 + DBUtil::rollback();
  853 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR);
  854 + }
  855 + if (PEAR::isError($result))
  856 + {
  857 + DBUtil::rollback();
  858 + return new KTAPI_Error(sprintf(_kt("Unexpected validation failure: %s."), $result->getMessage()));
  859 + }
  860 + DBUtil::commit();
  861 + }
  862 +
  863 +
  864 + /**
  865 + * This returns a workflow transition
  866 + *
  867 + * @return array
  868 + */
  869 + function get_workflow_transitions()
  870 + {
  871 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
  872 +
  873 + if (PEAR::isError($user))
  874 + {
  875 + return $user;
  876 + }
  877 +
  878 + $workflowid=$this->document->getWorkflowId();
  879 + if (empty($workflowid))
  880 + {
  881 + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
  882 + }
  883 +
  884 + $result = array();
  885 +
  886 + $transitions = KTWorkflowUtil::getTransitionsForDocumentUser($this->document, $user);
  887 + if (is_null($transitions) || PEAR::isError($transitions))
  888 + {
  889 + return new KTAPI_Error(KTAPI_ERROR_WORKFLOW_INVALID, $transitions);
  890 + }
  891 + foreach($transitions as $transition)
  892 + {
  893 + $result[] = $transition->getName();
  894 + }
  895 +
  896 + return $result;
  897 + }
  898 +
  899 + /**
  900 + * This returns the current workflow state
  901 + *
  902 + * @return string
  903 + */
  904 + function get_workflow_state()
  905 + {
  906 + $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
  907 +
  908 + if (PEAR::isError($user))
  909 + {
  910 + return $user;
  911 + }
  912 +
  913 + $workflowid=$this->document->getWorkflowId();
  914 + if (empty($workflowid))
  915 + {
  916 + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
  917 + }
  918 +
  919 + $result = array();
  920 +
  921 + $state = KTWorkflowUtil::getWorkflowStateForDocument($this->document);
  922 + if (is_null($state) || PEAR::isError($state))
  923 + {
  924 + return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
  925 + }
  926 +
  927 + $statename = $state->getName();
  928 +
  929 + return $statename;
  930 +
  931 + }
  932 +
  933 + /**
  934 + * This returns detailed information on the document.
  935 + *
  936 + * @return array
  937 + */
  938 + function get_detail()
  939 + {
  940 + $detail = array();
  941 + $document = $this->document;
  942 +
  943 + $detail['title'] = $document->getName();
  944 +
  945 + $documenttypeid=$document->getDocumentTypeID();
  946 + if (is_numeric($documenttypeid))
  947 + {
  948 + $documenttype = DocumentType::get($documenttypeid);
  949 +
  950 + $documenttype=$documenttype->getName();
  951 + }
  952 + else
  953 + {
  954 + $documenttype = '* unknown *';
  955 + }
  956 + $detail['document_type'] = $documenttype;
  957 +
  958 + $detail['version'] = $document->getVersion();
  959 + $detail['filename'] = $document->getFilename();
  960 +
  961 + $detail['created_date'] = $document->getCreatedDateTime();
  962 +
  963 + $userid = $document->getCreatorID();
  964 + if (is_numeric($userid))
  965 + {
  966 + $user = User::get($userid);
  967 + $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName();
  968 + }
  969 + else
  970 + {
  971 + $username='n/a';
  972 + }
  973 + $detail['created_by'] = $username;
  974 + $detail['updated_date'] = $document->getLastModifiedDate();
  975 +
  976 + $userid = $document->getModifiedUserId();
  977 + if (is_numeric($userid))
  978 + {
  979 + $user = User::get($userid);
  980 + $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName();
  981 + }
  982 + else
  983 + {
  984 + $username='n/a';
  985 + }
  986 + $detail['updated_by'] = $username;
  987 + $detail['document_id'] = (int) $document->getId();
  988 + $detail['folder_id'] = (int) $document->getFolderID();
  989 +
  990 + $workflowid = $document->getWorkflowId();
  991 + if (is_numeric($workflowid))
  992 + {
  993 + $workflow = KTWorkflow::get($workflowid);
  994 + $workflowname=(is_null($workflow) || PEAR::isError($workflow))?'* unknown *':$workflow->getName();
  995 + }
  996 + else
  997 + {
  998 + $workflowname='n/a';
  999 + }
  1000 + $detail['workflow'] = $workflowname;
  1001 +
  1002 + $stateid = $document->getWorkflowStateId();
  1003 + if (is_numeric($stateid))
  1004 + {
  1005 + $state = KTWorkflowState::get($stateid);
  1006 + $workflowstate=(is_null($state) || PEAR::isError($state))?'* unknown *':$state->getName();
  1007 + }
  1008 + else
  1009 + {
  1010 + $workflowstate = 'n/a';
  1011 + }
  1012 + $detail['workflow_state']=$workflowstate;
  1013 +
  1014 + $userid = $document->getCheckedOutUserID();
  1015 +
  1016 + if (is_numeric($userid))
  1017 + {
  1018 + $user = User::get($userid);
  1019 + $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName();
  1020 + }
  1021 + else
  1022 + {
  1023 + $username = 'n/a';
  1024 + }
  1025 + $detail['checkout_by'] = $username;
  1026 +
  1027 + $detail['full_path'] = $this->ktapi_folder->get_full_path() . '/' . $this->get_title();
  1028 +
  1029 + return $detail;
  1030 + }
  1031 +
  1032 + function get_title()
  1033 + {
  1034 + return $this->document->getDescription();
  1035 + }
  1036 +
  1037 + /**
  1038 + * This does a download of a version of the document.
  1039 + *
  1040 + * @param string $version
  1041 + */
  1042 + function download($version=null)
  1043 + {
  1044 + $storage =& KTStorageManagerUtil::getSingleton();
  1045 + $options = array();
  1046 +
  1047 +
  1048 + $oDocumentTransaction = & new DocumentTransaction($this->document, 'Document downloaded', 'ktcore.transactions.download', $aOptions);
  1049 + $oDocumentTransaction->create();
  1050 + }
  1051 +
  1052 + /**
  1053 + * This returns the transaction history for the document.
  1054 + *
  1055 + * @return array
  1056 + */
  1057 + function get_transaction_history()
  1058 + {
  1059 + $sQuery = 'SELECT DTT.name AS transaction_name, U.name AS username, DT.version AS version, DT.comment AS comment, DT.datetime AS datetime ' .
  1060 + 'FROM ' . KTUtil::getTableName('document_transactions') . ' AS DT INNER JOIN ' . KTUtil::getTableName('users') . ' AS U ON DT.user_id = U.id ' .
  1061 + 'INNER JOIN ' . KTUtil::getTableName('transaction_types') . ' AS DTT ON DTT.namespace = DT.transaction_namespace ' .
  1062 + 'WHERE DT.document_id = ? ORDER BY DT.datetime DESC';
  1063 + $aParams = array($this->documentid);
  1064 +
  1065 + $transactions = DBUtil::getResultArray(array($sQuery, $aParams));
  1066 + if (is_null($transactions) || PEAR::isError($transactions))
  1067 + {
  1068 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $transactions );
  1069 + }
  1070 +
  1071 + return $transactions;
  1072 + }
  1073 +
  1074 + /**
  1075 + * This returns the version history on the document.
  1076 + *
  1077 + * @return array
  1078 + */
  1079 + function get_version_history()
  1080 + {
  1081 + $metadata_versions = KTDocumentMetadataVersion::getByDocument($this->document);
  1082 +
  1083 + $versions = array();
  1084 + foreach ($metadata_versions as $version)
  1085 + {
  1086 + $document = &Document::get($this->documentid, $version->getId());
  1087 +
  1088 + $version = array();
  1089 +
  1090 + $userid = $document->getModifiedUserId();
  1091 + $user = User::get($userid);
  1092 +
  1093 + $version['user'] = $user->getName();
  1094 + $version['metadata_version'] = $document->getMetadataVersion();
  1095 + $version['content_version'] = $document->getVersion();
  1096 +
  1097 + $versions[] = $version;
  1098 + }
  1099 + return $versions;
  1100 + }
  1101 +
  1102 + /**
  1103 + * This expunges a document from the system.
  1104 + *
  1105 + * @access public
  1106 + */
  1107 + function expunge()
  1108 + {
  1109 + if ($this->document->getStatusID() != 3)
  1110 + {
  1111 + return new PEAR_Error('You should not purge this');
  1112 + }
  1113 + DBUtil::startTransaction();
  1114 +
  1115 + $transaction = & new DocumentTransaction($this->document, "Document expunged", 'ktcore.transactions.expunge');
  1116 +
  1117 + $transaction->create();
  1118 +
  1119 + $this->document->delete();
  1120 +
  1121 + $this->document->cleanupDocumentData($this->documentid);
  1122 +
  1123 + $storage =& KTStorageManagerUtil::getSingleton();
  1124 +
  1125 + $result= $storage->expunge($this->document);
  1126 +
  1127 + DBUtil::commit();
  1128 + }
  1129 +
  1130 + /**
  1131 + * This expunges a document from the system.
  1132 + *
  1133 + * @access public
  1134 + */
  1135 + function restore()
  1136 + {
  1137 + DBUtil::startTransaction();
  1138 +
  1139 + $storage =& KTStorageManagerUtil::getSingleton();
  1140 +
  1141 + $folder = Folder::get($this->document->getRestoreFolderId());
  1142 + if (PEAR::isError($folder))
  1143 + {
  1144 + $this->document->setFolderId(1);
  1145 + $folder = Folder::get(1);
  1146 + }
  1147 + else
  1148 + {
  1149 + $this->document->setFolderId($this->document->getRestoreFolderId());
  1150 + }
  1151 +
  1152 + $storage->restore($this->document);
  1153 +
  1154 + $this->document->setStatusId(LIVE);
  1155 + $this->document->setPermissionObjectId($folder->getPermissionObjectId());
  1156 + $res = $this->document->update();
  1157 +
  1158 + $res = KTPermissionUtil::updatePermissionLookup($this->document);
  1159 +
  1160 + $user = $this->ktapi->get_user();
  1161 +
  1162 + $oTransaction = new DocumentTransaction($this->document, 'Restored from deleted state by ' . $user->getName(), 'ktcore.transactions.update');
  1163 + $oTransaction->create();
  1164 +
  1165 + DBUtil::commit();
  1166 + }
  1167 +}
  1168 +?>
0 1169 \ No newline at end of file
... ...
ktapi/KTAPIFolder.inc.php 0 → 100644
  1 +<?
  2 +
  3 +
  4 +class KTAPI_Folder extends KTAPI_FolderItem
  5 +{
  6 + /**
  7 + * This is a reference to a base Folder object.
  8 + *
  9 + * @access private
  10 + * @var Folder
  11 + */
  12 + var $folder;
  13 +
  14 + /**
  15 + * This is the id of the folder on the database.
  16 + *
  17 + * @access private
  18 + * @var int
  19 + */
  20 + var $folderid;
  21 +
  22 + /**
  23 + * This is used to get a folder based on a folder id.
  24 + *
  25 + * @access private
  26 + * @param KTAPI $ktapi
  27 + * @param int $folderid
  28 + * @return KTAPI_Folder
  29 + */
  30 + function &get(&$ktapi, $folderid)
  31 + {
  32 + assert(!is_null($ktapi));
  33 + assert(is_a($ktapi, 'KTAPI'));
  34 + assert(is_numeric($folderid));
  35 +
  36 + $folderid += 0;
  37 +
  38 + $folder = &Folder::get($folderid);
  39 + if (is_null($folder) || PEAR::isError($folder))
  40 + {
  41 + return new KTAPI_Error(KTAPI_ERROR_FOLDER_INVALID,$folder);
  42 + }
  43 +
  44 + $user = $ktapi->can_user_access_object_requiring_permission($folder, KTAPI_PERMISSION_READ);
  45 +
  46 + if (is_null($user) || PEAR::isError($user))
  47 + {
  48 + return $user;
  49 + }
  50 +
  51 + return new KTAPI_Folder($ktapi, $folder);
  52 + }
  53 +
  54 + /**
  55 + * This is the constructor for the KTAPI_Folder.
  56 + *
  57 + * @access private
  58 + * @param KTAPI $ktapi
  59 + * @param Folder $folder
  60 + * @return KTAPI_Folder
  61 + */
  62 + function KTAPI_Folder(&$ktapi, &$folder)
  63 + {
  64 + $this->ktapi = &$ktapi;
  65 + $this->folder = &$folder;
  66 + $this->folderid = $folder->getId();
  67 + }
  68 +
  69 + /**
  70 + * This returns a reference to the internal folder object.
  71 + *
  72 + * @access protected
  73 + * @return Folder
  74 + */
  75 + function &get_folder()
  76 + {
  77 + return $this->folder;
  78 + }
  79 +
  80 +
  81 + /**
  82 + * This returns detailed information on the document.
  83 + *
  84 + * @return array
  85 + */
  86 + function get_detail()
  87 + {
  88 + $detail = array(
  89 + 'id'=>(int) $this->folderid,
  90 + 'folder_name'=>$this->get_folder_name(),
  91 + 'parent_id'=>(int) $this->get_parent_folder_id(),
  92 + 'full_path'=>$this->get_full_path(),
  93 + );
  94 +
  95 + return $detail;
  96 + }
  97 +
  98 + function get_parent_folder_id()
  99 + {
  100 + return (int) $this->folder->getParentID();
  101 + }
  102 +
  103 + function get_folder_name()
  104 + {
  105 + return $this->folder->getFolderName($this->folderid);
  106 + }
  107 +
  108 +
  109 + /**
  110 + * This returns the folderid.
  111 + *
  112 + * @return int
  113 + */
  114 + function get_folderid()
  115 + {
  116 + return (int) $this->folderid;
  117 + }
  118 +
  119 + /**
  120 + * This can resolve a folder relative to the current directy by name
  121 + *
  122 + * @access public
  123 + * @param string $foldername
  124 + * @return KTAPI_Folder
  125 + */
  126 + function &get_folder_by_name($foldername)
  127 + {
  128 + $foldername=trim($foldername);
  129 + if (empty($foldername))
  130 + {
  131 + return new PEAR_Error('A valid folder name must be specified.');
  132 + }
  133 +
  134 + $split = explode('/', $foldername);
  135 +
  136 + $folderid=$this->folderid;
  137 + foreach($split as $foldername)
  138 + {
  139 + if (empty($foldername))
  140 + {
  141 + continue;
  142 + }
  143 + $sql = "SELECT id FROM folders WHERE name='$foldername' and parent_id=$folderid";
  144 + $row = DBUtil::getOneResult($sql);
  145 + if (is_null($row) || PEAR::isError($row))
  146 + {
  147 + return new KTAPI_Error(KTAPI_ERROR_FOLDER_INVALID,$row);
  148 + }
  149 + $folderid = $row['id'];
  150 + }
  151 +
  152 + return KTAPI_Folder::get($this->ktapi, $folderid);
  153 + }
  154 +
  155 + function get_full_path()
  156 + {
  157 + $path = $this->folder->getFullPath() . '/' . $this->folder->getName();
  158 +
  159 + return $path;
  160 + }
  161 +
  162 + /**
  163 + * This gets a document by filename or name.
  164 + *
  165 + * @access private
  166 + * @param string $documentname
  167 + * @param string $function
  168 + * @return KTAPI_Document
  169 + */
  170 + function &_get_document_by_name($documentname, $function='getByNameAndFolder')
  171 + {
  172 + $documentname=trim($documentname);
  173 + if (empty($documentname))
  174 + {
  175 + return new PEAR_Error('A valid document name must be specified.');
  176 + }
  177 +
  178 + $foldername = dirname($documentname);
  179 + $documentname = basename($documentname);
  180 +
  181 + $ktapi_folder = $this;
  182 +
  183 + if (!empty($foldername) && ($foldername != '.'))
  184 + {
  185 + $ktapi_folder = $this->get_folder_by_name($foldername);
  186 + }
  187 +
  188 + if (is_null($ktapi_folder) || PEAR::isError($ktapi_folder))
  189 + {
  190 + return new KTAPI_Error(KTAPI_ERROR_FOLDER_INVALID, $ktapi_folder);
  191 + }
  192 +
  193 + //$folder = $ktapi_folder->get_folder();
  194 + $folderid = $ktapi_folder->folderid;
  195 +
  196 + $document = Document::$function($documentname, $folderid);
  197 + if (is_null($document) || PEAR::isError($document))
  198 + {
  199 + return new KTAPI_Error(KTAPI_ERROR_DOCUMENT_INVALID, $document);
  200 + }
  201 +
  202 + $user = $this->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ);
  203 + if (PEAR::isError($user))
  204 + {
  205 + return $user;
  206 + }
  207 +
  208 + return new KTAPI_Document($this->ktapi, $ktapi_folder, $document);
  209 + }
  210 +
  211 + /**
  212 + * This can resolve a document relative to the current directy by name.
  213 + *
  214 + * @access public
  215 + * @param string $documentname
  216 + * @return KTAPI_Document
  217 + */
  218 + function &get_document_by_name($documentname)
  219 + {
  220 + return $this->_get_document_by_name($documentname,'getByNameAndFolder');
  221 + }
  222 +
  223 + /**
  224 + * This can resolve a document relative to the current directy by filename .
  225 + *
  226 + * @access public
  227 + * @param string $documentname
  228 + * @return KTAPI_Document
  229 + */
  230 + function &get_document_by_filename($documentname)
  231 + {
  232 + return $this->_get_document_by_name($documentname,'getByFilenameAndFolder');
  233 + }
  234 +
  235 + function get_listing($depth=1, $what='DF')
  236 + {
  237 + if ($depth < 1)
  238 + {
  239 + return array();
  240 + }
  241 + $permission = &KTPermission::getByName(KTAPI_PERMISSION_READ);
  242 + $permissionid= $permission->getId();
  243 +
  244 + $user = $this->ktapi->get_user();
  245 + $descriptors=KTPermissionUtil::getPermissionDescriptorsForUser($user);
  246 + if (is_null($descriptors) || PEAR::isError($descriptors))
  247 + {
  248 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR . ': problem with descriptors for user', $descriptors);
  249 + }
  250 + if (count($descriptors == 0))
  251 + {
  252 + $descriptors=array(0);
  253 + }
  254 +
  255 + $aPermissionDescriptors = implode(',',$descriptors);
  256 +
  257 + $sql = '';
  258 + if (strpos($what,'D') !== false)
  259 + {
  260 + $sql .= "SELECT
  261 + d.id,
  262 + 'D' as item_type,
  263 + dmv.name as title,
  264 + ifnull(uc.name, 'n/a') AS creator,
  265 + ifnull(cou.name, 'n/a') AS checkedoutby,
  266 + ifnull(mu.name, 'n/a') AS modifiedby,
  267 + dcv.filename,
  268 + dcv.size,
  269 + dcv.major_version,
  270 + dcv.minor_version,
  271 + dcv.storage_path,
  272 + ifnull(mt.mimetypes, 'unknown') as mime_type,
  273 + ifnull(mt.icon_path, 'unknown') as mime_icon_path,
  274 + ifnull(mt.friendly_name, 'unknown') as mime_display
  275 + FROM
  276 + documents d
  277 + INNER JOIN permission_lookups AS PL ON d.permission_lookup_id = PL.id
  278 + INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid
  279 + INNER JOIN document_metadata_version AS dmv ON d.metadata_version_id=dmv.id
  280 + INNER JOIN document_content_version AS dcv ON dmv.content_version_id=dcv.id
  281 + LEFT OUTER JOIN mime_types mt ON dcv.mime_id = mt.id
  282 + LEFT OUTER JOIN users AS uc ON d.creator_id=uc.id
  283 + LEFT OUTER JOIN users AS cou ON d.checked_out_user_id=cou.id
  284 + LEFT OUTER JOIN users AS mu ON d.modified_user_id=mu.id
  285 + WHERE
  286 + d.folder_id=$this->folderid
  287 + AND d.status_id = 1
  288 + AND PLA.permission_descriptor_id IN ($aPermissionDescriptors)";
  289 + }
  290 +
  291 + if (strpos($what,'F') !== false)
  292 + {
  293 + if (strpos($what,'D') !== false)
  294 + {
  295 + $sql .= ' UNION ';
  296 + }
  297 +
  298 + $sql .= "
  299 + SELECT
  300 + f.id,
  301 + 'F' as item_type,
  302 + f.name as title,
  303 + ifnull(uc.name, 'n/a') AS creator,
  304 + 'n/a' checkedoutby,
  305 + 'n/a' AS modifiedby,
  306 + f.name as filename,
  307 + 'n/a' as size,
  308 + 'n/a' as major_version,
  309 + 'n/a' as minor_version,
  310 + 'n/a' as storage_path,
  311 + 'folder' as mime_type,
  312 + 'folder' as mime_icon_path,
  313 + 'Folder' as mime_display
  314 + FROM
  315 + folders f
  316 + INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id
  317 + INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid
  318 + LEFT OUTER JOIN users AS uc ON f.creator_id=uc.id
  319 +
  320 + WHERE
  321 + f.parent_id=$this->folderid
  322 +
  323 + AND PLA.permission_descriptor_id IN ($aPermissionDescriptors)
  324 + ORDER BY item_type DESC, title, filename
  325 + ";
  326 + }
  327 +
  328 + $contents = DBUtil::getResultArray($sql);
  329 + if (is_null($contents) || PEAR::isError($contents))
  330 + {
  331 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR , $contents);
  332 + }
  333 +
  334 + $num_items = count($contents);
  335 + for($i=0;$i<$num_items;$i++)
  336 + {
  337 + $contents[$i]['id'] = (int) $contents[$i]['id'];
  338 + if ($contents[$i]['item_type'] == 'D')
  339 + {
  340 + $contents[$i]['items'] = array();
  341 + }
  342 + else
  343 + {
  344 + if ($depth-1 > 0)
  345 + {
  346 + $folder = &$this->ktapi->get_folder_by_id($item['id']);
  347 + $contents[$i]['items'] = $folder->get_listing($depth-1);
  348 + }
  349 + else
  350 + {
  351 + $contents[$i]['items'] = array();
  352 + }
  353 + }
  354 + }
  355 +
  356 + return $contents;
  357 + }
  358 +
  359 + /**
  360 + * This adds a document to the current folder.
  361 + *
  362 + * @access public
  363 + * @param string $title This is the title for the file in the repository.
  364 + * @param string $filename This is the filename in the system for the file.
  365 + * @param string $documenttype This is the name or id of the document type. It first looks by name, then by id.
  366 + * @param string $tempfilename This is a reference to the file that is accessible locally on the file system.
  367 + * @return KTAPI_Document
  368 + */
  369 + function &add_document($title, $filename, $documenttype, $tempfilename)
  370 + {
  371 + if (!is_file($tempfilename))
  372 + {
  373 + return new PEAR_Error('File does not exist.');
  374 + }
  375 +
  376 + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_WRITE);
  377 + if (PEAR::isError($user))
  378 + {
  379 + return $user;
  380 + }
  381 +
  382 + $filename = basename($filename);
  383 + $documenttypeid = KTAPI::get_documenttypeid($documenttype);
  384 +
  385 + $options = array(
  386 + 'contents' => new KTFSFileLike($tempfilename),
  387 + 'novalidate' => true,
  388 + 'documenttype' => DocumentType::get($documenttypeid),
  389 + 'description' => $title,
  390 + 'metadata'=>array(),
  391 + 'cleanup_initial_file' => true
  392 + );
  393 +
  394 + DBUtil::startTransaction();
  395 + $document =& KTDocumentUtil::add($this->folder, $filename, $user, $options);
  396 +
  397 + if (!is_a($document,'Document'))
  398 + {
  399 + DBUtil::rollback();
  400 + return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR . ' : ' . $document->getMessage());
  401 + }
  402 + DBUtil::commit();
  403 +
  404 + $tempfilename=addslashes($tempfilename);
  405 + $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'";
  406 + $result = DBUtil::runQuery($sql);
  407 + if (PEAR::isError($result))
  408 + {
  409 + return $result;
  410 + }
  411 +
  412 + return new KTAPI_Document($this->ktapi, $this, $document);
  413 + }
  414 +
  415 + /**
  416 + * This adds a subfolder folder to the current folder.
  417 + *
  418 + * @access public
  419 + * @param string $foldername
  420 + * @return KTAPI_Folder
  421 + */
  422 + function &add_folder($foldername)
  423 + {
  424 + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_ADD_FOLDER);
  425 +
  426 + if (PEAR::isError($user))
  427 + {
  428 + return $user;
  429 + }
  430 +
  431 + DBUtil::startTransaction();
  432 + $result = KTFolderUtil::add($this->folder, $foldername, $user);
  433 +
  434 + if (PEAR::isError($result))
  435 + {
  436 + DBUtil::rollback();
  437 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result);
  438 + }
  439 + DBUtil::commit();
  440 + $folderid = $result->getId();
  441 +
  442 + return $this->ktapi->get_folder_by_id($folderid);
  443 + }
  444 +
  445 + /**
  446 + * This deletes the current folder.
  447 + *
  448 + * @param string $reason
  449 + */
  450 + function delete($reason)
  451 + {
  452 + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_DELETE);
  453 + if (PEAR::isError($user))
  454 + {
  455 + return $user;
  456 + }
  457 +
  458 + if ($this->folderid == 1)
  459 + {
  460 + return new PEAR_Error('Cannot delete root folder!');
  461 + }
  462 +
  463 + DBUtil::startTransaction();
  464 + $result = KTFolderUtil::delete($this->folder, $user, $reason);
  465 +
  466 + if (PEAR::isError($result))
  467 + {
  468 + DBUtil::rollback();
  469 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result);
  470 + }
  471 + DBUtil::commit();
  472 + }
  473 +
  474 + /**
  475 + * This renames the folder
  476 + *
  477 + * @param string $newname
  478 + */
  479 + function rename($newname)
  480 + {
  481 + $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_RENAME_FOLDER);
  482 + if (PEAR::isError($user))
  483 + {
  484 + return $user;
  485 + }
  486 +
  487 + DBUtil::startTransaction();
  488 + $result = KTFolderUtil::rename($this->folder, $newname, $user);
  489 +
  490 + if (PEAR::isError($result))
  491 + {
  492 + DBUtil::rollback();
  493 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result);
  494 + }
  495 + DBUtil::commit();
  496 + }
  497 +
  498 + /**
  499 + * This moves the folder to another location.
  500 + *
  501 + * @param KTAPI_Folder $ktapi_target_folder
  502 + * @param string $reason
  503 + */
  504 + function move($ktapi_target_folder, $reason='')
  505 + {
  506 + assert(!is_null($ktapi_target_folder));
  507 + assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
  508 +
  509 + $user = $this->ktapi->get_user();
  510 +
  511 + $target_folder = $ktapi_target_folder->get_folder();
  512 +
  513 + $result = $this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE);
  514 + if (PEAR::isError($result))
  515 + {
  516 + return $result;
  517 + }
  518 +
  519 + DBUtil::startTransaction();
  520 + $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason);
  521 +
  522 + if (PEAR::isError($result))
  523 + {
  524 + DBUtil::rollback();
  525 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result);
  526 + }
  527 + DBUtil::commit();
  528 + }
  529 +
  530 + /**
  531 + * This copies a folder to another location.
  532 + *
  533 + * @param KTAPI_Folder $ktapi_target_folder
  534 + * @param string $reason
  535 + */
  536 + function copy($ktapi_target_folder, $reason='')
  537 + {
  538 + assert(!is_null($ktapi_target_folder));
  539 + assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
  540 +
  541 + $user = $this->ktapi->get_user();
  542 +
  543 + $target_folder = $ktapi_target_folder->get_folder();
  544 +
  545 + $result =$this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE);
  546 +
  547 + if (PEAR::isError($result))
  548 + {
  549 + return $result;
  550 + }
  551 +
  552 + DBUtil::startTransaction();
  553 + $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason);
  554 +
  555 + if (PEAR::isError($result))
  556 + {
  557 + DBUtil::rollback();
  558 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $result);
  559 + }
  560 + DBUtil::commit();
  561 + }
  562 +
  563 + /**
  564 + * This returns all permissions linked to the folder.
  565 + *
  566 + * @access public
  567 + * @return array
  568 + */
  569 + function get_permissions()
  570 + {
  571 + return new PEAR_Error('TODO');
  572 + }
  573 +
  574 + /**
  575 + * This returns a transaction history listing.
  576 + *
  577 + * @access public
  578 + * @return array
  579 + */
  580 + function get_transaction_history()
  581 + {
  582 + return new PEAR_Error('TODO');
  583 + }
  584 +}
  585 +
  586 +?>
0 587 \ No newline at end of file
... ...
ktapi/KTAPISession.inc.php 0 → 100644
  1 +<?
  2 +
  3 +class KTAPI_Session
  4 +{
  5 + var $ktapi;
  6 + var $user = null;
  7 + var $session = '';
  8 + var $sessionid = -1;
  9 + var $active;
  10 +
  11 + function KTAPI_Session(&$ktapi, &$user)
  12 + {
  13 + assert(!is_null($ktapi));
  14 + assert(is_a($ktapi,'KTAPI'));
  15 + assert(!is_null($user));
  16 + assert(is_a($user,'User'));
  17 +
  18 + $this->ktapi=&$ktapi;
  19 + $this->user=&$user;
  20 + $this->active = false;
  21 + }
  22 +
  23 + /**
  24 + * return the session string
  25 + *
  26 + * @return string
  27 + */
  28 + function get_session()
  29 + {
  30 + die('get_session() should be overloaded!');
  31 + }
  32 +
  33 + /**
  34 + * Return the session id
  35 + *
  36 + * @return int
  37 + */
  38 + function get_sessionid()
  39 + {
  40 + die('get_sessionid() should be overloaded!');
  41 + }
  42 +
  43 + /**
  44 + * Return the user
  45 + *
  46 + * @return User
  47 + */
  48 + function &get_user()
  49 + {
  50 + return $this->user;
  51 + }
  52 +
  53 + function logout()
  54 + {
  55 + $this->active=false;
  56 + // don't need to do anything really
  57 + }
  58 +
  59 + function is_active()
  60 + {
  61 + return $this->active;
  62 + }
  63 +
  64 +}
  65 +
  66 +
  67 +class KTAPI_UserSession extends KTAPI_Session
  68 +{
  69 + var $ip = null;
  70 +
  71 + function KTAPI_UserSession(&$ktapi, &$user, $session, $sessionid, $ip)
  72 + {
  73 + parent::KTAPI_Session($ktapi, $user);
  74 +
  75 + $this->ktapi = &$ktapi;
  76 + $this->user = &$user;
  77 + $this->session = $session;
  78 + $this->sessionid = $sessionid;
  79 + $this->ip = $ip;
  80 +
  81 + // TODO: get documenttransaction to not look at the session variable!
  82 + $_SESSION["userID"] = $user->getId();
  83 + $_SESSION["sessionID"] = $this->sessionid;
  84 + $this->active = true;
  85 + }
  86 +
  87 + /**
  88 + * This returns the session string
  89 + *
  90 + * @return string
  91 + */
  92 + function get_session()
  93 + {
  94 + return $this->session;
  95 + }
  96 +
  97 + /**
  98 + * This returns the sessionid in the database.
  99 + *
  100 + * @return int
  101 + */
  102 + function get_sessionid()
  103 + {
  104 + return $this->sessionid;
  105 + }
  106 +
  107 + /**
  108 + * This resolves the user's ip
  109 + *
  110 + * @access private
  111 + * @return string
  112 + */
  113 + function resolveIP()
  114 + {
  115 + if (getenv("REMOTE_ADDR"))
  116 + {
  117 + $ip = getenv("REMOTE_ADDR");
  118 + }
  119 + elseif (getenv("HTTP_X_FORWARDED_FOR"))
  120 + {
  121 + $forwardedip = getenv("HTTP_X_FORWARDED_FOR");
  122 + list($ip,$ip2,$ip3,$ip4)= split (",", $forwardedip);
  123 + }
  124 + elseif (getenv("HTTP_CLIENT_IP"))
  125 + {
  126 + $ip = getenv("HTTP_CLIENT_IP");
  127 + }
  128 +
  129 + if ($ip == '')
  130 + {
  131 + $ip = '127.0.0.1';
  132 + }
  133 +
  134 + return $ip;
  135 + }
  136 +
  137 + /**
  138 + *
  139 + * @access protected
  140 + * @static
  141 + * @param User $user
  142 + */
  143 + function _check_session(&$user)
  144 + {
  145 + $user_id = $user->getId();
  146 +
  147 + $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";
  148 + $row = DBUtil::getOneResult($sql);
  149 + if (PEAR::isError($row))
  150 + {
  151 + return $row;
  152 + }
  153 + if (is_null($row))
  154 + {
  155 + return new PEAR_Error('No record found for user?');
  156 + }
  157 + if ($row['over_limit'] == 1)
  158 + {
  159 + return new PEAR_Error('Session limit exceeded. Logout of any active sessions.');
  160 + }
  161 +
  162 + $session = session_id();
  163 +
  164 + $sessionid = DBUtil::autoInsert('active_sessions',
  165 + array(
  166 + 'user_id' => $user_id,
  167 + 'session_id' => session_id(),
  168 + 'lastused' => date('Y-m-d H:i:s'),
  169 + 'ip' => $ip
  170 + ));
  171 + if (PEAR::isError($sessionid) )
  172 + {
  173 + return $sessionid;
  174 + }
  175 +
  176 + return array($session,$sessionid);
  177 + }
  178 +
  179 +
  180 + /**
  181 + * This returns a session object based on authentication credentials.
  182 + *
  183 + * @access public
  184 + * @static
  185 + * @param string $username
  186 + * @param string $password
  187 + * @return KTAPI_Session
  188 + */
  189 + function &start_session(&$ktapi, $username, $password, $ip=null)
  190 + {
  191 + $this->active=false;
  192 + if ( empty($username) )
  193 + {
  194 + return new PEAR_Error(_kt('The username is empty.'));
  195 + }
  196 +
  197 + $user =& User::getByUsername($username);
  198 + if (PEAR::isError($user) || ($user === false))
  199 + {
  200 + return new KTAPI_Error(_kt("The user '$username' cound not be found."),$user);
  201 + }
  202 +
  203 + if ( empty($password) )
  204 + {
  205 + return new PEAR_Error(_kt('The password is empty.'));
  206 + }
  207 +
  208 + $authenticated = KTAuthenticationUtil::checkPassword($user, $password);
  209 +
  210 + if (PEAR::isError($authenticated) || $authenticated === false)
  211 + {
  212 + return new KTAPI_Error(_kt("The password is invalid."),$authenticated);
  213 + }
  214 +
  215 + if (is_null($ip))
  216 + {
  217 + $ip = '127.0.0.1';
  218 + //$ip = KTAPI_Session::resolveIP();
  219 + }
  220 +
  221 + list($session,$sessionid) = KTAPI_UserSession::_check_session($user);
  222 + if (PEAR::isError($sessionid))
  223 + {
  224 + return $sessionid;
  225 + }
  226 +
  227 + $session = &new KTAPI_UserSession($ktapi, $user, $session, $sessionid, $ip);
  228 +
  229 + return $session;
  230 + }
  231 +
  232 + /**
  233 + * This returns an active session.
  234 + *
  235 + * @param KTAPI $ktapi
  236 + * @param string $session
  237 + * @param string $ip
  238 + * @return KTAPI_Session
  239 + */
  240 + function &get_active_session(&$ktapi, $session, $ip)
  241 + {
  242 + $sql = "SELECT id, user_id FROM active_sessions WHERE session_id='$session'";
  243 + if (!empty($ip))
  244 + {
  245 + $sql .= " AND ip='$ip'";
  246 + }
  247 +
  248 + $row = DBUtil::getOneResult($sql);
  249 + if (is_null($row) || PEAR::isError($row))
  250 + {
  251 + return new KTAPI_Error(KTAPI_ERROR_SESSION_INVALID, $row);
  252 + }
  253 +
  254 + $sessionid = $row['id'];
  255 + $userid = $row['user_id'];
  256 +
  257 + $user = &User::get($userid);
  258 + if (is_null($user) || PEAR::isError($user))
  259 + {
  260 + return new KTAPI_Error(KTAPI_ERROR_USER_INVALID, $user);
  261 + }
  262 +
  263 +
  264 +
  265 + $now=date('Y-m-d H:i:s');
  266 + $sql = "UPDATE active_sessions SET last_used='$now' WHERE id=$sessionid";
  267 + DBUtil::runQuery($sql);
  268 +
  269 + $session = &new KTAPI_UserSession($ktapi, $user, $session, $sessionid, $ip);
  270 + return $session;
  271 + }
  272 +
  273 + /**
  274 + * This closes the current session.
  275 + *
  276 + */
  277 + function logout()
  278 + {
  279 + $sql = "DELETE FROM active_sessions WHERE id=$this->sessionid";
  280 + $result = DBUtil::runQuery($sql);
  281 + if (PEAR::isError($result))
  282 + {
  283 + return $result;
  284 + }
  285 +
  286 + $this->user = null;
  287 + $this->session = '';
  288 + $this->sessionid = -1;
  289 + $this->active=false;
  290 + }
  291 +
  292 +}
  293 +
  294 +class KTAPI_AnonymousSession extends KTAPI_UserSession
  295 +{
  296 + function &start_session(&$ktapi, $ip=null)
  297 + {
  298 + $user =& User::get(-2);
  299 + if (is_null($user) || PEAR::isError($user) || ($user === false) || !$user->isAnonymous())
  300 + {
  301 + return new KTAPI_Error(_kt("The anonymous user could not be found."), $user);
  302 + }
  303 +
  304 + $authenticated = true;
  305 +
  306 + $config = &KTConfig::getSingleton();
  307 + $allow_anonymous = $config->get('session/allowAnonymousLogin', false);
  308 +
  309 + if (!$allow_anonymous)
  310 + {
  311 + return new PEAR_Error(_kt('Anonymous user not allowed'));
  312 + }
  313 +
  314 + if (is_null($ip))
  315 + {
  316 + $ip = '127.0.0.1';
  317 + //$ip = KTAPI_Session::resolveIP();
  318 + }
  319 +
  320 + list($session,$sessionid) = KTAPI_UserSession::_check_session($user);
  321 + if (PEAR::isError($sessionid))
  322 + {
  323 + return $sessionid;
  324 + }
  325 +
  326 + $session = &new KTAPI_AnonymousSession($ktapi, $user, $session, $sessionid, $ip);
  327 +
  328 + return $session;
  329 + }
  330 +}
  331 +
  332 +
  333 +
  334 +class KTAPI_SystemSession extends KTAPI_Session
  335 +{
  336 + function KTAPI_SystemSession(&$ktapi, &$user)
  337 + {
  338 + parent::KTAPI_Session($ktapi, $user);
  339 + $this->active=true;
  340 + }
  341 +}
  342 +
  343 +?>
0 344 \ No newline at end of file
... ...
ktapi/ktapi.inc.php
... ... @@ -30,2026 +30,44 @@
30 30 *
31 31 */
32 32  
  33 +session_start();
33 34 require_once('../config/dmsDefaults.php');
34 35 require_once(KT_LIB_DIR . '/filelike/fsfilelike.inc.php');
35 36 require_once(KT_LIB_DIR . '/foldermanagement/folderutil.inc.php');
36 37  
37   -// Generic error messages used in the API. There may be some others specific to functionality
38   -// directly in the code.
39   -// TODO: Check that they are all relevant.
40   -
41   -define('KTAPI_ERROR_SESSION_INVALID', 'The session could not be resolved.');
42   -define('KTAPI_ERROR_PERMISSION_INVALID', 'The permission could not be resolved.');
43   -define('KTAPI_ERROR_FOLDER_INVALID', 'The folder could not be resolved.');
44   -define('KTAPI_ERROR_DOCUMENT_INVALID', 'The document could not be resolved.');
45   -define('KTAPI_ERROR_USER_INVALID', 'The user could not be resolved.');
46   -define('KTAPI_ERROR_KTAPI_INVALID', 'The ktapi could not be resolved.');
47   -define('KTAPI_ERROR_INSUFFICIENT_PERMISSIONS', 'The user does not have sufficient permissions to access the resource.');
48   -define('KTAPI_ERROR_INTERNAL_ERROR', 'An internal error occurred. Please review the logs.');
49   -define('KTAPI_ERROR_DOCUMENT_TYPE_INVALID', 'The document type could not be resolved.');
50   -define('KTAPI_ERROR_DOCUMENT_CHECKED_OUT', 'The document is checked out.');
51   -define('KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT', 'The document is not checked out.');
52   -define('KTAPI_ERROR_WORKFLOW_INVALID', 'The workflow could not be resolved.');
53   -define('KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS', 'The workflow is not in progress.');
54   -
55   -// Mapping of permissions to actions.
56   -// TODO: Check that they are all correct.
57   -// Note, currently, all core actions have permissions that are defined in the plugins.
58   -// As the permissions are currently associated with actions which are quite closely linked
59   -// to the web interface, it is not the nicest way to do things. They should be associated at
60   -// a lower level, such as in the api. probably, better, would be at some stage to assocate
61   -// the permissions to the action/transaction in the database so administrators can really customise
62   -// as required.
63   -
64   -define('KTAPI_PERMISSION_DELETE', 'ktcore.permissions.delete');
65   -define('KTAPI_PERMISSION_READ', 'ktcore.permissions.read');
66   -define('KTAPI_PERMISSION_WRITE', 'ktcore.permissions.write');
67   -define('KTAPI_PERMISSION_ADD_FOLDER', 'ktcore.permissions.addFolder');
68   -define('KTAPI_PERMISSION_RENAME_FOLDER', 'ktcore.permissions.folder_rename');
69   -define('KTAPI_PERMISSION_CHANGE_OWNERSHIP', 'ktcore.permissions.security');
70   -define('KTAPI_PERMISSION_DOCUMENT_MOVE', 'ktcore.permissions.write');
71   -define('KTAPI_PERMISSION_WORKFLOW', 'ktcore.permissions.workflow');
72   -
73   -//
74   -
75   -class KTAPI_Session
76   -{
77   - var $ktapi;
78   - var $user = null;
79   - var $session = '';
80   - var $sessionid = -1;
81   - var $ip = null;
82   -
83   - function KTAPI_Session(&$ktapi, &$user, $session, $sessionid, $ip)
84   - {
85   - assert(!is_null($ktapi));
86   - assert(is_a($ktapi,'KTAPI'));
87   - assert(!is_null($user));
88   - assert(is_a($user,'User'));
89   -
90   - $this->ktapi = &$ktapi;
91   - $this->user = &$user;
92   - $this->session = $session;
93   - $this->sessionid = $sessionid;
94   - $this->ip = $ip;
95   -
96   - // TODO: get documenttransaction to not look at the session variable!
97   - $_SESSION["userID"] = $user->getId();
98   - $_SESSION["sessionID"] = $this->sessionid;
99   - }
100   -
101   - /**
102   - * This returns the session string
103   - *
104   - * @return string
105   - */
106   - function get_session()
107   - {
108   - return $this->session;
109   - }
110   -
111   - /**
112   - * This returns the sessionid in the database.
113   - *
114   - * @return int
115   - */
116   - function get_sessionid()
117   - {
118   - return $this->sessionid;
119   - }
120   -
121   - /**
122   - * This returns a user object for the use rassociated with the session.
123   - *
124   - * @return User
125   - */
126   - function &get_user()
127   - {
128   - return $this->user;
129   - }
130   -
131   - /**
132   - * This resolves the user's ip
133   - *
134   - * @access private
135   - * @return string
136   - */
137   - function resolveIP()
138   - {
139   - if (getenv("REMOTE_ADDR"))
140   - {
141   - $ip = getenv("REMOTE_ADDR");
142   - }
143   - elseif (getenv("HTTP_X_FORWARDED_FOR"))
144   - {
145   - $forwardedip = getenv("HTTP_X_FORWARDED_FOR");
146   - list($ip,$ip2,$ip3,$ip4)= split (",", $forwardedip);
147   - }
148   - elseif (getenv("HTTP_CLIENT_IP"))
149   - {
150   - $ip = getenv("HTTP_CLIENT_IP");
151   - }
152   -
153   - if ($ip == '')
154   - {
155   - $ip = '127.0.0.1';
156   - }
157   -
158   - return $ip;
159   - }
160   -
161   - /**
162   - * This returns a session object based on authentication credentials.
163   - *
164   - * @access private
165   - * @param string $username
166   - * @param string $password
167   - * @return KTAPI_Session
168   - */
169   - function &start_session(&$ktapi, $username, $password, $ip=null)
170   - {
171   -
172   - if ( empty($username) )
173   - {
174   - return new PEAR_Error(_kt('The username is empty.'));
175   - }
176   -
177   - $user =& User::getByUsername($username);
178   - if (PEAR::isError($user) || ($user === false))
179   - {
180   - return new PEAR_Error(_kt("The user '$username' cound not be found."));
181   - }
182   -
183   - if ($user->isAnonymous())
184   - {
185   - $authenticated = true;
186   -
187   - $config = &KTConfig::getSingleton();
188   - $allow_anonymous = $config->get('session/allowAnonymousLogin', false);
189   -
190   - if (!$allow_anonymous)
191   - {
192   - return new PEAR_Error(_kt('Anonymous user not allowed'));
193   - }
194   -
195   - }
196   - else
197   - {
198   -
199   - if ( empty($password) )
200   - {
201   - return new PEAR_Error(_kt('The password is empty.'));
202   - }
203   -
204   - $authenticated = KTAuthenticationUtil::checkPassword($user, $password);
205   -
206   - if (PEAR::isError($authenticated) || $authenticated === false)
207   - {
208   - return new PEAR_Error(_kt("The password is invalid."));
209   - }
210   - }
211   -
212   -
213   -
214   -
215   - if (is_null($ip))
216   - {
217   - $ip = '127.0.0.1';
218   - //$ip = KTAPI_Session::resolveIP();
219   - }
220   -
221   - session_start();
222   -
223   - $user_id = $user->getId();
224   -
225   - $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";
226   - $row = DBUtil::getOneResult($sql);
227   - if (PEAR::isError($row))
228   - {
229   - return $row;
230   - }
231   - if (is_null($row))
232   - {
233   - return new PEAR_Error('No record found for user?');
234   - }
235   - if ($row['over_limit'] == 1)
236   - {
237   - return new PEAR_Error('Session limit exceeded. Logout of any active sessions.');
238   - }
239   -
240   - $session = session_id();
241   -
242   - $sessionid = DBUtil::autoInsert('active_sessions',
243   - array(
244   - 'user_id' => $user_id,
245   - 'session_id' => session_id(),
246   - 'lastused' => date('Y-m-d H:i:s'),
247   - 'ip' => $ip
248   - ));
249   - if (PEAR::isError($sessionid) )
250   - {
251   - return $sessionid;
252   - }
253   -
254   - $session = &new KTAPI_Session($ktapi, $user, $session, $sessionid, $ip);
255   -
256   - return $session;
257   - }
258   -
259   - /**
260   - * This returns an active session.
261   - *
262   - * @param KTAPI $ktapi
263   - * @param string $session
264   - * @param string $ip
265   - * @return KTAPI_Session
266   - */
267   - function &get_active_session(&$ktapi, $session, $ip)
268   - {
269   - $sql = "SELECT id, user_id FROM active_sessions WHERE session_id='$session'";
270   - if (!empty($ip))
271   - {
272   - $sql .= " AND ip='$ip'";
273   - }
274   -
275   - $row = DBUtil::getOneResult($sql);
276   - if (is_null($row) || PEAR::isError($row))
277   - {
278   - return new PEAR_Error(KTAPI_ERROR_SESSION_INVALID);
279   - }
280   -
281   - $sessionid = $row['id'];
282   - $userid = $row['user_id'];
283   -
284   - $user = &User::get($userid);
285   - if (is_null($user) || PEAR::isError($user))
286   - {
287   - return new PEAR_Error(KTAPI_ERROR_USER_INVALID);
288   - }
289   -
290   -
291   -
292   - $now=date('Y-m-d H:i:s');
293   - $sql = "UPDATE active_sessions SET last_used='$now' WHERE id=$sessionid";
294   - DBUtil::runQuery($sql);
295   -
296   - $session = &new KTAPI_Session($ktapi, $user, $session, $sessionid, $ip);
297   - return $session;
298   - }
299   -
300   - /**
301   - * This closes the current session.
302   - *
303   - */
304   - function logout()
305   - {
306   - $sql = "DELETE FROM active_sessions WHERE id=$this->sessionid";
307   - $result = DBUtil::runQuery($sql);
308   - if (PEAR::isError($result))
309   - {
310   - return $result;
311   - }
312   -
313   - $this->user = null;
314   - $this->session = '';
315   - $this->sessionid = -1;
316   - }
317   -
318   -}
319   -
320   -class KTAPI_FolderItem
321   -{
322   - /**
323   - * This is a reference to the core KTAPI controller
324   - *
325   - * @access protected
326   - * @var KTAPI
327   - */
328   - var $ktapi;
329   -
330   - function &can_user_access_object_requiring_permission(&$object, $permission)
331   - {
332   - return $this->ktapi->can_user_access_object_requiring_permission($object, $permission);
333   - }
334   -}
335   -
336   -
337   -class KTAPI_Folder extends KTAPI_FolderItem
338   -{
339   - /**
340   - * This is a reference to a base Folder object.
341   - *
342   - * @access private
343   - * @var Folder
344   - */
345   - var $folder;
346   -
347   - /**
348   - * This is the id of the folder on the database.
349   - *
350   - * @access private
351   - * @var int
352   - */
353   - var $folderid;
354   -
355   - /**
356   - * This is used to get a folder based on a folder id.
357   - *
358   - * @access private
359   - * @param KTAPI $ktapi
360   - * @param int $folderid
361   - * @return KTAPI_Folder
362   - */
363   - function &get(&$ktapi, $folderid)
364   - {
365   - assert(!is_null($ktapi));
366   - assert(is_a($ktapi, 'KTAPI'));
367   - assert(is_numeric($folderid));
368   -
369   - $folderid += 0;
370   -
371   - $folder = &Folder::get($folderid);
372   - if (is_null($folder) || PEAR::isError($folder))
373   - {
374   - return new PEAR_Error(KTAPI_ERROR_FOLDER_INVALID);
375   - }
376   -
377   - $user = $ktapi->can_user_access_object_requiring_permission($folder, KTAPI_PERMISSION_READ);
378   -
379   - if (is_null($user) || PEAR::isError($user))
380   - {
381   - return $user;
382   - }
383   -
384   - return new KTAPI_Folder($ktapi, $folder);
385   - }
386   -
387   - /**
388   - * This is the constructor for the KTAPI_Folder.
389   - *
390   - * @access private
391   - * @param KTAPI $ktapi
392   - * @param Folder $folder
393   - * @return KTAPI_Folder
394   - */
395   - function KTAPI_Folder(&$ktapi, &$folder)
396   - {
397   - $this->ktapi = &$ktapi;
398   - $this->folder = &$folder;
399   - $this->folderid = $folder->getId();
400   - }
401   -
402   - /**
403   - * This returns a reference to the internal folder object.
404   - *
405   - * @access protected
406   - * @return Folder
407   - */
408   - function &get_folder()
409   - {
410   - return $this->folder;
411   - }
412   -
413   -
414   - /**
415   - * This returns detailed information on the document.
416   - *
417   - * @return array
418   - */
419   - function get_detail()
420   - {
421   - $detail = array(
422   - 'id'=>(int) $this->folderid,
423   - 'folder_name'=>$this->get_folder_name(),
424   - 'parent_id'=>(int) $this->get_parent_folder_id(),
425   - 'full_path'=>$this->get_full_path(),
426   - );
427   -
428   - return $detail;
429   - }
430   -
431   - function get_parent_folder_id()
432   - {
433   - return (int) $this->folder->getParentID();
434   - }
435   -
436   - function get_folder_name()
437   - {
438   - return $this->folder->getFolderName($this->folderid);
439   - }
440   -
441   -
442   - /**
443   - * This returns the folderid.
444   - *
445   - * @return int
446   - */
447   - function get_folderid()
448   - {
449   - return (int) $this->folderid;
450   - }
451   -
452   - /**
453   - * This can resolve a folder relative to the current directy by name
454   - *
455   - * @access public
456   - * @param string $foldername
457   - * @return KTAPI_Folder
458   - */
459   - function &get_folder_by_name($foldername)
460   - {
461   - $foldername=trim($foldername);
462   - if (empty($foldername))
463   - {
464   - return new PEAR_Error('A valid folder name must be specified.');
465   - }
466   -
467   - $split = explode('/', $foldername);
468   -
469   - $folderid=$this->folderid;
470   - foreach($split as $foldername)
471   - {
472   - $sql = "SELECT id FROM folders WHERE name='$foldername' and parent_id=$folderid";
473   - $row = DBUtil::getOneResult($sql);
474   - if (is_null($row) || PEAR::isError($row))
475   - {
476   - return new PEAR_Error(KTAPI_ERROR_FOLDER_INVALID);
477   - }
478   - $folderid = $row['id'];
479   - }
480   -
481   - return KTAPI_Folder::get($this->ktapi, $folderid);
482   - }
483   -
484   - function get_full_path()
485   - {
486   - $path = $this->folder->getFullPath() . '/' . $this->folder->getName();
487   -
488   - return $path;
489   - }
490   -
491   - /**
492   - * This gets a document by filename or name.
493   - *
494   - * @access private
495   - * @param string $documentname
496   - * @param string $function
497   - * @return KTAPI_Document
498   - */
499   - function &_get_document_by_name($documentname, $function='getByNameAndFolder')
500   - {
501   - $documentname=trim($documentname);
502   - if (empty($documentname))
503   - {
504   - return new PEAR_Error('A valid document name must be specified.');
505   - }
506   -
507   - $foldername = dirname($documentname);
508   - $documentname = basename($documentname);
509   -
510   - $ktapi_folder = $this;
511   -
512   - if (!empty($foldername) && ($foldername != '.'))
513   - {
514   - $ktapi_folder = $this->get_folder_by_name($foldername);
515   - }
516   -
517   - if (is_null($ktapi_folder) || PEAR::isError($ktapi_folder))
518   - {
519   - return new PEAR_Error(KTAPI_ERROR_FOLDER_INVALID);
520   - }
521   -
522   - //$folder = $ktapi_folder->get_folder();
523   - $folderid = $ktapi_folder->folderid;
524   -
525   - $document = Document::$function($documentname, $folderid);
526   - if (is_null($document) || PEAR::isError($document))
527   - {
528   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_INVALID);
529   - }
530   -
531   - $user = $this->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ);
532   - if (PEAR::isError($user))
533   - {
534   - return $user;
535   - }
536   -
537   - return new KTAPI_Document($this->ktapi, $ktapi_folder, $document);
538   - }
539   -
540   - /**
541   - * This can resolve a document relative to the current directy by name.
542   - *
543   - * @access public
544   - * @param string $documentname
545   - * @return KTAPI_Document
546   - */
547   - function &get_document_by_name($documentname)
548   - {
549   - return $this->_get_document_by_name($documentname,'getByNameAndFolder');
550   - }
551   -
552   - /**
553   - * This can resolve a document relative to the current directy by filename .
554   - *
555   - * @access public
556   - * @param string $documentname
557   - * @return KTAPI_Document
558   - */
559   - function &get_document_by_filename($documentname)
560   - {
561   - return $this->_get_document_by_name($documentname,'getByFilenameAndFolder');
562   - }
563   -
564   - function get_listing($depth=1, $what='DF')
565   - {
566   - if ($depth < 1)
567   - {
568   - return array();
569   - }
570   - $permission = &KTPermission::getByName(KTAPI_PERMISSION_READ);
571   - $permissionid= $permission->getId();
572   -
573   - $user = $this->ktapi->get_user();
574   - $aPermissionDescriptors = implode(',',KTPermissionUtil::getPermissionDescriptorsForUser($user));
575   -
576   - $sql = '';
577   - if (strpos($what,'D') !== false)
578   - {
579   - $sql .= "SELECT
580   - d.id,
581   - 'D' as item_type,
582   - dmv.name as title,
583   - ifnull(uc.name, 'n/a') AS creator,
584   - ifnull(cou.name, 'n/a') AS checkedoutby,
585   - ifnull(mu.name, 'n/a') AS modifiedby,
586   - dcv.filename,
587   - dcv.size,
588   - dcv.major_version,
589   - dcv.minor_version,
590   - dcv.storage_path,
591   - ifnull(mt.mimetypes, 'unknown') as mime_type,
592   - ifnull(mt.icon_path, 'unknown') as mime_icon_path,
593   - ifnull(mt.friendly_name, 'unknown') as mime_display
594   - FROM
595   - documents d
596   - INNER JOIN permission_lookups AS PL ON d.permission_lookup_id = PL.id
597   - INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid
598   - INNER JOIN document_metadata_version AS dmv ON d.metadata_version_id=dmv.id
599   - INNER JOIN document_content_version AS dcv ON dmv.content_version_id=dcv.id
600   - LEFT OUTER JOIN mime_types mt ON dcv.mime_id = mt.id
601   - LEFT OUTER JOIN users AS uc ON d.creator_id=uc.id
602   - LEFT OUTER JOIN users AS cou ON d.checked_out_user_id=cou.id
603   - LEFT OUTER JOIN users AS mu ON d.modified_user_id=mu.id
604   - WHERE
605   - d.folder_id=$this->folderid
606   - AND d.status_id = 1
607   - AND PLA.permission_descriptor_id IN ($aPermissionDescriptors)";
608   - }
609   -
610   - if (strpos($what,'F') !== false)
611   - {
612   - if (strpos($what,'D') !== false)
613   - {
614   - $sql .= ' UNION ';
615   - }
616   -
617   - $sql .= "
618   - SELECT
619   - f.id,
620   - 'F' as item_type,
621   - f.name as title,
622   - ifnull(uc.name, 'n/a') AS creator,
623   - 'n/a' checkedoutby,
624   - 'n/a' AS modifiedby,
625   - f.name as filename,
626   - 'n/a' as size,
627   - 'n/a' as major_version,
628   - 'n/a' as minor_version,
629   - 'n/a' as storage_path,
630   - 'folder' as mime_type,
631   - 'folder' as mime_icon_path,
632   - 'Folder' as mime_display
633   - FROM
634   - folders f
635   - INNER JOIN permission_lookups AS PL ON f.permission_lookup_id = PL.id
636   - INNER JOIN permission_lookup_assignments AS PLA ON PL.id = PLA.permission_lookup_id AND PLA.permission_id = $permissionid
637   - LEFT OUTER JOIN users AS uc ON f.creator_id=uc.id
638   -
639   - WHERE
640   - f.parent_id=$this->folderid
641   -
642   - AND PLA.permission_descriptor_id IN ($aPermissionDescriptors)
643   - ORDER BY item_type DESC, title, filename
644   - ";
645   - }
646   -
647   - $contents = DBUtil::getResultArray($sql);
648   - if (is_null($contents) || PEAR::isError($contents))
649   - {
650   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
651   - }
652   -
653   - $num_items = count($contents);
654   - for($i=0;$i<$num_items;$i++)
655   - {
656   - $contents[$i]['id'] = (int) $contents[$i]['id'];
657   - if ($contents[$i]['item_type'] == 'D')
658   - {
659   - $contents[$i]['items'] = array();
660   - }
661   - else
662   - {
663   - if ($depth-1 > 0)
664   - {
665   - $folder = &$this->ktapi->get_folder_by_id($item['id']);
666   - $contents[$i]['items'] = $folder->get_listing($depth-1);
667   - }
668   - else
669   - {
670   - $contents[$i]['items'] = array();
671   - }
672   - }
673   - }
674   -
675   - return $contents;
676   - }
677   -
678   - /**
679   - * This adds a document to the current folder.
680   - *
681   - * @access public
682   - * @param string $title This is the title for the file in the repository.
683   - * @param string $filename This is the filename in the system for the file.
684   - * @param string $documenttype This is the name or id of the document type. It first looks by name, then by id.
685   - * @param string $tempfilename This is a reference to the file that is accessible locally on the file system.
686   - * @return KTAPI_Document
687   - */
688   - function &add_document($title, $filename, $documenttype, $tempfilename)
689   - {
690   - if (!is_file($tempfilename))
691   - {
692   - return new PEAR_Error('File does not exist.');
693   - }
694   -
695   - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_WRITE);
696   - if (PEAR::isError($user))
697   - {
698   - return $user;
699   - }
700   -
701   - $filename = basename($filename);
702   - $documenttypeid = KTAPI::get_documenttypeid($documenttype);
703   -
704   - $options = array(
705   - 'contents' => new KTFSFileLike($tempfilename),
706   - 'novalidate' => true,
707   - 'documenttype' => DocumentType::get($documenttypeid),
708   - 'description' => $title,
709   - 'metadata'=>array(),
710   - 'cleanup_initial_file' => true
711   - );
712   -
713   - DBUtil::startTransaction();
714   - $document =& KTDocumentUtil::add($this->folder, $filename, $user, $options);
715   -
716   - if (!is_a($document,'Document'))
717   - {
718   - DBUtil::rollback();
719   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
720   - }
721   - DBUtil::commit();
722   -
723   - $tempfilename=addslashes($tempfilename);
724   - $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'";
725   - $result = DBUtil::runQuery($sql);
726   - if (PEAR::isError($result))
727   - {
728   - return $result;
729   - }
730   -
731   - return new KTAPI_Document($this->ktapi, $this, $document);
732   - }
733   -
734   - /**
735   - * This adds a subfolder folder to the current folder.
736   - *
737   - * @access public
738   - * @param string $foldername
739   - * @return KTAPI_Folder
740   - */
741   - function &add_folder($foldername)
742   - {
743   - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_ADD_FOLDER);
744   -
745   - if (PEAR::isError($user))
746   - {
747   - return $user;
748   - }
749   -
750   - DBUtil::startTransaction();
751   - $result = KTFolderUtil::add($this->folder, $foldername, $user);
752   -
753   - if (PEAR::isError($result))
754   - {
755   - DBUtil::rollback();
756   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
757   - }
758   - DBUtil::commit();
759   - $folderid = $result->getId();
760   -
761   - return $this->ktapi->get_folder_by_id($folderid);
762   - }
763   -
764   - /**
765   - * This deletes the current folder.
766   - *
767   - * @param string $reason
768   - */
769   - function delete($reason)
770   - {
771   - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_DELETE);
772   - if (PEAR::isError($user))
773   - {
774   - return $user;
775   - }
776   -
777   - if ($this->folderid == 1)
778   - {
779   - return new PEAR_Error('Cannot delete root folder!');
780   - }
781   -
782   - DBUtil::startTransaction();
783   - $result = KTFolderUtil::delete($this->folder, $user, $reason);
784   -
785   - if (PEAR::isError($result))
786   - {
787   - DBUtil::rollback();
788   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
789   - }
790   - DBUtil::commit();
791   - }
792   -
793   - /**
794   - * This renames the folder
795   - *
796   - * @param string $newname
797   - */
798   - function rename($newname)
799   - {
800   - $user = $this->can_user_access_object_requiring_permission($this->folder, KTAPI_PERMISSION_RENAME_FOLDER);
801   - if (PEAR::isError($user))
802   - {
803   - return $user;
804   - }
805   -
806   - DBUtil::startTransaction();
807   - $result = KTFolderUtil::rename($this->folder, $newname, $user);
808   -
809   - if (PEAR::isError($result))
810   - {
811   - DBUtil::rollback();
812   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
813   - }
814   - DBUtil::commit();
815   - }
816   -
817   - /**
818   - * This moves the folder to another location.
819   - *
820   - * @param KTAPI_Folder $ktapi_target_folder
821   - * @param string $reason
822   - */
823   - function move($ktapi_target_folder, $reason='')
824   - {
825   - assert(!is_null($ktapi_target_folder));
826   - assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
827   -
828   - $user = $this->ktapi->get_user();
829   -
830   - $target_folder = $ktapi_target_folder->get_folder();
831   -
832   - $result = $this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE);
833   - if (PEAR::isError($result))
834   - {
835   - return $result;
836   - }
837   -
838   - DBUtil::startTransaction();
839   - $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason);
840   -
841   - if (PEAR::isError($result))
842   - {
843   - DBUtil::rollback();
844   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
845   - }
846   - DBUtil::commit();
847   - }
848   -
849   - /**
850   - * This copies a folder to another location.
851   - *
852   - * @param KTAPI_Folder $ktapi_target_folder
853   - * @param string $reason
854   - */
855   - function copy($ktapi_target_folder, $reason='')
856   - {
857   - assert(!is_null($ktapi_target_folder));
858   - assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
859   -
860   - $user = $this->ktapi->get_user();
861   -
862   - $target_folder = $ktapi_target_folder->get_folder();
863   -
864   - $result =$this->can_user_access_object_requiring_permission($target_folder, KTAPI_PERMISSION_WRITE);
865   -
866   - if (PEAR::isError($result))
867   - {
868   - return $result;
869   - }
870   -
871   - DBUtil::startTransaction();
872   - $result = KTFolderUtil::copy($this->folder, $target_folder, $user, $reason);
873   -
874   - if (PEAR::isError($result))
875   - {
876   - DBUtil::rollback();
877   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
878   - }
879   - DBUtil::commit();
880   - }
881   -
882   - /**
883   - * This returns all permissions linked to the folder.
884   - *
885   - * @access public
886   - * @return array
887   - */
888   - function get_permissions()
889   - {
890   - return new PEAR_Error('TODO');
891   - }
892   -
893   - /**
894   - * This returns a transaction history listing.
895   - *
896   - * @access public
897   - * @return array
898   - */
899   - function get_transaction_history()
900   - {
901   - return new PEAR_Error('TODO');
902   - }
903   -}
904   -
905   -class KTAPI_Document extends KTAPI_FolderItem
906   -{
907   - /**
908   - * This is a reference to the internal document object.
909   - *
910   - * @var Document
911   - */
912   - var $document;
913   - /**
914   - * This is the id of the document.
915   - *
916   - * @var int
917   - */
918   - var $documentid;
919   - /**
920   - * This is a reference to the parent folder.
921   - *
922   - * @var KTAPI_Folder
923   - */
924   - var $ktapi_folder;
925   -
926   - /**
927   - * This is used to get a document based on document id.
928   - *
929   - * @static
930   - * @access public
931   - * @param KTAPI $ktapi
932   - * @param int $documentid
933   - * @return KTAPI_Document
934   - */
935   - function &get(&$ktapi, $documentid)
936   - {
937   - assert(!is_null($ktapi));
938   - assert(is_a($ktapi, 'KTAPI'));
939   - assert(is_numeric($documentid));
940   -
941   - $documentid += 0;
942   -
943   - $document = &Document::get($documentid);
944   - if (is_null($document) || PEAR::isError($document))
945   - {
946   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_INVALID);
947   - }
948   -
949   - $user = $ktapi->can_user_access_object_requiring_permission($document, KTAPI_PERMISSION_READ);
950   -
951   - if (is_null($user) || PEAR::isError($user))
952   - {
953   - return $user;
954   - }
955   -
956   - $folderid = $document->getParentID();
957   -
958   - if (!is_null($folderid))
959   - {
960   - $ktapi_folder = &KTAPI_Folder::get($ktapi, $folderid);
961   - }
962   - else
963   - {
964   - $ktapi_folder = null;
965   - }
966   - // We don't do any checks on this folder as it could possibly be deleted, and is not required right now.
967   -
968   - return new KTAPI_Document($ktapi, $ktapi_folder, $document);
969   - }
970   -
971   - /**
972   - * This is the constructor for the KTAPI_Folder.
973   - *
974   - * @access private
975   - * @param KTAPI $ktapi
976   - * @param Document $document
977   - * @return KTAPI_Document
978   - */
979   - function KTAPI_Document(&$ktapi, &$ktapi_folder, &$document)
980   - {
981   - assert(is_a($ktapi,'KTAPI'));
982   - assert(is_null($ktapi_folder) || is_a($ktapi_folder,'KTAPI_Folder'));
983   -
984   - $this->ktapi = &$ktapi;
985   - $this->ktapi_folder = &$ktapi_folder;
986   - $this->document = &$document;
987   - $this->documentid = $document->getId();
988   - }
989   -
990   - /**
991   - * This checks a document into the repository
992   - *
993   - * @param string $filename
994   - * @param string $reason
995   - * @param string $tempfilename
996   - * @param bool $major_update
997   - */
998   - function checkin($filename, $reason, $tempfilename, $major_update=false)
999   - {
1000   - if (!is_file($tempfilename))
1001   - {
1002   - return new PEAR_Error('File does not exist.');
1003   - }
1004   -
1005   - $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE);
1006   -
1007   - if (PEAR::isError($user))
1008   - {
1009   - return $user;
1010   - }
1011   -
1012   - if (!$this->document->getIsCheckedOut())
1013   - {
1014   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT);
1015   - }
1016   -
1017   - $options = array('major_update'=>$major_update);
1018   -
1019   - $currentfilename = $this->document->getFileName();
1020   - if ($filename != $currentfilename)
1021   - {
1022   - $options['newfilename'] = $filename;
1023   - }
1024   -
1025   - DBUtil::startTransaction();
1026   - $result = KTDocumentUtil::checkin($this->document, $tempfilename, $reason, $user, $options);
1027   -
1028   - if (PEAR::isError($result))
1029   - {
1030   - DBUtil::rollback();
1031   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1032   - }
1033   - DBUtil::commit();
1034   -
1035   - $tempfilename=addslashes($tempfilename);
1036   - $sql = "DELETE FROM uploaded_files WHERE tempfilename='$tempfilename'";
1037   - $result = DBUtil::runQuery($sql);
1038   - if (PEAR::isError($result))
1039   - {
1040   - return $result;
1041   - }
1042   -
1043   - }
1044   -
1045   - /**
1046   - * This reverses the checkout process.
1047   - *
1048   - * @param string $reason
1049   - */
1050   - function undo_checkout($reason)
1051   - {
1052   - $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE);
1053   -
1054   - if (PEAR::isError($user))
1055   - {
1056   - return $user;
1057   - }
1058   -
1059   - if (!$this->document->getIsCheckedOut())
1060   - {
1061   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_NOT_CHECKED_OUT);
1062   - }
1063   -
1064   - DBUtil::startTransaction();
1065   -
1066   - $this->document->setIsCheckedOut(0);
1067   - $this->document->setCheckedOutUserID(-1);
1068   - $res = $this->document->update();
1069   - if (($res === false) || PEAR::isError($res))
1070   - {
1071   - DBUtil::rollback();
1072   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1073   - }
1074   -
1075   - $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.force_checkin');
1076   -
1077   - $res = $oDocumentTransaction->create();
1078   - if (($res === false) || PEAR::isError($res)) {
1079   - DBUtil::rollback();
1080   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1081   - }
1082   - DBUtil::commit();
1083   - }
1084   -
1085   - /**
1086   - * This returns a URL to the file that can be downloaded.
1087   - *
1088   - * @param string $reason
1089   - */
1090   - function checkout($reason)
1091   - {
1092   - $user = $this->can_user_access_object_requiring_permission($this->document, KTAPI_PERMISSION_WRITE);
1093   -
1094   - if (PEAR::isError($user))
1095   - {
1096   - return $user;
1097   - }
1098   -
1099   - if ($this->document->getIsCheckedOut())
1100   - {
1101   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
1102   - }
1103   -
1104   - DBUtil::startTransaction();
1105   - $res = KTDocumentUtil::checkout($this->document, $reason, $user);
1106   - if (PEAR::isError($res))
1107   - {
1108   - DBUtil::rollback();
1109   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1110   - }
1111   -
1112   - DBUtil::commit();
1113   - }
1114   -
1115   - /**
1116   - * This deletes a document from the folder.
1117   - *
1118   - * @param string $reason
1119   - */
1120   - function delete($reason)
1121   - {
1122   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DELETE);
1123   -
1124   - if (PEAR::isError($user))
1125   - {
1126   - return $user;
1127   - }
1128   -
1129   - if ($this->document->getIsCheckedOut())
1130   - {
1131   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
1132   - }
1133   -
1134   - DBUtil::startTransaction();
1135   - $res = KTDocumentUtil::delete($this->document, $reason);
1136   - if (PEAR::isError($res))
1137   - {
1138   - DBUtil::rollback();
1139   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1140   - }
1141   -
1142   - DBUtil::commit();
1143   - }
1144   -
1145   - /**
1146   - * This changes the owner of the file.
1147   - *
1148   - * @param string $ktapi_newuser
1149   - */
1150   - function change_owner($newusername, $reason='Changing of owner.')
1151   - {
1152   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_CHANGE_OWNERSHIP);
1153   -
1154   - if (PEAR::isError($user))
1155   - {
1156   - return $user;
1157   - }
1158   -
1159   - DBUtil::startTransaction();
1160   -
1161   - $user = &User::getByUserName($newusername);
1162   - if (is_null($user) || PEAR::isError($user))
1163   - {
1164   - return new PEAR_Error('User could not be found');
1165   - }
1166   -
1167   - $newuserid = $user->getId();
1168   -
1169   - $this->document->setOwnerID($newuserid);
1170   -
1171   - $res = $this->document->update();
1172   -
1173   - if (PEAR::isError($res))
1174   - {
1175   - DBUtil::rollback();
1176   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1177   - }
1178   -
1179   - $res = KTPermissionUtil::updatePermissionLookup($this->document);
1180   - if (PEAR::isError($res))
1181   - {
1182   - DBUtil::rollback();
1183   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1184   - }
1185   -
1186   - $oDocumentTransaction = & new DocumentTransaction($this->document, $reason, 'ktcore.transactions.permissions_change');
1187   -
1188   - $res = $oDocumentTransaction->create();
1189   - if (($res === false) || PEAR::isError($res)) {
1190   - DBUtil::rollback();
1191   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1192   - }
1193   -
1194   - DBUtil::commit();
1195   - }
1196   -
1197   - /**
1198   - * This copies the document to another folder.
1199   - *
1200   - * @param KTAPI_Folder $ktapi_target_folder
1201   - * @param string $reason
1202   - * @param string $newname
1203   - * @param string $newfilename
1204   - */
1205   - function copy(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null)
1206   - {
1207   - assert(!is_null($ktapi_target_folder));
1208   - assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
1209   -
1210   - if (empty($newname))
1211   - {
1212   - $newname=null;
1213   - }
1214   - if (empty($newfilename))
1215   - {
1216   - $newfilename=null;
1217   - }
1218   -
1219   - $user = $this->ktapi->get_user();
1220   -
1221   - if ($this->document->getIsCheckedOut())
1222   - {
1223   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
1224   - }
1225   -
1226   - $target_folder = &$ktapi_target_folder->get_folder();
1227   -
1228   - $result = $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE);
1229   -
1230   - if (PEAR::isError($result))
1231   - {
1232   - return $result;
1233   - }
1234   -
1235   - $name = $this->document->getName();
1236   - $clash = KTDocumentUtil::nameExists($target_folder, $name);
1237   - if ($clash && !is_null($newname))
1238   - {
1239   - $name = $newname;
1240   - $clash = KTDocumentUtil::nameExists($target_folder, $name);
1241   - }
1242   - if ($clash)
1243   - {
1244   - 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.');
1245   - }
1246   -
1247   - $filename=$this->document->getFilename();
1248   - $clash = KTDocumentUtil::fileExists($target_folder, $filename);
1249   -
1250   - if ($clash && !is_null($newname))
1251   - {
1252   - $filename = $newfilename;
1253   - $clash = KTDocumentUtil::fileExists($target_folder, $filename);
1254   - }
1255   - if ($clash)
1256   - {
1257   - 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.');
1258   - }
1259   -
1260   - DBUtil::startTransaction();
1261   -
1262   - $new_document = KTDocumentUtil::copy($this->document, $target_folder, $reason);
1263   - if (PEAR::isError($new_document))
1264   - {
1265   - DBUtil::rollback();
1266   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1267   - }
1268   -
1269   - $new_document->setName($name);
1270   - $new_document->setFilename($filename);
1271   -
1272   - $res = $new_document->update();
1273   -
1274   - if (PEAR::isError($res))
1275   - {
1276   - DBUtil::rollback();
1277   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1278   - }
1279   -
1280   - DBUtil::commit();
1281   -
1282   - // FIXME do we need to refactor all trigger usage into the util function?
1283   - $oKTTriggerRegistry = KTTriggerRegistry::getSingleton();
1284   - $aTriggers = $oKTTriggerRegistry->getTriggers('copyDocument', 'postValidate');
1285   - foreach ($aTriggers as $aTrigger) {
1286   - $sTrigger = $aTrigger[0];
1287   - $oTrigger = new $sTrigger;
1288   - $aInfo = array(
1289   - 'document' => $new_document,
1290   - 'old_folder' => $this->folder->get_folder(),
1291   - 'new_folder' => $target_folder,
1292   - );
1293   - $oTrigger->setInfo($aInfo);
1294   - $ret = $oTrigger->postValidate();
1295   - }
1296   - }
1297   -
1298   - /**
1299   - * This moves the document to another folder.
1300   - *
1301   - * @param KTAPI_Folder $ktapi_target_folder
1302   - * @param string $reason
1303   - * @param string $newname
1304   - * @param string $newfilename
1305   - */
1306   - function move(&$ktapi_target_folder, $reason, $newname=null, $newfilename=null)
1307   - {
1308   - assert(!is_null($ktapi_target_folder));
1309   - assert(is_a($ktapi_target_folder,'KTAPI_Folder'));
1310   -
1311   - if (empty($newname))
1312   - {
1313   - $newname=null;
1314   - }
1315   - if (empty($newfilename))
1316   - {
1317   - $newfilename=null;
1318   - }
1319   -
1320   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_DOCUMENT_MOVE);
1321   -
1322   - if (PEAR::isError($user))
1323   - {
1324   - return $user;
1325   - }
1326   -
1327   - if ($this->document->getIsCheckedOut())
1328   - {
1329   - return new PEAR_Error(KTAPI_ERROR_DOCUMENT_CHECKED_OUT);
1330   - }
1331   -
1332   - $target_folder = $ktapi_target_folder->get_folder();
1333   -
1334   - $result= $this->can_user_access_object_requiring_permission( $target_folder, KTAPI_PERMISSION_WRITE);
1335   -
1336   - if (PEAR::isError($result))
1337   - {
1338   - return $result;
1339   - }
1340   -
1341   - if (!KTDocumentUtil::canBeMoved($this->document))
1342   - {
1343   - return new PEAR_Error('Document cannot be moved.');
1344   - }
1345   -
1346   - $name = $this->document->getName();
1347   - $clash = KTDocumentUtil::nameExists($target_folder, $name);
1348   - if ($clash && !is_null($newname))
1349   - {
1350   - $name = $newname;
1351   - $clash = KTDocumentUtil::nameExists($target_folder, $name);
1352   - }
1353   - if ($clash)
1354   - {
1355   - 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.');
1356   - }
1357   -
1358   - $filename=$this->document->getFilename();
1359   - $clash = KTDocumentUtil::fileExists($target_folder, $filename);
1360   -
1361   - if ($clash && !is_null($newname))
1362   - {
1363   - $filename = $newfilename;
1364   - $clash = KTDocumentUtil::fileExists($target_folder, $filename);
1365   - }
1366   - if ($clash)
1367   - {
1368   - 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.');
1369   - }
1370   -
1371   - DBUtil::startTransaction();
1372   -
1373   - $res = KTDocumentUtil::move($this->document, $target_folder, $user, $reason);
1374   - if (PEAR::isError($res))
1375   - {
1376   - DBUtil::rollback();
1377   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1378   - }
1379   -
1380   - $this->document->setName($name);
1381   - $this->document->setFilename($filename);
1382   -
1383   - $res = $this->document->update();
1384   -
1385   - if (PEAR::isError($res))
1386   - {
1387   - DBUtil::rollback();
1388   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1389   - }
1390   -
1391   - DBUtil::commit();
1392   - }
1393   -
1394   - /**
1395   - * This changes the filename of the document.
1396   - *
1397   - * @param string $newname
1398   - */
1399   - function renameFile($newname)
1400   - {
1401   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
1402   -
1403   - if (PEAR::isError($user))
1404   - {
1405   - return $user;
1406   - }
1407   -
1408   - DBUtil::startTransaction();
1409   - $res = KTDocumentUtil::rename($this->document, $newname, $user);
1410   - if (PEAR::isError($res))
1411   - {
1412   - DBUtil::rollback();
1413   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1414   - }
1415   - DBUtil::commit();
1416   - }
1417   -
1418   - /**
1419   - * This changes the document type of the document.
1420   - *
1421   - * @param string $newname
1422   - */
1423   - function change_document_type($documenttype)
1424   - {
1425   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
1426   -
1427   - if (PEAR::isError($user))
1428   - {
1429   - return $user;
1430   - }
1431   -
1432   - $doctypeid = KTAPI::get_documenttypeid($documenttype);
1433   -
1434   - if ($this->document->getDocumentTypeId() != $doctypeid)
1435   - {
1436   - DBUtil::startTransaction();
1437   - $this->document->setDocumentTypeId($doctypeid);
1438   - $res = $this->document->update();
1439   -
1440   - if (PEAR::isError($res))
1441   - {
1442   - DBUtil::rollback();
1443   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1444   - }
1445   - DBUtil::commit();
1446   - }
1447   - }
1448   -
1449   - /**
1450   - * This changes the title of the document.
1451   - *
1452   - * @param string $newname
1453   - */
1454   - function rename($newname)
1455   - {
1456   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
1457   -
1458   - if (PEAR::isError($user))
1459   - {
1460   - return $user;
1461   - }
1462   -
1463   - if ($this->document->getName() != $newname)
1464   - {
1465   -
1466   - DBUtil::startTransaction();
1467   - $this->document->setName($newname);
1468   - $res = $this->document->update();
1469   -
1470   - if (PEAR::isError($res))
1471   - {
1472   - DBUtil::rollback();
1473   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1474   - }
1475   - DBUtil::commit();
1476   - }
1477   - }
1478   -
1479   - /**
1480   - * This flags the document as 'archived'.
1481   - *
1482   - * @param string $reason
1483   - */
1484   - function archive($reason)
1485   - {
1486   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WRITE);
1487   -
1488   - if (PEAR::isError($user))
1489   - {
1490   - return $user;
1491   - }
1492   -
1493   - list($permission, $user) = $perm_and_user;
1494   -
1495   - DBUtil::startTransaction();
1496   - $this->document->setStatusID(ARCHIVED);
1497   - $res = $this->document->update();
1498   - if (($res === false) || PEAR::isError($res)) {
1499   - DBUtil::rollback();
1500   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1501   - }
1502   -
1503   - $oDocumentTransaction = & new DocumentTransaction($this->document, sprintf(_kt('Document archived: %s'), $reason), 'ktcore.transactions.update');
1504   - $oDocumentTransaction->create();
1505   -
1506   - DBUtil::commit();
1507   -
1508   - $oKTTriggerRegistry = KTTriggerRegistry::getSingleton();
1509   - $aTriggers = $oKTTriggerRegistry->getTriggers('archive', 'postValidate');
1510   - foreach ($aTriggers as $aTrigger)
1511   - {
1512   - $sTrigger = $aTrigger[0];
1513   - $oTrigger = new $sTrigger;
1514   - $aInfo = array(
1515   - 'document' => $this->document,
1516   - );
1517   - $oTrigger->setInfo($aInfo);
1518   - $ret = $oTrigger->postValidate();
1519   - }
1520   - }
1521   -
1522   - /**
1523   - * This starts a workflow on a document.
1524   - *
1525   - * @param string $workflow
1526   - */
1527   - function start_workflow($workflow)
1528   - {
1529   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
1530   -
1531   - if (PEAR::isError($user))
1532   - {
1533   - return $user;
1534   - }
1535   -
1536   - $workflowid = $this->document->getWorkflowId();
1537   -
1538   - if (!empty($workflowid))
1539   - {
1540   - return new PEAR_Error('A workflow is already defined.');
1541   - }
1542   -
1543   - $workflow = KTWorkflow::getByName($workflow);
1544   - if (is_null($workflow) || PEAR::isError($workflow))
1545   - {
1546   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
1547   - }
1548   -
1549   - DBUtil::startTransaction();
1550   - $result = KTWorkflowUtil::startWorkflowOnDocument($workflow, $this->document);
1551   - if (is_null($result) || PEAR::isError($result))
1552   - {
1553   - DBUtil::rollback();
1554   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
1555   - }
1556   - DBUtil::commit();
1557   - }
1558   -
1559   - /**
1560   - * This deletes the workflow on the document.
1561   - *
1562   - */
1563   - function delete_workflow()
1564   - {
1565   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
1566   -
1567   - if (PEAR::isError($user))
1568   - {
1569   - return $user;
1570   - }
1571   -
1572   - $workflowid=$this->document->getWorkflowId();
1573   - if (!empty($workflowid))
1574   - {
1575   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
1576   - }
1577   -
1578   - DBUtil::startTransaction();
1579   - $result = KTWorkflowUtil::startWorkflowOnDocument(null, $this->document);
1580   - if (is_null($result) || PEAR::isError($result))
1581   - {
1582   - DBUtil::rollback();
1583   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
1584   - }
1585   - DBUtil::commit();
1586   - }
1587   -
1588   - /**
1589   - * This performs a transition on the workflow
1590   - *
1591   - * @param string $transition
1592   - * @param string $reason
1593   - */
1594   - function perform_workflow_transition($transition, $reason)
1595   - {
1596   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
1597   -
1598   - if (PEAR::isError($user))
1599   - {
1600   - return $user;
1601   - }
1602   -
1603   - $workflowid=$this->document->getWorkflowId();
1604   - if (empty($workflowid))
1605   - {
1606   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
1607   - }
1608   -
1609   - $transition = &KTWorkflowTransition::getByName($transition);
1610   - if (is_null($transition) || PEAR::isError($transition))
1611   - {
1612   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
1613   - }
1614   -
1615   - DBUtil::startTransaction();
1616   - $result = KTWorkflowUtil::performTransitionOnDocument($transition, $this->document, $user, $reason);
1617   - if (is_null($result) || PEAR::isError($result))
1618   - {
1619   - DBUtil::rollback();
1620   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
1621   - }
1622   - DBUtil::commit();
1623   - }
1624   -
1625   -
1626   -
1627   - /**
1628   - * This returns all metadata for the document.
1629   - *
1630   - * @return array
1631   - */
1632   - function get_metadata()
1633   - {
1634   - $doctypeid = $this->document->getDocumentTypeID();
1635   - $fieldsets = (array) KTMetadataUtil::fieldsetsForDocument($this->document, $doctypeid);
1636   -
1637   - $results = array();
1638   -
1639   - foreach ($fieldsets as $fieldset)
1640   - {
1641   - if ($fieldset->getIsConditional()) { /* this is not implemented...*/ continue; }
1642   -
1643   - $fields = $fieldset->getFields();
1644   - $result = array('fieldset' => $fieldset->getName(),
1645   - 'description' => $fieldset->getDescription());
1646   -
1647   - $fieldsresult = array();
1648   -
1649   - foreach ($fields as $field)
1650   - {
1651   - $value = 'n/a';
1652   -
1653   - $fieldvalue = DocumentFieldLink::getByDocumentAndField($this->document, $field);
1654   - if (!is_null($fieldvalue) && (!PEAR::isError($fieldvalue)))
1655   - {
1656   - $value = $fieldvalue->getValue();
1657   - }
1658   -
1659   - $controltype = 'string';
1660   - if ($field->getHasLookup())
1661   - {
1662   - $controltype = 'lookup';
1663   - if ($field->getHasLookupTree())
1664   - {
1665   - $controltype = 'tree';
1666   - }
1667   - }
1668   -
1669   - switch ($controltype)
1670   - {
1671   - case 'lookup':
1672   - $selection = KTAPI::get_metadata_lookup($field->getId());
1673   - break;
1674   - case 'tree':
1675   - $selection = KTAPI::get_metadata_tree($field->getId());
1676   - break;
1677   - default:
1678   - $selection= array();
1679   - }
1680   -
1681   -
1682   - $fieldsresult[] = array(
1683   - 'name' => $field->getName(),
1684   - 'required' => $field->getIsMandatory(),
1685   - 'value' => $value,
1686   - 'description' => $field->getDescription(),
1687   - 'control_type' => $controltype,
1688   - 'selection' => $selection
1689   -
1690   - );
1691   -
1692   - }
1693   - $result['fields'] = $fieldsresult;
1694   - $results [] = $result;
1695   - }
1696   -
1697   - return $results;
1698   - }
1699   -
1700   - /**
1701   - * This updates the metadata on the file. This includes the 'title'.
1702   - *
1703   - * @param array This is an array containing the metadata to be associated with the file.
1704   - */
1705   - function update_metadata($metadata)
1706   - {
1707   - $packed = array();
1708   -
1709   - foreach($metadata as $fieldset_metadata)
1710   - {
1711   - $fieldsetname=$fieldset_metadata['fieldset'];
1712   - $fieldset = KTFieldset::getByName($fieldsetname);
1713   - if (is_null($fieldset) || PEAR::isError($fieldset))
1714   - {
1715   - // exit graciously
1716   - continue;
1717   - }
1718   -
1719   - foreach($fieldset_metadata['fields'] as $fieldinfo)
1720   - {
1721   - $fieldname = $fieldinfo['name'];
1722   - $field = DocumentField::getByFieldsetAndName($fieldset, $fieldname);
1723   - if (is_null($field) || PEAR::isError($fieldset))
1724   - {
1725   - // exit graciously
1726   - continue;
1727   - }
1728   - $value = $fieldinfo['value'];
1729   -
1730   - $packed[] = array($field, $value);
1731   - }
1732   - }
1733   -
1734   - DBUtil::startTransaction();
1735   - $result = KTDocumentUtil::saveMetadata($this->document, $packed);
1736   -
1737   - if (is_null($result))
1738   - {
1739   - DBUtil::rollback();
1740   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1741   - }
1742   - if (PEAR::isError($result))
1743   - {
1744   - DBUtil::rollback();
1745   - return new PEAR_Error(sprintf(_kt("Unexpected validation failure: %s."), $result->getMessage()));
1746   - }
1747   - DBUtil::commit();
1748   - }
1749   -
1750   -
1751   - /**
1752   - * This returns a workflow transition
1753   - *
1754   - * @return array
1755   - */
1756   - function get_workflow_transitions()
1757   - {
1758   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
1759   -
1760   - if (PEAR::isError($user))
1761   - {
1762   - return $user;
1763   - }
1764   -
1765   - $workflowid=$this->document->getWorkflowId();
1766   - if (empty($workflowid))
1767   - {
1768   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
1769   - }
1770   -
1771   - $result = array();
1772   -
1773   - $transitions = KTWorkflowUtil::getTransitionsForDocumentUser($this->document, $user);
1774   - if (is_null($transitions) || PEAR::isError($transitions))
1775   - {
1776   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
1777   - }
1778   - foreach($transitions as $transition)
1779   - {
1780   - $result[] = $transition->getName();
1781   - }
1782   -
1783   - return $result;
1784   - }
1785   -
1786   - /**
1787   - * This returns the current workflow state
1788   - *
1789   - * @return string
1790   - */
1791   - function get_workflow_state()
1792   - {
1793   - $user = $this->can_user_access_object_requiring_permission( $this->document, KTAPI_PERMISSION_WORKFLOW);
1794   -
1795   - if (PEAR::isError($user))
1796   - {
1797   - return $user;
1798   - }
1799   -
1800   - $workflowid=$this->document->getWorkflowId();
1801   - if (empty($workflowid))
1802   - {
1803   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_NOT_IN_PROGRESS);
1804   - }
1805   -
1806   - $result = array();
1807   -
1808   - $state = KTWorkflowUtil::getWorkflowStateForDocument($this->document);
1809   - if (is_null($state) || PEAR::isError($state))
1810   - {
1811   - return new PEAR_Error(KTAPI_ERROR_WORKFLOW_INVALID);
1812   - }
1813   -
1814   - $statename = $state->getName();
1815   -
1816   - return $statename;
1817   -
1818   - }
1819   -
1820   - /**
1821   - * This returns detailed information on the document.
1822   - *
1823   - * @return array
1824   - */
1825   - function get_detail()
1826   - {
1827   - $detail = array();
1828   - $document = $this->document;
1829   -
1830   - $detail['title'] = $document->getName();
1831   -
1832   - $documenttypeid=$document->getDocumentTypeID();
1833   - if (is_numeric($documenttypeid))
1834   - {
1835   - $documenttype = DocumentType::get($documenttypeid);
1836   -
1837   - $documenttype=$documenttype->getName();
1838   - }
1839   - else
1840   - {
1841   - $documenttype = '* unknown *';
1842   - }
1843   - $detail['document_type'] = $documenttype;
1844   -
1845   - $detail['version'] = $document->getVersion();
1846   - $detail['filename'] = $document->getFilename();
1847   -
1848   - $detail['created_date'] = $document->getCreatedDateTime();
1849   -
1850   - $userid = $document->getCreatorID();
1851   - if (is_numeric($userid))
1852   - {
1853   - $user = User::get($userid);
1854   - $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName();
1855   - }
1856   - else
1857   - {
1858   - $username='n/a';
1859   - }
1860   - $detail['created_by'] = $username;
1861   - $detail['updated_date'] = $document->getLastModifiedDate();
1862   -
1863   - $userid = $document->getModifiedUserId();
1864   - if (is_numeric($userid))
1865   - {
1866   - $user = User::get($userid);
1867   - $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName();
1868   - }
1869   - else
1870   - {
1871   - $username='n/a';
1872   - }
1873   - $detail['updated_by'] = $username;
1874   - $detail['document_id'] = (int) $document->getId();
1875   - $detail['folder_id'] = (int) $document->getFolderID();
1876   -
1877   - $workflowid = $document->getWorkflowId();
1878   - if (is_numeric($workflowid))
1879   - {
1880   - $workflow = KTWorkflow::get($workflowid);
1881   - $workflowname=(is_null($workflow) || PEAR::isError($workflow))?'* unknown *':$workflow->getName();
1882   - }
1883   - else
1884   - {
1885   - $workflowname='n/a';
1886   - }
1887   - $detail['workflow'] = $workflowname;
1888   -
1889   - $stateid = $document->getWorkflowStateId();
1890   - if (is_numeric($stateid))
1891   - {
1892   - $state = KTWorkflowState::get($stateid);
1893   - $workflowstate=(is_null($state) || PEAR::isError($state))?'* unknown *':$state->getName();
1894   - }
1895   - else
1896   - {
1897   - $workflowstate = 'n/a';
1898   - }
1899   - $detail['workflow_state']=$workflowstate;
1900   -
1901   - $userid = $document->getCheckedOutUserID();
1902   -
1903   - if (is_numeric($userid))
1904   - {
1905   - $user = User::get($userid);
1906   - $username=(is_null($user) || PEAR::isError($user))?'* unknown *':$user->getName();
1907   - }
1908   - else
1909   - {
1910   - $username = 'n/a';
1911   - }
1912   - $detail['checkout_by'] = $username;
1913   -
1914   - $detail['full_path'] = $this->ktapi_folder->get_full_path() . '/' . $this->get_title();
1915   -
1916   - return $detail;
1917   - }
1918   -
1919   - function get_title()
1920   - {
1921   - return $this->document->getDescription();
1922   - }
1923   -
1924   - /**
1925   - * This does a download of a version of the document.
1926   - *
1927   - * @param string $version
1928   - */
1929   - function download($version=null)
1930   - {
1931   - $storage =& KTStorageManagerUtil::getSingleton();
1932   - $options = array();
  38 +require_once('KTAPIConstants.inc.php');
  39 +require_once('KTAPISession.inc.php');
  40 +require_once('KTAPIFolder.inc.php');
  41 +require_once('KTAPIDocument.inc.php');
1933 42  
1934   -
1935   - $oDocumentTransaction = & new DocumentTransaction($this->document, 'Document downloaded', 'ktcore.transactions.download', $aOptions);
1936   - $oDocumentTransaction->create();
1937   - }
1938   -
  43 +class KTAPI_FolderItem
  44 +{
1939 45 /**
1940   - * This returns the transaction history for the document.
  46 + * This is a reference to the core KTAPI controller
1941 47 *
1942   - * @return array
  48 + * @access protected
  49 + * @var KTAPI
1943 50 */
1944   - function get_transaction_history()
1945   - {
1946   - $sQuery = 'SELECT DTT.name AS transaction_name, U.name AS username, DT.version AS version, DT.comment AS comment, DT.datetime AS datetime ' .
1947   - 'FROM ' . KTUtil::getTableName('document_transactions') . ' AS DT INNER JOIN ' . KTUtil::getTableName('users') . ' AS U ON DT.user_id = U.id ' .
1948   - 'INNER JOIN ' . KTUtil::getTableName('transaction_types') . ' AS DTT ON DTT.namespace = DT.transaction_namespace ' .
1949   - 'WHERE DT.document_id = ? ORDER BY DT.datetime DESC';
1950   - $aParams = array($this->documentid);
1951   -
1952   - $transactions = DBUtil::getResultArray(array($sQuery, $aParams));
1953   - if (is_null($transactions) || PEAR::isError($transactions))
1954   - {
1955   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
1956   - }
1957   -
1958   - return $transactions;
1959   - }
  51 + var $ktapi;
1960 52  
1961   - /**
1962   - * This returns the version history on the document.
1963   - *
1964   - * @return array
1965   - */
1966   - function get_version_history()
1967   - {
1968   - $metadata_versions = KTDocumentMetadataVersion::getByDocument($this->document);
1969   -
1970   - $versions = array();
1971   - foreach ($metadata_versions as $version)
1972   - {
1973   - $document = &Document::get($this->documentid, $version->getId());
1974   -
1975   - $version = array();
1976   -
1977   - $userid = $document->getModifiedUserId();
1978   - $user = User::get($userid);
1979   -
1980   - $version['user'] = $user->getName();
1981   - $version['metadata_version'] = $document->getMetadataVersion();
1982   - $version['content_version'] = $document->getVersion();
1983   -
1984   - $versions[] = $version;
1985   - }
1986   - return $versions;
  53 + function &can_user_access_object_requiring_permission(&$object, $permission)
  54 + {
  55 + return $this->ktapi->can_user_access_object_requiring_permission($object, $permission);
1987 56 }
  57 +}
1988 58  
1989   - /**
1990   - * This expunges a document from the system.
1991   - *
1992   - * @access public
1993   - */
1994   - function expunge()
  59 +class KTAPI_Error extends PEAR_Error
  60 +{
  61 + function KTAPI_Error($msg, $obj)
1995 62 {
1996   - if ($this->document->getStatusID() != 3)
  63 + if (PEAR::isError($obj))
1997 64 {
1998   - return new PEAR_Error('You should not purge this');
  65 + parent::PEAR_Error($msg . ' - ' . $obj->getMessage());
1999 66 }
2000   - DBUtil::startTransaction();
2001   -
2002   - $transaction = & new DocumentTransaction($this->document, "Document expunged", 'ktcore.transactions.expunge');
2003   -
2004   - $transaction->create();
2005   -
2006   - $this->document->delete();
2007   -
2008   - $this->document->cleanupDocumentData($this->documentid);
2009   -
2010   - $storage =& KTStorageManagerUtil::getSingleton();
2011   -
2012   - $result= $storage->expunge($this->document);
2013   -
2014   - DBUtil::commit();
2015   - }
2016   -
2017   - /**
2018   - * This expunges a document from the system.
2019   - *
2020   - * @access public
2021   - */
2022   - function restore()
2023   - {
2024   - DBUtil::startTransaction();
2025   -
2026   - $storage =& KTStorageManagerUtil::getSingleton();
2027   -
2028   - $folder = Folder::get($this->document->getRestoreFolderId());
2029   - if (PEAR::isError($folder))
2030   - {
2031   - $this->document->setFolderId(1);
2032   - $folder = Folder::get(1);
2033   - }
2034 67 else
2035 68 {
2036   - $this->document->setFolderId($this->document->getRestoreFolderId());
  69 + parent::PEAR_Error($msg);
2037 70 }
2038   -
2039   - $storage->restore($this->document);
2040   -
2041   - $this->document->setStatusId(LIVE);
2042   - $this->document->setPermissionObjectId($folder->getPermissionObjectId());
2043   - $res = $this->document->update();
2044   -
2045   - $res = KTPermissionUtil::updatePermissionLookup($this->document);
2046   -
2047   - $user = $this->ktapi->get_user();
2048   -
2049   - $oTransaction = new DocumentTransaction($this->document, 'Restored from deleted state by ' . $user->getName(), 'ktcore.transactions.update');
2050   - $oTransaction->create();
2051   -
2052   - DBUtil::commit();
2053 71 }
2054 72 }
2055 73  
... ... @@ -2163,7 +181,7 @@ class KTAPI
2163 181 return new PEAR_Error('A session is currently active.');
2164 182 }
2165 183  
2166   - $session = &KTAPI_Session::get_active_session($this, $session, $ip);
  184 + $session = &KTAPI_UserSession::get_active_session($this, $session, $ip);
2167 185  
2168 186 if (is_null($session) || PEAR::isError($session))
2169 187 {
... ... @@ -2189,7 +207,7 @@ class KTAPI
2189 207 return new PEAR_Error('A session is currently active.');
2190 208 }
2191 209  
2192   - $session = &KTAPI_Session::start_session($this, $username, $password, $ip);
  210 + $session = &KTAPI_UserSession::start_session($this, $username, $password, $ip);
2193 211 if (is_null($session))
2194 212 {
2195 213 return new PEAR_Error('Session is null.');
... ... @@ -2203,6 +221,19 @@ class KTAPI
2203 221 return $session;
2204 222 }
2205 223  
  224 +
  225 + function & start_system_session()
  226 + {
  227 + $user = User::get(1);
  228 +
  229 + $session = & new KTAPI_SystemSession($this, $user);
  230 + $this->session = &$session;
  231 +
  232 + return $session;
  233 + }
  234 +
  235 +
  236 +
2206 237 /**
2207 238 * Starts an anonymous session.
2208 239 *
... ... @@ -2211,7 +242,23 @@ class KTAPI
2211 242 */
2212 243 function &start_anonymous_session($ip=null)
2213 244 {
2214   - return $this->start_session('anonymous','',$ip);
  245 + if (!is_null($this->session))
  246 + {
  247 + return new PEAR_Error('A session is currently active.');
  248 + }
  249 +
  250 + $session = &KTAPI_AnonymousSession::start_session($this, $ip);
  251 + if (is_null($session))
  252 + {
  253 + return new PEAR_Error('Session is null.');
  254 + }
  255 + if (PEAR::isError($session))
  256 + {
  257 + return new PEAR_Error('Session is invalid. ' . $session->getMessage());
  258 + }
  259 + $this->session = &$session;
  260 +
  261 + return $session;
2215 262 }
2216 263  
2217 264  
... ... @@ -2288,7 +335,7 @@ class KTAPI
2288 335 $rows = DBUtil::getResultArray($sql);
2289 336 if (is_null($rows) || PEAR::isError($rows))
2290 337 {
2291   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
  338 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows);
2292 339 }
2293 340  
2294 341 $result = array();
... ... @@ -2313,7 +360,7 @@ class KTAPI
2313 360 $rows = DBUtil::getResultArray($sql);
2314 361 if (is_null($rows) || PEAR::isError($rows))
2315 362 {
2316   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
  363 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows);
2317 364 }
2318 365 return $rows;
2319 366 }
... ... @@ -2332,7 +379,7 @@ class KTAPI
2332 379 $rows = DBUtil::getResultArray($sql);
2333 380 if (is_null($rows) || PEAR::isError($rows))
2334 381 {
2335   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
  382 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows);
2336 383 }
2337 384 $results=array();
2338 385 foreach($rows as $row)
... ... @@ -2398,7 +445,7 @@ class KTAPI
2398 445 $rows=DBUtil::getResultArray($sql);
2399 446 if (is_null($rows) || PEAR::isError($rows))
2400 447 {
2401   - return new PEAR_Error(KTAPI_ERROR_INTERNAL_ERROR);
  448 + return new KTAPI_Error(KTAPI_ERROR_INTERNAL_ERROR, $rows);
2402 449 }
2403 450 $results=array();
2404 451 foreach($rows as $row)
... ...
lib/documentmanagement/Document.inc
... ... @@ -230,7 +230,11 @@ class Document {
230 230 $this->_oDocumentMetadataVersion = KTDocumentMetadataVersion::get($iMetadataVersionId);
231 231 $this->iCurrentMetadataVersionId = $iMetadataVersionId;
232 232 }
233   - if (PEAR::isError($this->_oDocumentMetadataVersion)) { var_dump($this->_oDocumentMetadataVersion); return $this->_oDocumentMetadataVersion; }
  233 + if (PEAR::isError($this->_oDocumentMetadataVersion))
  234 + {
  235 + // var_dump($this->_oDocumentMetadataVersion);
  236 + return $this->_oDocumentMetadataVersion;
  237 + }
234 238  
235 239 $this->_oDocumentContentVersion = KTDocumentContentVersion::get($this->_oDocumentMetadataVersion->getContentVersionId());
236 240 if (PEAR::isError($this->_oDocumentContentVersion)) { return $this->_oDocumentContentVersion; }
... ...
lib/foldermanagement/folderutil.inc.php
... ... @@ -68,15 +68,19 @@ class KTFolderUtil {
68 68 }
69 69  
70 70 function add ($oParentFolder, $sFolderName, $oUser) {
71   - $oFolder = KTFolderUtil::_add($oParentFolder, $sFolderName, $oUser);
  71 +
  72 +
  73 + $folderid=$oParentFolder->getId();
  74 + // check for conflicts first
  75 + if (Folder::folderExistsName($sFolderName,$folderid)) {
  76 + return PEAR::raiseError(sprintf(_kt('The folder %s already exists.'), $sFolderName));
  77 + }
  78 +
  79 + $oFolder = KTFolderUtil::_add($oParentFolder, $sFolderName, $oUser);
72 80 if (PEAR::isError($oFolder)) {
73 81 return $oFolder;
74 82 }
75 83  
76   - // check for conflicts first
77   - if (Folder::folderExistsName(KTUtil::getId($oParentFolder), $sFolderName)) {
78   - return PEAR::raiseError(sprintf(_kt('The folder %s already exists.'), $sFolderName));
79   - }
80 84  
81 85 $oTransaction = KTFolderTransaction::createFromArray(array(
82 86 'folderid' => $oFolder->getId(),
... ...
lib/templating/kt3template.inc.php
... ... @@ -39,6 +39,7 @@
39 39 *
40 40 */
41 41  
  42 +require_once(KT_LIB_DIR . "/plugins/pluginregistry.inc.php");
42 43 require_once(KT_LIB_DIR . "/templating/templating.inc.php");
43 44 require_once(KT_LIB_DIR . "/session/control.inc");
44 45  
... ...
tests/runtests.php
... ... @@ -5,6 +5,9 @@ require_once(&#39;test.php&#39;);
5 5 class UnitTests extends GroupTest {
6 6 function UnitTests() {
7 7 $this->GroupTest('Unit tests');
  8 + $this->addTestFile('api/authentication.php');
  9 + $this->addTestFile('api/document.php');
  10 + $this->addTestFile('api/folder.php');
8 11 $this->addTestFile('SQLFile/test_sqlfile.php');
9 12 $this->addTestFile('cache/testCache.php');
10 13 $this->addTestFile('config/testConfig.php');
... ... @@ -29,3 +32,4 @@ if (SimpleReporter::inCli()) {
29 32 }
30 33 $test->run(new HtmlReporter());
31 34  
  35 +?>
32 36 \ No newline at end of file
... ...
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
5 5 dramatic changes are in the alpha releases. Here is a list of possible
6 6 problems and their fixes...
7 7  
  8 +No method _getTest() on mocks
  9 +-----------------------------
  10 +This has finally been removed. It was a pretty esoteric
  11 +flex point anyway. It was there to allow the mocks to
  12 +work with other test tools, but no one does this anyway.
  13 +
  14 +No method assertError(), assertNoErrors(), swallowErrors()
  15 +----------------------------------------------------------
  16 +These have been deprecated in 1.0.1beta in favour of
  17 +expectError() and expectException(). assertNoErrors() is
  18 +redundant if you use expectError() as failures are now reporterted
  19 +immediately.
  20 +
  21 +No method TestCase::signal()
  22 +----------------------------
  23 +This has been deprecated in favour of triggering an error or
  24 +throwing an exception. Deprecated as of 1.0.1beta.
  25 +
  26 +No method TestCase::sendMessage()
  27 +---------------------------------
  28 +This has been deprecated as of 1.0.1beta.
  29 +
8 30 Failure to connect now emits failures
9 31 -------------------------------------
10 32 It used to be that you would have to use the
... ... @@ -46,7 +68,7 @@ No method addPartialMockCode()
46 68 ------------------------------
47 69 The ability to insert arbitrary partial mock code
48 70 has been removed. This was a low value feature
49   -causing needless complications.It was removed
  71 +causing needless complications. It was removed
50 72 in the 1.0.1beta release.
51 73  
52 74 No method setMockBaseClass()
... ... @@ -139,7 +161,7 @@ My custom test case ignored by tally()
139 161 The _assertTrue method has had it's signature changed due to a bug
140 162 in the PHP 5.0.1 release. You must now use getTest() from within
141 163 that method to get the test case. Mock compatibility with other
142   -unit testers is now deprecated as of 1.0.1alpha as PEAR::PHUnit2
  164 +unit testers is now deprecated as of 1.0.1alpha as PEAR::PHPUnit2
143 165 should soon have mock support of it's own.
144 166  
145 167 Broken code extending SimpleRunner
... ...
thirdparty/simpletest/simpletest/LICENSE
... ... @@ -2,7 +2,7 @@
2 2 Version 2.1, February 1999
3 3  
4 4 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5   - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  5 + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 6 Everyone is permitted to copy and distribute verbatim copies
7 7 of this license document, but changing it is not allowed.
8 8  
... ... @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the
485 485  
486 486 You should have received a copy of the GNU Lesser General Public
487 487 License along with this library; if not, write to the Free Software
488   - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  488 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
489 489  
490 490 Also add information on how to contact you by electronic and paper mail.
491 491  
... ...
thirdparty/simpletest/simpletest/README
1 1 SimpleTest
2 2 ==========
3 3 You probably got this package from...
4   -http://sourceforge.net/projects/simpletest/
  4 +http://simpletest.sourceforge.net/projects/simpletest/
5 5  
6 6 If there is no licence agreement with this package please download
7 7 a version from the location above. You must read and accept that
... ...
thirdparty/simpletest/simpletest/VERSION
1   -1.0.1alpha3
2 1 \ No newline at end of file
  2 +1.0.1beta
3 3 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/browser.php
... ... @@ -894,6 +894,17 @@
894 894 $form->submitButton(new SimpleById($id), $additional));
895 895 return ($success ? $this->getContent() : $success);
896 896 }
  897 +
  898 + /**
  899 + * Tests to see if a submit button exists with this
  900 + * label.
  901 + * @param string $label Button label.
  902 + * @return boolean True if present.
  903 + * @access public
  904 + */
  905 + function isSubmit($label) {
  906 + return (boolean)$this->_page->getFormBySubmit(new SimpleByLabel($label));
  907 + }
897 908  
898 909 /**
899 910 * Clicks the submit image by some kind of label. Usually
... ... @@ -962,6 +973,17 @@
962 973 $form->submitImage(new SimpleById($id), $x, $y, $additional));
963 974 return ($success ? $this->getContent() : $success);
964 975 }
  976 +
  977 + /**
  978 + * Tests to see if an image exists with this
  979 + * title or alt text.
  980 + * @param string $label Image text.
  981 + * @return boolean True if present.
  982 + * @access public
  983 + */
  984 + function isImage($label) {
  985 + return (boolean)$this->_page->getFormByImage(new SimpleByLabel($label));
  986 + }
965 987  
966 988 /**
967 989 * Submits a form by the ID.
... ... @@ -981,16 +1003,16 @@
981 1003 }
982 1004  
983 1005 /**
984   - * Follows a link by label. Will click the first link
  1006 + * Finds a URL by label. Will find the first link
985 1007 * found with this link text by default, or a later
986 1008 * one if an index is given. The match ignores case and
987 1009 * white space issues.
988 1010 * @param string $label Text between the anchor tags.
989 1011 * @param integer $index Link position counting from zero.
990   - * @return string/boolean Page on success.
  1012 + * @return string/boolean URL on success.
991 1013 * @access public
992 1014 */
993   - function clickLink($label, $index = 0) {
  1015 + function getLink($label, $index = 0) {
994 1016 $urls = $this->_page->getUrlsByLabel($label);
995 1017 if (count($urls) == 0) {
996 1018 return false;
... ... @@ -998,18 +1020,36 @@
998 1020 if (count($urls) < $index + 1) {
999 1021 return false;
1000 1022 }
1001   - $this->_load($urls[$index], new SimpleGetEncoding());
1002   - return $this->getContent();
  1023 + return $urls[$index];
1003 1024 }
1004 1025  
1005 1026 /**
1006   - * Tests to see if a link is present by label.
1007   - * @param string $label Text of value attribute.
1008   - * @return boolean True if link present.
  1027 + * Follows a link by label. Will click the first link
  1028 + * found with this link text by default, or a later
  1029 + * one if an index is given. The match ignores case and
  1030 + * white space issues.
  1031 + * @param string $label Text between the anchor tags.
  1032 + * @param integer $index Link position counting from zero.
  1033 + * @return string/boolean Page on success.
  1034 + * @access public
  1035 + */
  1036 + function clickLink($label, $index = 0) {
  1037 + $url = $this->getLink($label, $index);
  1038 + if ($url === false) {
  1039 + return false;
  1040 + }
  1041 + $this->_load($url, new SimpleGetEncoding());
  1042 + return $this->getContent();
  1043 + }
  1044 +
  1045 + /**
  1046 + * Finds a link by id attribute.
  1047 + * @param string $id ID attribute value.
  1048 + * @return string/boolean URL on success.
1009 1049 * @access public
1010 1050 */
1011   - function isLink($label) {
1012   - return (count($this->_page->getUrlsByLabel($label)) > 0);
  1051 + function getLinkById($id) {
  1052 + return $this->_page->getUrlById($id);
1013 1053 }
1014 1054  
1015 1055 /**
... ... @@ -1019,7 +1059,7 @@
1019 1059 * @access public
1020 1060 */
1021 1061 function clickLinkById($id) {
1022   - if (! ($url = $this->_page->getUrlById($id))) {
  1062 + if (! ($url = $this->getLinkById($id))) {
1023 1063 return false;
1024 1064 }
1025 1065 $this->_load($url, new SimpleGetEncoding());
... ... @@ -1027,16 +1067,6 @@
1027 1067 }
1028 1068  
1029 1069 /**
1030   - * Tests to see if a link is present by ID attribute.
1031   - * @param string $id Text of id attribute.
1032   - * @return boolean True if link present.
1033   - * @access public
1034   - */
1035   - function isLinkById($id) {
1036   - return (boolean)$this->_page->getUrlById($id);
1037   - }
1038   -
1039   - /**
1040 1070 * Clicks a visible text item. Will first try buttons,
1041 1071 * then links and then images.
1042 1072 * @param string $label Visible text or alt text.
... ... @@ -1053,5 +1083,15 @@
1053 1083 }
1054 1084 return $raw;
1055 1085 }
  1086 +
  1087 + /**
  1088 + * Tests to see if a click target exists.
  1089 + * @param string $label Visible text or alt text.
  1090 + * @return boolean True if target present.
  1091 + * @access public
  1092 + */
  1093 + function isClickable($label) {
  1094 + return $this->isSubmit($label) || ($this->getLink($label) !== false) || $this->isImage($label);
  1095 + }
1056 1096 }
1057 1097 ?>
1058 1098 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/collector.php
1 1 <?php
2   -/**
3   - * This file contains the following classes: {@link SimpleCollector},
4   - * {@link SimplePatternCollector}.
5   - *
6   - * @author Travis Swicegood <development@domain51.com>
7   - * @package SimpleTest
8   - * @subpackage UnitTester
9   - * @version $Id$
10   - */
11   -
12   -/**
13   - * The basic collector for {@link GroupTest}
14   - *
15   - * @see collect(), GroupTest::collect()
16   - * @package SimpleTest
17   - * @subpackage UnitTester
18   - */
19   -class SimpleCollector {
20   -
21 2 /**
22   - * Strips off any kind of slash at the end so as to normalise the path
  3 + * This file contains the following classes: {@link SimpleCollector},
  4 + * {@link SimplePatternCollector}.
23 5 *
24   - * @param string $path Path to normalise.
  6 + * @author Travis Swicegood <development@domain51.com>
  7 + * @package SimpleTest
  8 + * @subpackage UnitTester
  9 + * @version $Id$
25 10 */
26   - function _removeTrailingSlash($path) {
27   - return preg_replace('|[\\/]$|', '', $path);
28   -
29   - /**
30   - * @internal
31   - * Try benchmarking the following. It's more code, but by not using the
32   - * regex, it may be faster? Also, shouldn't be looking for
33   - * DIRECTORY_SEPERATOR instead of a manual "/"?
34   - */
35   - if (substr($path, -1) == DIRECTORY_SEPERATOR) {
36   - return substr($path, 0, -1);
37   - } else {
38   - return $path;
39   - }
40   - }
41   -
  11 +
42 12 /**
43   - * Scans the directory and adds what it can.
44   - * @param object $test Group test with {@link GroupTest::addTestFile()} method.
45   - * @param string $path Directory to scan.
46   - * @see _attemptToAdd()
  13 + * The basic collector for {@link GroupTest}
  14 + *
  15 + * @see collect(), GroupTest::collect()
  16 + * @package SimpleTest
  17 + * @subpackage UnitTester
47 18 */
48   - function collect(&$test, $path) {
49   - $path = $this->_removeTrailingSlash($path);
50   - if ($handle = opendir($path)) {
51   - while (($entry = readdir($handle)) !== false) {
52   - $this->_handle($test, $path . DIRECTORY_SEPARATOR . $entry);
  19 + class SimpleCollector {
  20 +
  21 + /**
  22 + * Strips off any kind of slash at the end so as to normalise the path.
  23 + * @param string $path Path to normalise.
  24 + * @return string Path without trailing slash.
  25 + */
  26 + function _removeTrailingSlash($path) {
  27 + if (substr($path, -1) == DIRECTORY_SEPARATOR) {
  28 + return substr($path, 0, -1);
  29 + } elseif (substr($path, -1) == '/') {
  30 + return substr($path, 0, -1);
  31 + } else {
  32 + return $path;
53 33 }
54   - closedir($handle);
55 34 }
56   - }
57 35  
58   - /**
59   - * This method determines what should be done with a given file and adds
60   - * it via {@link GroupTest::addTestFile()} if necessary.
61   - *
62   - * This method should be overriden to provide custom matching criteria,
63   - * such as pattern matching, recursive matching, etc. For an example, see
64   - * {@link SimplePatternCollector::_handle()}.
65   - *
66   - * @param object $test Group test with {@link GroupTest::addTestFile()} method.
67   - * @param string $filename A filename as generated by {@link collect()}
68   - * @see collect()
69   - * @access protected
70   - */
71   - function _handle(&$test, $file) {
72   - if (!is_dir($file)) {
73   - $test->addTestFile($file);
  36 + /**
  37 + * Scans the directory and adds what it can.
  38 + * @param object $test Group test with {@link GroupTest::addTestFile()} method.
  39 + * @param string $path Directory to scan.
  40 + * @see _attemptToAdd()
  41 + */
  42 + function collect(&$test, $path) {
  43 + $path = $this->_removeTrailingSlash($path);
  44 + if ($handle = opendir($path)) {
  45 + while (($entry = readdir($handle)) !== false) {
  46 + $this->_handle($test, $path . DIRECTORY_SEPARATOR . $entry);
  47 + }
  48 + closedir($handle);
  49 + }
74 50 }
75   - }
76   -}
77   -
78   -/**
79   - * An extension to {@link SimpleCollector} that only adds files matching a
80   - * given pattern.
81   - *
82   - * @package SimpleTest
83   - * @subpackage UnitTester
84   - * @see SimpleCollector
85   - */
86   -class SimplePatternCollector extends SimpleCollector {
87   - var $_pattern;
88 51  
  52 + /**
  53 + * This method determines what should be done with a given file and adds
  54 + * it via {@link GroupTest::addTestFile()} if necessary.
  55 + *
  56 + * This method should be overriden to provide custom matching criteria,
  57 + * such as pattern matching, recursive matching, etc. For an example, see
  58 + * {@link SimplePatternCollector::_handle()}.
  59 + *
  60 + * @param object $test Group test with {@link GroupTest::addTestFile()} method.
  61 + * @param string $filename A filename as generated by {@link collect()}
  62 + * @see collect()
  63 + * @access protected
  64 + */
  65 + function _handle(&$test, $file) {
  66 + if (! is_dir($file)) {
  67 + $test->addTestFile($file);
  68 + }
  69 + }
  70 + }
89 71  
90 72 /**
  73 + * An extension to {@link SimpleCollector} that only adds files matching a
  74 + * given pattern.
91 75 *
92   - * @param string $pattern Perl compatible regex to test name against
93   - * See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE}
94   - * for full documentation of valid pattern.s
  76 + * @package SimpleTest
  77 + * @subpackage UnitTester
  78 + * @see SimpleCollector
95 79 */
96   - function SimplePatternCollector($pattern = '/php$/i') {
97   - $this->_pattern = $pattern;
98   - }
  80 + class SimplePatternCollector extends SimpleCollector {
  81 + var $_pattern;
99 82  
  83 + /**
  84 + *
  85 + * @param string $pattern Perl compatible regex to test name against
  86 + * See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE}
  87 + * for full documentation of valid pattern.s
  88 + */
  89 + function SimplePatternCollector($pattern = '/php$/i') {
  90 + $this->_pattern = $pattern;
  91 + }
100 92  
101   - /**
102   - * Attempts to add files that match a given pattern.
103   - *
104   - * @see SimpleCollector::_handle()
105   - * @param object $test Group test with {@link GroupTest::addTestFile()} method.
106   - * @param string $path Directory to scan.
107   - * @access protected
108   - */
109   - function _handle(&$test, $filename) {
110   - if (preg_match($this->_pattern, $filename)) {
111   - parent::_handle($test, $filename);
  93 + /**
  94 + * Attempts to add files that match a given pattern.
  95 + *
  96 + * @see SimpleCollector::_handle()
  97 + * @param object $test Group test with {@link GroupTest::addTestFile()} method.
  98 + * @param string $path Directory to scan.
  99 + * @access protected
  100 + */
  101 + function _handle(&$test, $filename) {
  102 + if (preg_match($this->_pattern, $filename)) {
  103 + parent::_handle($test, $filename);
  104 + }
112 105 }
113 106 }
114   -}
115 107 ?>
116 108 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/compatibility.php
... ... @@ -70,6 +70,9 @@
70 70 if (is_array($first) && is_array($second)) {
71 71 return SimpleTestCompatibility::_isArrayOfIdenticalTypes($first, $second);
72 72 }
  73 + if ($first !== $second) {
  74 + return false;
  75 + }
73 76 return true;
74 77 }
75 78  
... ... @@ -105,8 +108,7 @@
105 108 * @static
106 109 */
107 110 function isReference(&$first, &$second) {
108   - if (version_compare(phpversion(), '5', '>=')
109   - && is_object($first)) {
  111 + if (version_compare(phpversion(), '5', '>=') && is_object($first)) {
110 112 return ($first === $second);
111 113 }
112 114 if (is_object($first) && is_object($second)) {
... ... @@ -133,9 +135,6 @@
133 135 * @static
134 136 */
135 137 function isA($object, $class) {
136   - if (function_exists('is_a')) {
137   - return is_a($object, $class);
138   - }
139 138 if (version_compare(phpversion(), '5') >= 0) {
140 139 if (! class_exists($class, false)) {
141 140 if (function_exists('interface_exists')) {
... ... @@ -147,6 +146,9 @@
147 146 eval("\$is_a = \$object instanceof $class;");
148 147 return $is_a;
149 148 }
  149 + if (function_exists('is_a')) {
  150 + return is_a($object, $class);
  151 + }
150 152 return ((strtolower($class) == get_class($object))
151 153 or (is_subclass_of($object, $class)));
152 154 }
... ... @@ -167,18 +169,5 @@
167 169 set_socket_timeout($handle, $timeout, 0);
168 170 }
169 171 }
170   -
171   - /**
172   - * Gets the current stack trace topmost first.
173   - * @return array List of stack frames.
174   - * @access public
175   - * @static
176   - */
177   - function getStackTrace() {
178   - if (function_exists('debug_backtrace')) {
179   - return array_reverse(debug_backtrace());
180   - }
181   - return array();
182   - }
183 172 }
184 173 -?>
  174 +?>
185 175 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/dumper.php
... ... @@ -356,47 +356,5 @@
356 356 ob_end_clean();
357 357 return $formatted;
358 358 }
359   -
360   - /**
361   - * Extracts the last assertion that was not within
362   - * Simpletest itself. The name must start with "assert".
363   - * @param array $stack List of stack frames.
364   - * @access public
365   - * @static
366   - */
367   - function getFormattedAssertionLine($stack) {
368   - foreach ($stack as $frame) {
369   - if (isset($frame['file'])) {
370   - if (strpos($frame['file'], SIMPLE_TEST) !== false) {
371   - if (dirname($frame['file']) . '/' == SIMPLE_TEST) {
372   - continue;
373   - }
374   - }
375   - }
376   - if (SimpleDumper::_stackFrameIsAnAssertion($frame)) {
377   - return ' at [' . $frame['file'] . ' line ' . $frame['line'] . ']';
378   - }
379   - }
380   - return '';
381   - }
382   -
383   - /**
384   - * Tries to determine if the method call is an assertion.
385   - * @param array $frame PHP stack frame.
386   - * @access private
387   - * @static
388   - */
389   - function _stackFrameIsAnAssertion($frame) {
390   - if (($frame['function'] == 'fail') || ($frame['function'] == 'pass')) {
391   - return true;
392   - }
393   - if (strncmp($frame['function'], 'assert', 6) == 0) {
394   - return true;
395   - }
396   - if (strncmp($frame['function'], 'expect', 6) == 0) {
397   - return true;
398   - }
399   - return false;
400   - }
401 359 }
402 360 ?>
403 361 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/encoding.php
... ... @@ -37,7 +37,7 @@
37 37 * @access public
38 38 */
39 39 function asRequest() {
40   - return $this->_key . '=' . urlencode($this->_value);
  40 + return urlencode($this->_key) . '=' . urlencode($this->_value);
41 41 }
42 42  
43 43 /**
... ...
thirdparty/simpletest/simpletest/errors.php
... ... @@ -15,6 +15,8 @@
15 15 * Includes SimpleTest files.
16 16 */
17 17 require_once(dirname(__FILE__) . '/invoker.php');
  18 + require_once(dirname(__FILE__) . '/test_case.php');
  19 + require_once(dirname(__FILE__) . '/expectation.php');
18 20  
19 21 /**
20 22 * Extension that traps errors into an error queue.
... ... @@ -39,13 +41,15 @@
39 41 * @access public
40 42 */
41 43 function invoke($method) {
42   - set_error_handler('simpleTestErrorHandler');
  44 + $context = &SimpleTest::getContext();
  45 + $queue = &$context->get('SimpleErrorQueue');
  46 + $queue->setTestCase($this->GetTestCase());
  47 + set_error_handler('SimpleTestErrorHandler');
43 48 parent::invoke($method);
44   - $queue = &SimpleErrorQueue::instance();
45   - while (list($severity, $message, $file, $line, $globals) = $queue->extract()) {
  49 + while (list($severity, $message, $file, $line) = $queue->extract()) {
46 50 $severity = SimpleErrorQueue::getSeverityAsString($severity);
47   - $test_case = &$this->getTestCase();
48   - $test_case->error($severity, $message, $file, $line);
  51 + $test = &$this->getTestCase();
  52 + $test->error($severity, $message, $file, $line);
49 53 }
50 54 restore_error_handler();
51 55 }
... ... @@ -59,28 +63,63 @@
59 63 */
60 64 class SimpleErrorQueue {
61 65 var $_queue;
  66 + var $_expectation_queue;
  67 + var $_test;
62 68  
63 69 /**
64 70 * Starts with an empty queue.
65   - * @access public
66 71 */
67 72 function SimpleErrorQueue() {
68 73 $this->clear();
69 74 }
70 75  
71 76 /**
  77 + * Sets the currently running test case.
  78 + * @param SimpleTestCase $test Test case to send messages to.
  79 + * @access public
  80 + */
  81 + function setTestCase(&$test) {
  82 + $this->_test = &$test;
  83 + }
  84 +
  85 + /**
72 86 * Adds an error to the front of the queue.
73   - * @param $severity PHP error code.
74   - * @param $message Text of error.
75   - * @param $filename File error occoured in.
76   - * @param $line Line number of error.
77   - * @param $super_globals Hash of PHP super global arrays.
  87 + * @param integer $severity PHP error code.
  88 + * @param string $content Text of error.
  89 + * @param string $filename File error occoured in.
  90 + * @param integer $line Line number of error.
78 91 * @access public
79 92 */
80   - function add($severity, $message, $filename, $line, $super_globals) {
81   - array_push(
82   - $this->_queue,
83   - array($severity, $message, $filename, $line, $super_globals));
  93 + function add($severity, $content, $filename, $line) {
  94 + $content = str_replace('%', '%%', $content);
  95 + if (count($this->_expectation_queue)) {
  96 + $this->_testLatestError($severity, $content, $filename, $line);
  97 + } else {
  98 + array_push(
  99 + $this->_queue,
  100 + array($severity, $content, $filename, $line));
  101 + }
  102 + }
  103 +
  104 + /**
  105 + * Tests the error against the most recent expected
  106 + * error.
  107 + * @param integer $severity PHP error code.
  108 + * @param string $content Text of error.
  109 + * @param string $filename File error occoured in.
  110 + * @param integer $line Line number of error.
  111 + * @access private
  112 + */
  113 + function _testLatestError($severity, $content, $filename, $line) {
  114 + list($expected, $message) = array_shift($this->_expectation_queue);
  115 + $severity = $this->getSeverityAsString($severity);
  116 + $is_match = $this->_test->assert(
  117 + $expected,
  118 + $content,
  119 + sprintf($message, "%s -> PHP error [$content] severity [$severity] in [$filename] line [$line]"));
  120 + if (! $is_match) {
  121 + $this->_test->error($severity, $content, $filename, $line);
  122 + }
84 123 }
85 124  
86 125 /**
... ... @@ -105,32 +144,52 @@
105 144 */
106 145 function clear() {
107 146 $this->_queue = array();
  147 + $this->_expectation_queue = array();
108 148 }
109 149  
110 150 /**
111   - * Tests to see if the queue is empty.
112   - * @return True if empty.
  151 + * @deprecated
113 152 */
114   - function isEmpty() {
115   - return (count($this->_queue) == 0);
  153 + function assertNoErrors($message) {
  154 + return $this->_test->assert(
  155 + new TrueExpectation(),
  156 + count($this->_queue) == 0,
  157 + sprintf($message, 'Should be no errors'));
116 158 }
117 159  
118 160 /**
119   - * Global access to a single error queue.
120   - * @return Global error queue object.
121   - * @access public
122   - * @static
  161 + * @deprecated
123 162 */
124   - function &instance() {
125   - static $queue = false;
126   - if (! $queue) {
127   - $queue = new SimpleErrorQueue();
  163 + function assertError($expected, $message) {
  164 + if (count($this->_queue) == 0) {
  165 + $this->_test->fail(sprintf($message, 'Expected error not found'));
  166 + return false;
128 167 }
129   - return $queue;
  168 + list($severity, $content, $file, $line) = $this->extract();
  169 + $severity = $this->getSeverityAsString($severity);
  170 + return $this->_test->assert(
  171 + $expected,
  172 + $content,
  173 + sprintf($message, "Expected PHP error [$content] severity [$severity] in [$file] line [$line]"));
  174 + }
  175 +
  176 + /**
  177 + * Sets up an expectation of an error. If this is
  178 + * not fulfilled at the end of the test, a failure
  179 + * will occour. If the error does happen, then this
  180 + * will cancel it out and send a pass message.
  181 + * @param SimpleExpectation $expected Expected error match.
  182 + * @param string $message Message to display.
  183 + * @access public
  184 + */
  185 + function expectError($expected, $message) {
  186 + array_push(
  187 + $this->_expectation_queue,
  188 + array($expected, $message));
130 189 }
131 190  
132 191 /**
133   - * Converst an error code into it's string
  192 + * Converts an error code into it's string
134 193 * representation.
135 194 * @param $severity PHP integer error code.
136 195 * @return String version of error code.
... ... @@ -167,16 +226,17 @@
167 226 * @static
168 227 * @access public
169 228 */
170   - function simpleTestErrorHandler($severity, $message, $filename, $line, $super_globals) {
  229 + function SimpleTestErrorHandler($severity, $message, $filename, $line, $super_globals) {
171 230 if ($severity = $severity & error_reporting()) {
172 231 restore_error_handler();
173 232 if (ini_get('log_errors')) {
174 233 $label = SimpleErrorQueue::getSeverityAsString($severity);
175 234 error_log("$label: $message in $filename on line $line");
176 235 }
177   - $queue = &SimpleErrorQueue::instance();
178   - $queue->add($severity, $message, $filename, $line, $super_globals);
179   - set_error_handler('simpleTestErrorHandler');
  236 + $context = &SimpleTest::getContext();
  237 + $queue = &$context->get('SimpleErrorQueue');
  238 + $queue->add($severity, $message, $filename, $line);
  239 + set_error_handler('SimpleTestErrorHandler');
180 240 }
181 241 }
182 242 ?>
183 243 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/exceptions.php 0 → 100644
  1 +<?php
  2 + /**
  3 + * base include file for SimpleTest
  4 + * @package SimpleTest
  5 + * @subpackage UnitTester
  6 + * @version $Id: exceptions.php,v 1.12 2006/11/09 23:14:48 lastcraft Exp $
  7 + */
  8 +
  9 + /**#@+
  10 + * Includes SimpleTest files and defined the root constant
  11 + * for dependent libraries.
  12 + */
  13 + require_once(dirname(__FILE__) . '/invoker.php');
  14 + require_once(dirname(__FILE__) . '/expectation.php');
  15 +
  16 + /**
  17 + * Extension that traps exceptions and turns them into
  18 + * an error message. PHP5 only.
  19 + * @package SimpleTest
  20 + * @subpackage UnitTester
  21 + */
  22 + class SimpleExceptionTrappingInvoker extends SimpleInvokerDecorator {
  23 +
  24 + /**
  25 + * Stores the invoker to be wrapped.
  26 + * @param SimpleInvoker $invoker Test method runner.
  27 + */
  28 + function SimpleExceptionTrappingInvoker($invoker) {
  29 + $this->SimpleInvokerDecorator($invoker);
  30 + }
  31 +
  32 + /**
  33 + * Invokes a test method whilst trapping expected
  34 + * exceptions. Any left over unthrown exceptions
  35 + * are then reported as failures.
  36 + * @param string $method Test method to call.
  37 + */
  38 + function invoke($method) {
  39 + $trap = SimpleTest::getContext()->get('SimpleExceptionTrap');
  40 + $trap->clear();
  41 + try {
  42 + parent::invoke($method);
  43 + } catch (Exception $exception) {
  44 + if (! $trap->isExpected($this->getTestCase(), $exception)) {
  45 + $this->getTestCase()->exception($exception);
  46 + }
  47 + $trap->clear();
  48 + }
  49 + if ($message = $trap->getOutstanding()) {
  50 + $this->getTestCase()->fail($message);
  51 + }
  52 + }
  53 + }
  54 +
  55 + /**
  56 + * Tests exceptions either by type or the exact
  57 + * exception. This could be improved to accept
  58 + * a pattern expectation to test the error
  59 + * message, but that will have to come later.
  60 + * @package SimpleTest
  61 + * @subpackage UnitTester
  62 + */
  63 + class ExceptionExpectation extends SimpleExpectation {
  64 + private $expected;
  65 +
  66 + /**
  67 + * Sets up the conditions to test against.
  68 + * If the expected value is a string, then
  69 + * it will act as a test of the class name.
  70 + * An exception as the comparison will
  71 + * trigger an identical match. Writing this
  72 + * down now makes it look doubly dumb. I hope
  73 + * come up with a better scheme later.
  74 + * @param mixed $expected A class name or an actual
  75 + * exception to compare with.
  76 + * @param string $message Message to display.
  77 + */
  78 + function __construct($expected, $message = '%s') {
  79 + $this->expected = $expected;
  80 + parent::__construct($message);
  81 + }
  82 +
  83 + /**
  84 + * Carry out the test.
  85 + * @param Exception $compare Value to check.
  86 + * @return boolean True if matched.
  87 + */
  88 + function test($compare) {
  89 + if (is_string($this->expected)) {
  90 + return ($compare instanceof $this->expected);
  91 + }
  92 + if (get_class($compare) != get_class($this->expected)) {
  93 + return false;
  94 + }
  95 + return $compare->getMessage() == $this->expected->getMessage();
  96 + }
  97 +
  98 + /**
  99 + * Create the message to display describing the test.
  100 + * @param Exception $compare Exception to match.
  101 + * @return string Final message.
  102 + */
  103 + function testMessage($compare) {
  104 + if (is_string($this->expected)) {
  105 + return "Exception [" . $this->describeException($compare) .
  106 + "] should be type [" . $this->expected . "]";
  107 + }
  108 + return "Exception [" . $this->describeException($compare) .
  109 + "] should match [" .
  110 + $this->describeException($this->expected) . "]";
  111 + }
  112 +
  113 + /**
  114 + * Summary of an Exception object.
  115 + * @param Exception $compare Exception to describe.
  116 + * @return string Text description.
  117 + */
  118 + protected function describeException($exception) {
  119 + return get_class($exception) . ": " . $exception->getMessage();
  120 + }
  121 + }
  122 +
  123 + /**
  124 + * Stores expected exceptions for when they
  125 + * get thrown. Saves the irritating try...catch
  126 + * block.
  127 + * @package SimpleTest
  128 + * @subpackage UnitTester
  129 + */
  130 + class SimpleExceptionTrap {
  131 + private $expected;
  132 + private $message;
  133 +
  134 + /**
  135 + * Clears down the queue ready for action.
  136 + */
  137 + function __construct() {
  138 + $this->clear();
  139 + }
  140 +
  141 + /**
  142 + * Sets up an expectation of an exception.
  143 + * This has the effect of intercepting an
  144 + * exception that matches.
  145 + * @param SimpleExpectation $expected Expected exception to match.
  146 + * @param string $message Message to display.
  147 + * @access public
  148 + */
  149 + function expectException($expected = false, $message = '%s') {
  150 + if ($expected === false) {
  151 + $expected = new AnythingExpectation();
  152 + }
  153 + if (! SimpleExpectation::isExpectation($expected)) {
  154 + $expected = new ExceptionExpectation($expected);
  155 + }
  156 + $this->expected = $expected;
  157 + $this->message = $message;
  158 + }
  159 +
  160 + /**
  161 + * Compares the expected exception with any
  162 + * in the queue. Issues a pass or fail and
  163 + * returns the state of the test.
  164 + * @param SimpleTestCase $test Test case to send messages to.
  165 + * @param Exception $exception Exception to compare.
  166 + * @return boolean False on no match.
  167 + */
  168 + function isExpected($test, $exception) {
  169 + if ($this->expected) {
  170 + return $test->assert($this->expected, $exception, $this->message);
  171 + }
  172 + return false;
  173 + }
  174 +
  175 + /**
  176 + * Tests for any left over exception.
  177 + * @return string/false The failure message or false if none.
  178 + */
  179 + function getOutstanding() {
  180 + return sprintf($this->message, 'Failed to trap exception');
  181 + }
  182 +
  183 + /**
  184 + * Discards the contents of the error queue.
  185 + */
  186 + function clear() {
  187 + $this->expected = false;
  188 + $this->message = false;
  189 + }
  190 + }
  191 +?>
0 192 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/expectation.php
... ... @@ -30,7 +30,6 @@
30 30 * @param string $message Customised message on failure.
31 31 */
32 32 function SimpleExpectation($message = '%s') {
33   - $this->_dumper = &new SimpleDumper();
34 33 $this->_message = $message;
35 34 }
36 35  
... ... @@ -58,12 +57,14 @@
58 57 /**
59 58 * Overlays the generated message onto the stored user
60 59 * message. An additional message can be interjected.
61   - * @param mixed $compare Comparison value.
62   - * @return string Description of success
63   - * or failure.
  60 + * @param mixed $compare Comparison value.
  61 + * @param SimpleDumper $dumper For formatting the results.
  62 + * @return string Description of success
  63 + * or failure.
64 64 * @access public
65 65 */
66   - function overlayMessage($compare) {
  66 + function overlayMessage($compare, $dumper) {
  67 + $this->_dumper = $dumper;
67 68 return sprintf($this->_message, $this->testMessage($compare));
68 69 }
69 70  
... ... @@ -91,6 +92,96 @@
91 92 SimpleTestCompatibility::isA($expectation, 'SimpleExpectation');
92 93 }
93 94 }
  95 +
  96 + /**
  97 + * A wildcard expectation always matches.
  98 + * @package SimpleTest
  99 + * @subpackage MockObjects
  100 + */
  101 + class AnythingExpectation extends SimpleExpectation {
  102 +
  103 + /**
  104 + * Tests the expectation. Always true.
  105 + * @param mixed $compare Ignored.
  106 + * @return boolean True.
  107 + * @access public
  108 + */
  109 + function test($compare) {
  110 + return true;
  111 + }
  112 +
  113 + /**
  114 + * Returns a human readable test message.
  115 + * @param mixed $compare Comparison value.
  116 + * @return string Description of success
  117 + * or failure.
  118 + * @access public
  119 + */
  120 + function testMessage($compare) {
  121 + $dumper = &$this->_getDumper();
  122 + return 'Anything always matches [' . $dumper->describeValue($compare) . ']';
  123 + }
  124 + }
  125 +
  126 + /**
  127 + * An expectation that passes on boolean true.
  128 + * @package SimpleTest
  129 + * @subpackage MockObjects
  130 + */
  131 + class TrueExpectation extends SimpleExpectation {
  132 +
  133 + /**
  134 + * Tests the expectation.
  135 + * @param mixed $compare Should be true.
  136 + * @return boolean True on match.
  137 + * @access public
  138 + */
  139 + function test($compare) {
  140 + return (boolean)$compare;
  141 + }
  142 +
  143 + /**
  144 + * Returns a human readable test message.
  145 + * @param mixed $compare Comparison value.
  146 + * @return string Description of success
  147 + * or failure.
  148 + * @access public
  149 + */
  150 + function testMessage($compare) {
  151 + $dumper = &$this->_getDumper();
  152 + return 'Expected true, got [' . $dumper->describeValue($compare) . ']';
  153 + }
  154 + }
  155 +
  156 + /**
  157 + * An expectation that passes on boolean false.
  158 + * @package SimpleTest
  159 + * @subpackage MockObjects
  160 + */
  161 + class FalseExpectation extends SimpleExpectation {
  162 +
  163 + /**
  164 + * Tests the expectation.
  165 + * @param mixed $compare Should be false.
  166 + * @return boolean True on match.
  167 + * @access public
  168 + */
  169 + function test($compare) {
  170 + return ! (boolean)$compare;
  171 + }
  172 +
  173 + /**
  174 + * Returns a human readable test message.
  175 + * @param mixed $compare Comparison value.
  176 + * @return string Description of success
  177 + * or failure.
  178 + * @access public
  179 + */
  180 + function testMessage($compare) {
  181 + $dumper = &$this->_getDumper();
  182 + return 'Expected false, got [' . $dumper->describeValue($compare) . ']';
  183 + }
  184 + }
94 185  
95 186 /**
96 187 * Test for equality.
... ... @@ -471,8 +562,8 @@
471 562 /**
472 563 * Describes a pattern match including the string
473 564 * found and it's position.
474   - * @package SimpleTest
475   - * @subpackage UnitTester
  565 + * @package SimpleTest
  566 + * @subpackage UnitTester
476 567 * @param string $pattern Regex to match against.
477 568 * @param string $subject Subject to search.
478 569 * @access protected
... ... @@ -480,7 +571,7 @@
480 571 function _describePatternMatch($pattern, $subject) {
481 572 preg_match($pattern, $subject, $matches);
482 573 $position = strpos($subject, $matches[0]);
483   - $dumper = &$this->_getDumper();
  574 + $dumper = $this->_getDumper();
484 575 return "Pattern [$pattern] detected at character [$position] in [" .
485 576 $dumper->describeValue($subject) . "] as [" .
486 577 $matches[0] . "] in region [" .
... ... @@ -717,4 +808,4 @@
717 808 "] should contain method [$method]";
718 809 }
719 810 }
720 811 -?>
  812 +?>
721 813 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/extensions/pear_test_case.php
... ... @@ -58,7 +58,7 @@
58 58 * @public
59 59 */
60 60 function assertNotNull($value, $message = "%s") {
61   - parent::assertTrue(isset($value), $message);
  61 + parent::assert(new TrueExpectation(), isset($value), $message);
62 62 }
63 63  
64 64 /**
... ... @@ -68,7 +68,7 @@
68 68 * @public
69 69 */
70 70 function assertNull($value, $message = "%s") {
71   - parent::assertTrue(!isset($value), $message);
  71 + parent::assert(new TrueExpectation(), !isset($value), $message);
72 72 }
73 73  
74 74 /**
... ... @@ -86,7 +86,8 @@
86 86 "[" . $dumper->describeValue($first) .
87 87 "] and [" . $dumper->describeValue($second) .
88 88 "] should reference the same object");
89   - return $this->assertTrue(
  89 + return $this->assert(
  90 + new TrueExpectation(),
90 91 SimpleTestCompatibility::isReference($first, $second),
91 92 $message);
92 93 }
... ... @@ -106,7 +107,8 @@
106 107 "[" . $dumper->describeValue($first) .
107 108 "] and [" . $dumper->describeValue($second) .
108 109 "] should not be the same object");
109   - return $this->assertFalse(
  110 + return $this->assert(
  111 + new falseExpectation(),
110 112 SimpleTestCompatibility::isReference($first, $second),
111 113 $message);
112 114 }
... ... @@ -119,7 +121,7 @@
119 121 * @public
120 122 */
121 123 function assertTrue($condition, $message = "%s") {
122   - parent::assertTrue($condition, $message);
  124 + parent::assert(new TrueExpectation(), $condition, $message);
123 125 }
124 126  
125 127 /**
... ... @@ -130,7 +132,7 @@
130 132 * @public
131 133 */
132 134 function assertFalse($condition, $message = "%s") {
133   - parent::assertTrue(!$condition, $message);
  135 + parent::assert(new FalseExpectation(), $condition, $message);
134 136 }
135 137  
136 138 /**
... ... @@ -152,7 +154,7 @@
152 154 * @public
153 155 */
154 156 function assertType($value, $type, $message = "%s") {
155   - parent::assertTrue(gettype($value) == strtolower($type), $message);
  157 + parent::assert(new TrueExpectation(), gettype($value) == strtolower($type), $message);
156 158 }
157 159  
158 160 /**
... ...
thirdparty/simpletest/simpletest/extensions/phpunit_test_case.php
... ... @@ -38,7 +38,7 @@
38 38 * @public
39 39 */
40 40 function assert($condition, $message = false) {
41   - parent::assertTrue($condition, $message);
  41 + parent::assert(new TrueExpectation(), $condition, $message);
42 42 }
43 43  
44 44 /**
... ...
thirdparty/simpletest/simpletest/mock_objects.php
... ... @@ -27,36 +27,6 @@
27 27 }
28 28  
29 29 /**
30   - * A wildcard expectation always matches.
31   - * @package SimpleTest
32   - * @subpackage MockObjects
33   - */
34   - class AnythingExpectation extends SimpleExpectation {
35   -
36   - /**
37   - * Tests the expectation. Always true.
38   - * @param mixed $compare Ignored.
39   - * @return boolean True.
40   - * @access public
41   - */
42   - function test($compare) {
43   - return true;
44   - }
45   -
46   - /**
47   - * Returns a human readable test message.
48   - * @param mixed $compare Comparison value.
49   - * @return string Description of success
50   - * or failure.
51   - * @access public
52   - */
53   - function testMessage($compare) {
54   - $dumper = &$this->_getDumper();
55   - return 'Anything always matches [' . $dumper->describeValue($compare) . ']';
56   - }
57   - }
58   -
59   - /**
60 30 * Parameter comparison assertion.
61 31 * @package SimpleTest
62 32 * @subpackage MockObjects
... ... @@ -70,8 +40,6 @@
70 40 * those that are wildcarded.
71 41 * If the value is not an array
72 42 * then it is considered to match any.
73   - * @param mixed $wildcard Any parameter matching this
74   - * will always match.
75 43 * @param string $message Customised message on failure.
76 44 * @access public
77 45 */
... ... @@ -151,7 +119,7 @@
151 119 $comparison = $this->_coerceToExpectation($expected[$i]);
152 120 if (! $comparison->test($parameters[$i])) {
153 121 $messages[] = "parameter " . ($i + 1) . " with [" .
154   - $comparison->overlayMessage($parameters[$i]) . "]";
  122 + $comparison->overlayMessage($parameters[$i], $this->_getDumper()) . "]";
155 123 }
156 124 }
157 125 return "Parameter expectation differs at " . implode(" and ", $messages);
... ... @@ -473,7 +441,8 @@
473 441 * @access protected
474 442 */
475 443 function &_getCurrentTestCase() {
476   - return SimpleTest::getCurrent();
  444 + $context = &SimpleTest::getContext();
  445 + return $context->getTest();
477 446 }
478 447  
479 448 /**
... ... @@ -809,20 +778,17 @@
809 778 * test method has finished. Totals up the call
810 779 * counts and triggers a test assertion if a test
811 780 * is present for expected call counts.
812   - * @param string $method Current method name.
  781 + * @param string $test_method Current method name.
  782 + * @param SimpleTestCase $test Test to send message to.
813 783 * @access public
814 784 */
815   - function atTestEnd($method) {
  785 + function atTestEnd($test_method, &$test) {
816 786 foreach ($this->_expected_counts as $method => $expectation) {
817   - $this->_assertTrue(
818   - $expectation->test($this->getCallCount($method)),
819   - $expectation->overlayMessage($this->getCallCount($method)));
  787 + $test->assert($expectation, $this->getCallCount($method));
820 788 }
821 789 foreach ($this->_max_counts as $method => $expectation) {
822 790 if ($expectation->test($this->getCallCount($method))) {
823   - $this->_assertTrue(
824   - true,
825   - $expectation->overlayMessage($this->getCallCount($method)));
  791 + $test->assert($expectation, $this->getCallCount($method));
826 792 }
827 793 }
828 794 }
... ... @@ -880,39 +846,24 @@
880 846 * @access private
881 847 */
882 848 function _checkExpectations($method, $args, $timing) {
  849 + $test = &$this->_getCurrentTestCase();
883 850 if (isset($this->_max_counts[$method])) {
884 851 if (! $this->_max_counts[$method]->test($timing + 1)) {
885   - $this->_assertTrue(
886   - false,
887   - $this->_max_counts[$method]->overlayMessage($timing + 1));
  852 + $test->assert($this->_max_counts[$method], $timing + 1);
888 853 }
889 854 }
890 855 if (isset($this->_expected_args_at[$timing][$method])) {
891   - $this->_assertTrue(
892   - $this->_expected_args_at[$timing][$method]->test($args),
893   - "Mock method [$method] at [$timing] -> " .
894   - $this->_expected_args_at[$timing][$method]->overlayMessage($args));
  856 + $test->assert(
  857 + $this->_expected_args_at[$timing][$method],
  858 + $args,
  859 + "Mock method [$method] at [$timing] -> %s");
895 860 } elseif (isset($this->_expected_args[$method])) {
896   - $this->_assertTrue(
897   - $this->_expected_args[$method]->test($args),
898   - "Mock method [$method] -> " . $this->_expected_args[$method]->overlayMessage($args));
  861 + $test->assert(
  862 + $this->_expected_args[$method],
  863 + $args,
  864 + "Mock method [$method] -> %s");
899 865 }
900 866 }
901   -
902   - /**
903   - * Triggers an assertion on the held test case.
904   - * Should be overridden when using another test
905   - * framework other than the SimpleTest one if the
906   - * assertion method has a different name.
907   - * @param boolean $assertion True will pass.
908   - * @param string $message Message that will go with
909   - * the test event.
910   - * @access protected
911   - */
912   - function _assertTrue($assertion, $message) {
913   - $test = &$this->_getCurrentTestCase();
914   - $test->assertTrue($assertion, $message);
915   - }
916 867 }
917 868  
918 869 /**
... ... @@ -928,7 +879,7 @@
928 879 * @access public
929 880 */
930 881 function Mock() {
931   - trigger_error('Mock factory methods are class only.');
  882 + trigger_error('Mock factory methods are static.');
932 883 }
933 884  
934 885 /**
... ... @@ -970,19 +921,12 @@
970 921  
971 922 /**
972 923 * Uses a stack trace to find the line of an assertion.
973   - * @param array $stack Stack frames top most first. Only
974   - * needed if not using the PHP
975   - * backtrace function.
976   - * @return string Location of first expect*
977   - * method embedded in format string.
978 924 * @access public
979 925 * @static
980 926 */
981   - function getExpectationLine($stack = false) {
982   - if ($stack === false) {
983   - $stack = SimpleTestCompatibility::getStackTrace();
984   - }
985   - return SimpleDumper::getFormattedAssertionLine($stack);
  927 + function getExpectationLine() {
  928 + $trace = new SimpleStackTrace(array('expect'));
  929 + return $trace->traceMethod();
986 930 }
987 931 }
988 932  
... ... @@ -1051,7 +995,7 @@
1051 995 }
1052 996 $mock_reflection = new SimpleReflection($this->_mock_class);
1053 997 if ($mock_reflection->classExistsSansAutoload()) {
1054   - trigger_error("Partial mock class [$mock_class] already exists");
  998 + trigger_error('Partial mock class [' . $this->_mock_class . '] already exists');
1055 999 return false;
1056 1000 }
1057 1001 return eval($this->_extendClassCode($methods));
... ...
thirdparty/simpletest/simpletest/parser.php
... ... @@ -645,13 +645,15 @@
645 645 * @access public
646 646 */
647 647 function acceptAttributeToken($token, $event) {
648   - if ($event == LEXER_UNMATCHED) {
649   - $this->_attributes[$this->_current_attribute] .=
650   - SimpleHtmlSaxParser::decodeHtml($token);
651   - }
652   - if ($event == LEXER_SPECIAL) {
653   - $this->_attributes[$this->_current_attribute] .=
654   - preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token));
  648 + if ($this->_current_attribute) {
  649 + if ($event == LEXER_UNMATCHED) {
  650 + $this->_attributes[$this->_current_attribute] .=
  651 + SimpleHtmlSaxParser::decodeHtml($token);
  652 + }
  653 + if ($event == LEXER_SPECIAL) {
  654 + $this->_attributes[$this->_current_attribute] .=
  655 + preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token));
  656 + }
655 657 }
656 658 return true;
657 659 }
... ...
thirdparty/simpletest/simpletest/reflection_php5.php
... ... @@ -8,8 +8,8 @@
8 8  
9 9 /**
10 10 * Version specific reflection API.
11   - * @package SimpleTest
12   - * @subpackage UnitTester
  11 + * @package SimpleTest
  12 + * @subpackage UnitTester
13 13 */
14 14 class SimpleReflection {
15 15 var $_interface;
... ... @@ -193,12 +193,17 @@
193 193 * @access public
194 194 */
195 195 function getSignature($name) {
196   - if ($name == '__get') {
197   - return 'function __get($key)';
198   - }
199 196 if ($name == '__set') {
200 197 return 'function __set($key, $value)';
201 198 }
  199 + if ($name == '__call') {
  200 + return 'function __call($method, $arguments)';
  201 + }
  202 + if (version_compare(phpversion(), '5.1.0', '>=')) {
  203 + if (in_array($name, array('__get', '__isset', $name == '__unset'))) {
  204 + return "function {$name}(\$key)";
  205 + }
  206 + }
202 207 if (! is_callable(array($this->_interface, $name))) {
203 208 return "function $name()";
204 209 }
... ... @@ -217,31 +222,31 @@
217 222 * @access private
218 223 */
219 224 function _getFullSignature($name) {
220   - $interface = new ReflectionClass($this->_interface);
221   - $method = $interface->getMethod($name);
222   - $reference = $method->returnsReference() ? '&' : '';
223   - return "function $reference$name(" .
224   - implode(', ', $this->_getParameterSignatures($method)) .
225   - ")";
  225 + $interface = new ReflectionClass($this->_interface);
  226 + $method = $interface->getMethod($name);
  227 + $reference = $method->returnsReference() ? '&' : '';
  228 + return "function $reference$name(" .
  229 + implode(', ', $this->_getParameterSignatures($method)) .
  230 + ")";
226 231 }
227 232  
228 233 /**
229 234 * Gets the source code for each parameter.
230 235 * @param ReflectionMethod $method Method object from
231   - * reflection API
  236 + * reflection API
232 237 * @return array List of strings, each
233 238 * a snippet of code.
234 239 * @access private
235 240 */
236 241 function _getParameterSignatures($method) {
237   - $signatures = array();
  242 + $signatures = array();
238 243 foreach ($method->getParameters() as $parameter) {
239 244 $type = $parameter->getClass();
240 245 $signatures[] =
241   - (! is_null($type) ? $type->getName() . ' ' : '') .
242   - ($parameter->isPassedByReference() ? '&' : '') .
243   - '$' . $this->_suppressSpurious($parameter->getName()) .
244   - ($this->_isOptional($parameter) ? ' = null' : '');
  246 + (! is_null($type) ? $type->getName() . ' ' : '') .
  247 + ($parameter->isPassedByReference() ? '&' : '') .
  248 + '$' . $this->_suppressSpurious($parameter->getName()) .
  249 + ($this->_isOptional($parameter) ? ' = null' : '');
245 250 }
246 251 return $signatures;
247 252 }
... ...
thirdparty/simpletest/simpletest/remote.php
... ... @@ -75,7 +75,8 @@
75 75 * @access protected
76 76 */
77 77 function &_createBrowser() {
78   - return new SimpleBrowser();
  78 + $browser = &new SimpleBrowser();
  79 + return $browser;
79 80 }
80 81  
81 82 /**
... ... @@ -85,7 +86,8 @@
85 86 * @access protected
86 87 */
87 88 function &_createParser(&$reporter) {
88   - return new SimpleTestXmlParser($reporter);
  89 + $parser = &new SimpleTestXmlParser($reporter);
  90 + return $parser;
89 91 }
90 92  
91 93 /**
... ...
thirdparty/simpletest/simpletest/reporter.php
... ... @@ -40,6 +40,7 @@
40 40 */
41 41 function paintHeader($test_name) {
42 42 $this->sendNoCacheHeaders();
  43 + print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">";
43 44 print "<html>\n<head>\n<title>$test_name</title>\n";
44 45 print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" .
45 46 $this->_character_set . "\">\n";
... ... @@ -74,7 +75,9 @@
74 75 * @access protected
75 76 */
76 77 function _getCss() {
77   - return ".fail { color: red; } pre { background-color: lightgray; }";
  78 + return ".fail { background-color: inherit; color: red; }" .
  79 + ".pass { background-color: inherit; color: green; }" .
  80 + " pre { background-color: lightgray; color: inherit; }";
78 81 }
79 82  
80 83 /**
... ... @@ -115,10 +118,9 @@
115 118 }
116 119  
117 120 /**
118   - * Paints a PHP error or exception.
  121 + * Paints a PHP error.
119 122 * @param string $message Message is ignored.
120 123 * @access public
121   - * @abstract
122 124 */
123 125 function paintError($message) {
124 126 parent::paintError($message);
... ... @@ -130,6 +132,38 @@
130 132 }
131 133  
132 134 /**
  135 + * Paints a PHP exception.
  136 + * @param Exception $exception Exception to display.
  137 + * @access public
  138 + */
  139 + function paintException($exception) {
  140 + parent::paintException($exception);
  141 + print "<span class=\"fail\">Exception</span>: ";
  142 + $breadcrumb = $this->getTestList();
  143 + array_shift($breadcrumb);
  144 + print implode(" -&gt; ", $breadcrumb);
  145 + $message = 'Unexpected exception of type [' . get_class($exception) .
  146 + '] with message ['. $exception->getMessage() .
  147 + '] in ['. $exception->getFile() .
  148 + ' line ' . $exception->getLine() . ']';
  149 + print " -&gt; <strong>" . $this->_htmlEntities($message) . "</strong><br />\n";
  150 + }
  151 +
  152 + /**
  153 + * Prints the message for skipping tests.
  154 + * @param string $message Text of skip condition.
  155 + * @access public
  156 + */
  157 + function paintSkip($message) {
  158 + parent::paintSkip($message);
  159 + print "<span class=\"pass\">Skipped</span>: ";
  160 + $breadcrumb = $this->getTestList();
  161 + array_shift($breadcrumb);
  162 + print implode(" -&gt; ", $breadcrumb);
  163 + print " -&gt; " . $this->_htmlEntities($message) . "<br />\n";
  164 + }
  165 +
  166 + /**
133 167 * Paints formatted text such as dumped variables.
134 168 * @param string $message Text to show.
135 169 * @access public
... ... @@ -218,7 +252,7 @@
218 252  
219 253 /**
220 254 * Paints a PHP error or exception.
221   - * @param string $message Message is ignored.
  255 + * @param string $message Message to be shown.
222 256 * @access public
223 257 * @abstract
224 258 */
... ... @@ -228,6 +262,31 @@
228 262 }
229 263  
230 264 /**
  265 + * Paints a PHP error or exception.
  266 + * @param Exception $exception Exception to describe.
  267 + * @access public
  268 + * @abstract
  269 + */
  270 + function paintException($exception) {
  271 + parent::paintException($exception);
  272 + $message = 'Unexpected exception of type [' . get_class($exception) .
  273 + '] with message ['. $exception->getMessage() .
  274 + '] in ['. $exception->getFile() .
  275 + ' line ' . $exception->getLine() . ']';
  276 + print "Exception " . $this->getExceptionCount() . "!\n$message\n";
  277 + }
  278 +
  279 + /**
  280 + * Prints the message for skipping tests.
  281 + * @param string $message Text of skip condition.
  282 + * @access public
  283 + */
  284 + function paintSkip($message) {
  285 + parent::paintSkip($message);
  286 + print "Skip: $message\n";
  287 + }
  288 +
  289 + /**
231 290 * Paints formatted text such as dumped variables.
232 291 * @param string $message Text to show.
233 292 * @access public
... ...
thirdparty/simpletest/simpletest/scorer.php
... ... @@ -149,8 +149,7 @@
149 149 }
150 150  
151 151 /**
152   - * Deals with PHP 4 throwing an error or PHP 5
153   - * throwing an exception.
  152 + * Deals with PHP 4 throwing an error.
154 153 * @param string $message Text of error formatted by
155 154 * the test case.
156 155 * @access public
... ... @@ -160,6 +159,23 @@
160 159 }
161 160  
162 161 /**
  162 + * Deals with PHP 5 throwing an exception.
  163 + * @param Exception $exception The actual exception thrown.
  164 + * @access public
  165 + */
  166 + function paintException($exception) {
  167 + $this->_exceptions++;
  168 + }
  169 +
  170 + /**
  171 + * Prints the message for skipping tests.
  172 + * @param string $message Text of skip condition.
  173 + * @access public
  174 + */
  175 + function paintSkip($message) {
  176 + }
  177 +
  178 + /**
163 179 * Accessor for the number of passes so far.
164 180 * @return integer Number of passes.
165 181 * @access public
... ... @@ -237,6 +253,16 @@
237 253 $this->_size = null;
238 254 $this->_progress = 0;
239 255 }
  256 +
  257 + /**
  258 + * Gets the formatter for variables and other small
  259 + * generic data items.
  260 + * @return SimpleDumper Formatter.
  261 + * @access public
  262 + */
  263 + function getDumper() {
  264 + return new SimpleDumper();
  265 + }
240 266  
241 267 /**
242 268 * Paints the start of a group test. Will also paint
... ... @@ -393,7 +419,7 @@
393 419 var $_reporter;
394 420  
395 421 /**
396   - * Mediates between teh reporter and the test case.
  422 + * Mediates between the reporter and the test case.
397 423 * @param SimpleScorer $reporter Reporter to receive events.
398 424 */
399 425 function SimpleReporterDecorator(&$reporter) {
... ... @@ -443,6 +469,16 @@
443 469 function &createInvoker(&$invoker) {
444 470 return $this->_reporter->createInvoker($invoker);
445 471 }
  472 +
  473 + /**
  474 + * Gets the formatter for variables and other small
  475 + * generic data items.
  476 + * @return SimpleDumper Formatter.
  477 + * @access public
  478 + */
  479 + function getDumper() {
  480 + return $this->_reporter->getDumper();
  481 + }
446 482  
447 483 /**
448 484 * Paints the start of a group test.
... ... @@ -529,6 +565,24 @@
529 565  
530 566 /**
531 567 * Chains to the wrapped reporter.
  568 + * @param Exception $exception Exception to show.
  569 + * @access public
  570 + */
  571 + function paintException($exception) {
  572 + $this->_reporter->paintException($exception);
  573 + }
  574 +
  575 + /**
  576 + * Prints the message for skipping tests.
  577 + * @param string $message Text of skip condition.
  578 + * @access public
  579 + */
  580 + function paintSkip($message) {
  581 + $this->_reporter->paintSkip($message);
  582 + }
  583 +
  584 + /**
  585 + * Chains to the wrapped reporter.
532 586 * @param string $message Text to display.
533 587 * @access public
534 588 */
... ... @@ -635,6 +689,16 @@
635 689 }
636 690 return $invoker;
637 691 }
  692 +
  693 + /**
  694 + * Gets the formatter for variables and other small
  695 + * generic data items.
  696 + * @return SimpleDumper Formatter.
  697 + * @access public
  698 + */
  699 + function getDumper() {
  700 + return new SimpleDumper();
  701 + }
638 702  
639 703 /**
640 704 * Paints the start of a group test.
... ... @@ -736,6 +800,28 @@
736 800 $this->_reporters[$i]->paintError($message);
737 801 }
738 802 }
  803 +
  804 + /**
  805 + * Chains to the wrapped reporter.
  806 + * @param Exception $exception Exception to display.
  807 + * @access public
  808 + */
  809 + function paintException($exception) {
  810 + for ($i = 0; $i < count($this->_reporters); $i++) {
  811 + $this->_reporters[$i]->paintException($exception);
  812 + }
  813 + }
  814 +
  815 + /**
  816 + * Prints the message for skipping tests.
  817 + * @param string $message Text of skip condition.
  818 + * @access public
  819 + */
  820 + function paintSkip($message) {
  821 + for ($i = 0; $i < count($this->_reporters); $i++) {
  822 + $this->_reporters[$i]->paintSkip($message);
  823 + }
  824 + }
739 825  
740 826 /**
741 827 * Chains to the wrapped reporter.
... ...
thirdparty/simpletest/simpletest/shell_tester.php
... ... @@ -63,7 +63,7 @@
63 63  
64 64 /**
65 65 * Test case for testing of command line scripts and
66   - * utilities. Usually scripts taht are external to the
  66 + * utilities. Usually scripts that are external to the
67 67 * PHP code, but support it in some way.
68 68 * @package SimpleTest
69 69 * @subpackage UnitTester
... ... @@ -127,6 +127,33 @@
127 127 $shell = &$this->_getShell();
128 128 return $shell->getOutputAsList();
129 129 }
  130 +
  131 + /**
  132 + * Called from within the test methods to register
  133 + * passes and failures.
  134 + * @param boolean $result Pass on true.
  135 + * @param string $message Message to display describing
  136 + * the test state.
  137 + * @return boolean True on pass
  138 + * @access public
  139 + */
  140 + function assertTrue($result, $message = false) {
  141 + return $this->assert(new TrueExpectation(), $result, $message);
  142 + }
  143 +
  144 + /**
  145 + * Will be true on false and vice versa. False
  146 + * is the PHP definition of false, so that null,
  147 + * empty strings, zero and an empty array all count
  148 + * as false.
  149 + * @param boolean $result Pass on false.
  150 + * @param string $message Message to display.
  151 + * @return boolean True on pass
  152 + * @access public
  153 + */
  154 + function assertFalse($result, $message = '%s') {
  155 + return $this->assert(new FalseExpectation(), $result, $message);
  156 + }
130 157  
131 158 /**
132 159 * Will trigger a pass if the two parameters have
... ... @@ -303,4 +330,4 @@
303 330 return $shell;
304 331 }
305 332 }
306   -?>
307 333 \ No newline at end of file
  334 +?>
... ...
thirdparty/simpletest/simpletest/simpletest.php
... ... @@ -17,10 +17,8 @@
17 17 /**#@-*/
18 18  
19 19 /**
20   - * Static global directives and options. I hate this
21   - * class. It's a mixture of reference hacks, configuration
22   - * and previous design screw-ups that I have to maintain
23   - * to keep backward compatibility.
  20 + * Registry and test context. Includes a few
  21 + * global options that I'm slowly getting rid of.
24 22 * @package SimpleTest
25 23 */
26 24 class SimpleTest {
... ... @@ -59,8 +57,8 @@
59 57 * missing abstract declarations. This cannot
60 58 * be done whilst loading classes wiithout forcing
61 59 * a particular order on the class declarations and
62   - * the ignore() calls. It's nice to havethe ignore()
63   - * calls at the top of teh file.
  60 + * the ignore() calls. It's just nice to have the ignore()
  61 + * calls at the top of the file before the actual declarations.
64 62 * @param array $classes Class names of interest.
65 63 * @static
66 64 * @access public
... ... @@ -157,30 +155,6 @@
157 155 }
158 156  
159 157 /**
160   - * Sets the current test case instance. This
161   - * global instance can be used by the mock objects
162   - * to send message to the test cases.
163   - * @param SimpleTestCase $test Test case to register.
164   - * @access public
165   - * @static
166   - */
167   - function setCurrent(&$test) {
168   - $registry = &SimpleTest::_getRegistry();
169   - $registry['CurrentTestCase'] = &$test;
170   - }
171   -
172   - /**
173   - * Accessor for current test instance.
174   - * @return SimpleTEstCase Currently running test.
175   - * @access public
176   - * @static
177   - */
178   - function &getCurrent() {
179   - $registry = &SimpleTest::_getRegistry();
180   - return $registry['CurrentTestCase'];
181   - }
182   -
183   - /**
184 158 * Accessor for global registry of options.
185 159 * @return hash All stored values.
186 160 * @access private
... ... @@ -195,6 +169,21 @@
195 169 }
196 170  
197 171 /**
  172 + * Accessor for the context of the current
  173 + * test run.
  174 + * @return SimpleTestContext Current test run.
  175 + * @access public
  176 + * @static
  177 + */
  178 + function &getContext() {
  179 + static $context = false;
  180 + if (! $context) {
  181 + $context = new SimpleTestContext();
  182 + }
  183 + return $context;
  184 + }
  185 +
  186 + /**
198 187 * Constant default values.
199 188 * @return hash All registry defaults.
200 189 * @access private
... ... @@ -212,6 +201,168 @@
212 201 }
213 202  
214 203 /**
  204 + * Container for all components for a specific
  205 + * test run. Makes things like error queues
  206 + * available to PHP event handlers, and also
  207 + * gets around some nasty reference issues in
  208 + * the mocks.
  209 + * @package SimpleTest
  210 + */
  211 + class SimpleTestContext {
  212 + var $_test;
  213 + var $_reporter;
  214 + var $_resources;
  215 +
  216 + /**
  217 + * Clears down the current context.
  218 + * @access public
  219 + */
  220 + function clear() {
  221 + $this->_resources = array();
  222 + }
  223 +
  224 + /**
  225 + * Sets the current test case instance. This
  226 + * global instance can be used by the mock objects
  227 + * to send message to the test cases.
  228 + * @param SimpleTestCase $test Test case to register.
  229 + * @access public
  230 + */
  231 + function setTest(&$test) {
  232 + $this->clear();
  233 + $this->_test = &$test;
  234 + }
  235 +
  236 + /**
  237 + * Accessor for currently running test case.
  238 + * @return SimpleTestCase Current test.
  239 + * @acess pubic
  240 + */
  241 + function &getTest() {
  242 + return $this->_test;
  243 + }
  244 +
  245 + /**
  246 + * Sets the current reporter. This
  247 + * global instance can be used by the mock objects
  248 + * to send messages.
  249 + * @param SimpleReporter $reporter Reporter to register.
  250 + * @access public
  251 + */
  252 + function setReporter(&$reporter) {
  253 + $this->clear();
  254 + $this->_reporter = &$reporter;
  255 + }
  256 +
  257 + /**
  258 + * Accessor for current reporter.
  259 + * @return SimpleReporter Current reporter.
  260 + * @acess pubic
  261 + */
  262 + function &getReporter() {
  263 + return $this->_reporter;
  264 + }
  265 +
  266 + /**
  267 + * Accessor for the Singleton resource.
  268 + * @return object Global resource.
  269 + * @access public
  270 + * @static
  271 + */
  272 + function &get($resource) {
  273 + if (! isset($this->_resources[$resource])) {
  274 + $this->_resources[$resource] = &new $resource();
  275 + }
  276 + return $this->_resources[$resource];
  277 + }
  278 + }
  279 +
  280 + /**
  281 + * Interrogates the stack trace to recover the
  282 + * failure point.
  283 + * @package SimpleTest
  284 + * @subpackage UnitTester
  285 + */
  286 + class SimpleStackTrace {
  287 + var $_prefixes;
  288 +
  289 + /**
  290 + * Stashes the list of target prefixes.
  291 + * @param array $prefixes List of method prefixes
  292 + * to search for.
  293 + */
  294 + function SimpleStackTrace($prefixes) {
  295 + $this->_prefixes = $prefixes;
  296 + }
  297 +
  298 + /**
  299 + * Extracts the last method name that was not within
  300 + * Simpletest itself. Captures a stack trace if none given.
  301 + * @param array $stack List of stack frames.
  302 + * @return string Snippet of test report with line
  303 + * number and file.
  304 + * @access public
  305 + */
  306 + function traceMethod($stack = false) {
  307 + $stack = $stack ? $stack : $this->_captureTrace();
  308 + foreach ($stack as $frame) {
  309 + if ($this->_frameLiesWithinSimpleTestFolder($frame)) {
  310 + continue;
  311 + }
  312 + if ($this->_frameMatchesPrefix($frame)) {
  313 + return ' at [' . $frame['file'] . ' line ' . $frame['line'] . ']';
  314 + }
  315 + }
  316 + return '';
  317 + }
  318 +
  319 + /**
  320 + * Test to see if error is generated by SimpleTest itself.
  321 + * @param array $frame PHP stack frame.
  322 + * @return boolean True if a SimpleTest file.
  323 + * @access private
  324 + */
  325 + function _frameLiesWithinSimpleTestFolder($frame) {
  326 + if (isset($frame['file'])) {
  327 + $path = substr(SIMPLE_TEST, 0, -1);
  328 + if (strpos($frame['file'], $path) === 0) {
  329 + if (dirname($frame['file']) == $path) {
  330 + return true;
  331 + }
  332 + }
  333 + }
  334 + return false;
  335 + }
  336 +
  337 + /**
  338 + * Tries to determine if the method call is an assert, etc.
  339 + * @param array $frame PHP stack frame.
  340 + * @return boolean True if matches a target.
  341 + * @access private
  342 + */
  343 + function _frameMatchesPrefix($frame) {
  344 + foreach ($this->_prefixes as $prefix) {
  345 + if (strncmp($frame['function'], $prefix, strlen($prefix)) == 0) {
  346 + return true;
  347 + }
  348 + }
  349 + return false;
  350 + }
  351 +
  352 + /**
  353 + * Grabs a current stack trace.
  354 + * @return array Fulle trace.
  355 + * @access private
  356 + */
  357 + function _captureTrace() {
  358 + if (function_exists('debug_backtrace')) {
  359 + return array_reverse(debug_backtrace());
  360 + }
  361 + return array();
  362 + }
  363 + }
  364 +
  365 + /**
215 366 * @deprecated
216 367 */
217 368 class SimpleTestOptions extends SimpleTest {
... ... @@ -279,4 +430,4 @@
279 430 return Simpletest::getDefaultProxyPassword();
280 431 }
281 432 }
282   -?>
283 433 \ No newline at end of file
  434 +?>
... ...
thirdparty/simpletest/simpletest/test_case.php
... ... @@ -24,10 +24,8 @@
24 24 require_once(dirname(__FILE__) . '/reflection_php4.php');
25 25 }
26 26 if (! defined('SIMPLE_TEST')) {
27   - /**
28   - * @ignore
29   - */
30   - define('SIMPLE_TEST', dirname(__FILE__) . '/');
  27 + /** @ignore */
  28 + define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR);
31 29 }
32 30 /**#@-*/
33 31  
... ... @@ -43,6 +41,7 @@
43 41 var $_label = false;
44 42 var $_reporter;
45 43 var $_observers;
  44 + var $_should_skip = false;
46 45  
47 46 /**
48 47 * Sets up the test with no display.
... ... @@ -66,6 +65,41 @@
66 65 }
67 66  
68 67 /**
  68 + * This is a placeholder for skipping tests. In this
  69 + * method you place skipIf() and skipUnless() calls to
  70 + * set the skipping state.
  71 + * @access public
  72 + */
  73 + function skip() {
  74 + }
  75 +
  76 + /**
  77 + * Will issue a message to the reporter and tell the test
  78 + * case to skip if the incoming flag is true.
  79 + * @param string $should_skip Condition causing the tests to be skipped.
  80 + * @param string $message Text of skip condition.
  81 + * @access public
  82 + */
  83 + function skipIf($should_skip, $message = '%s') {
  84 + if ($should_skip && ! $this->_should_skip) {
  85 + $this->_should_skip = true;
  86 + $message = sprintf($message, 'Skipping [' . get_class($this) . ']');
  87 + $this->_reporter->paintSkip($message . $this->getAssertionLine());
  88 + }
  89 + }
  90 +
  91 + /**
  92 + * Will issue a message to the reporter and tell the test
  93 + * case to skip if the incoming flag is false.
  94 + * @param string $shouldnt_skip Condition causing the tests to be run.
  95 + * @param string $message Text of skip condition.
  96 + * @access public
  97 + */
  98 + function skipUnless($shouldnt_skip, $message = false) {
  99 + $this->skipIf(! $shouldnt_skip, $message);
  100 + }
  101 +
  102 + /**
69 103 * Used to invoke the single tests.
70 104 * @return SimpleInvoker Individual test runner.
71 105 * @access public
... ... @@ -83,21 +117,27 @@
83 117 * starting with the string "test" unless a method
84 118 * is specified.
85 119 * @param SimpleReporter $reporter Current test reporter.
  120 + * @return boolean True if all tests passed.
86 121 * @access public
87 122 */
88 123 function run(&$reporter) {
89   - SimpleTest::setCurrent($this);
  124 + $context = &SimpleTest::getContext();
  125 + $context->setTest($this);
  126 + $context->setReporter($reporter);
90 127 $this->_reporter = &$reporter;
91   - $this->_reporter->paintCaseStart($this->getLabel());
92   - foreach ($this->getTests() as $method) {
93   - if ($this->_reporter->shouldInvoke($this->getLabel(), $method)) {
94   - $invoker = &$this->_reporter->createInvoker($this->createInvoker());
95   - $invoker->before($method);
96   - $invoker->invoke($method);
97   - $invoker->after($method);
  128 + $reporter->paintCaseStart($this->getLabel());
  129 + $this->skip();
  130 + if (! $this->_should_skip) {
  131 + foreach ($this->getTests() as $method) {
  132 + if ($reporter->shouldInvoke($this->getLabel(), $method)) {
  133 + $invoker = &$this->_reporter->createInvoker($this->createInvoker());
  134 + $invoker->before($method);
  135 + $invoker->invoke($method);
  136 + $invoker->after($method);
  137 + }
98 138 }
99 139 }
100   - $this->_reporter->paintCaseEnd($this->getLabel());
  140 + $reporter->paintCaseEnd($this->getLabel());
101 141 unset($this->_reporter);
102 142 return $reporter->getStatus();
103 143 }
... ... @@ -169,7 +209,7 @@
169 209 */
170 210 function after($method) {
171 211 for ($i = 0; $i < count($this->_observers); $i++) {
172   - $this->_observers[$i]->atTestEnd($method);
  212 + $this->_observers[$i]->atTestEnd($method, $this);
173 213 }
174 214 $this->_reporter->paintMethodEnd($method);
175 215 }
... ... @@ -185,9 +225,7 @@
185 225 }
186 226  
187 227 /**
188   - * Sends a pass event with a message.
189   - * @param string $message Message to send.
190   - * @access public
  228 + * @deprecated
191 229 */
192 230 function pass($message = "Pass") {
193 231 if (! isset($this->_reporter)) {
... ... @@ -226,7 +264,7 @@
226 264 trigger_error('Can only make assertions within test methods');
227 265 }
228 266 $this->_reporter->paintError(
229   - "Unexpected PHP error [$message] severity [$severity] in [$file] line [$line]");
  267 + "Unexpected PHP error [$message] severity [$severity] in [$file line $line]");
230 268 }
231 269  
232 270 /**
... ... @@ -236,21 +274,11 @@
236 274 * @access public
237 275 */
238 276 function exception($exception) {
239   - $this->_reporter->paintError(
240   - 'Unexpected exception of type [' . get_class($exception) .
241   - '] with message ['. $exception->getMessage() .
242   - '] in ['. $exception->getFile() .
243   - '] line [' . $exception->getLine() . ']');
  277 + $this->_reporter->paintException($exception);
244 278 }
245 279  
246 280 /**
247   - * Sends a user defined event to the test reporter.
248   - * This is for small scale extension where
249   - * both the test case and either the reporter or
250   - * display are subclassed.
251   - * @param string $type Type of event.
252   - * @param mixed $payload Object or message to deliver.
253   - * @access public
  281 + * @deprecated
254 282 */
255 283 function signal($type, &$payload) {
256 284 if (! isset($this->_reporter)) {
... ... @@ -260,15 +288,6 @@
260 288 }
261 289  
262 290 /**
263   - * Cancels any outstanding errors.
264   - * @access public
265   - */
266   - function swallowErrors() {
267   - $queue = &SimpleErrorQueue::instance();
268   - $queue->clear();
269   - }
270   -
271   - /**
272 291 * Runs an expectation directly, for extending the
273 292 * tests with new expectation classes.
274 293 * @param SimpleExpectation $expectation Expectation subclass.
... ... @@ -278,9 +297,15 @@
278 297 * @access public
279 298 */
280 299 function assert(&$expectation, $compare, $message = '%s') {
281   - return $this->assertTrue(
282   - $expectation->test($compare),
283   - sprintf($message, $expectation->overlayMessage($compare)));
  300 + if ($expectation->test($compare)) {
  301 + return $this->pass(sprintf(
  302 + $message,
  303 + $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
  304 + } else {
  305 + return $this->fail(sprintf(
  306 + $message,
  307 + $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
  308 + }
284 309 }
285 310  
286 311 /**
... ... @@ -291,57 +316,14 @@
291 316 }
292 317  
293 318 /**
294   - * Called from within the test methods to register
295   - * passes and failures.
296   - * @param boolean $result Pass on true.
297   - * @param string $message Message to display describing
298   - * the test state.
299   - * @return boolean True on pass
300   - * @access public
301   - */
302   - function assertTrue($result, $message = false) {
303   - if (! $message) {
304   - $message = 'True assertion got ' . ($result ? 'True' : 'False');
305   - }
306   - if ($result) {
307   - return $this->pass($message);
308   - } else {
309   - return $this->fail($message);
310   - }
311   - }
312   -
313   - /**
314   - * Will be true on false and vice versa. False
315   - * is the PHP definition of false, so that null,
316   - * empty strings, zero and an empty array all count
317   - * as false.
318   - * @param boolean $result Pass on false.
319   - * @param string $message Message to display.
320   - * @return boolean True on pass
321   - * @access public
322   - */
323   - function assertFalse($result, $message = false) {
324   - if (! $message) {
325   - $message = 'False assertion got ' . ($result ? 'True' : 'False');
326   - }
327   - return $this->assertTrue(! $result, $message);
328   - }
329   -
330   - /**
331 319 * Uses a stack trace to find the line of an assertion.
332   - * @param string $format String formatting.
333   - * @param array $stack Stack frames top most first. Only
334   - * needed if not using the PHP
335   - * backtrace function.
336 320 * @return string Line number of first assert*
337 321 * method embedded in format string.
338 322 * @access public
339 323 */
340   - function getAssertionLine($stack = false) {
341   - if ($stack === false) {
342   - $stack = SimpleTestCompatibility::getStackTrace();
343   - }
344   - return SimpleDumper::getFormattedAssertionLine($stack);
  324 + function getAssertionLine() {
  325 + $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip'));
  326 + return $trace->traceMethod();
345 327 }
346 328  
347 329 /**
... ... @@ -354,7 +336,8 @@
354 336 * @access public
355 337 */
356 338 function dump($variable, $message = false) {
357   - $formatted = SimpleDumper::dump($variable);
  339 + $dumper = $this->_reporter->getDumper();
  340 + $formatted = $dumper->dump($variable);
358 341 if ($message) {
359 342 $formatted = $message . "\n" . $formatted;
360 343 }
... ... @@ -363,10 +346,7 @@
363 346 }
364 347  
365 348 /**
366   - * Dispatches a text message straight to the
367   - * test suite. Useful for status bar displays.
368   - * @param string $message Message to show.
369   - * @access public
  349 + * @deprecated
370 350 */
371 351 function sendMessage($message) {
372 352 $this->_reporter->PaintMessage($message);
... ... @@ -390,7 +370,7 @@
390 370 * @package SimpleTest
391 371 * @subpackage UnitTester
392 372 */
393   - class GroupTest {
  373 + class TestSuite {
394 374 var $_label;
395 375 var $_test_cases;
396 376 var $_old_track_errors;
... ... @@ -402,7 +382,7 @@
402 382 * of the test.
403 383 * @access public
404 384 */
405   - function GroupTest($label = false) {
  385 + function TestSuite($label = false) {
406 386 $this->_label = $label ? $label : get_class($this);
407 387 $this->_test_cases = array();
408 388 $this->_old_track_errors = ini_get('track_errors');
... ... @@ -440,7 +420,7 @@
440 420 * @access public
441 421 */
442 422 function addTestClass($class) {
443   - if ($this->_getBaseTestCase($class) == 'grouptest') {
  423 + if ($this->_getBaseTestCase($class) == 'testsuite' || $this->_getBaseTestCase($class) == 'grouptest') {
444 424 $this->_test_cases[] = &new $class();
445 425 } else {
446 426 $this->_test_cases[] = $class;
... ... @@ -457,12 +437,12 @@
457 437 function addTestFile($test_file) {
458 438 $existing_classes = get_declared_classes();
459 439 if ($error = $this->_requireWithError($test_file)) {
460   - $this->addTestCase(new BadGroupTest($test_file, $error));
  440 + $this->addTestCase(new BadTestSuite($test_file, $error));
461 441 return;
462 442 }
463 443 $classes = $this->_selectRunnableTests($existing_classes, get_declared_classes());
464 444 if (count($classes) == 0) {
465   - $this->addTestCase(new BadGroupTest($test_file, "No runnable test cases in [$test_file]"));
  445 + $this->addTestCase(new BadTestSuite($test_file, "No runnable test cases in [$test_file]"));
466 446 return;
467 447 }
468 448 $group = &$this->_createGroupFromClasses($test_file, $classes);
... ... @@ -482,11 +462,14 @@
482 462 $error = isset($php_errormsg) ? $php_errormsg : false;
483 463 $this->_disableErrorReporting();
484 464 $self_inflicted_errors = array(
485   - 'Assigning the return value of new by reference is deprecated',
486   - 'var: Deprecated. Please use the public/private/protected modifiers');
487   - if (in_array($error, $self_inflicted_errors)) {
488   - return false;
489   - }
  465 + '/Assigning the return value of new by reference/i',
  466 + '/var: Deprecated/i',
  467 + '/Non-static method/i');
  468 + foreach ($self_inflicted_errors as $pattern) {
  469 + if (preg_match($pattern, $error)) {
  470 + return false;
  471 + }
  472 + }
490 473 return $error;
491 474 }
492 475  
... ... @@ -548,13 +531,13 @@
548 531 * Builds a group test from a class list.
549 532 * @param string $title Title of new group.
550 533 * @param array $classes Test classes.
551   - * @return GroupTest Group loaded with the new
  534 + * @return TestSuite Group loaded with the new
552 535 * test cases.
553 536 * @access private
554 537 */
555 538 function &_createGroupFromClasses($title, $classes) {
556 539 SimpleTest::ignoreParentsIfIgnored($classes);
557   - $group = &new GroupTest($title);
  540 + $group = &new TestSuite($title);
558 541 foreach ($classes as $class) {
559 542 if (! SimpleTest::isIgnored($class)) {
560 543 $group->addTestClass($class);
... ... @@ -572,7 +555,7 @@
572 555 function _getBaseTestCase($class) {
573 556 while ($class = get_parent_class($class)) {
574 557 $class = strtolower($class);
575   - if ($class == "simpletestcase" || $class == "grouptest") {
  558 + if ($class == 'simpletestcase' || $class == 'testsuite' || $class == 'grouptest') {
576 559 return $class;
577 560 }
578 561 }
... ... @@ -603,6 +586,7 @@
603 586 $class = $this->_test_cases[$i];
604 587 $test = &new $class();
605 588 $test->run($reporter);
  589 + unset($test);
606 590 } else {
607 591 $this->_test_cases[$i]->run($reporter);
608 592 }
... ... @@ -628,6 +612,11 @@
628 612 return $count;
629 613 }
630 614 }
  615 +
  616 + /**
  617 + * @deprecated
  618 + */
  619 + class GroupTest extends TestSuite { }
631 620  
632 621 /**
633 622 * This is a failing group test for when a test suite hasn't
... ... @@ -635,7 +624,7 @@
635 624 * @package SimpleTest
636 625 * @subpackage UnitTester
637 626 */
638   - class BadGroupTest {
  627 + class BadTestSuite {
639 628 var $_label;
640 629 var $_error;
641 630  
... ... @@ -645,7 +634,7 @@
645 634 * of the test.
646 635 * @access public
647 636 */
648   - function BadGroupTest($label, $error) {
  637 + function BadTestSuite($label, $error) {
649 638 $this->_label = $label;
650 639 $this->_error = $error;
651 640 }
... ... @@ -666,7 +655,7 @@
666 655 */
667 656 function run(&$reporter) {
668 657 $reporter->paintGroupStart($this->getLabel(), $this->getSize());
669   - $reporter->paintFail('Bad GroupTest [' . $this->getLabel() .
  658 + $reporter->paintFail('Bad TestSuite [' . $this->getLabel() .
670 659 '] with error [' . $this->_error . ']');
671 660 $reporter->paintGroupEnd($this->getLabel());
672 661 return $reporter->getStatus();
... ... @@ -681,4 +670,9 @@
681 670 return 0;
682 671 }
683 672 }
684   -?>
  673 +
  674 + /**
  675 + * @deprecated
  676 + */
  677 + class BadGroupTest extends BadTestSuite { }
  678 +?>
685 679 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/unit_tester.php
... ... @@ -37,17 +37,44 @@
37 37 }
38 38  
39 39 /**
  40 + * Called from within the test methods to register
  41 + * passes and failures.
  42 + * @param boolean $result Pass on true.
  43 + * @param string $message Message to display describing
  44 + * the test state.
  45 + * @return boolean True on pass
  46 + * @access public
  47 + */
  48 + function assertTrue($result, $message = false) {
  49 + return $this->assert(new TrueExpectation(), $result, $message);
  50 + }
  51 +
  52 + /**
  53 + * Will be true on false and vice versa. False
  54 + * is the PHP definition of false, so that null,
  55 + * empty strings, zero and an empty array all count
  56 + * as false.
  57 + * @param boolean $result Pass on false.
  58 + * @param string $message Message to display.
  59 + * @return boolean True on pass
  60 + * @access public
  61 + */
  62 + function assertFalse($result, $message = '%s') {
  63 + return $this->assert(new FalseExpectation(), $result, $message);
  64 + }
  65 +
  66 + /**
40 67 * Will be true if the value is null.
41 68 * @param null $value Supposedly null value.
42 69 * @param string $message Message to display.
43 70 * @return boolean True on pass
44 71 * @access public
45 72 */
46   - function assertNull($value, $message = "%s") {
  73 + function assertNull($value, $message = '%s') {
47 74 $dumper = &new SimpleDumper();
48 75 $message = sprintf(
49 76 $message,
50   - "[" . $dumper->describeValue($value) . "] should be null");
  77 + '[' . $dumper->describeValue($value) . '] should be null');
51 78 return $this->assertTrue(! isset($value), $message);
52 79 }
53 80  
... ... @@ -58,11 +85,11 @@
58 85 * @return boolean True on pass.
59 86 * @access public
60 87 */
61   - function assertNotNull($value, $message = "%s") {
  88 + function assertNotNull($value, $message = '%s') {
62 89 $dumper = &new SimpleDumper();
63 90 $message = sprintf(
64 91 $message,
65   - "[" . $dumper->describeValue($value) . "] should not be null");
  92 + '[' . $dumper->describeValue($value) . '] should not be null');
66 93 return $this->assertTrue(isset($value), $message);
67 94 }
68 95  
... ... @@ -76,7 +103,7 @@
76 103 * @return boolean True on pass.
77 104 * @access public
78 105 */
79   - function assertIsA($object, $type, $message = "%s") {
  106 + function assertIsA($object, $type, $message = '%s') {
80 107 return $this->assert(
81 108 new IsAExpectation($type),
82 109 $object,
... ... @@ -93,7 +120,7 @@
93 120 * @return boolean True on pass.
94 121 * @access public
95 122 */
96   - function assertNotA($object, $type, $message = "%s") {
  123 + function assertNotA($object, $type, $message = '%s') {
97 124 return $this->assert(
98 125 new NotAExpectation($type),
99 126 $object,
... ... @@ -109,7 +136,7 @@
109 136 * @return boolean True on pass
110 137 * @access public
111 138 */
112   - function assertEqual($first, $second, $message = "%s") {
  139 + function assertEqual($first, $second, $message = '%s') {
113 140 return $this->assert(
114 141 new EqualExpectation($first),
115 142 $second,
... ... @@ -125,7 +152,7 @@
125 152 * @return boolean True on pass
126 153 * @access public
127 154 */
128   - function assertNotEqual($first, $second, $message = "%s") {
  155 + function assertNotEqual($first, $second, $message = '%s') {
129 156 return $this->assert(
130 157 new NotEqualExpectation($first),
131 158 $second,
... ... @@ -142,7 +169,7 @@
142 169 * @return boolean True on pass
143 170 * @access public
144 171 */
145   - function assertWithinMargin($first, $second, $margin, $message = "%s") {
  172 + function assertWithinMargin($first, $second, $margin, $message = '%s') {
146 173 return $this->assert(
147 174 new WithinMarginExpectation($first, $margin),
148 175 $second,
... ... @@ -159,7 +186,7 @@
159 186 * @return boolean True on pass
160 187 * @access public
161 188 */
162   - function assertOutsideMargin($first, $second, $margin, $message = "%s") {
  189 + function assertOutsideMargin($first, $second, $margin, $message = '%s') {
163 190 return $this->assert(
164 191 new OutsideMarginExpectation($first, $margin),
165 192 $second,
... ... @@ -175,7 +202,7 @@
175 202 * @return boolean True on pass
176 203 * @access public
177 204 */
178   - function assertIdentical($first, $second, $message = "%s") {
  205 + function assertIdentical($first, $second, $message = '%s') {
179 206 return $this->assert(
180 207 new IdenticalExpectation($first),
181 208 $second,
... ... @@ -191,7 +218,7 @@
191 218 * @return boolean True on pass
192 219 * @access public
193 220 */
194   - function assertNotIdentical($first, $second, $message = "%s") {
  221 + function assertNotIdentical($first, $second, $message = '%s') {
195 222 return $this->assert(
196 223 new NotIdenticalExpectation($first),
197 224 $second,
... ... @@ -207,13 +234,13 @@
207 234 * @return boolean True on pass
208 235 * @access public
209 236 */
210   - function assertReference(&$first, &$second, $message = "%s") {
  237 + function assertReference(&$first, &$second, $message = '%s') {
211 238 $dumper = &new SimpleDumper();
212 239 $message = sprintf(
213 240 $message,
214   - "[" . $dumper->describeValue($first) .
215   - "] and [" . $dumper->describeValue($second) .
216   - "] should reference the same object");
  241 + '[' . $dumper->describeValue($first) .
  242 + '] and [' . $dumper->describeValue($second) .
  243 + '] should reference the same object');
217 244 return $this->assertTrue(
218 245 SimpleTestCompatibility::isReference($first, $second),
219 246 $message);
... ... @@ -229,13 +256,13 @@
229 256 * @return boolean True on pass
230 257 * @access public
231 258 */
232   - function assertClone(&$first, &$second, $message = "%s") {
  259 + function assertClone(&$first, &$second, $message = '%s') {
233 260 $dumper = &new SimpleDumper();
234 261 $message = sprintf(
235 262 $message,
236   - "[" . $dumper->describeValue($first) .
237   - "] and [" . $dumper->describeValue($second) .
238   - "] should not be the same object");
  263 + '[' . $dumper->describeValue($first) .
  264 + '] and [' . $dumper->describeValue($second) .
  265 + '] should not be the same object');
239 266 $identical = &new IdenticalExpectation($first);
240 267 return $this->assertTrue(
241 268 $identical->test($second) &&
... ... @@ -268,7 +295,7 @@
268 295 * @return boolean True on pass
269 296 * @access public
270 297 */
271   - function assertPattern($pattern, $subject, $message = "%s") {
  298 + function assertPattern($pattern, $subject, $message = '%s') {
272 299 return $this->assert(
273 300 new PatternExpectation($pattern),
274 301 $subject,
... ... @@ -278,7 +305,7 @@
278 305 /**
279 306 * @deprecated
280 307 */
281   - function assertWantedPattern($pattern, $subject, $message = "%s") {
  308 + function assertWantedPattern($pattern, $subject, $message = '%s') {
282 309 return $this->assertPattern($pattern, $subject, $message);
283 310 }
284 311  
... ... @@ -292,7 +319,7 @@
292 319 * @return boolean True on pass
293 320 * @access public
294 321 */
295   - function assertNoPattern($pattern, $subject, $message = "%s") {
  322 + function assertNoPattern($pattern, $subject, $message = '%s') {
296 323 return $this->assert(
297 324 new NoPatternExpectation($pattern),
298 325 $subject,
... ... @@ -302,50 +329,63 @@
302 329 /**
303 330 * @deprecated
304 331 */
305   - function assertNoUnwantedPattern($pattern, $subject, $message = "%s") {
  332 + function assertNoUnwantedPattern($pattern, $subject, $message = '%s') {
306 333 return $this->assertNoPattern($pattern, $subject, $message);
307 334 }
308 335  
309 336 /**
310   - * Confirms that no errors have occoured so
311   - * far in the test method.
312   - * @param string $message Message to display.
313   - * @return boolean True on pass
  337 + * @deprecated
  338 + */
  339 + function swallowErrors() {
  340 + $context = &SimpleTest::getContext();
  341 + $queue = &$context->get('SimpleErrorQueue');
  342 + $queue->clear();
  343 + }
  344 +
  345 + /**
  346 + * @deprecated
  347 + */
  348 + function assertNoErrors($message = '%s') {
  349 + $context = &SimpleTest::getContext();
  350 + $queue = &$context->get('SimpleErrorQueue');
  351 + return $queue->assertNoErrors($message);
  352 + }
  353 +
  354 + /**
  355 + * @deprecated
  356 + */
  357 + function assertError($expected = false, $message = '%s') {
  358 + $context = &SimpleTest::getContext();
  359 + $queue = &$context->get('SimpleErrorQueue');
  360 + return $queue->assertError($this->_coerceExpectation($expected), $message);
  361 + }
  362 +
  363 + /**
  364 + * Prepares for an error. If the error mismatches it
  365 + * passes through, otherwise it is swallowed. Any
  366 + * left over errors trigger failures.
  367 + * @param SimpleExpectation/string $expected The error to match.
  368 + * @param string $message Message on failure.
314 369 * @access public
315 370 */
316   - function assertNoErrors($message = "%s") {
317   - $queue = &SimpleErrorQueue::instance();
318   - return $this->assertTrue(
319   - $queue->isEmpty(),
320   - sprintf($message, "Should be no errors"));
  371 + function expectError($expected = false, $message = '%s') {
  372 + $context = &SimpleTest::getContext();
  373 + $queue = &$context->get('SimpleErrorQueue');
  374 + $queue->expectError($this->_coerceExpectation($expected), $message);
321 375 }
322 376  
323 377 /**
324   - * Confirms that an error has occoured and
325   - * optionally that the error text matches exactly.
326   - * @param string $expected Expected error text or
327   - * false for no check.
328   - * @param string $message Message to display.
329   - * @return boolean True on pass
  378 + * Prepares for an exception. If the error mismatches it
  379 + * passes through, otherwise it is swallowed. Any
  380 + * left over errors trigger failures.
  381 + * @param SimpleExpectation/Exception $expected The error to match.
  382 + * @param string $message Message on failure.
330 383 * @access public
331 384 */
332   - function assertError($expected = false, $message = "%s") {
333   - $queue = &SimpleErrorQueue::instance();
334   - if ($queue->isEmpty()) {
335   - $this->fail(sprintf($message, "Expected error not found"));
336   - return;
337   - }
338   - list($severity, $content, $file, $line, $globals) = $queue->extract();
339   - $severity = SimpleErrorQueue::getSeverityAsString($severity);
340   - if (! $expected) {
341   - return $this->pass(
342   - "Captured a PHP error of [$content] severity [$severity] in [$file] line [$line] -> %s");
343   - }
344   - $expected = $this->_coerceToExpectation($expected);
345   - return $this->assert(
346   - $expected,
347   - $content,
348   - "Expected PHP error [$content] severity [$severity] in [$file] line [$line] -> %s");
  385 + function expectException($expected = false, $message = '%s') {
  386 + $context = &SimpleTest::getContext();
  387 + $queue = &$context->get('SimpleExceptionTrap');
  388 + $queue->expectException($expected, $message . $this->getAssertionLine());
349 389 }
350 390  
351 391 /**
... ... @@ -356,18 +396,24 @@
356 396 * @return SimpleExpectation Expectation object.
357 397 * @access private
358 398 */
359   - function _coerceToExpectation($expected) {
  399 + function _coerceExpectation($expected) {
  400 + if ($expected == false) {
  401 + return new AnythingExpectation();
  402 + }
360 403 if (SimpleTestCompatibility::isA($expected, 'SimpleExpectation')) {
361 404 return $expected;
362 405 }
  406 + if(is_string($expected)) {
  407 + $expected = str_replace('%', '%%', $expected);
  408 + }
363 409 return new EqualExpectation($expected);
364 410 }
365 411  
366 412 /**
367 413 * @deprecated
368 414 */
369   - function assertErrorPattern($pattern, $message = "%s") {
  415 + function assertErrorPattern($pattern, $message = '%s') {
370 416 return $this->assertError(new PatternExpectation($pattern), $message);
371 417 }
372 418 }
373 419 -?>
  420 +?>
374 421 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/url.php
... ... @@ -507,8 +507,8 @@
507 507 * @access public
508 508 */
509 509 function normalisePath($path) {
510   - $path = preg_replace('|/[^/]+/\.\./|', '/', $path);
511   - return preg_replace('|/\./|', '/', $path);
  510 + $path = preg_replace('|/\./|', '/', $path);
  511 + return preg_replace('|/[^/]+/\.\./|', '/', $path);
512 512 }
513 513  
514 514 /**
... ...
thirdparty/simpletest/simpletest/web_tester.php
... ... @@ -120,8 +120,8 @@
120 120 } else {
121 121 return "Field expectation [" . $dumper->describeValue($this->_value) .
122 122 "] fails with [" .
123   - $this->_dumper->describeValue($compare) . "] " .
124   - $this->_dumper->describeDifference($this->_value, $compare);
  123 + $dumper->describeValue($compare) . "] " .
  124 + $dumper->describeDifference($this->_value, $compare);
125 125 }
126 126 }
127 127 }
... ... @@ -242,7 +242,7 @@
242 242 */
243 243 function testMessage($compare) {
244 244 if (SimpleExpectation::isExpectation($this->_expected_value)) {
245   - $message = $this->_expected_value->testMessage($compare);
  245 + $message = $this->_expected_value->overlayMessage($compare, $this->_getDumper());
246 246 } else {
247 247 $message = $this->_expected_header .
248 248 ($this->_expected_value ? ': ' . $this->_expected_value : '');
... ... @@ -784,7 +784,7 @@
784 784 * @param string $expiry Expiry date.
785 785 * @access public
786 786 */
787   - function setCookie($name, $value, $host = false, $path = "/", $expiry = false) {
  787 + function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
788 788 $this->_browser->setCookie($name, $value, $host, $path, $expiry);
789 789 }
790 790  
... ... @@ -841,6 +841,18 @@
841 841 }
842 842  
843 843 /**
  844 + * Checks for a click target.
  845 + * @param string $label Visible text or alt text.
  846 + * @return boolean True if click target.
  847 + * @access public
  848 + */
  849 + function assertClickable($label, $message = '%s') {
  850 + return $this->assertTrue(
  851 + $this->_browser->isClickable($label),
  852 + sprintf($message, "Click target [$label] should exist"));
  853 + }
  854 +
  855 + /**
844 856 * Clicks the submit button by label. The owning
845 857 * form will be submitted by this.
846 858 * @param string $label Button label. An unlabeled
... ... @@ -881,6 +893,18 @@
881 893 }
882 894  
883 895 /**
  896 + * Checks for a valid button label.
  897 + * @param string $label Visible text.
  898 + * @return boolean True if click target.
  899 + * @access public
  900 + */
  901 + function assertSubmit($label, $message = '%s') {
  902 + return $this->assertTrue(
  903 + $this->_browser->isSubmit($label),
  904 + sprintf($message, "Submit button [$label] should exist"));
  905 + }
  906 +
  907 + /**
884 908 * Clicks the submit image by some kind of label. Usually
885 909 * the alt tag or the nearest equivalent. The owning
886 910 * form will be submitted by this. Clicking outside of
... ... @@ -934,6 +958,18 @@
934 958 }
935 959  
936 960 /**
  961 + * Checks for a valid image with atht alt text or title.
  962 + * @param string $label Visible text.
  963 + * @return boolean True if click target.
  964 + * @access public
  965 + */
  966 + function assertImage($label, $message = '%s') {
  967 + return $this->assertTrue(
  968 + $this->_browser->isImage($label),
  969 + sprintf($message, "Image with text [$label] should exist"));
  970 + }
  971 +
  972 + /**
937 973 * Submits a form by the ID.
938 974 * @param string $id Form ID. No button information
939 975 * is submitted this way.
... ... @@ -969,52 +1005,24 @@
969 1005 }
970 1006  
971 1007 /**
972   - * Will trigger a pass if the two parameters have
973   - * the same value only. Otherwise a fail. This
974   - * is for testing hand extracted text, etc.
975   - * @param mixed $first Value to compare.
976   - * @param mixed $second Value to compare.
977   - * @param string $message Message to display.
978   - * @return boolean True on pass
979   - * @access public
980   - */
981   - function assertEqual($first, $second, $message = "%s") {
982   - return $this->assert(
983   - new EqualExpectation($first),
984   - $second,
985   - $message);
986   - }
987   -
988   - /**
989   - * Will trigger a pass if the two parameters have
990   - * a different value. Otherwise a fail. This
991   - * is for testing hand extracted text, etc.
992   - * @param mixed $first Value to compare.
993   - * @param mixed $second Value to compare.
994   - * @param string $message Message to display.
995   - * @return boolean True on pass
996   - * @access public
997   - */
998   - function assertNotEqual($first, $second, $message = "%s") {
999   - return $this->assert(
1000   - new NotEqualExpectation($first),
1001   - $second,
1002   - $message);
1003   - }
1004   -
1005   - /**
1006 1008 * Tests for the presence of a link label. Match is
1007 1009 * case insensitive with normalised space.
1008 1010 * @param string $label Text between the anchor tags.
  1011 + * @param mixed $expected Expected URL or expectation object.
1009 1012 * @param string $message Message to display. Default
1010 1013 * can be embedded with %s.
1011 1014 * @return boolean True if link present.
1012 1015 * @access public
1013 1016 */
1014   - function assertLink($label, $message = "%s") {
1015   - return $this->assertTrue(
1016   - $this->_browser->isLink($label),
1017   - sprintf($message, "Link [$label] should exist"));
  1017 + function assertLink($label, $expected = true, $message = '%s') {
  1018 + $url = $this->_browser->getLink($label);
  1019 + if ($expected === true) {
  1020 + return $this->assertTrue($url !== false, sprintf($message, "Link [$label] should exist"));
  1021 + }
  1022 + if (! SimpleExpectation::isExpectation($expected)) {
  1023 + $expected = new IdenticalExpectation($expected);
  1024 + }
  1025 + return $this->assert($expected, $url->asString(), sprintf($message, "Link [$label] should match"));
1018 1026 }
1019 1027  
1020 1028 /**
... ... @@ -1027,24 +1035,30 @@
1027 1035 * @return boolean True if link missing.
1028 1036 * @access public
1029 1037 */
1030   - function assertNoLink($label, $message = "%s") {
1031   - return $this->assertFalse(
1032   - $this->_browser->isLink($label),
  1038 + function assertNoLink($label, $message = '%s') {
  1039 + return $this->assertTrue(
  1040 + $this->_browser->getLink($label) === false,
1033 1041 sprintf($message, "Link [$label] should not exist"));
1034 1042 }
1035 1043  
1036 1044 /**
1037 1045 * Tests for the presence of a link id attribute.
1038 1046 * @param string $id Id attribute value.
  1047 + * @param mixed $expected Expected URL or expectation object.
1039 1048 * @param string $message Message to display. Default
1040 1049 * can be embedded with %s.
1041 1050 * @return boolean True if link present.
1042 1051 * @access public
1043 1052 */
1044   - function assertLinkById($id, $message = "%s") {
1045   - return $this->assertTrue(
1046   - $this->_browser->isLinkById($id),
1047   - sprintf($message, "Link ID [$id] should exist"));
  1053 + function assertLinkById($id, $expected = true, $message = '%s') {
  1054 + $url = $this->_browser->getLinkById($id);
  1055 + if ($expected === true) {
  1056 + return $this->assertTrue($url !== false, sprintf($message, "Link ID [$id] should exist"));
  1057 + }
  1058 + if (! SimpleExpectation::isExpectation($expected)) {
  1059 + $expected = new IdenticalExpectation($expected);
  1060 + }
  1061 + return $this->assert($expected, $url->asString(), sprintf($message, "Link ID [$id] should match"));
1048 1062 }
1049 1063  
1050 1064 /**
... ... @@ -1056,9 +1070,9 @@
1056 1070 * @return boolean True if link missing.
1057 1071 * @access public
1058 1072 */
1059   - function assertNoLinkById($id, $message = "%s") {
1060   - return $this->assertFalse(
1061   - $this->_browser->isLinkById($id),
  1073 + function assertNoLinkById($id, $message = '%s') {
  1074 + return $this->assertTrue(
  1075 + $this->_browser->getLinkById($id) === false,
1062 1076 sprintf($message, "Link ID [$id] should not exist"));
1063 1077 }
1064 1078  
... ... @@ -1313,9 +1327,9 @@
1313 1327  
1314 1328 /**
1315 1329 * Tests the text between the title tags.
1316   - * @param string $title Expected title.
1317   - * @param string $message Message to display.
1318   - * @return boolean True if pass.
  1330 + * @param string/SimpleExpectation $title Expected title.
  1331 + * @param string $message Message to display.
  1332 + * @return boolean True if pass.
1319 1333 * @access public
1320 1334 */
1321 1335 function assertTitle($title = false, $message = '%s') {
... ... @@ -1451,5 +1465,77 @@
1451 1465 $this->getCookie($name) === false,
1452 1466 sprintf($message, "Not expecting cookie [$name]"));
1453 1467 }
  1468 +
  1469 + /**
  1470 + * Called from within the test methods to register
  1471 + * passes and failures.
  1472 + * @param boolean $result Pass on true.
  1473 + * @param string $message Message to display describing
  1474 + * the test state.
  1475 + * @return boolean True on pass
  1476 + * @access public
  1477 + */
  1478 + function assertTrue($result, $message = false) {
  1479 + return $this->assert(new TrueExpectation(), $result, $message);
  1480 + }
  1481 +
  1482 + /**
  1483 + * Will be true on false and vice versa. False
  1484 + * is the PHP definition of false, so that null,
  1485 + * empty strings, zero and an empty array all count
  1486 + * as false.
  1487 + * @param boolean $result Pass on false.
  1488 + * @param string $message Message to display.
  1489 + * @return boolean True on pass
  1490 + * @access public
  1491 + */
  1492 + function assertFalse($result, $message = '%s') {
  1493 + return $this->assert(new FalseExpectation(), $result, $message);
  1494 + }
  1495 +
  1496 + /**
  1497 + * Will trigger a pass if the two parameters have
  1498 + * the same value only. Otherwise a fail. This
  1499 + * is for testing hand extracted text, etc.
  1500 + * @param mixed $first Value to compare.
  1501 + * @param mixed $second Value to compare.
  1502 + * @param string $message Message to display.
  1503 + * @return boolean True on pass
  1504 + * @access public
  1505 + */
  1506 + function assertEqual($first, $second, $message = '%s') {
  1507 + return $this->assert(
  1508 + new EqualExpectation($first),
  1509 + $second,
  1510 + $message);
  1511 + }
  1512 +
  1513 + /**
  1514 + * Will trigger a pass if the two parameters have
  1515 + * a different value. Otherwise a fail. This
  1516 + * is for testing hand extracted text, etc.
  1517 + * @param mixed $first Value to compare.
  1518 + * @param mixed $second Value to compare.
  1519 + * @param string $message Message to display.
  1520 + * @return boolean True on pass
  1521 + * @access public
  1522 + */
  1523 + function assertNotEqual($first, $second, $message = '%s') {
  1524 + return $this->assert(
  1525 + new NotEqualExpectation($first),
  1526 + $second,
  1527 + $message);
  1528 + }
  1529 +
  1530 + /**
  1531 + * Uses a stack trace to find the line of an assertion.
  1532 + * @return string Line number of first assert*
  1533 + * method embedded in format string.
  1534 + * @access public
  1535 + */
  1536 + function getAssertionLine() {
  1537 + $trace = new SimpleStackTrace(array('assert', 'click', 'pass', 'fail'));
  1538 + return $trace->traceMethod();
  1539 + }
1454 1540 }
1455 1541 ?>
1456 1542 \ No newline at end of file
... ...
thirdparty/simpletest/simpletest/xml.php
... ... @@ -23,7 +23,9 @@
23 23 var $_namespace;
24 24  
25 25 /**
26   - * Does nothing yet.
  26 + * Sets up indentation and namespace.
  27 + * @param string $namespace Namespace to add to each tag.
  28 + * @param string $indent Indenting to add on each nesting.
27 29 * @access public
28 30 */
29 31 function XmlReporter($namespace = false, $indent = ' ') {
... ... @@ -140,8 +142,8 @@
140 142 }
141 143  
142 144 /**
143   - * Increments the pass count.
144   - * @param string $message Message is ignored.
  145 + * Paints pass as XML.
  146 + * @param string $message Message to encode.
145 147 * @access public
146 148 */
147 149 function paintPass($message) {
... ... @@ -153,8 +155,8 @@
153 155 }
154 156  
155 157 /**
156   - * Increments the fail count.
157   - * @param string $message Message is ignored.
  158 + * Paints failure as XML.
  159 + * @param string $message Message to encode.
158 160 * @access public
159 161 */
160 162 function paintFail($message) {
... ... @@ -166,10 +168,9 @@
166 168 }
167 169  
168 170 /**
169   - * Paints a PHP error or exception.
170   - * @param string $message Message is ignored.
  171 + * Paints error as XML.
  172 + * @param string $message Message to encode.
171 173 * @access public
172   - * @abstract
173 174 */
174 175 function paintError($message) {
175 176 parent::paintError($message);
... ... @@ -180,6 +181,36 @@
180 181 }
181 182  
182 183 /**
  184 + * Paints exception as XML.
  185 + * @param Exception $exception Exception to encode.
  186 + * @access public
  187 + */
  188 + function paintException($exception) {
  189 + parent::paintException($exception);
  190 + print $this->_getIndent(1);
  191 + print "<" . $this->_namespace . "exception>";
  192 + $message = 'Unexpected exception of type [' . get_class($exception) .
  193 + '] with message ['. $exception->getMessage() .
  194 + '] in ['. $exception->getFile() .
  195 + ' line ' . $exception->getLine() . ']';
  196 + print $this->toParsedXml($message);
  197 + print "</" . $this->_namespace . "exception>\n";
  198 + }
  199 +
  200 + /**
  201 + * Paints the skipping message and tag.
  202 + * @param string $message Text to display in skip tag.
  203 + * @access public
  204 + */
  205 + function paintSkip($message) {
  206 + parent::paintSkip($message);
  207 + print $this->_getIndent(1);
  208 + print "<" . $this->_namespace . "skip>";
  209 + print $this->toParsedXml($message);
  210 + print "</" . $this->_namespace . "skip>\n";
  211 + }
  212 +
  213 + /**
183 214 * Paints a simple supplementary message.
184 215 * @param string $message Text to display.
185 216 * @access public
... ... @@ -531,7 +562,7 @@
531 562 */
532 563 function _isLeaf($tag) {
533 564 return in_array($tag, array(
534   - 'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'MESSAGE', 'FORMATTED', 'SIGNAL'));
  565 + 'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'SKIP', 'MESSAGE', 'FORMATTED', 'SIGNAL'));
535 566 }
536 567  
537 568 /**
... ... @@ -578,6 +609,8 @@
578 609 $this->_listener->paintFail($this->_content);
579 610 } elseif ($tag == 'EXCEPTION') {
580 611 $this->_listener->paintError($this->_content);
  612 + } elseif ($tag == 'SKIP') {
  613 + $this->_listener->paintSkip($this->_content);
581 614 } elseif ($tag == 'SIGNAL') {
582 615 $this->_listener->paintSignal(
583 616 $this->_attributes['TYPE'],
... ...