Location: PHPKode > projects > Community Learning Network > cln/lib/CLN/Cln_KO.php
<?php
/*
 * KO Base Class
 *
 * Copyright (c) 2003-4 St. Christopher House
 *
 * Developed by The Working Group Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * @version $Id: Cln_KO.php,v 1.168 2005/01/23 20:35:31 cbooth7575 Exp $
 *
 */

//require_once 'PEAR.php';

class Cln_KO
{
	/*
	 *
	 * Class Attributes:  Cln_KO
	 *
	 * 		The attributes for this class are:
	 *
	 *		TBD
	 *
	 */
	var $koId;				// The koId
	var $modId;			// The module for this ko
	var $modName;			// The module for this ko
	var $parts;
	var $existingLanguages;

	var $currentPart;

	var $isRolesLoaded;
	var $currentEditProcess;

	/*
	 *
	 * Constructor:  Cln_KO()
	 *
	 * 		Loads the KO specific data from what info it has.
	 *
	 * @access public
	 * @return reference 	instance of Cln_KO
	 *
	 */
	function Cln_KO($koId, $modId = '', $roles = FALSE)
	{
		d('KoId: ' . $koId . ': Cln_KO Constructor');
		$this->koId = $koId;
		$this->modId = $modId;
		if ($roles) {
			$this->roles = $roles;
			$this->isRolesLoaded = TRUE;
		}
		else {
			$this->isRolesLoaded = FALSE;
		}
		$this->parts = Array();
		$this->currentPart = FALSE;
		$this->currentEditProcess = FALSE;
		return $this->initialize();
	}


	/*
	 *
	 * Function:  initialize()
	 *
	 * 		Load general parts information
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function initialize()
	{
		d('KoId: ' . $this->koId . ': Cln_KO initialize');
		// For new KOs create some simple parts info
		if ($this->koId == 'NEW') {
			d('creating NEW '.$_SESSION['Module_Loader']->getModuleName($this->modId),2);
			// We need a modId, if not, this is an error
			if (empty($this->modId)) {
				PEAR::raiseError('You didn\'t specify a modId for your new KO', E_ERROR);
				return FALSE;
			}
			$thisObjectId = 'NEW';
			$this->parts[$thisObjectId]['objectId'] = $thisObjectId;
			$this->parts[$thisObjectId]['status'] = 0;
			$this->parts[$thisObjectId]['lang'] = CLN_DEFAULT_LANGUAGE;
			$this->parts[$thisObjectId]['original'] = 1;
			$this->parts[$thisObjectId]['title'] = '';
			$this->parts[$thisObjectId]['object'] = FALSE;
			$this->parts[$thisObjectId]['metadata'] = FALSE;
			$this->parts[$thisObjectId]['isMetadataLoaded'] = FALSE;
			$this->parts[$thisObjectId]['displayMetadataDetails'] = FALSE;
			$this->parts[$thisObjectId]['newPart'] = TRUE;
			$this->parts[$thisObjectId]['modifiedBy'] = $_SESSION['User']->userId;
			$this->parts[$thisObjectId]['created'] = NULL;
			$this->parts[$thisObjectId]['createdBy'] = $_SESSION['User']->userId;

			if(empty($this->creator) && isset($_SESSION['User']->userId)) $this->creator = $_SESSION['User']->userId;

			$this->currentEditProcess = 'Content';
			$this->isRolesLoaded = TRUE;

			$this->existingLanguages[CLN_DEFAULT_LANGUAGE] = 0;
		}

		// For existing KOs load all the parts info
		else {
			// Load what we can about the KO
			$sql = sprintf("SELECT objectId, modId, status, lang, original, title, modified, modifiedBy, created, createdBy
							FROM `%s` WHERE koId = '%s'", KO_TABLE, $this->koId);

			$db = &Cln_Db::singleton(MAIN_CLN_DSN);
			$result = $db->query($sql);
			if (PEAR::isError($result)) {
				PEAR::raiseError("Couldn't get ko info for koId $koId", E_WARNING);
				return FALSE;
			}

			// Loop through the results filling our $parts array with info
			for ($x = 0; $row = $result->fetchRow(DB_FETCHMODE_ASSOC); $x++) {
				$this->modId = $row['modId'];

				$thisObjectId = $row['objectId'];
				$this->parts[$thisObjectId]['objectId'] = $thisObjectId;
				$this->parts[$thisObjectId]['status'] = $row['status'];
				$this->parts[$thisObjectId]['lang'] = stripslashes($row['lang']);
				$this->parts[$thisObjectId]['original'] = $row['original'];
				$this->parts[$thisObjectId]['title'] = stripslashes($row['title']);
				$this->parts[$thisObjectId]['object'] = FALSE;
				$this->parts[$thisObjectId]['metadata'] = FALSE;
				$this->parts[$thisObjectId]['isMetadataLoaded'] = FALSE;
				$this->parts[$thisObjectId]['displayMetadataDetails'] = FALSE;
				$this->parts[$thisObjectId]['modified'] = $row['modified'];
				$this->parts[$thisObjectId]['modifiedBy'] = $row['modifiedBy'];
				$this->parts[$thisObjectId]['created'] = $row['created'];
				$this->parts[$thisObjectId]['createdBy'] = $row['createdBy'];

				$this->creator = $row['createdBy'];

				$lang = $row['lang'];
				$this->existingLanguages[$lang] = $row['original'];

			}
		}

		$this->modName = $_SESSION['Module_Loader']->getModuleName($this->modId);
		return TRUE;
	}


	/*
	 *
	 * Function:  copy()
	 *
	 * 		copy the KO
	 *
	 *			a copy is only drafts
	 *			permissions should be limited to new user (may require prompting)
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function copy($copiedKoId, $copyLive = FALSE, $creator = '')
	{
		d('copying koId: '.$copiedKoId,3);

		$copiedKO = new $GLOBALS['classes']['ko']['classname']($copiedKoId);
		$copiedKO->loadAllParts();
		$copiedKO->loadMetadata();

		// copy roles - eventually, this needs to be smarter
		//$copiedKO->loadRoles(); // :TODO: I think what need to happen here is
		// that the copiedKO's roles get deleted, and then replaced with the roles
		// from the copier. don't know if this should happen here, in which case
		// the roles would have to be passed to this function, or if it hsould
		// happen in the place where copy() is called. need to check how this
		// works today.- cdb

		$copiedKO->koId = $copiedKO->getNewKoId();

		// setup creator
		if(empty($creator)) {
			$creator = $_SESSION['User']->userId;
		}
		$copiedKO->creator = $creator;


		// load class libraries
		include_once($_SESSION['Module_Loader']->getModulePath($copiedKO->modId));
		$classname = $_SESSION['Module_Loader']->getModuleClassname($copiedKO->modId);

		if (!class_exists($classname)) {
			PEAR::raiseError("Couldn't load class $classname", E_ERROR);
			return FALSE;
		}

		$partsArray = $copiedKO->parts;

		unset($copiedKO->parts);
		$oldCurrentPartId = $copiedKO->currentPart['objectId'];

		foreach($partsArray as $passingDataPartId => $part) {
			$passingData = $partsArray[$passingDataPartId]['object']->getPublishData();
			d($passingData);

			// ignore live versions - not to be copied
			// :TODO: need to decide whether we can actually see the draft and therefore copy
			// it or whether we need to just take the live and set it to draft
			if(!$copyLive && $partsArray[$passingDataPartId]['status'] == 1) continue;

			$object = new $classname('NEW', $passingData);
			$object->_super = & $copiedKO;
			$objectId = $object->saveModuleData();

			$copiedKO->parts[$objectId] = $partsArray[$passingDataPartId];
			$copiedKO->parts[$objectId]['objectId'] = $objectId;
			$copiedKO->parts[$objectId]['object'] = $object;

			// :TODO: Currently with a copied object the metadata isn't copied. need to try to do that, and probably it should be done here, by loading the original metadata, getting the info, and passing it through to the new one.
			$copiedKO->parts[$objectId]['metadata'] = & new $GLOBALS['classes']['metadata']['classname']($copiedKO->koId, $objectId);
			$copiedKO->parts[$objectId]['isMetadataLoaded'] = TRUE;
			$copiedKO->parts[$objectId]['displayMetadataDetails'] = FALSE;
			$copiedKO->parts[$objectId]['newPart'] = 1;

			$copiedKO->parts[$objectId]['object']->_part =& $copiedKO->parts[$objectId];
			$copiedKO->parts[$objectId]['object']->_super = & $copiedKO;

			// temporarily make currentPart and currentMetadata for saving
			$copiedKO->currentPart =& $copiedKO->parts[$objectId];
			$copiedKO->currentMetadata =& $copiedKO->parts[$objectId]['metadata'];
			$copiedKO->savePart();

			if($passingDataPartId == $oldCurrentPartId) {
				$currentPartId = $objectId;
				d('setting currentPartId to '.$currentPartId);
			}
		}

		//d('----------------');
		//d($copiedKO);
		//d('----------------');

		// reset currentPart
		if(isset($currentPartId)) {
			$copiedKO->currentPart =& $copiedKO->parts[$currentPartId];
		}

		return $copiedKO;
	}


	/*
	 *
	 * Function:  setEditLang()
	 *
	 * 		Sets the edit language for this object
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function setEditLang()
	{
		if (!isset($this->editLang)) {
			if (isset($_SESSION['User']->lang)) {
				$testLangs[] = $_SESSION['User']->lang;
			}
			$testLangs[] = CLN_DEFAULT_LANGUAGE;

			$foundOne = FALSE;
			foreach ($testLangs as $testLang) {
				if (!$foundOne && array_key_exists($testLang, $this->existingLanguages)) {
					$this->editLang = $testLang;
					$foundOne = TRUE;
				}
			}

			if (!isset($editLang)) {
				foreach ($this->existingLanguages as $lang => $original) {
					if ($original) {
						$this->editLang = $lang;
					}
				}
			}
		}

		return TRUE;
	}


	/*
	 *
	 * Function:  getContent()
	 *
	 * 		gets content for knowledge object through module
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function getContent($status = 1)
	{
		d('KoId: ' . $this->koId . ': Cln_KO getContent');
		// If they are in edit mode and allowed to edit, show the Draft version
		if ($_SESSION['editMode'] == 'edit' && $this->isEditable()) {
			$status = 0;
		}

		// If they are editing an existing KO, do that
		if (isset($_REQUEST['editKoId'])) {
			// Get the content from the appropriate part
			$this->loadPartObject($status);

			// This will set the template path. It needed to be here so that it would be before the bort.php script is called
			if (!defined('CLN_TEMPLATE_PATH') && $this->modId == 1) {
				define('CLN_TEMPLATE_PATH', CLN_CLEAN_URL_BASE . 'template/' . $this->currentPart['object']->template . '/');
			}

			$modContent = $this->currentPart['object']->getContent();
		}

		// Else if the KO is viewable, display it
		else if ($this->isViewable()) {
			d('isViewable',4);
			$partLoaded = $this->loadPartObject($status);

			// This will set the template path. It needed to be here so that it would be before the bort.php script is called
			if (!defined('CLN_TEMPLATE_PATH') && $this->modId == 1) {
				define('CLN_TEMPLATE_PATH', CLN_CLEAN_URL_BASE . 'template/' . $this->currentPart['object']->template . '/');
			}

			// If we're in edit mode, prepare the BORTs for ROOM and PAGE
			if ($this->modId <= 2 && isset($_SESSION['editMode']) && $_SESSION['editMode'] == 'edit') {
				include('BORT/bort.php');

				// BORT stuff for ROOMs
				if ($this->modId == 1) {
					$GLOBALS['CLN_ROOM_BORT'] = $bort;
				}

				// BORT stuff for PAGES
				else if ($this->modId == 2) {
					$GLOBALS['CLN_PAGE_BORT'] = $bort;
				}
			}


			// Load the correct part's content
			if($partLoaded) {
				d('part loaded',4);
				$modContent = $this->currentPart['object']->getContent();

				// BORT stuff for BLOCKS
				if ($this->modId > 2 && isset($_SESSION['editMode']) && $_SESSION['editMode'] == 'edit') {
					include('BORT/bort.php');
					$modContent = $bort . $modContent;
				}
			}
			// Or else it doesn't exists
			else {
				d('part does not exist',4);
				// if the live room does not exist, redirect to draft
				if($this->modId == 1 || $this->modId == 2) {
					PEAR::raiseError('Live version does not yet exist.  Please publish the room, page and blocks first.', E_USER_WARNING);
					clnRedirect(appendToURL($_SERVER['REQUEST_URI'],'editMode=edit', TRUE));
					exit;
				} else {
					// Live version does not yet exist
					// dom requested that this be removed and i agree with him - dwc
					$modContent = '';	// $this->getNoLive();
				}
			}
		}

		// Else they can't edit or view it
		else {
			if($this->modId == 1 || $this->modId == 2) {
				PEAR::raiseError($this->getNotViewable(), E_USER_WARNING);
				clnRedirect(cleanURL(CLN_UNAUTHORIZED_PAGE));
				exit;
			} else {
				$modContent = ''; // $this->getNotViewable(); removing this - cdb
			}
		}

		return $modContent;
	}


	/*
	 *
	 * Function:  getEditContent()
	 *
	 * 		gets content for knowledge object through module
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function getEditContent()
	{
		d('KoId: ' . $this->koId . ': Cln_KO getEditContent');
		// ***************************************************************************
		// 1. Permissions
		//
		// Kick them out if they don't have the right to be here
		if (!$this->isEditable()) {
			PEAR::raiseError('You aren\'t allowed to edit this object'.$this->koId, E_USER_WARNING);
			clnRedirect($_SERVER['PHP_SELF']);
			exit;
		}

		// ***************************************************************************
		// 2. Record Locking
		//
		// Check if there is a record locked for this KO, if so, verify against it
		// otherwise, kick them out
		$this->checkRecordLocks();


		// ***************************************************************************
		// 3. Language
		//

		// Set the edit language if needed
		if (!isset($this->editLang)) {
			$this->setEditLang();
		}
		if (isset($_GET['editLang'])) {
			$this->editLang = $_GET['editLang'];
		}

		// ***************************************************************************
		// 4. Calling/Sending
		//


		// Figure out what they're sending/calling
		if (isset($_GET['editProcess'])) {
			$this->currentEditProcess = $_GET['editProcess'];
		}
		else if (!$this->currentEditProcess) {
			$this->currentEditProcess = 'Panel';
		}

		// Return to panel catcher: used for cancel buttons
		if(isset($_REQUEST['ReturnToPanel'])) {
			$this->currentEditProcess = 'Panel';
		}

		// ***************************************************************************
		// 5. Logic
		//
		// Do the logic for the page

		// Load the object
		$this->loadPartObject(0);

		$display = '';

		switch ($this->currentEditProcess) {

		// ******************************************
		// 1. Panel
		case 'Panel':
			$display = 'Panel';
		break;

		case 'Content':
			$this->loadMetadata();

			// Is the simple metadata being submitted?
			if (isset($_REQUEST['simpleMetadataSubmitted'])) {
				$metadataSubmitted = TRUE;

				// If it's been captured OK, then savePart
				if ($this->captureMetadata()) {
					// Save the part info (title, language)
					if ($this->savePart()) {
						$metadataOK = $this->saveMetadata();
					}
					else {
						$metadataOK = FALSE;
					}
				}

				// Else it wasn't captured OK
				else {
					$metadataOK = FALSE;
				}
			}
			// Otherwise, simpleMetadata isn't being submitted, we don't need to savePart
			else {
				$metadataSubmitted = FALSE;
				$metadataOK = NULL;
			}

			// Get the content form, if it is needed right now
			$contentForm = $this->currentPart['object']->getEditContent();

			// Is there more no content to display from the module?
			if (!$contentForm) {
				// Was some simple metadata submitted?
				if ($metadataSubmitted) {
					// Were there any errors in the simple metadata
					if (!$metadataOK) {
						// We need to override the content to display errors in metadata,
						// passing TRUE to getEditContent forces override
						$contentForm = $this->currentPart['object']->getEditContent(TRUE);
						$display = 'Content';
					}

					// Else, the simple metadata was OK
					else {
						// Are they asking to edit the detailed metadata?
						if (isset($this->currentPart['displayMetadataDetails']) && $this->currentPart['displayMetadataDetails']) {
							$this->currentEditProcess = 'Metadata';
							$display = 'Metadata';
						}

						// Else, just throw them back to the Panel
						else {
							$this->currentEditProcess = 'Panel';
							$display = 'Panel';
						}
					}
				}

				// Else, there was no metadata submitted, throw them back to the panel.
				else {
					$this->currentEditProcess = 'Panel';
					$display = 'Panel';
				}
			}

			// Else, there still is content to display from the module
			else {
				$display = 'Content';
			}
		break;

		case 'Metadata':
			// If they're submitting valid metadata, capture it, and save for existing KOs
			$this->loadMetadata();
			if ($this->captureMetadata()) {
				if ($this->koId != 'NEW') {
					$this->saveMetadata();
					$this->savePart();
				}
				PEAR::raiseError('The metadata information has been updated successfully.', E_USER_NOTICE);
				$this->currentEditProcess = 'Panel';
				$display = 'Panel';
			}
			// Else they are just getting here, so simply display the metadata form
			else {
				$this->loadMetadata();
				$display = 'Metadata';
			}
		break;

		case 'Roles':
			// If they're submitting the roles
			if (isset($_POST['editRoles']) && $this->captureRoles()) {
				$this->currentEditProcess = 'Panel';
				$display = 'Panel';
				PEAR::raiseError('The roles have been updated successfully.', E_USER_NOTICE);
			}
			// Or if they're trying to select groups
			else if (isset($_POST['selectGroups'])) {
				$this->currentEditProcess = 'selectGroups';
				$display = 'selectGroups';

			}
			// Or if they're cancelling
			else if (isset($_POST['cancel'])) {
				$this->currentEditProcess = 'Panel';
				$display = 'Panel';
			}
			// Else they are just getting here, so simply display the metadata form
			else {
				$display = 'Roles';
			}
		break;

		case 'selectGroups':
			if (isset($_POST['selectedGroups'])) {
				if (isset($_POST['group'])) {
					foreach ($_POST['group'] as $groupId => $groupName) {
						$this->roles[$groupId][] = '4';
					}
				}
				$display = 'Roles';
			}
			else if (isset($_POST['addGroup'])) {
				// Must be a super user or a group editor to do this
				if (in_array(CLN_SUPERUSER_GROUPID,$_SESSION['User']->groupList) ||
					in_array(CLN_GROUPID_GROUPCREATORS,$_SESSION['User']->groupList)) {
					$this->editGroup = & new $GLOBALS['classes']['group']['classname']('NEW');
					$this->currentEditProcess = 'editGroup';
					$display = 'editGroup';
				}
				else {
					PEAR::RaiseError('You don\'t have the rights to add a new group.', E_USER_WARNING);
					$display = 'selectGroups';
				}
			}
			else if (isset($_GET['editGroupId'])) {
				if (!isset($this->editGroup)) {
					$this->editGroup = & new $GLOBALS['classes']['group']['classname']($_GET['editGroupId']);
				}
				else if (isset($this->editGroup) && $this->editGroup->groupId != $_GET['editGroupId']) {
					unset($this->editGroup);
					$this->editGroup = & new $GLOBALS['classes']['group']['classname']($_GET['editGroupId']);
				}
				$this->currentEditProcess = 'editGroup';
				$display = 'editGroup';
			}
			else {
				$display = 'selectGroups';
			}
		break;

		case 'editGroup':
			// If they want to add users
			if (isset($_POST['selectUsers'])) {
				// Capture any name changes
				$this->editGroup->captureFormData();
				$this->currentEditProcess = 'selectUsers';
				$display = 'selectUsers';
			}

			// Else if they have submitted the form, deal with it
			else if (isset($_POST['editGroup'])) {
				// Capture the form data
				$this->editGroup->captureFormData();

				// Validate the data
				if (!$this->editGroup->validateData()) {
					$display = 'editGroup';
				}

				// Otherwise, there are no errors, continue
				else {
					$this->editGroup->save();
					unset($this->editGroup);
					$display = 'selectGroups';
				}
			}

			// Else, they're just getting here, display the form
			else {
				$display = 'editGroup';
			}
		break;

		case 'selectUsers':
			if (isset($_POST['selectedUsers'])) {

				if (isset($_POST['user'])) {
					foreach ($_POST['user'] as $userId => $userName) {
						$this->editGroup->addUser($userId, $userName);
					}
				}

				$this->currentEditProcess = 'editGroup';
				$display = 'editGroup';
			}
			else {
				$display = 'selectUsers';
			}
		break;


		case 'Revert':
			if ($this->isEditable(1) && $this->isEditable()) {
				if($this->revertKO()) {
					PEAR::raiseError('This '.$this->modName.' has been reverted to the live data.', E_USER_NOTICE);
				}

				$display = 'Panel';
			}
			else {
				$display = 'Panel';
			}
		break;

		case 'Publish':
			if ($this->isEditable(1)) {
				if(isset($_REQUEST['publishChildren'])) {
					// if room, get pages
					// if page, get blocks
					//$test = '<pre>'.print_r($this,TRUE).'</pre>';
					//PEAR::raiseError($test, E_USER_NOTICE);
				} else {
					// If they're not coming from the Control Panel, we should publish their current viewing language, to avoid confusion.
					if (isset($_REQUEST['exitPanel'])) {
						$this->editLang = $_SESSION['User']->lang;
					}
					if($this->publishKO()) {
						PEAR::raiseError('This '.$this->modName.' has been published', E_USER_NOTICE);
					}
				}

				if(isset($_REQUEST['exitPanel'])) {
					$display = '';
				} else {
					$display = 'Panel';
				}
			}
			else {
				$display = 'Panel';
			}
		break;

		case 'ReturnToParent':
			$_SESSION['ProcessManager']->popAndGoToNext();
		break;

		case 'TranslateMe':
			$this->currentEditProcess = 'SelectLanguageForTranslate';
			$display = 'SelectLanguage';
		break;

		case 'SelectLanguageForTranslate':
			if (isset($_POST['selectLanguage'])) {
				if (isset($_POST['langChoice'])) {

					if (array_key_exists($_POST['langChoice'], $this->existingLanguages)) {
						PEAR::raiseError('The language you chose already exists, please switch to that language version of this object and edit that one', E_USER_WARNING);
						$display = 'Panel';
					}
					else {

						$this->createTranslation($_POST['langChoice']);

						$this->currentEditProcess = 'Content';
						$contentForm = $this->currentPart['object']->getEditContent();
						$display = 'Content';
					}
				}
				else {
					PEAR::raiseError('Sorry, you didn\'t choose a language', E_USER_WARNING);
					$display = 'SelectLanguage';
				}

			}
			else {
				$display = 'SelectLanguage';
			}
		break;

		default:
			$display = 'Panel';
		break;
		}

		// ***************************************************************************
		// 5. Display
		//
		// Do the display
		$displayContent = ''; //'<h2>Editing '.$this->modName.': '.$this->currentPart['title'].'</h2>';


		switch ($display) {
			case 'Panel':
				$displayContent .= $this->getEditPanel();
			break;

			case 'Content':
				$displayContent .= $contentForm;
			break;

			case 'Metadata':
				$displayContent .= $this->getEditMetadata();
			break;

			case 'Roles':
				$displayContent .= $this->getEditRoles();
			break;

			case 'selectGroups':
				$displayContent .= $this->getSelectGroups();
			break;

			case 'editGroup':
				$displayContent .= $this->getEditGroup();
			break;

			case 'selectUsers';
				$displayContent .= $this->getSelectUsers();
			break;

			case 'SelectLanguage':
				$displayContent .= $this->getSelectLanguage();
			break;

			default:
				// returns to main edit screen
				clnRedirect(cleanURL($GLOBALS['path']));
				exit;
			break;
		}

		// setting up container to color whole panel
		if($this->modId == 1) {
			$class = 'roomBortContainer';
		}
		else if($this->modId == 2) {
			$class = 'pageBortContainer';
		} else {
			$class = 'blockBortContainer';
		}

		include_once('BORT/bort.php');
		$GLOBALS['CLN_PAGE_BORT'] = $bort.'<!-- START BORT CONTAINER --> <div class="'.$class.'">';

		// i've added this simple layout structure to the admin content, so that we can place
		// all of our stylings on the layouts, rather than the borts - dwc
		return '<div class="layoutRow"><div class="layoutColumn">'.$displayContent.'</div></div></div> <!-- end bort container -->';
	}



	/***********************************************************************************
	 *
	 * Core functions
	 *
	 **********************************************************************************/

	/*
	 *
	 * Function:  revertKO()
	 *
	 * 		Reverts the KO
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function revertKO()
	{
		d('KoId: ' . $this->koId . ': revertKO()');
		// *****************************************
		// Load the live part and object
		$this->loadPartObject(1);
		$this->loadMetadata();
		$livePart = & $this->currentPart;
		$liveMetadata = & $this->currentMetadata;

		$this->loadPartObject(0);
		$this->loadMetadata();
		$draftPartId = $this->currentPart['objectId'];
		$draftPart = & $this->parts[$draftPartId];
		$draftMetadata = & $draftPart['metadata'];

		// *****************************************
		// Publish the Metadata
		$metadataFields = (array)$liveMetadata;

		foreach ($metadataFields as $fieldName => $fieldValue) {
			if ($fieldName != 'koId' && $fieldName != 'objectId') {
				$draftMetadata->$fieldName = $fieldValue;
			}
		}

		$draftMetadata->saveMetadata();

		// *****************************************
		// Publish the Content
		$publishContent = $livePart['object']->getPublishData();
		$draftPart['object']->publish($publishContent);

		// *****************************************
		// Publish the Parts
		$draftPart['title'] = $livePart['title'];
		$this->savePart();

		$this->loadPartObject(1);
		$this->updateModified();
		$this->loadPartObject(0);
		return TRUE;
	}


	/*
	 *
	 * Function:  publishKO()
	 *
	 * 		Publishes the KO
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function publishKO()
	{
		d('KoId: ' . $this->koId . ': publishKO()');
		// *****************************************
		// Load the draft part and object
		$this->loadPartObject(0);
		$this->loadMetadata();
		$draftPart = & $this->currentPart;
		$draftMetadata = & $this->currentMetadata;

		// if a live part doesn't exist for this language
		if (!$this->doesLivePartExist($draftPart['lang'])) {
			//  AND If there is a live part for Another language
			if ($partId = $this->doesLivePartExist()) {
				// Create a live part for this lang
				$this->createLivePart($draftPart['lang'], $draftPart['original'], $partId);
			}
			// ELSE if there isn't a live part for another language
			else {
				$this->createLivePart($draftPart['lang'], $draftPart['original']);
			}
		}

		$this->loadPartObject();
		$this->loadMetadata();
		$livePartId = $this->currentPart['objectId'];
		$livePart = & $this->parts[$livePartId];
		$liveMetadata = & $livePart['metadata'];

		// *****************************************
		// Publish the Metadata
		$metadataFields = (array)$draftMetadata;

		foreach ($metadataFields as $fieldName => $fieldValue) {
			if ($fieldName != 'koId' && $fieldName != 'objectId') {
				$liveMetadata->$fieldName = $fieldValue;
			}
		}

		$liveMetadata->saveMetadata();

		// *****************************************
		// Publish the Content
		$publishContent = $draftPart['object']->getPublishData();
		$livePart['object']->publish($publishContent);

		// *****************************************
		// Publish the Parts
		$livePart['title'] = $draftPart['title'];
		$this->savePart();

		$this->loadPartObject(0);
		return TRUE;
	}


	/*
	 *
	 * Function:  createTranslation()
	 *
	 * 		creates a translation part
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function createTranslation($lang)
	{
		d('KoId: ' . $this->koId . ": Cln_KO createTranslation() lang: $lang", 3);
		$this->editLang = $lang;

		// Create the translation draft object and get the objectId
		include_once($_SESSION['Module_Loader']->getModulePath($this->modId));
		$classname = $_SESSION['Module_Loader']->getModuleClassname($this->modId);

		if (!class_exists($classname)) {
			PEAR::raiseError("Couldn't load class $classname", E_WARNING);
			return FALSE;
		}

		// Get any data that needs passing through
		$passingData = $this->currentPart['object']->getNewTranslationData();
		$object = new $classname('NEW', $passingData);
		$object->_super =& $this;
		$objectId = $object->saveModuleData();

		// Add the part
		$this->parts[$objectId]['objectId'] = $objectId;
		$this->parts[$objectId]['status'] = 0;
		$this->parts[$objectId]['lang'] = $lang;
		$this->parts[$objectId]['original'] = 0;
		$this->parts[$objectId]['title'] = '';
		$this->parts[$objectId]['object'] = $object;
		$this->parts[$objectId]['metadata'] = & new $GLOBALS['classes']['metadata']['classname']($this->koId, $objectId);
		$this->parts[$objectId]['isMetadataLoaded'] = TRUE;
		$this->parts[$objectId]['displayMetadataDetails'] = FALSE;
		$this->parts[$objectId]['newPart'] = TRUE;
		$this->parts[$objectId]['modifiedBy'] = $_SESSION['User']->userId;
		$this->parts[$objectId]['created'] = NULL;
		$this->parts[$objectId]['createdBy'] = $_SESSION['User']->userId;


		$this->parts[$objectId]['object']->_part =& $this->parts[$objectId];

		$this->loadPartObject(0);

		$this->existingLanguages[$lang] = '0';

		$this->currentMetadata = & $this->currentPart['metadata'];

		$this->saveMetadata();


		// *****************************************
		// Publish the Parts
		$this->savePart();

		return $objectId;
	}


	/*
	 *
	 * Function:  createLivePart()
	 *
	 * 		creates a live part if none exists already
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function createLivePart($lang, $original, $passingDataPartId = FALSE)
	{
		d('koId: ' . $this->koId . ": createLivePart lang: $lang original: $original passingDataPartId: $passingDataPartId");

		// Create the live object and get the objectId
		include_once($_SESSION['Module_Loader']->getModulePath($this->modId));
		$classname = $_SESSION['Module_Loader']->getModuleClassname($this->modId);

		if (!class_exists($classname)) {
			PEAR::raiseError("Couldn't load class $classname", E_WARNING);
			return FALSE;
		}

		// If there is another part for us to look for data in
		if ($passingDataPartId) {
			if (!is_object($this->parts[$passingDataPartId]['object'])) {
				$this->loadPartObjectById($passingDataPartId);
			}
			$passingData = $this->parts[$passingDataPartId]['object']->getNewTranslationData(); // :TODO: look into why this is getNewTranslationData, not getPublishData or something like that.
		}
		else {
			$passingData = FALSE;
		}

		$object = new $classname('NEW', $passingData);
		$object->_super = & $this;
		$objectId = $object->saveModuleData();

		// Add the part
		$this->parts[$objectId]['objectId'] = $objectId;
		$this->parts[$objectId]['status'] = 1;
		$this->parts[$objectId]['lang'] = $lang;
		$this->parts[$objectId]['original'] = $original;
		$this->parts[$objectId]['title'] = '';
		$this->parts[$objectId]['object'] = $object;
		$this->parts[$objectId]['metadata'] = & new $GLOBALS['classes']['metadata']['classname']($this->koId, $objectId);
		$this->parts[$objectId]['isMetadataLoaded'] = TRUE;
		$this->parts[$objectId]['displayMetadataDetails'] = FALSE;
		$this->parts[$objectId]['newPart'] = TRUE;
		$this->parts[$objectId]['modifiedBy'] = $_SESSION['User']->userId;
		$this->parts[$objectId]['created'] = NULL;
		$this->parts[$objectId]['createdBy'] = $_SESSION['User']->userId;

		$this->parts[$objectId]['metadata']->saveMetadata();

		// Return
		return TRUE;
	}


	/*
	 *
	 * Function:  delete()
	 *
	 *		Completely deletes a KO from the KB
	 *			- expects to call a delete method in all modules
	 *
	 * @access public
	 * @return Boolean		TRUE or FALSE
	 *
	 */

	function delete()
	{
		// Delete each part

		$this->loadAllParts();
		foreach ($this->parts as $partNumber => $partInfo) {
			$this->currentPart = & $this->parts[$partNumber];
			$this->loadMetadata();
			$this->currentMetadata = & $this->parts[$partNumber]['metadata'];
			$this->currentPart['object']->delete();

			$this->currentMetadata->deleteMetadata();
		}

		// Delete the KO itself
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		$sql = sprintf("DELETE FROM `%s` WHERE koId = '%d'; ", KO_TABLE, $this->koId);

		$result = $db->query($sql);

		if (PEAR::isError($result)) {
			PEAR::raiseError("There was an error deleting the Main KO: $sql", E_ERROR);
			return FALSE;
		}

		// And the roles
		$sql = sprintf("DELETE FROM `%s` WHERE koId = '%d'; ", PERMISSION_TABLE, $this->koId);

		$result = $db->query($sql);

		if (PEAR::isError($result)) {
			PEAR::raiseError("There was an error deleting the KO Roles: $sql", E_ERROR);
			return FALSE;
		}

		return TRUE;
	}


	/***********************************************************************************
	 *
	 * Testing things methods
	 *
	 **********************************************************************************/


	/*
	 *
	 * Function:  doesLivePartExist()
	 *
	 * 		Checks if there is a live part. If lang is passed, then it looks to match that lang
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function doesLivePartExist($lang = FALSE)
	{
		$existingPartId = FALSE;
		foreach ($this->parts as $partId => $part) {
			if (!$existingPartId && $part['status'] == 1) {
				if ($lang && $part['lang'] == $lang) {
					$existingPartId = $partId;
				}
				else if (!$lang) {
					$existingPartId = $partId;
				}
			}
		}
		return $existingPartId;
	}

	/***********************************************************************************
	 *
	 * Capture related methods
	 *
	 **********************************************************************************/

	/*
	 *
	 * Function:  loadPartObject()
	 *
	 * 		Loads the Part if it is not already loaded
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function loadPartObject($status = 1)
	{
		d('KoId: ' . $this->koId . ": Cln_KO loadPartObject status: $status", 3);
		if ($status == 0 && !$this->isEditable()) {
			$status = 1;
		}

		// If they're editing something, override based on the editlang
		if (isset($_REQUEST['editKoId'])) {
			$this->setEditLang();
			$lang = $this->editLang;
		}

		if (empty($lang)) {
			d('KoId: ' . $this->koId . ': Cln_KO loadPartObject - Lang is empty', 3);
			// test for most appropriate language
			foreach ($this->parts as $objectId => $objectInfo) {
				if($objectInfo['status'] == $status) {
					$availableLanguages[] = $objectInfo['lang'];
					//print $objectInfo['lang'];
				}
			}
			if(isset($availableLanguages)) {
				$useThisLang = whichLanguage($availableLanguages);
				d('useThisLang: '.$useThisLang,4);
				foreach ($this->parts as $objectId => $objectInfo) {
					if ($objectInfo['lang'] == $useThisLang && $objectInfo['status'] == $status) {
						$relevantPartId = $objectId;
						$relevantLang = $objectInfo['lang'];
						break;
					}
				}
			}
		} else {
			d('KoId: ' . $this->koId . ": Cln_KO loadPartObject - Lang is $lang", 3);
			foreach ($this->parts as $objectId => $objectInfo) {
				if ($objectInfo['lang'] == $lang && $objectInfo['status'] == $status) {
					$relevantPartId = $objectId;
					$relevantLang = $objectInfo['lang'];
				}
			}
		}

		// If there isn't a relevant part, return FALSE
		// this is causing trouble with background processes (ie. creating rooms in the background)
		//  we may need to rethink this section - dwc
		if (!isset($relevantPartId)) {
			d('there isn\'t any relatent part');
			return FALSE;
		}

		// At one point, the above if statement was causing trouble with background
		// processes (ie. creating rooms in the background), but this should fix that.
 		$this->currentPart = & $this->parts[$relevantPartId];

		$this->loadKOPart($relevantPartId);

		return TRUE;
	}


	/*
	 *
	 * Function:  loadPartObjectById()
	 *
	 * 		Loads the object for the PartId passed if it is not already loaded
	 *
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function loadPartObjectById($partObjectId)
	{

		// If there isn't a relevant part, return FALSE
		if (!isset($this->parts[$partObjectId])) {
			return FALSE;
		}

		$this->loadKOPart($partObjectId);

		return TRUE;
	}
	
	
	/*
	 *
	 * Function:  loadKOPart()
	 *
	 * 		creates the instance of this object
	 *
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function loadKOPart($partObjectId)
	{
		// Define the class path constant, etc.
		$className = $_SESSION['Module_Loader']->getModuleClassname($this->modId);
		$classPath = $_SESSION['Module_Loader']->getModulePath($this->modId);
		$modPath = dirname($classPath)."/";
		$pathConstant = "MOD_".strtoupper($className)."_PATH";
		if(!defined($pathConstant)) define($pathConstant,$modPath);

		// Load the object if it's not there
		if (!$this->parts[$partObjectId]['object']) {
			// Load the class for that module
			include_once($classPath);
			
			if (!class_exists($className)) {
				PEAR::raiseError("Couldn't load class $className", E_WARNING);
				return FALSE;
			}

			$this->parts[$partObjectId]['object'] = new $className($partObjectId);
			$this->parts[$partObjectId]['object']->_super =& $this;
			$this->parts[$partObjectId]['object']->_part =& $this->parts[$partObjectId];
			$this->parts[$partObjectId]['object']->loadModuleData();
		}

		return TRUE;
	}


	/*
	 *
	 * Function:  loadAllParts()
	 *
	 * 		Loads all of the parts
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function loadAllParts()
	{
		// kick starts it
		$this->loadPartObject(0);

		$oldCurrentPartId = $this->currentPart['objectId'];

		foreach($this->parts as $partId => $object) {
			$this->loadPartObjectById($partId);
			if(isset($oldCurrentPartId)) {
				d('setting currentPart');
				$this->currentPart =& $this->parts[$partId];
			}

		}

		return TRUE;
	}


	/*
	 *
	 * Function:  loadMetadata()
	 *
	 * 		Loads the current part's metadata
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function loadMetadata()
	{
		d('KoId: ' . $this->koId . ': Cln_KO loadMetadata');

		if (!$this->currentPart['metadata']) {
			$this->currentPart['metadata'] = & new $GLOBALS['classes']['metadata']['classname']($this->koId, $this->currentPart['objectId']);
			$this->currentPart['isMetadataLoaded'] = TRUE;
		}

		$this->currentMetadata = & $this->currentPart['metadata'];
	}


	/***********************************************************************************
	 *
	 * Capture related methods
	 *
	 **********************************************************************************/

	/*
	 *
	 * Function:  captureMetadata()
	 *
	 * 		Captures the metadata if it's submitted
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function captureMetadata()
	{
		d('----capturing metadata for '.$this->currentPart['title'],2);
		$noErrors = TRUE;
		$noErrors = $this->currentMetadata->captureMetadata();

		// The Title
		if (isset($_POST['koMetadataTitle'])) {
			$this->currentPart['title'] = $_POST['koMetadataTitle'];
			if (empty($this->currentPart['title'])) {
				PEAR::raiseError('You need to enter a title for this KO', E_USER_WARNING);
				$noErrors = FALSE;
			}
			// Do the unique title searching here, if it's set to be unique titles
			if (CLN_FORCE_UNIQUE_TITLES) {
				$sql = sprintf("SELECT koId FROM `%s` WHERE title = '%s' AND koId != %d",
								KO_TABLE, $this->currentPart['title'], $this->koId);
				$db = &Cln_Db::singleton(MAIN_CLN_DSN);
				$result = $db->query($sql);
				if ($result->numRows() > 0) {
					PEAR::raiseError('The title you entered is used on another knowledge object. Please change the title to something unique.', E_USER_WARNING);
					$noErrors = FALSE;
				}
			}
		}
		else {
			// If the title isn't submitted, then they musn't be on a page with
			// metadata :HACK: this is kind of a hack, there must be a better way
			return FALSE;
		}

		// The Language
		if (isset($_POST['koMetadataLang'])) {
			$this->currentPart['lang'] = $_POST['koMetadataLang'];
		}

		// The Unit
		if (isset($_POST['koMetadataDetailRequest'])) {
			$this->currentPart['displayMetadataDetails'] =  TRUE;
		}
		else {
			$this->currentPart['displayMetadataDetails'] = FALSE;
		}

		return $noErrors;
	}


	/*
	 *
	 * Function:  captureRoles()
	 *
	 * 		Captures the roles if it's submitted
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function captureRoles()
	{
		unset($this->roles);
		$errors = FALSE;
		$haveOwner = FALSE;
		$haveViewer = FALSE;

		foreach ($_POST['groupRoles'] as $groupId => $roleArray) {
			foreach ($roleArray as $key => $roleId) {
				if ($roleId == 1) {
					$haveOwner = TRUE;
				}
				else if ($roleId == 4) {
					$haveViewer = TRUE;
				}
				$this->roles[$groupId][] = $roleId;
			}
		}

		// We need an owner, unless we're the superUser
		if (!$haveOwner && !in_array(CLN_SUPERUSER_GROUPID,$_SESSION['User']->groupList)) {
			PEAR::raiseError('You need to at least have one group marked as Owner', E_USER_WARNING);
			$errors = TRUE;
		}

		// We need a viewer
		if (!$haveViewer) {
			PEAR::raiseError('You need to at least have one group marked as Viewer', E_USER_WARNING);
			$errors = TRUE;
		}

		// If no errors, then save the roles
		if (!$errors) {
			return $this->saveRoles();
		}
		else {
			return FALSE;
		}
	}




	/***********************************************************************************
	 *
	 * Saving methods
	 *
	 **********************************************************************************/

	/*
	 *
	 * Function:  save()
	 *
	 * 		saves the whole KO including all parts, roles and metadata
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function save()
	{
		$savedCurrentId = $this->currentPart['objectId'];
		foreach($this->parts as $partId => $part) {
			$this->currentPart =& $this->parts[$partId];
			if($this->koId == 'NEW') $this->currentPart['newPart'] = TRUE;

			// it would be nice if saveModuleData was inside of savePart()
			$this->savePart();
		}
		if ($savedCurrentId != 'NEW') {
			$this->currentPart =& $this->parts[$savedCurrentId];
		}
		$this->saveRoles();
		return TRUE;
	}


	/*
	 *
	 * Function:  savePart()
	 *
	 * 		saves current knowledge object part
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function savePart()
	{
		d('KoId: ' . $this->koId . ': Cln_KO savePart');
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		// First save the part object
		if ($this->currentPart['objectId'] == 'NEW') {
			$newObjectId = $this->currentPart['object']->saveModuleData();
			$this->currentPart['objectId'] = $newObjectId;
			//$this->currentMetadata->objectId = $newObjectId;
			$this->parts[$newObjectId] = $this->currentPart;
			unset($this->parts['NEW']);
			$this->currentPart = & $this->parts[$newObjectId];
			$this->currentPart['newPart'] = TRUE;
			$this->currentMetadata->objectId = $newObjectId;

		}
		else {
			$this->currentPart['object']->saveModuleData();
		}

		// Then save the KO part info
		// Get a new koid if needed
		if ($this->koId == 'NEW') {
			$this->koId = $this->getNewKoId();
			$this->currentMetadata->koId = $this->koId;
		}

		// If it's a new part build the SQL
		if (isset($this->currentPart['newPart']) && $this->currentPart['newPart']) {
			$sql = sprintf("INSERT INTO `%s` (`koId`, `objectId`, `modId`, `status`, `lang`, "
						   ."`original`, `title`, `created`, `createdBy`, `modified`, `modifiedBy`) "
						   ."VALUES (%d, %d, %d, %d, '%s', %d, '%s', NOW(), %d, NOW(), %d)",
							KO_TABLE, $this->koId, $this->currentPart['objectId'], $this->modId,
							$this->currentPart['status'], addslashes($this->currentPart['lang']),
							$this->currentPart['original'], addslashes($this->currentPart['title']),
							$this->creator, $this->creator);
			$this->currentPart['newPart'] = FALSE;
		}

		// For existing parts, build the SQL
		else {
			$sql = sprintf("UPDATE `%s` SET title = '%s', modifiedBy = %d WHERE koId = '%s' AND objectId = '%s'",
							KO_TABLE, addslashes($this->currentPart['title']), $_SESSION['User']->userId,
							$this->koId, $this->currentPart['objectId']);
		}

		// Run the SQL
		$result = $db->query($sql);
		if (PEAR::isError($result))	 {
			// :NOTE: i keep having to comment these out, until we fix our language system
			//PEAR::raiseError('ADMIN_EM_DB_ERROR_INSERT, E_USER_ERROR);
			PEAR::raiseError('Unable to insert into '.KO_TABLE.' table: '.$sql, E_ERROR);
			return FALSE;
		}

		// can this stay here? - dwc - NO ;-) cdb - now I've added it back up above...hmm..better verify
		// why I said it couldn't be here, because it makes sense being up above in this method to me right now
		// $this->currentPart['object']->saveModuleData();

		// Then save the metadata
		$this->saveMetadata();

		// Then update modified
		$this->updateModified();
		return TRUE;
	}


	/*
	 *
	 * Function:  saveRoles()
	 *
	 * 		saves the roles
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function saveRoles()
	{
		d('KoId: ' . $this->koId . ': Cln_KO saveRoles');
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		$sql = sprintf("DELETE FROM `%s` WHERE koId = '%s'", PERMISSION_TABLE, $this->koId);

		$result = $db->query($sql);

		if (PEAR::isError($result)) {
			PEAR::raiseError("Couldn't delete the roles before saving them with $sql", E_USER_ERROR);
			PEAR::raiseError('There was an error saving the roles, please try again', E_USER_WARNING);
			return FALSE;
		}
		else {
			foreach ($this->roles as $groupId => $rolesArray) {
				foreach ($rolesArray as $key => $roleId) {
					$valuesArray[] = sprintf("('%s','%s','%s',NOW())", $this->koId, $roleId, $groupId);
				}
			}

			if(isset($valuesArray)) {
				$valuesList = implode(', ', $valuesArray);

				$sql = sprintf("INSERT INTO `%s` (`koId`, `roleId`, `groupId`, `created`) VALUES %s", PERMISSION_TABLE, $valuesList);
				$result = $db->query($sql);

				if (PEAR::isError($result)) {
					PEAR::raiseError("Couldn't save the roles with $sql", E_USER_ERROR);
					PEAR::raiseError('There was an error saving the roles, please try again', E_USER_WARNING);
					return FALSE;
				}
				else {
					return TRUE;
				}
			}
		}
	}


	/*
	 *
	 * Function:  saveMetadata()
	 *
	 * 		saves the metadata
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */
	function saveMetadata()
	{
		d('KoId: ' . $this->koId . ': Cln_KO saveMetadata');
		if ($this->koId != 'NEW') {
			if (isset($this->currentMetadata) && !$this->currentMetadata->saveMetadata($this->koId)) {
				PEAR::raiseError('There was an error saving the metadata, please try again', E_ERROR);
				return FALSE;
			}
			else {
				return TRUE;
			}
		}
		else {
			return TRUE;
		}
	}


	/*
	 *
	 * Function:  getNewKoId()
	 *
	 * 		gets new koId from KO table
	 *
	 * @access public
	 * @return Boolean 	TRUE or FALSE
	 *
	 */

	function getNewKoId()
	{
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		return $db->nextId(KO_TABLE);
	}


	/***********************************************************************************
	 *
	 * Interface/Display related methods
	 *
	 **********************************************************************************/
	/*
	 *
	 * Function:  getEditPanel()
	 *
	 * 		Returns the edit panel
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getEditPanel()
	{
		d('KoId: ' . $this->koId . ': Cln_KO getEditPanel');
		$koGeneralParts = Array();
		$moduleGeneralParts = $this->currentPart['object']->getEditPanelGeneral();
		$moduleLanguageParts = $this->currentPart['object']->getEditPanelLanguages();

		if($this->isManageable()) {
			$koGeneralParts[0]['title'] = 'Change Roles';
			$koGeneralParts[0]['description'] = 'Choose who can edit, view, and publish this '.$this->modName;
			$koGeneralParts[0]['editProcess'] = 'Roles';
		}

		$koLanguageParts[0]['title'] = 'Change Metadata Description';
		$koLanguageParts[0]['description'] = 'Describe this '.$this->modName.' so others can find it easily';
		$koLanguageParts[0]['editProcess'] = 'Metadata';


		// Publish - only if they are allowed
		if ($this->koId != 'NEW' && $this->isEditable(1)) {
			$koLanguageParts[1]['title'] = 'Publish';
			$koLanguageParts[1]['description'] = 'Publish the draft version of this '.$this->modName.' to the live site';
			$koLanguageParts[1]['editProcess'] = 'Publish';
			
			if ($this->doesLivePartExist($this->currentPart['lang'])) {
				$koLanguageParts[2]['title'] = 'Revert';
				$koLanguageParts[2]['description'] = 'Revert to the live version of this '.$this->modName . '. This will write over any changes in the draft version.';
				$koLanguageParts[2]['editProcess'] = 'Revert';
			}
		}

		// Display a Return to Parent link if there is a parent process
		if ($_SESSION['ProcessManager']->processCount > 2) {
			$parentProcessNumber = $_SESSION['ProcessManager']->processCount - 2;
			$parentProcess = & $_SESSION['ProcessManager']->processStack[$parentProcessNumber];

			if ($parentProcess['process'] == 'editPage') {
				$saveMessage['title'] = 'Return to the Page this Block is On';
				$saveMessage['description'] = 'Return to the Control Panel for the Page this Block was Added To';
			}
			else if ($parentProcess['process'] == 'editRoom') {
				$currentProcessNumber = $parentProcessNumber + 1;
				$currentProcess = & $_SESSION['ProcessManager']->processStack[$currentProcessNumber];
				if ($currentProcess['process'] == 'editRoom') {
					$saveMessage['title'] = 'Return to the Room this Room is in.';
					$saveMessage['description'] = 'Return to the Control Panel for the Room that this room is located in.';
					// :TODO: This doesn't actually return you right now, which needs fixing
				}
				else {
					$saveMessage['title'] = 'Return to the Room this Page is in.';
					$saveMessage['description'] = 'Return to the Control Panel for the Room this page is in.';
				}
			}
			else {
				$saveMessage['title'] = 'Return to Parent';
				$saveMessage['description'] = 'Return to the Control Panel for the Parent';
			}
			$saveMessage['editProcess'] = 'ReturnToParent';
		}

		ob_start();
		include('interfaces/panel.html');
		$panel = ob_get_contents();
		ob_end_clean();
		return $panel;
	}


	/*
	 *
	 * Function:  getEditMetadata()
	 *
	 * 		Returns the metadata form
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getEditMetadata()
	{
		$this->loadMetadata();
		return $this->getInterface('interfaces/editMetadata.html');
	}


	/*
	 *
	 * Function:  getEditRoles()
	 *
	 * 		Returns the roles form
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getEditRoles()
	{
		$this->loadRoles();
		return $this->getInterface('interfaces/editRoles.html');
	}


	/*
	 *
	 * Function:  getSelectGroups()
	 *
	 * 		Returns the select groups form
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getSelectGroups()
	{
		return $this->getInterface('interfaces/selectGroups.html');
	}


	/*
	 *
	 * Function:  getEditGroup()
	 *
	 * 		Returns the edit group form
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getEditGroup()
	{
		return $this->getInterface('interfaces/Group/editGroup.html');
	}


	/*
	 *
	 * Function:  getSelectUsers()
	 *
	 * 		Returns the select users form
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getSelectUsers()
	{
		return $this->getInterface('interfaces/Group/selectUsers.html');
	}


	/*
	 *
	 * Function:  getSelectLanguage()
	 *
	 * 		Returns the select languages form
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getSelectLanguage()
	{
		return $this->getInterface('interfaces/SelectLanguage.html');
	}


	/*
	 *
	 * Function:  getNotViewable()
	 *
	 * 		shows a message about KO which is not available
	 *		for viewing due to permissions issues.
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getNotViewable()
	{
		$message = 'This ' . $this->modName.' is not available for viewing.';
		return $message;
	}


	/*
	 *
	 * Function:  getNotEditable()
	 *
	 * 		shows a message about KO which is not available
	 *		for editing due to permissions issues.
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getNotEditable()
	{
		$message = 'This ' . $this->modName.' is not available for editing.';
		return $message;
	}


	/*
	 *
	 * Function:  getNoLive()
	 *
	 * 		shows a message about KO which is not available
	 *		for editing due to permissions issues.
	 *
	 * @access public
	 * @return String 		$message
	 *
	 */
	function getNoLive()
	{
		$message = 'Live '.$this->modName.' is not available.';
		return $message;
	}


	/*
	 *
	 * Function:  getInterface()
	 *
	 * 		returns an admin interface
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function getInterface($file)
	{
		ob_start();
		include($file);
		$content = ob_get_contents();
		ob_end_clean();
		return $content;
	}


	/*
	 *
	 * Function:  getStatus()
	 *
	 * 		returns the status of a KO
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function getStatus()
	{
		// get lang from currentPart
		$lang = $this->currentPart['lang'];

		// Loop through each part, getting basic modified data
		foreach ($this->parts as $partId => $part) {
			// Get the data
			$partModified = $part['modified'];

			// Put it in a useful place
			if ($part['status'] == 0) {
				$draftModified[$lang] = $partModified;
			}
			else {
				$liveModified[$lang] = $partModified;
			}
		}

		// Loop through the results, figuring out what's what
		foreach ($draftModified as $thisLanguage => $modified) {
			if($thisLanguage == $lang) {
				if (!isset($liveModified[$lang])) {
					return 0;
				}
				else if ($modified > $liveModified[$lang]) {
					return 1;
				}
				else {
					return 2;
				}
			}
		}
	}


	/*
	 *
	 * Function:  updateModified()
	 *
	 * 		updates the Modified of the current part from the KO table
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function updateModified()
	{
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		$sql = sprintf("UPDATE `%s` SET modified = NOW() WHERE koId = %d and objectId = %d",
						KO_TABLE, $this->koId, $this->currentPart['objectId']);
		$result = $db->query($sql);

		// If there weren't any errors, get the new modified
		if (PEAR::isError($result)) {
			PEAR::raiseError('Error on updateModified from Cln_KO', E_ERROR);
			return FALSE;
		}
		else {
			$sql = sprintf("SELECT modified FROM `%s` WHERE koId = %d and objectId = %d",
							KO_TABLE, $this->koId, $this->currentPart['objectId']);
			$result = $db->getOne($sql);
			$this->currentPart['modified'] = $result;
			return TRUE;
		}
	}


	/***********************************************************************************
	 *
	 * Permissions related methods
	 *
	 **********************************************************************************/

	/*
	 *
	 * Function:  isViewable()
	 *
	 * 		checks to see whether KO is viewable by user
	 *
	 * @access public
	 * @return Boolean 		TRUE or FALSE
	 *
	 */
	function isViewable()
	{
		// Load the roles, and prepare the relevant info
		$this->loadRoles();
		$viewableRoleIds = explode(',', CLN_VIEWER_ROLEIDS);

		// Loop through each loaded role, to see if there's a match
		$viewable = FALSE;

		// giving da man, all the power
		if(in_array(CLN_SUPERUSER_GROUPID,$_SESSION['User']->groupList)) {
			return TRUE;
		} else {
			foreach ($this->roles as $groupId => $rolesArray) {
				if (!$viewable && in_array($groupId, $_SESSION['User']->groupList)) {
					$result = array_intersect($rolesArray, $viewableRoleIds);
					if (count($result) > 0) {
						$viewable = TRUE;
					}
				}
			}

			if(!$viewable && $this->isCreator()) {
				$viewable = TRUE;
			}
		}

		if($viewable) $this->viewable = TRUE;

		return $viewable;
	}


	/*
	 *
	 * Function:  isEditable()
	 *
	 * 		checks to see whether KO is viewable by user
	 *
	 * @access public
	 * @return Boolean 		TRUE or FALSE
	 *
	 */
	function isEditable($partStatus = 0)
	{
		// Load the roles, and prepare the relevant info
		$this->loadRoles();

		// If they're trying to edit the Live (ie. Publish)
		if ($partStatus == 1) {
			$editableRoleIds = explode(',', CLN_PUBLISHER_ROLEIDS);
		}
		// Else, they're trying to edit a draft (default)
		else {
			$editableRoleIds = explode(',', CLN_COLLABORATOR_ROLEIDS);
		}

		// Loop through each loaded role, to see if there's a match
		$editable = FALSE;

		// giving the super user, all the power
		if(in_array(CLN_SUPERUSER_GROUPID,$_SESSION['User']->groupList)) {
			$editable = TRUE;
		} else {
			foreach ($this->roles as $groupId => $rolesArray) {
				if (!$editable && in_array($groupId, $_SESSION['User']->groupList)) {
					$result = array_intersect($rolesArray, $editableRoleIds);
					if (count($result) > 0) {
						$editable = TRUE;
					}
				}
			}

			if(!$editable && $this->isCreator()) {
				$editable = TRUE;
			}
		}

		if($editable) $this->editable = TRUE;

		return $editable;
	}


	/*
	 *
	 * Function:  isManageable()
	 *
	 * 		checks to see whether KO is manageable by user
	 *
	 * @access public
	 * @return Boolean 		TRUE or FALSE
	 *
	 */
	function isManageable()
	{
		d('Cln_KO isManageable()',4);

		// Load the roles, and prepare the relevant info
		$this->loadRoles();

		$manageableRoleIds = explode(',', CLN_OWNER_ROLEIDS);

		// Loop through each loaded role, to see if there's a match
		$manageable = FALSE;

		// giving da man, all the power
		if(in_array(CLN_SUPERUSER_GROUPID,$_SESSION['User']->groupList)) {
			$manageable = TRUE;
		} else {
			foreach ($this->roles as $groupId => $rolesArray) {
				if (!$manageable && in_array($groupId, $_SESSION['User']->groupList)) {
					$result = array_intersect($rolesArray, $manageableRoleIds);
					if (count($result) > 0) {
						$manageable = TRUE;
					}
				}
			}
		}

		if($manageable) $this->manageable = TRUE;

		return $manageable;
	}


	/*
	 *
	 * Function:  isCreator()
	 *
	 * 		checks to see whether KO was created by user
	 *
	 * @access public
	 * @return Boolean 		TRUE or FALSE
	 *
	 */
	function isCreator()
	{
		/*$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		$sql = sprintf("SELECT createdBy FROM `%s` WHERE koId = %d",
						KO_TABLE, $this->koId);

		if($db->getOne($sql) == $_SESSION['User']->userId) {*/

		if($this->creator == $_SESSION['User']->userId) {
			return TRUE;
		} else {
			return FALSE;
		}
	}


	/*
	 *
	 * Function:  loadRoles()
	 *
	 * 		Loads all the roles
	 *
	 * @access public
	 * @return Boolean 		TRUE or FALSE
	 *
	 */
	function loadRoles()
	{
		if (!$this->isRolesLoaded) {
			d('KoId: ' . $this->koId . ': loadRoles()',3);
			
			unset($this->roles);
			$this->roles = Array();

			$sql = sprintf("SELECT groupId, roleId FROM %s WHERE koId = '%s'",
							PERMISSION_TABLE, $this->koId);

			$db = &Cln_Db::singleton(MAIN_CLN_DSN);
			$result = $db->query($sql);

			while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
				$roleId = $row['roleId'];
				$groupId = $row['groupId'];
				$this->roles[$groupId][] = $roleId;
			}

			$this->isRolesLoaded = TRUE;
		}

		return TRUE;
	}


	/*
	 *
	 * Function:  loadAllGroups()
	 *
	 *		Returns an associative array of all available groups, with groupId as the key
	 *
	 * @access public
	 * @return Boolean		TRUE or FALSE
	 *
	 */

	function loadAllGroups()
	{
		// Query the DB
		$sql = sprintf("SELECT groupId, name FROM `%s`",
					   GROUP_TABLE);
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);
		$groups = stripslashesArray($db->getAssoc($sql));

		// Output errors
		if (PEAR::isError($groups)) {
			PEAR::raiseError('Unable to select all the groups '.$sql, E_WARNING);
			return FALSE;
		} else {
			return $groups;
		}
	}


	/*
	 *
	 * Function:  loadAllRoles()
	 *
	 *		Returns an associative array of all available roles, with roleId as the key
	 *
	 * @access public
	 * @return Boolean		TRUE or FALSE
	 *
	 */

	function loadAllRoles()
	{
		// Query the DB
		$sql = sprintf('SELECT roleId, role FROM `%s`',
					   ROLES_TABLE);
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);
		$roles = stripslashesArray($db->getAssoc($sql));

		// Output errors
		if (PEAR::isError($roles)) {
			PEAR::raiseError('Unable to select all the roles '.$sql, E_WARNING);
			return FALSE;
		} else {
			return $roles;
		}
	}


	/*
	 *
	 * Function:  checkRecordLocks()
	 *
	 * 		Checks for record locks on this object. If there is one, verifies
	 *		that it's for this user. If not, it checks the time and then either
	 *		deletes it and resets another one, or kicks them out. If it is for this
	 *		user, updates the lock to NOW
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function checkRecordLocks()
	{
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		// If this is a NEW ko, return TRUE right away (no lock setting)
		if ($this->koId == 'NEW') {
			return TRUE;
		}

		// Get any record locks for this koId
		$sql = sprintf('SELECT userId, time FROM `%s` WHERE koId = %d',
					   RECORD_LOCK_TABLE, $this->koId);
		$result = $db->query($sql);

		// If there are any:
		if ($result->numRows() > 0) {
			$row = $result->fetchRow(DB_FETCHMODE_ASSOC);

			// If it is for this user, update the record lock
			if ($row['userId'] == $_SESSION['User']->userId) {
				$this->updateRecordLock();
			}

			// Else it's for another user, check the time on it
			else {
				$timeTest = $row['time'] + CLN_RECORD_LOCK_TIME;

				// If it's passed, delete it, and create a new lock
				if ($timeTest < time()) {
					$this->deleteRecordLocks();
					$this->setRecordLock();
				}
				// Else kick them out of here, with a message
				else {
					$userName = $_SESSION['User']->getUserName($row['userId']);
					PEAR::raiseError("It appears that <b>{$userName}</b> is currently editing the block you are trying to access, please try again in a few minutes.", E_USER_WARNING);
					clnRedirect(cleanURL($GLOBALS['path']));
					exit;
				}
			}
		}
		// Else there aren't any, set one
		else {
			$this->setRecordLock();
			return TRUE;
		}
	}


	/*
	 *
	 * Function:  setRecordLock()
	 *
	 * 		Sets a record lock for this user for this koId for now
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function setRecordLock()
	{
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		$sql = sprintf('INSERT INTO `%s` (koId, userId, time) VALUES (%d, %d, %d)',
					   RECORD_LOCK_TABLE, $this->koId, $_SESSION['User']->userId, time());
		$result = $db->query($sql);
		$_SESSION['recordsLocked'] = TRUE;
	}


	/*
	 *
	 * Function:  updateRecordLock()
	 *
	 * 		Updates the record lock for this user for this koId for now
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function updateRecordLock()
	{
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		$sql = sprintf('UPDATE `%s` SET time = %d WHERE koId = %d AND userId = %d',
					   RECORD_LOCK_TABLE, time(), $this->koId, $_SESSION['User']->userId);
		$result = $db->query($sql);
	}


	/*
	 *
	 * Function:  deleteRecordLocks()
	 *
	 * 		Deletes ALL record locks for this koId
	 *
	 * @access public
	 * @return String 	$content
	 *
	 */
	function deleteRecordLocks()
	{
		$db = &Cln_Db::singleton(MAIN_CLN_DSN);

		$sql = sprintf('DELETE FROM `%s` WHERE koId = %d',
					   RECORD_LOCK_TABLE, $this->koId, $_SESSION['User']->userId);
		$result = $db->query($sql);
	}

	/***********************************************************************************
	 *
	 * Misc/Incomplete methods
	 *
	 **********************************************************************************/

}
?>
Return current item: Community Learning Network