<commit_before><?

defined('C5_EXECUTE') or die(_("Access Denied."));

/**
*
* The page object in Concrete encapsulates all the functionality used by a typical page and their contents
* including blocks, page metadata, page permissions. 
* @package Pages
*
*/
class Page extends Collection {

	public static function getByPath($path, $version = 'RECENT') {
		$db = Loader::db();
		$cID = $db->GetOne("select cID from PagePaths where cPath = ?", array($path));
		return Page::getByID($cID, $version);
	}
	
	public static function getByID($cID, $version = 'RECENT') {
		if ($version) {
			$version = CollectionVersion::getNumericalVersionID($cID, $version);
		}
		$ca = new Cache();
		$c = ($version > 0) ? $ca->get('page', $cID . ':' . $version) : $ca->get('page', $cID);

		if ($c instanceof Page) {
			return $c;
		}
		
		$where = "where Pages.cID = ?";
		$c = new Page;
		$c->populatePage($cID, $where, $version);
		if ($c->cPointerID < 1) {
			if ($version > 0) {
				$ca->set('page', $c->getCollectionID() . ':' . $version, $c);
			} else {
				$ca->set('page', $c->getCollectionID(), $c);
			}
		}
		return $c;
	}
	
	protected function populatePage($cInfo, $where, $cvID) {
		$db = Loader::db();
		
		$q0 = "select Pages.cID, Pages.pkgID, Pages.cPointerID, Pages.cPointerExternalLink, Pages.cFilename, Collections.cDateAdded, Pages.cDisplayOrder, Collections.cDateModified, cInheritPermissionsFromCID, cInheritPermissionsFrom, cOverrideTemplatePermissions, cPendingAction, cPendingActionUID, cPendingActionTargetCID, cPendingActionDatetime, cCheckedOutUID, cIsTemplate, uID, cPath, Pages.ctID, ctHandle, ctIcon, ptID, cParentID, cChildren, ctName from Pages inner join Collections on Pages.cID = Collections.cID left join PageTypes on (PageTypes.ctID = Pages.ctID) left join PagePaths on (Pages.cID = PagePaths.cID and PagePaths.ppIsCanonical = 1) ";
		$q2 = "select cParentID, cPointerID, cPath, Pages.cID from Pages left join PagePaths on (Pages.cID = PagePaths.cID and PagePaths.ppIsCanonical = 1) ";

		if ($cInfo != 1) {
			if (empty($where)) {
				$ppWhere = "where ";
			} else {
				$ppWhere = " and ";
			}
			$ppWhere .= "PagePaths.ppIsCanonical = 1";
		} else {
			$ppWhere = '';
		}
//TBD
$ppWhere = '';
		
		$v = array($cInfo);
		$q2 .= $where . $ppWhere;

		$r = $db->query($q2, $v);
		$row = $r->fetchRow();
		if ($row['cPointerID'] > 0) {
			$q1 = $q0 . "where Pages.cID = ?" . $ppWhere;
			$cPointerOriginalID = $row['cID'];
			$v = array($row['cPointerID']);
			$cParentIDOverride = $row['cParentID'];
			$cPathOverride = $row['cPath'];
			$cPointerID = $row['cPointerID'];
		} else {
			$q1 = $q0 . $where . $ppWhere;
		}

		// isTemplate = 0 because we don't want to get return templates

		$r = $db->query($q1, $v);
		if ($r) {
			$row = $r->fetchRow();
			if ($row) {
				foreach ($row as $key => $value) {
					$this->{$key} = $value;
				}
				if (isset($cParentIDOverride)) {
					$this->cPointerID = $cPointerID;
					$this->cPointerOriginalID = $cPointerOriginalID;
					$this->cPath = $cPathOverride;
					$this->cParentID = $cParentIDOverride;
				}
				$this->isMasterCollection = $row['cIsTemplate'];
			} else {
				if ($cInfo == 1) {
					$this->cID = '1';
					$this->loadError(COLLECTION_INIT);
				} else {
					// there was no record of this particular collection in the database
					$this->loadError(COLLECTION_NOT_FOUND);
				}
			}
			$r->free();
		} else {
			$this->loadError(COLLECTION_NOT_FOUND);
		}
	

		if ($cvID != false) {
			// we don't do this on the front page
			$this->loadVersionObject($cvID);
		}
	}		

	public function isEditMode() {
		$v = View::getInstance();
		return ($this->isCheckedOutByMe() && ($v->editingEnabled()));
	}
	
	public function getPackageID() {return $this->pkgID;}
	public function getPackageHandle() {
		return PackageList::getHandle($this->pkgID);
	}
	public function isArrangeMode() {return ($this->isCheckedOutByMe() && ($_REQUEST['btask'] == 'arrange'));}
				
	public function forceCheckIn() {
		// This function forces checkin to take place
		$db = Loader::db();
		$q = "update Pages set cIsCheckedOut = 0, cCheckedOutUID = null, cCheckedOutDatetime = null, cCheckedOutDatetimeLastEdit = null where cID = '{$this->cID}'";
		$r = $db->query($q);
	}
	
	public function isAdminArea() {
		if ($this->isGeneratedCollection()) {
			$pos = strpos($this->getCollectionFilename(), "/" . DIRNAME_DASHBOARD);
			return ($pos > -1);
		}			
		return false;
	}
	
	/** 
	 * Takes an array of area/block values and makes that the arrangement for this page's version
	 * Format is like: $area[10][0] = 2, $area[10][1] = 8, $area[15][0] = 27, with the area ID being the 
	 * key and the block IDs being 1-n values inside it
	 */
	public function processArrangement($areas) {
		// this function is called via ajax, so it's a bit wonky, but the format is generally
		// a{areaID} = array(b1, b2, b3) (where b1, etc... are blocks with ids appended.)
		$db = Loader::db();
		foreach($areas as $arID => $blocks) {
			if (intval($arID) > 0) {
				// this is a serialized area;
				$arHandle = $db->getOne("select arHandle from Areas where arID = ?", array($arID));
				$startDO = 0;
				
				foreach($blocks as $bID) {
					if (intval($bID) > 0) {
						$v = array($startDO, $arHandle, $bID, $this->getCollectionID(), $this->getVersionID());
						try {
							$db->query("update CollectionVersionBlocks set cbDisplayOrder = ?, arHandle = ? where bID = ? and cID = ? and cvID = ?", $v);
						} catch(Exception $e) {}
						
						$startDO++;
					}
				}
			}
		}
	}
	
	function isCheckedOut() {
		// function to inform us as to whether the current collection is checked out
		$db = Loader::db();
		if (isset($this->isCheckedOutCache)) {
			return $this->isCheckedOutCache;
		}
		
		$dh = Loader::helper('date');
				
		$q = "select cIsCheckedOut, UNIX_TIMESTAMP('" . $dh->getLocalDateTime() . "') - UNIX_TIMESTAMP(cCheckedOutDatetimeLastEdit) as timeout from Pages where cID = '{$this->cID}'";
		$r = $db->query($q);
		if ($r) {
			$row = $r->fetchRow();
			if ($row['cIsCheckedOut'] == 0) {
				return false;
			} else {
				if ($row['timeout'] > CHECKOUT_TIMEOUT) {
					$this->forceCheckIn();
					$this->isCheckedOutCache = false;
					return false;
				} else {
					$this->isCheckedOutCache = true;
					return true;
				}
			}
		}
	}

	function isCheckedOutByMe() {
		$u = new User();
		return ($this->getCollectionCheckedOutUserID() > 0 && $this->getCollectionCheckedOutUserID() == $u->getUserID());
	}

	function isGeneratedCollection() {
		// generated collections are collections without types, that have special cFilename attributes
		return $this->cFilename != null && $this->ctID == 0;
	}
	
	function assignPermissionSet($permissionsArray) {
		$db = Loader::db();
		// first, we make sure to set this collection's permission inheritance to override
		if ($this->getCollectionInheritance() != 'OVERRIDE') {
			// now, if we were inheriting permissions from elsewhere, we'll grab those, copy them to this node, before we
			// assign/add new permission
			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, uID, gID, cgPermissions, cgStartDate, cgEndDate from PagePermissions where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['uID'], $row['gID'], $row['cgPermissions'], $row['cgStartDate'], $row['cgEndDate']);
				$q = "insert into PagePermissions (cID, uID, gID, cgPermissions, cgStartDate, cgEndDate) values (?, ?, ?, ?, ?, ?)";
				$db->query($q, $v);
			}
			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, uID, gID, ctID from PagePermissionPageTypes where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['uID'], $row['gID'], $row['ctID']);
				$q = "insert into PagePermissionPageTypes (cID, uID, gID, ctID) values (?, ?, ?, ?)";
				$db->query($q, $v);
			}
			// ack - we need to copy area permissions from that page as well
			// wait, i'm not sure if we need to do this anymore. 
			
			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, arHandle, gID, uID, agPermissions from AreaGroups where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['arHandle'], $row['gID'], $row['uID'], $row['agPermissions']);
				$q = "insert into AreaGroups (cID, arHandle, gID, uID, agPermissions) values (?, ?, ?, ?, ?)";
				$db->query($q, $v);
			}

			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, arHandle, gID, uID, btID from AreaGroupBlockTypes where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['arHandle'], $row['gID'], $row['uID'], $row['btID']);
				$q = "insert into AreaGroupBlockTypes (cID, arHandle, gID, uID, btID) values (?, ?, ?, ?, ?)";
				$db->query($q, $v);
			}
			

		}
		// now that we're done copying, we set the collection to override
		$q = "update Pages set cInheritPermissionsFromCID = {$this->cID}, cInheritPermissionsFrom = 'OVERRIDE' where cID = {$this->cID}";
		$r = $db->query($q);
		
		$this->cInheritPermissionsFromCID = $this->cID;
		$this->cInheritPermissionsFrom = 'OVERRIDE';
		
		// permissions format is read from XML into an array by simplexml
		// this gets us something like this
		// $pxml->guests['canRead'] = 1;
		// $pxml->registered['canWrite'] = 1;
		// $pxml->group[0]['canWrite'] = 1;
		// $pxml->group[0]['canRead'] = 1;
		
		$px = $permissionsArray;
		
		// this is too verbose but i don't know of a good place to stash a reusable function :-( 
		
		// not sure if we want to ALWAYS remove permissions for everything then stack the new ones
		// or not, so for now i'm commenting out the selective removal and removing everything
		// THIS IS DUMB: If you want to turn off permissions for a group don't auto-delete them just make sure
		// your permissions XML set's them to not be able to read
		
		//$db->query("delete from PagePermissions where cID = '{$this->cID}'");
		
		if (isset($px->guests)) {
			$permissions = Permissions::buildPermissionsFromArray($px->guests);
			$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . GUEST_GROUP_ID;
			$r = $db->query($q);
			if ($permissions != '') {
				$v = array($this->cID, GUEST_GROUP_ID, $permissions);
				$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
				$db->query($q, $v);
			}
			
		}
		if (isset($px->registered)) {
			$permissions = Permissions::buildPermissionsFromArray($px->registered);
			$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . REGISTERED_GROUP_ID;
			$r = $db->query($q);
			if ($permissions != '') {
				$v = array($this->cID, REGISTERED_GROUP_ID, $permissions);
				$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
				$db->query($q, $v);
			}
		}
		if (isset($px->administrators)) {
			$permissions = Permissions::buildPermissionsFromArray($px->administrators);
			$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . ADMIN_GROUP_ID;
			$r = $db->query($q);
			if ($permissions != '') {
				$v = array($this->cID, ADMIN_GROUP_ID, $permissions);
				$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
				$db->query($q, $v);
			}
		}		
		if (isset($px->group)) {
			foreach($px->group as $g) {
				
				$permissions = Permissions::buildPermissionsFromArray($g);
				$gID = $g['gID'];
				if (isset($g['gName'])) {
					$gID = $db->getOne("select gID from Groups where gName = ?", array($g['gName']));
				}
				
				$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . $gID;
				$r = $db->query($q);
				if ($permissions != '') {
					$v = array($this->cID, $gID, $permissions);
					$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
					$db->query($q, $v);
				}
			
			}
		}
		
		if (isset($px->user)) {
			foreach($px->user as $_u) {
				
				$permissions = Permissions::buildPermissionsFromArray($_u);
				$uID = $_u['uID'];
				if (isset($_u['uName'])) {
					$uID = $db->getOne("select uID from Users where uName = ?", array($_u['uName']));
				}
				
				$q = "delete from PagePermissions where cID = '{$this->cID}' and uID = " . $uID;
				$r = $db->query($q);
				if ($permissions != '') {
					$v = array($this->cID, $uID, $permissions);
					$q = "insert into PagePermissions (cID, uID, cgPermissions) values (?, ?, ?)";
					$db->query($q, $v);
				}
			
			}
		}
	}

	function addCollectionAlias($c) {
		$db = Loader::db();
		// the passed collection is the parent collection
		$cParentID = $c->getCollectionID();

		$u = new User();
		$uID = $u->getUserID();
		$ctID = 0;
		
		$dh = Loader::helper('date');
				
		$cDate = $dh->getLocalDateTime();
		$cDatePublic = $dh->getLocalDateTime();
		$handle = $this->getCollectionHandle();

		$_cParentID = $c->getCollectionID();
		$q = "select PagePaths.cPath from PagePaths where cID = '{$_cParentID}'";
		if ($_cParentID > 1) {
			$q .=  " and ppIsCanonical = 1";
		}
		$cPath = $db->getOne($q);
		
		$data['handle'] = $this->getCollectionHandle();
		$data['name'] = $this->getCollectionName();
		
		$cobj = parent::add($data);
		$newCID = $cobj->getCollectionID();
		
		$v = array($newCID, $ctID, $cParentID, $uID, $this->getCollectionID());
		$q = "insert into Pages (cID, ctID, cParentID, uID, cPointerID) values (?, ?, ?, ?, ?)";
		$r = $db->prepare($q);
		
		$res = $db->execute($r, $v);
		$newCID = $db->Insert_ID();

		Loader::model('page_statistics');		
		PageStatistics::incrementParents($newCID);

		
		if ($cPath) {
			$q2 = "insert into PagePaths (cID, cPath) values (?, ?)";
			$v2 = array($newCID, $cPath . '/' . $handle);
			$db->query($q2, $v2);
		}
		
		$c->refreshCache();

		return $newCID;
	}
	
	function updateCollectionAliasExternal($cName, $cLink) {
		if ($this->cPointerExternalLink != '') {
			$db = Loader::db();
			$this->markModified();
			$db->query("update CollectionVersions set cvName = ? where cID = ?", array($cName, $this->cID));
			$db->query("update Pages set cPointerExternalLink = ? where cID = ?", array($cLink, $this->cID));
		}
	}
	
	function addCollectionAliasExternal($cName, $cLink) {

		$db = Loader::db();
		$dh = Loader::helper('date');
		$dt = Loader::helper('text');
		$u = new User();

		$cParentID = $this->getCollectionID();
		$uID = $u->getUserID();
		$ctID = 0;
				
		$cDate = $dh->getLocalDateTime();
		$cDatePublic = $dh->getLocalDateTime();
		$handle = $this->getCollectionHandle();
		
		// make the handle out of the title
		$handle = $dt->sanitizeFileSystem($cLink);
		$data['handle'] = $handle;
		$data['name'] = $cName;
		
		$cobj = parent::add($data);
		$newCID = $cobj->getCollectionID();
		
		$v = array($newCID, $ctID, $cParentID, $uID, $cLink);
		$q = "insert into Pages (cID, ctID, cParentID, uID, cPointerExternalLink) values (?, ?, ?, ?, ?)";
		$r = $db->prepare($q);
		
		$res = $db->execute($r, $v);
		$newCID = $db->Insert_ID();

		Loader::model('page_statistics');		
		PageStatistics::incrementParents($newCID);

		return $newCID;

	}
	
	public function isSystemPage() {
		if ($this->isGeneratedCollection()) {
			return (file_exists(DIR_FILES_CONTENT_REQUIRED . $this->getCollectionPath()) || 
			file_exists(DIR_FILES_CONTENT_REQUIRED . $this->getCollectionPath() . '.php'));
		}		
		return false;
	}
	
	public function getCollectionIcon() {
		// returns a fully qualified image link for this page's icon, either based on its collection type or if icon.png appears in its view directory
		$icon = '';
		if ($this->isGeneratedCollection()) {
			if ($this->getPackageID() > 0) {
				if (is_dir(DIR_PACKAGES . '/' . $this->getPackageHandle())) {
					$dirp = DIR_PACKAGES;
					$url = BASE_URL . DIR_REL;
				} else {
					$dirp = DIR_PACKAGES_CORE;
					$url = ASSETS_URL;
				}
				$file = $dirp . '/' . $this->getPackageHandle() . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
				if (file_exists($file)) {
					$icon = $url . '/' . DIRNAME_PACKAGES . '/' . $this->getPackageHandle() . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
				}
			} else if (file_exists(DIR_FILES_CONTENT . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON)) {
				$icon = BASE_URL . DIR_REL . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
			} else if (file_exists(DIR_FILES_CONTENT_REQUIRED . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON)) {
				$icon = ASSETS_URL . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
			}
	
		} else {
			/*
			if ($this->ctIcon != null) {
				$icon = REL_DIR_FILES_COLLECTION_TYPE_ICONS . '/' . $this->ctIcon;
			}*/
			
		}
		return $icon;
	}
	
	function removeThisAlias() {
		$cIDRedir = $this->getCollectionPointerID();
		$cPointerExternalLink = $this->getCollectionPointerExternalLink();
		
		parent::refreshCache();

		if ($cPointerExternalLink != '') {
			$this->delete();
		} else if ($cIDRedir > 0) {
			$db = Loader::db();

			Loader::model('page_statistics');		
			PageStatistics::decrementParents($this->getCollectionPointerOriginalID());

			$args = array($this->getCollectionPointerOriginalID());
			$q = "delete from Pages where cID = ?";
			$r = $db->query($q, $args);

			$q = "delete from Collections where cID = ?";
			$r = $db->query($q, $args);

			$q = "delete from CollectionVersions where cID = ?";
			$r = $db->query($q, $args);
			
			$q = "delete from PagePaths where cID = ?";
			$r = $db->query($q, $args);

			return $cIDRedir;
		}
	}

	function getCollectionCheckedOutUserID() {
		return $this->cCheckedOutUID;
	}


	function getAllowedSubCollections() {
		return $this->allowedSubCollections;
	}

	function getCollectionPath() {
		return $this->cPath;
	}
	
	public static function getCollectionPathFromID($cID) {
		$path = Cache::get('page_path', $cID);
		if ($path != false) {
			return $path;
		}
		
		$db = Loader::db();
		$path = $db->GetOne("select cPath from PagePaths inner join CollectionVersions on (PagePaths.cID = CollectionVersions.cID and CollectionVersions.cvIsApproved = 1) where PagePaths.cID = ?", array($cID));
		
		Cache::set('page_path', $cID, $path);
		return $path;
	}

	function getCollectionUserID() {
		return $this->uID;
	}

	function getCollectionHandle() {
		return $this->vObj->cvHandle;
	}

	function getCollectionTypeName() {
		return $this->ctName;
	}

	function getCollectionTypeID() {
		return $this->ctID;
	}

	function getCollectionTypeHandle() {
		return $this->ctHandle;
	}

	function getCollectionThemeID() {
		$db = Loader::db();
		if ($this->ptID < 1) {
			return $db->GetOne("select ptID from Pages where cID = 1");
		} else {
			return $this->ptID;
		}	
	}
	
	function getCollectionThemeObject() {
		if ($this->ptID < 1) {
			return PageTheme::getSiteTheme();
		} else {
			// this is probably not terribly efficient
			$pl = PageTheme::getByID($this->ptID);
			return $pl;
		}		
	}
	
	function getCollectionName() {
		if (isset($this->vObj)) {
			return $this->vObj->cvName;
		}
		return $this->cvName;
	}

	function getCollectionPointerID() {
		return $this->cPointerID;
	}

	function getCollectionPointerExternalLink() {
		return $this->cPointerExternalLink;
	}
	
	function isAlias() {
		return $this->cPointerID > 0 || $this->cPointerExternalLink != null;
	}
	
	function isExternalLink() {
		return ($this->cPointerExternalLink != null);
	}
	
	function getCollectionPointerOriginalID()  {
		return $this->cPointerOriginalID;
	}

	function getCollectionFilename() {
		return $this->cFilename;
	}

	function getCollectionDatePublic($dateFormat = null) {
		return ($dateFormat) ? date($dateFormat, strtotime($this->vObj->cvDatePublic)) : $this->vObj->cvDatePublic;
	}

	function getCollectionDescription() {
		return $this->vObj->cvDescription;
	}

	function getCollectionParentID() {
		return $this->cParentID;
	}
	
	//returns an array of this cParentID and aliased parentIDs
	function getCollectionParentIDs(){
		$cIDs=array($this->cParentID);
		$db = Loader::db(); 
		$aliasedParents=$db->getAll('SELECT cParentID FROM Pages WHERE cPointerID='.intval($this->cID).' ');
		foreach($aliasedParents as $aliasedParent)
			$cIDs[]=$aliasedParent['cParentID'];
		return $cIDs;
	}

	function isMasterCollection() {
		return $this->isMasterCollection;
	}

	function getPendingAction() {
		return $this->cPendingAction;
	}

	function getPendingActionUserID() {
		return $this->cPendingActionUID;
	}

	function getPendingActionDateTime() {
		return $this->cPendingActionDatetime;
	}

	function getPendingActionTargetCollectionID() {
		return $this->cPendingActionTargetCID;
	}

	function isPendingMove() {
		return ($this->cPendingAction == 'MOVE');
	}

	function isPendingCopy() {
		return ($this->cPendingAction == 'COPY');
	}

	function isPendingDelete() {
		return ($this->cPendingAction == 'DELETE');
	}

	function overrideTemplatePermissions() {
		return $this->cOverrideTemplatePermissions;
	}
	
	function getCollectionDisplayOrder() {
		return $this->cDisplayOrder;
	}
	
	public function setTheme($pl) {
		$db = Loader::db();
		$db->query('update Pages set ptID = ? where cID = ?', array($pl->getThemeID(), $this->cID));
		parent::refreshCache();
	}
	
	function setPermissionsInheritanceToTemplate() {
		// this is a short hack function that will set the permissions of sub-collections added beneath this permissions to inherit as parent
		$db = Loader::db();
		if ($this->cID) {
			$db->query("update Pages set cOverrideTemplatePermissions = 0 where cID = {$this->cID}");
		}
	}
	
	function setPermissionsInheritanceToOverride() {
		// this is a short hack function that will set the permissions of sub-collections added beneath this permissions to inherit as parent
		$db = Loader::db();
		if ($this->cID) {
			$db->query("update Pages set cOverrideTemplatePermissions = 1 where cID = {$this->cID}");
		}
	}

	function getPermissionsCollectionID() {
		return $this->cInheritPermissionsFromCID;
	}

	function getCollectionInheritance() {
		return $this->cInheritPermissionsFrom;
	}

	function getParentPermissionsCollectionID() {
		$db = Loader::db();
		$v = array($this->cParentID);
		$q = "select cInheritPermissionsFromCID from Pages where cID = ?";
		$ppID = $db->getOne($q, $v);
		return $ppID;
	}

	function getPermissionsCollectionObject() {
		return Page::getByID($this->cInheritPermissionsFromCID, "RECENT");
	}
	
	function getMasterCollectionID() {
		$db = Loader::db();
		$q = "select cID from Pages where Pages.ctID = '{$this->ctID}' and cIsTemplate = 1";
		$cID = $db->getOne($q);
		if ($cID) {
			return $cID;
		}
	}

	function getOriginalCollectionID() {
		// this is a bit weird...basically, when editing a master collection, we store the
		// master collection ID in session, along with the collection ID we were looking at before
		// moving to the master collection. This allows us to get back to that original collection
		return $_SESSION['ocID'];
	}

	function getNumChildren() {
		return $this->cChildren;
	}
	
	function getNumChildrenDirect() {
		// direct children only
		$db = Loader::db();
		$v = array($this->cID);
		$num = $db->getOne('select count(cID) as total from Pages where cParentID = ?', $v);
		if ($num) {
			return $num;
		}
		return 0;
	}
	
	/** 
	 * Returns the first child of the current page, or null if there is no child
	 * @param string $sortColumn
	 * @return Page
	 */
	public function getFirstChild($sortColumn = 'cDisplayOrder asc') {
		$db = Loader::db();
		$cID = $db->GetOne("select Pages.cID from Pages inner join CollectionVersions on Pages.cID = CollectionVersions.cID where cvIsApproved = 1 and cParentID = ? order by {$sortColumn}", array($this->cID));
		if ($cID > 1) {
			return Page::getByID($cID, "ACTIVE");
		}
		return false;
	}

	function getCollectionChildrenArray( $oneLevelOnly=0 ) {
		$this->childrenCIDArray = array();
		$this->_getNumChildren($this->cID,$oneLevelOnly);
		return $this->childrenCIDArray;
	}

	function _getNumChildren($cID,$oneLevelOnly=0) {
		$db = Loader::db();
		$q = "select cID from Pages left join Packages on Pages.pkgID = Packages.pkgID where cParentID = {$cID} and cIsTemplate = 0 and (Packages.pkgHandle <> 'core' or pkgHandle is null or Pages.ctID > 0)";
		$r = $db->query($q);
		if ($r) {
			while ($row = $r->fetchRow()) {
				if ($row['cID'] > 0) {
					$this->childrenCIDArray[] = $row['cID'];
					if( !$oneLevelOnly ) $this->_getNumChildren($row['cID']);
				}
			}
		}
	}

	function canMoveCopyTo($cobj) {
		// ensures that we're not moving or copying to a collection inside our part of the tree
		$children = $this->getCollectionChildrenArray();
		$children[] = $this->getCollectionID();
		return (!in_array($cobj->getCollectionID(), $children));
	}

	function update($data) {
		$db = Loader::db();

		$vo = $this->getVersionObject();
		$cvID = $vo->getVersionID();
		$this->markModified();
		parent::refreshCache();
		
		$cName = $this->getCollectionName();
		$cDescription = $this->getCollectionDescription();
		$cDatePublic = $this->getCollectionDatePublic;
		$ctID = $this->getCollectionTypeID();
		$uID = $this->getCollectionUserID();
		$pkgID = $this->getPackageID();
		$cFilename = $this->getCollectionFilename();
		if (isset($data['cName'])) {
			$cName = $data['cName'];
		}
		if (isset($data['cDescription'])) {
			$cDescription = $data['cDescription'];
		}
		if (isset($data['cDatePublic'])) {
			$cDatePublic = $data['cDatePublic'];
		}
		if (isset($data['uID'])) {
			$uID = $data['uID'];
		}
		if (isset($data['ctID'])) {
			$ctID = $data['ctID'];
			// we grab the package that this ct belongs to
			$pkgID = $db->GetOne("select pkgID from PageTypes where ctID = ?", array($data['ctID']));
		}

		$txt = Loader::helper('text');
        if (!isset($data['cHandle']) && ($this->getCollectionHandle() != '')) {
            $cHandle = $this->getCollectionHandle();
        } else if (!$data['cHandle']) {
            // make the handle out of the title
            $cHandle = $txt->sanitizeFileSystem($cName);
        } else {
            $cHandle = $txt->sanitizeFileSystem($data['cHandle']);
        }
		
		// Update the non-canonical page paths
		$this->rescanPagePaths($data['ppURL']);

		if ($this->isGeneratedCollection()) {
			if (isset($data['cFilename'])) {
				$cFilename = $data['cFilename'];
			}
			// we only update a subset
			$v = array($cName, $cHandle, $cDescription, $cDatePublic, $cvID, $this->cID);
			$q = "update CollectionVersions set cvName = ?, cvHandle = ?, cvDescription = ?, cvDatePublic = ? where cvID = ? and cID = ?";
			$r = $db->prepare($q);
			$res = $db->execute($r, $v);

		} else {

			$v = array($cName, $cHandle, $cDescription, $cDatePublic, $cvID, $this->cID);
			$q = "update CollectionVersions set cvName = ?, cvHandle = ?, cvDescription = ?, cvDatePublic = ? where cvID = ? and cID = ?";
			$r = $db->prepare($q);
			$res = $db->execute($r, $v);				
		}

		$db->query("update Pages set uID = ?, ctID = ?, pkgID = ?, cFilename = ? where cID = ?", array($uID, $ctID, $pkgID, $cFilename, $this->cID));

		// run any internal event we have for page update
		$ret = Events::fire('on_page_update', $this);
	}
	
	public function uniquifyPagePath($origPath) {
		$db = Loader::db();

		$proceed = false;
		$suffix = 0;
		while ($proceed != true) {
			$newPath = ($suffix == 0) ? $origPath : $origPath . $suffix;
			$v = array($newPath, $this->cID);
			$q = "select cID from PagePaths where cPath = ? and cID <> ?";
			$r = $db->query($q, $v);
			if ($r->numRows() == 0) {
				$proceed = true;
			} else {
				$suffix++;
			}
		}

		return $newPath;
	}

	public function rescanPagePaths($newPaths) {
		$db = Loader::db();
		$txt = Loader::helper('text');

		// First, get the list of page paths from the DB.
		$ppaths = $this->getPagePaths();

		// Second, reset all of their cPath values to null.
		$paths = array();
		foreach ($ppaths as $ppath) {
			if (!$ppath['ppIsCanonical']) {
				$paths[$ppath['ppID']] = null;
			}
		}

		// Third, fill in the cPath values from the user updated data.
		foreach ($newPaths as $key=>$val) {
			if (!empty($val)) {
				// Auto-prepend a slash if one is missing.
				$val = $txt->sanitizeFileSystem($val, true);
				if ($val{0} != '/') {
					$val = '/' . $val;
				}
				$paths[$key] = $val;
			}
		}

		// Fourth, delete, update, or insert page paths as necessary.<commit_msg>added is_array check to rescan page paths - was causeing error on new install<commit_after><?

defined('C5_EXECUTE') or die(_("Access Denied."));

/**
*
* The page object in Concrete encapsulates all the functionality used by a typical page and their contents
* including blocks, page metadata, page permissions. 
* @package Pages
*
*/
class Page extends Collection {

	public static function getByPath($path, $version = 'RECENT') {
		$db = Loader::db();
		$cID = $db->GetOne("select cID from PagePaths where cPath = ?", array($path));
		return Page::getByID($cID, $version);
	}
	
	public static function getByID($cID, $version = 'RECENT') {
		if ($version) {
			$version = CollectionVersion::getNumericalVersionID($cID, $version);
		}
		$ca = new Cache();
		$c = ($version > 0) ? $ca->get('page', $cID . ':' . $version) : $ca->get('page', $cID);

		if ($c instanceof Page) {
			return $c;
		}
		
		$where = "where Pages.cID = ?";
		$c = new Page;
		$c->populatePage($cID, $where, $version);
		if ($c->cPointerID < 1) {
			if ($version > 0) {
				$ca->set('page', $c->getCollectionID() . ':' . $version, $c);
			} else {
				$ca->set('page', $c->getCollectionID(), $c);
			}
		}
		return $c;
	}
	
	protected function populatePage($cInfo, $where, $cvID) {
		$db = Loader::db();
		
		$q0 = "select Pages.cID, Pages.pkgID, Pages.cPointerID, Pages.cPointerExternalLink, Pages.cFilename, Collections.cDateAdded, Pages.cDisplayOrder, Collections.cDateModified, cInheritPermissionsFromCID, cInheritPermissionsFrom, cOverrideTemplatePermissions, cPendingAction, cPendingActionUID, cPendingActionTargetCID, cPendingActionDatetime, cCheckedOutUID, cIsTemplate, uID, cPath, Pages.ctID, ctHandle, ctIcon, ptID, cParentID, cChildren, ctName from Pages inner join Collections on Pages.cID = Collections.cID left join PageTypes on (PageTypes.ctID = Pages.ctID) left join PagePaths on (Pages.cID = PagePaths.cID and PagePaths.ppIsCanonical = 1) ";
		$q2 = "select cParentID, cPointerID, cPath, Pages.cID from Pages left join PagePaths on (Pages.cID = PagePaths.cID and PagePaths.ppIsCanonical = 1) ";

		if ($cInfo != 1) {
			if (empty($where)) {
				$ppWhere = "where ";
			} else {
				$ppWhere = " and ";
			}
			$ppWhere .= "PagePaths.ppIsCanonical = 1";
		} else {
			$ppWhere = '';
		}
//TBD
$ppWhere = '';
		
		$v = array($cInfo);
		$q2 .= $where . $ppWhere;

		$r = $db->query($q2, $v);
		$row = $r->fetchRow();
		if ($row['cPointerID'] > 0) {
			$q1 = $q0 . "where Pages.cID = ?" . $ppWhere;
			$cPointerOriginalID = $row['cID'];
			$v = array($row['cPointerID']);
			$cParentIDOverride = $row['cParentID'];
			$cPathOverride = $row['cPath'];
			$cPointerID = $row['cPointerID'];
		} else {
			$q1 = $q0 . $where . $ppWhere;
		}

		// isTemplate = 0 because we don't want to get return templates

		$r = $db->query($q1, $v);
		if ($r) {
			$row = $r->fetchRow();
			if ($row) {
				foreach ($row as $key => $value) {
					$this->{$key} = $value;
				}
				if (isset($cParentIDOverride)) {
					$this->cPointerID = $cPointerID;
					$this->cPointerOriginalID = $cPointerOriginalID;
					$this->cPath = $cPathOverride;
					$this->cParentID = $cParentIDOverride;
				}
				$this->isMasterCollection = $row['cIsTemplate'];
			} else {
				if ($cInfo == 1) {
					$this->cID = '1';
					$this->loadError(COLLECTION_INIT);
				} else {
					// there was no record of this particular collection in the database
					$this->loadError(COLLECTION_NOT_FOUND);
				}
			}
			$r->free();
		} else {
			$this->loadError(COLLECTION_NOT_FOUND);
		}
	

		if ($cvID != false) {
			// we don't do this on the front page
			$this->loadVersionObject($cvID);
		}
	}		

	public function isEditMode() {
		$v = View::getInstance();
		return ($this->isCheckedOutByMe() && ($v->editingEnabled()));
	}
	
	public function getPackageID() {return $this->pkgID;}
	public function getPackageHandle() {
		return PackageList::getHandle($this->pkgID);
	}
	public function isArrangeMode() {return ($this->isCheckedOutByMe() && ($_REQUEST['btask'] == 'arrange'));}
				
	public function forceCheckIn() {
		// This function forces checkin to take place
		$db = Loader::db();
		$q = "update Pages set cIsCheckedOut = 0, cCheckedOutUID = null, cCheckedOutDatetime = null, cCheckedOutDatetimeLastEdit = null where cID = '{$this->cID}'";
		$r = $db->query($q);
	}
	
	public function isAdminArea() {
		if ($this->isGeneratedCollection()) {
			$pos = strpos($this->getCollectionFilename(), "/" . DIRNAME_DASHBOARD);
			return ($pos > -1);
		}			
		return false;
	}
	
	/** 
	 * Takes an array of area/block values and makes that the arrangement for this page's version
	 * Format is like: $area[10][0] = 2, $area[10][1] = 8, $area[15][0] = 27, with the area ID being the 
	 * key and the block IDs being 1-n values inside it
	 */
	public function processArrangement($areas) {
		// this function is called via ajax, so it's a bit wonky, but the format is generally
		// a{areaID} = array(b1, b2, b3) (where b1, etc... are blocks with ids appended.)
		$db = Loader::db();
		foreach($areas as $arID => $blocks) {
			if (intval($arID) > 0) {
				// this is a serialized area;
				$arHandle = $db->getOne("select arHandle from Areas where arID = ?", array($arID));
				$startDO = 0;
				
				foreach($blocks as $bID) {
					if (intval($bID) > 0) {
						$v = array($startDO, $arHandle, $bID, $this->getCollectionID(), $this->getVersionID());
						try {
							$db->query("update CollectionVersionBlocks set cbDisplayOrder = ?, arHandle = ? where bID = ? and cID = ? and cvID = ?", $v);
						} catch(Exception $e) {}
						
						$startDO++;
					}
				}
			}
		}
	}
	
	function isCheckedOut() {
		// function to inform us as to whether the current collection is checked out
		$db = Loader::db();
		if (isset($this->isCheckedOutCache)) {
			return $this->isCheckedOutCache;
		}
		
		$dh = Loader::helper('date');
				
		$q = "select cIsCheckedOut, UNIX_TIMESTAMP('" . $dh->getLocalDateTime() . "') - UNIX_TIMESTAMP(cCheckedOutDatetimeLastEdit) as timeout from Pages where cID = '{$this->cID}'";
		$r = $db->query($q);
		if ($r) {
			$row = $r->fetchRow();
			if ($row['cIsCheckedOut'] == 0) {
				return false;
			} else {
				if ($row['timeout'] > CHECKOUT_TIMEOUT) {
					$this->forceCheckIn();
					$this->isCheckedOutCache = false;
					return false;
				} else {
					$this->isCheckedOutCache = true;
					return true;
				}
			}
		}
	}

	function isCheckedOutByMe() {
		$u = new User();
		return ($this->getCollectionCheckedOutUserID() > 0 && $this->getCollectionCheckedOutUserID() == $u->getUserID());
	}

	function isGeneratedCollection() {
		// generated collections are collections without types, that have special cFilename attributes
		return $this->cFilename != null && $this->ctID == 0;
	}
	
	function assignPermissionSet($permissionsArray) {
		$db = Loader::db();
		// first, we make sure to set this collection's permission inheritance to override
		if ($this->getCollectionInheritance() != 'OVERRIDE') {
			// now, if we were inheriting permissions from elsewhere, we'll grab those, copy them to this node, before we
			// assign/add new permission
			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, uID, gID, cgPermissions, cgStartDate, cgEndDate from PagePermissions where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['uID'], $row['gID'], $row['cgPermissions'], $row['cgStartDate'], $row['cgEndDate']);
				$q = "insert into PagePermissions (cID, uID, gID, cgPermissions, cgStartDate, cgEndDate) values (?, ?, ?, ?, ?, ?)";
				$db->query($q, $v);
			}
			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, uID, gID, ctID from PagePermissionPageTypes where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['uID'], $row['gID'], $row['ctID']);
				$q = "insert into PagePermissionPageTypes (cID, uID, gID, ctID) values (?, ?, ?, ?)";
				$db->query($q, $v);
			}
			// ack - we need to copy area permissions from that page as well
			// wait, i'm not sure if we need to do this anymore. 
			
			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, arHandle, gID, uID, agPermissions from AreaGroups where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['arHandle'], $row['gID'], $row['uID'], $row['agPermissions']);
				$q = "insert into AreaGroups (cID, arHandle, gID, uID, agPermissions) values (?, ?, ?, ?, ?)";
				$db->query($q, $v);
			}

			$v = array($this->getPermissionsCollectionID());
			$q = "select cID, arHandle, gID, uID, btID from AreaGroupBlockTypes where cID = ?";
			$r = $db->query($q, $v);
			while($row = $r->fetchRow()) {
				$v = array($this->cID, $row['arHandle'], $row['gID'], $row['uID'], $row['btID']);
				$q = "insert into AreaGroupBlockTypes (cID, arHandle, gID, uID, btID) values (?, ?, ?, ?, ?)";
				$db->query($q, $v);
			}
			

		}
		// now that we're done copying, we set the collection to override
		$q = "update Pages set cInheritPermissionsFromCID = {$this->cID}, cInheritPermissionsFrom = 'OVERRIDE' where cID = {$this->cID}";
		$r = $db->query($q);
		
		$this->cInheritPermissionsFromCID = $this->cID;
		$this->cInheritPermissionsFrom = 'OVERRIDE';
		
		// permissions format is read from XML into an array by simplexml
		// this gets us something like this
		// $pxml->guests['canRead'] = 1;
		// $pxml->registered['canWrite'] = 1;
		// $pxml->group[0]['canWrite'] = 1;
		// $pxml->group[0]['canRead'] = 1;
		
		$px = $permissionsArray;
		
		// this is too verbose but i don't know of a good place to stash a reusable function :-( 
		
		// not sure if we want to ALWAYS remove permissions for everything then stack the new ones
		// or not, so for now i'm commenting out the selective removal and removing everything
		// THIS IS DUMB: If you want to turn off permissions for a group don't auto-delete them just make sure
		// your permissions XML set's them to not be able to read
		
		//$db->query("delete from PagePermissions where cID = '{$this->cID}'");
		
		if (isset($px->guests)) {
			$permissions = Permissions::buildPermissionsFromArray($px->guests);
			$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . GUEST_GROUP_ID;
			$r = $db->query($q);
			if ($permissions != '') {
				$v = array($this->cID, GUEST_GROUP_ID, $permissions);
				$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
				$db->query($q, $v);
			}
			
		}
		if (isset($px->registered)) {
			$permissions = Permissions::buildPermissionsFromArray($px->registered);
			$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . REGISTERED_GROUP_ID;
			$r = $db->query($q);
			if ($permissions != '') {
				$v = array($this->cID, REGISTERED_GROUP_ID, $permissions);
				$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
				$db->query($q, $v);
			}
		}
		if (isset($px->administrators)) {
			$permissions = Permissions::buildPermissionsFromArray($px->administrators);
			$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . ADMIN_GROUP_ID;
			$r = $db->query($q);
			if ($permissions != '') {
				$v = array($this->cID, ADMIN_GROUP_ID, $permissions);
				$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
				$db->query($q, $v);
			}
		}		
		if (isset($px->group)) {
			foreach($px->group as $g) {
				
				$permissions = Permissions::buildPermissionsFromArray($g);
				$gID = $g['gID'];
				if (isset($g['gName'])) {
					$gID = $db->getOne("select gID from Groups where gName = ?", array($g['gName']));
				}
				
				$q = "delete from PagePermissions where cID = '{$this->cID}' and gID = " . $gID;
				$r = $db->query($q);
				if ($permissions != '') {
					$v = array($this->cID, $gID, $permissions);
					$q = "insert into PagePermissions (cID, gID, cgPermissions) values (?, ?, ?)";
					$db->query($q, $v);
				}
			
			}
		}
		
		if (isset($px->user)) {
			foreach($px->user as $_u) {
				
				$permissions = Permissions::buildPermissionsFromArray($_u);
				$uID = $_u['uID'];
				if (isset($_u['uName'])) {
					$uID = $db->getOne("select uID from Users where uName = ?", array($_u['uName']));
				}
				
				$q = "delete from PagePermissions where cID = '{$this->cID}' and uID = " . $uID;
				$r = $db->query($q);
				if ($permissions != '') {
					$v = array($this->cID, $uID, $permissions);
					$q = "insert into PagePermissions (cID, uID, cgPermissions) values (?, ?, ?)";
					$db->query($q, $v);
				}
			
			}
		}
	}

	function addCollectionAlias($c) {
		$db = Loader::db();
		// the passed collection is the parent collection
		$cParentID = $c->getCollectionID();

		$u = new User();
		$uID = $u->getUserID();
		$ctID = 0;
		
		$dh = Loader::helper('date');
				
		$cDate = $dh->getLocalDateTime();
		$cDatePublic = $dh->getLocalDateTime();
		$handle = $this->getCollectionHandle();

		$_cParentID = $c->getCollectionID();
		$q = "select PagePaths.cPath from PagePaths where cID = '{$_cParentID}'";
		if ($_cParentID > 1) {
			$q .=  " and ppIsCanonical = 1";
		}
		$cPath = $db->getOne($q);
		
		$data['handle'] = $this->getCollectionHandle();
		$data['name'] = $this->getCollectionName();
		
		$cobj = parent::add($data);
		$newCID = $cobj->getCollectionID();
		
		$v = array($newCID, $ctID, $cParentID, $uID, $this->getCollectionID());
		$q = "insert into Pages (cID, ctID, cParentID, uID, cPointerID) values (?, ?, ?, ?, ?)";
		$r = $db->prepare($q);
		
		$res = $db->execute($r, $v);
		$newCID = $db->Insert_ID();

		Loader::model('page_statistics');		
		PageStatistics::incrementParents($newCID);

		
		if ($cPath) {
			$q2 = "insert into PagePaths (cID, cPath) values (?, ?)";
			$v2 = array($newCID, $cPath . '/' . $handle);
			$db->query($q2, $v2);
		}
		
		$c->refreshCache();

		return $newCID;
	}
	
	function updateCollectionAliasExternal($cName, $cLink) {
		if ($this->cPointerExternalLink != '') {
			$db = Loader::db();
			$this->markModified();
			$db->query("update CollectionVersions set cvName = ? where cID = ?", array($cName, $this->cID));
			$db->query("update Pages set cPointerExternalLink = ? where cID = ?", array($cLink, $this->cID));
		}
	}
	
	function addCollectionAliasExternal($cName, $cLink) {

		$db = Loader::db();
		$dh = Loader::helper('date');
		$dt = Loader::helper('text');
		$u = new User();

		$cParentID = $this->getCollectionID();
		$uID = $u->getUserID();
		$ctID = 0;
				
		$cDate = $dh->getLocalDateTime();
		$cDatePublic = $dh->getLocalDateTime();
		$handle = $this->getCollectionHandle();
		
		// make the handle out of the title
		$handle = $dt->sanitizeFileSystem($cLink);
		$data['handle'] = $handle;
		$data['name'] = $cName;
		
		$cobj = parent::add($data);
		$newCID = $cobj->getCollectionID();
		
		$v = array($newCID, $ctID, $cParentID, $uID, $cLink);
		$q = "insert into Pages (cID, ctID, cParentID, uID, cPointerExternalLink) values (?, ?, ?, ?, ?)";
		$r = $db->prepare($q);
		
		$res = $db->execute($r, $v);
		$newCID = $db->Insert_ID();

		Loader::model('page_statistics');		
		PageStatistics::incrementParents($newCID);

		return $newCID;

	}
	
	public function isSystemPage() {
		if ($this->isGeneratedCollection()) {
			return (file_exists(DIR_FILES_CONTENT_REQUIRED . $this->getCollectionPath()) || 
			file_exists(DIR_FILES_CONTENT_REQUIRED . $this->getCollectionPath() . '.php'));
		}		
		return false;
	}
	
	public function getCollectionIcon() {
		// returns a fully qualified image link for this page's icon, either based on its collection type or if icon.png appears in its view directory
		$icon = '';
		if ($this->isGeneratedCollection()) {
			if ($this->getPackageID() > 0) {
				if (is_dir(DIR_PACKAGES . '/' . $this->getPackageHandle())) {
					$dirp = DIR_PACKAGES;
					$url = BASE_URL . DIR_REL;
				} else {
					$dirp = DIR_PACKAGES_CORE;
					$url = ASSETS_URL;
				}
				$file = $dirp . '/' . $this->getPackageHandle() . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
				if (file_exists($file)) {
					$icon = $url . '/' . DIRNAME_PACKAGES . '/' . $this->getPackageHandle() . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
				}
			} else if (file_exists(DIR_FILES_CONTENT . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON)) {
				$icon = BASE_URL . DIR_REL . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
			} else if (file_exists(DIR_FILES_CONTENT_REQUIRED . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON)) {
				$icon = ASSETS_URL . '/' . DIRNAME_PAGES . $this->getCollectionPath() . '/' . FILENAME_PAGE_ICON;
			}
	
		} else {
			/*
			if ($this->ctIcon != null) {
				$icon = REL_DIR_FILES_COLLECTION_TYPE_ICONS . '/' . $this->ctIcon;
			}*/
			
		}
		return $icon;
	}
	
	function removeThisAlias() {
		$cIDRedir = $this->getCollectionPointerID();
		$cPointerExternalLink = $this->getCollectionPointerExternalLink();
		
		parent::refreshCache();

		if ($cPointerExternalLink != '') {
			$this->delete();
		} else if ($cIDRedir > 0) {
			$db = Loader::db();

			Loader::model('page_statistics');		
			PageStatistics::decrementParents($this->getCollectionPointerOriginalID());

			$args = array($this->getCollectionPointerOriginalID());
			$q = "delete from Pages where cID = ?";
			$r = $db->query($q, $args);

			$q = "delete from Collections where cID = ?";
			$r = $db->query($q, $args);

			$q = "delete from CollectionVersions where cID = ?";
			$r = $db->query($q, $args);
			
			$q = "delete from PagePaths where cID = ?";
			$r = $db->query($q, $args);

			return $cIDRedir;
		}
	}

	function getCollectionCheckedOutUserID() {
		return $this->cCheckedOutUID;
	}


	function getAllowedSubCollections() {
		return $this->allowedSubCollections;
	}

	function getCollectionPath() {
		return $this->cPath;
	}
	
	public static function getCollectionPathFromID($cID) {
		$path = Cache::get('page_path', $cID);
		if ($path != false) {
			return $path;
		}
		
		$db = Loader::db();
		$path = $db->GetOne("select cPath from PagePaths inner join CollectionVersions on (PagePaths.cID = CollectionVersions.cID and CollectionVersions.cvIsApproved = 1) where PagePaths.cID = ?", array($cID));
		
		Cache::set('page_path', $cID, $path);
		return $path;
	}

	function getCollectionUserID() {
		return $this->uID;
	}

	function getCollectionHandle() {
		return $this->vObj->cvHandle;
	}

	function getCollectionTypeName() {
		return $this->ctName;
	}

	function getCollectionTypeID() {
		return $this->ctID;
	}

	function getCollectionTypeHandle() {
		return $this->ctHandle;
	}

	function getCollectionThemeID() {
		$db = Loader::db();
		if ($this->ptID < 1) {
			return $db->GetOne("select ptID from Pages where cID = 1");
		} else {
			return $this->ptID;
		}	
	}
	
	function getCollectionThemeObject() {
		if ($this->ptID < 1) {
			return PageTheme::getSiteTheme();
		} else {
			// this is probably not terribly efficient
			$pl = PageTheme::getByID($this->ptID);
			return $pl;
		}		
	}
	
	function getCollectionName() {
		if (isset($this->vObj)) {
			return $this->vObj->cvName;
		}
		return $this->cvName;
	}

	function getCollectionPointerID() {
		return $this->cPointerID;
	}

	function getCollectionPointerExternalLink() {
		return $this->cPointerExternalLink;
	}
	
	function isAlias() {
		return $this->cPointerID > 0 || $this->cPointerExternalLink != null;
	}
	
	function isExternalLink() {
		return ($this->cPointerExternalLink != null);
	}
	
	function getCollectionPointerOriginalID()  {
		return $this->cPointerOriginalID;
	}

	function getCollectionFilename() {
		return $this->cFilename;
	}

	function getCollectionDatePublic($dateFormat = null) {
		return ($dateFormat) ? date($dateFormat, strtotime($this->vObj->cvDatePublic)) : $this->vObj->cvDatePublic;
	}

	function getCollectionDescription() {
		return $this->vObj->cvDescription;
	}

	function getCollectionParentID() {
		return $this->cParentID;
	}
	
	//returns an array of this cParentID and aliased parentIDs
	function getCollectionParentIDs(){
		$cIDs=array($this->cParentID);
		$db = Loader::db(); 
		$aliasedParents=$db->getAll('SELECT cParentID FROM Pages WHERE cPointerID='.intval($this->cID).' ');
		foreach($aliasedParents as $aliasedParent)
			$cIDs[]=$aliasedParent['cParentID'];
		return $cIDs;
	}

	function isMasterCollection() {
		return $this->isMasterCollection;
	}

	function getPendingAction() {
		return $this->cPendingAction;
	}

	function getPendingActionUserID() {
		return $this->cPendingActionUID;
	}

	function getPendingActionDateTime() {
		return $this->cPendingActionDatetime;
	}

	function getPendingActionTargetCollectionID() {
		return $this->cPendingActionTargetCID;
	}

	function isPendingMove() {
		return ($this->cPendingAction == 'MOVE');
	}

	function isPendingCopy() {
		return ($this->cPendingAction == 'COPY');
	}

	function isPendingDelete() {
		return ($this->cPendingAction == 'DELETE');
	}

	function overrideTemplatePermissions() {
		return $this->cOverrideTemplatePermissions;
	}
	
	function getCollectionDisplayOrder() {
		return $this->cDisplayOrder;
	}
	
	public function setTheme($pl) {
		$db = Loader::db();
		$db->query('update Pages set ptID = ? where cID = ?', array($pl->getThemeID(), $this->cID));
		parent::refreshCache();
	}
	
	function setPermissionsInheritanceToTemplate() {
		// this is a short hack function that will set the permissions of sub-collections added beneath this permissions to inherit as parent
		$db = Loader::db();
		if ($this->cID) {
			$db->query("update Pages set cOverrideTemplatePermissions = 0 where cID = {$this->cID}");
		}
	}
	
	function setPermissionsInheritanceToOverride() {
		// this is a short hack function that will set the permissions of sub-collections added beneath this permissions to inherit as parent
		$db = Loader::db();
		if ($this->cID) {
			$db->query("update Pages set cOverrideTemplatePermissions = 1 where cID = {$this->cID}");
		}
	}

	function getPermissionsCollectionID() {
		return $this->cInheritPermissionsFromCID;
	}

	function getCollectionInheritance() {
		return $this->cInheritPermissionsFrom;
	}

	function getParentPermissionsCollectionID() {
		$db = Loader::db();
		$v = array($this->cParentID);
		$q = "select cInheritPermissionsFromCID from Pages where cID = ?";
		$ppID = $db->getOne($q, $v);
		return $ppID;
	}

	function getPermissionsCollectionObject() {
		return Page::getByID($this->cInheritPermissionsFromCID, "RECENT");
	}
	
	function getMasterCollectionID() {
		$db = Loader::db();
		$q = "select cID from Pages where Pages.ctID = '{$this->ctID}' and cIsTemplate = 1";
		$cID = $db->getOne($q);
		if ($cID) {
			return $cID;
		}
	}

	function getOriginalCollectionID() {
		// this is a bit weird...basically, when editing a master collection, we store the
		// master collection ID in session, along with the collection ID we were looking at before
		// moving to the master collection. This allows us to get back to that original collection
		return $_SESSION['ocID'];
	}

	function getNumChildren() {
		return $this->cChildren;
	}
	
	function getNumChildrenDirect() {
		// direct children only
		$db = Loader::db();
		$v = array($this->cID);
		$num = $db->getOne('select count(cID) as total from Pages where cParentID = ?', $v);
		if ($num) {
			return $num;
		}
		return 0;
	}
	
	/** 
	 * Returns the first child of the current page, or null if there is no child
	 * @param string $sortColumn
	 * @return Page
	 */
	public function getFirstChild($sortColumn = 'cDisplayOrder asc') {
		$db = Loader::db();
		$cID = $db->GetOne("select Pages.cID from Pages inner join CollectionVersions on Pages.cID = CollectionVersions.cID where cvIsApproved = 1 and cParentID = ? order by {$sortColumn}", array($this->cID));
		if ($cID > 1) {
			return Page::getByID($cID, "ACTIVE");
		}
		return false;
	}

	function getCollectionChildrenArray( $oneLevelOnly=0 ) {
		$this->childrenCIDArray = array();
		$this->_getNumChildren($this->cID,$oneLevelOnly);
		return $this->childrenCIDArray;
	}

	function _getNumChildren($cID,$oneLevelOnly=0) {
		$db = Loader::db();
		$q = "select cID from Pages left join Packages on Pages.pkgID = Packages.pkgID where cParentID = {$cID} and cIsTemplate = 0 and (Packages.pkgHandle <> 'core' or pkgHandle is null or Pages.ctID > 0)";
		$r = $db->query($q);
		if ($r) {
			while ($row = $r->fetchRow()) {
				if ($row['cID'] > 0) {
					$this->childrenCIDArray[] = $row['cID'];
					if( !$oneLevelOnly ) $this->_getNumChildren($row['cID']);
				}
			}
		}
	}

	function canMoveCopyTo($cobj) {
		// ensures that we're not moving or copying to a collection inside our part of the tree
		$children = $this->getCollectionChildrenArray();
		$children[] = $this->getCollectionID();
		return (!in_array($cobj->getCollectionID(), $children));
	}

	function update($data) {
		$db = Loader::db();

		$vo = $this->getVersionObject();
		$cvID = $vo->getVersionID();
		$this->markModified();
		parent::refreshCache();
		
		$cName = $this->getCollectionName();
		$cDescription = $this->getCollectionDescription();
		$cDatePublic = $this->getCollectionDatePublic;
		$ctID = $this->getCollectionTypeID();
		$uID = $this->getCollectionUserID();
		$pkgID = $this->getPackageID();
		$cFilename = $this->getCollectionFilename();
		if (isset($data['cName'])) {
			$cName = $data['cName'];
		}
		if (isset($data['cDescription'])) {
			$cDescription = $data['cDescription'];
		}
		if (isset($data['cDatePublic'])) {
			$cDatePublic = $data['cDatePublic'];
		}
		if (isset($data['uID'])) {
			$uID = $data['uID'];
		}
		if (isset($data['ctID'])) {
			$ctID = $data['ctID'];
			// we grab the package that this ct belongs to
			$pkgID = $db->GetOne("select pkgID from PageTypes where ctID = ?", array($data['ctID']));
		}

		$txt = Loader::helper('text');
        if (!isset($data['cHandle']) && ($this->getCollectionHandle() != '')) {
            $cHandle = $this->getCollectionHandle();
        } else if (!$data['cHandle']) {
            // make the handle out of the title
            $cHandle = $txt->sanitizeFileSystem($cName);
        } else {
            $cHandle = $txt->sanitizeFileSystem($data['cHandle']);
        }
		
		// Update the non-canonical page paths
		$this->rescanPagePaths($data['ppURL']);

		if ($this->isGeneratedCollection()) {
			if (isset($data['cFilename'])) {
				$cFilename = $data['cFilename'];
			}
			// we only update a subset
			$v = array($cName, $cHandle, $cDescription, $cDatePublic, $cvID, $this->cID);
			$q = "update CollectionVersions set cvName = ?, cvHandle = ?, cvDescription = ?, cvDatePublic = ? where cvID = ? and cID = ?";
			$r = $db->prepare($q);
			$res = $db->execute($r, $v);

		} else {

			$v = array($cName, $cHandle, $cDescription, $cDatePublic, $cvID, $this->cID);
			$q = "update CollectionVersions set cvName = ?, cvHandle = ?, cvDescription = ?, cvDatePublic = ? where cvID = ? and cID = ?";
			$r = $db->prepare($q);
			$res = $db->execute($r, $v);				
		}

		$db->query("update Pages set uID = ?, ctID = ?, pkgID = ?, cFilename = ? where cID = ?", array($uID, $ctID, $pkgID, $cFilename, $this->cID));

		// run any internal event we have for page update
		$ret = Events::fire('on_page_update', $this);
	}
	
	public function uniquifyPagePath($origPath) {
		$db = Loader::db();

		$proceed = false;
		$suffix = 0;
		while ($proceed != true) {
			$newPath = ($suffix == 0) ? $origPath : $origPath . $suffix;
			$v = array($newPath, $this->cID);
			$q = "select cID from PagePaths where cPath = ? and cID <> ?";
			$r = $db->query($q, $v);
			if ($r->numRows() == 0) {
				$proceed = true;
			} else {
				$suffix++;
			}
		}

		return $newPath;
	}

	public function rescanPagePaths($newPaths) {
		$db = Loader::db();
		$txt = Loader::helper('text');

		// First, get the list of page paths from the DB.
		$ppaths = $this->getPagePaths();

		// Second, reset all of their cPath values to null.
		$paths = array();
		foreach ($ppaths as $ppath) {
			if (!$ppath['ppIsCanonical']) {
				$paths[$ppath['ppID']] = null;
			}
		}

		// Third, fill in the cPath values from the user updated data.
		if(is_array($newPaths)) {
			foreach ($newPaths as $key=>$val) {
				if (!empty($val)) {
					// Auto-prepend a slash if one is missing.
					$val = $txt->sanitizeFileSystem($val, true);
					if ($val{0} != '/') {
						$val = '/' . $val;
					}
					$paths[$key] = $val;
				}
			}
		}
		
		// Fourth, delete, update, or insert page paths as necessary.
{'fraction_non_alphanumeric': 0.17621594988386008, 'fraction_numerical': 0.0029210952347434363, 'mean_word_length': 3.6665298078502215, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 607, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 10}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2745210', 'n_tokens_mistral': 22568, 'n_tokens_neox': 19697, 'n_words': 4688}