File: D:/HostingSpaces/PvdBoogaard/indoorski.nl/backup/oude-site/cms/api/class.content.php
<?php
/**
* This file contains the iwp_content class
*
* @version $Id$
*
*
* @package IWP
* @subpackage IWP_API
*/
/**
* IWP Content Class
* This class extends the iwp_engine abstract class.
* This class handles all operations pertaining to the content table
*
* @package IWP
* @subpackage IWP_API
*/
class iwp_content extends iwp_engine {
/**
* This static variable contains permissions that are available to be chosen for setting Content Type permissions. This is the base list of permission options that all custom content types inherit. The list is later merged with options specific to any (field) modules that are added to the content type.
*
* @var Array
*/
public static $PermissionOptions;
/**
* This is the name of the field that is the primary key for this table. It is a private and final variable
*
* @var string
**/
protected $primaryKey = 'contentid';
/**
* This holds an array of the additional pages if this content item has any.
*
* @var string
*/
protected $additionalPages = null;
/**
* This is the name of the table without its prefix. This is a final private class variable and cannot be changed.
*
* @var string
**/
protected $baseTableName = 'content';
/**
* This is the array of the categories associated with the current content item
*
* @var array
**/
public $categories = array();
/**
* This is the final structure of the table in use. It can not be changed after this declaration.
*
* @var _columns
*
* @see ValidColumn
**/
public $_columns = array(
'parentid' => 0,
'typeid' => '',
'title' => '',
'summary' => '',
'author' => '',
'content' => '',
'startdate' => '',
'visible' => 1,
'status' => 1,
'featured' => 0,
'metakeywords' => '',
'metadesc' => '',
'metatitle' => '',
'views' => 0,
'expirydate' => '',
'enableexpiry' => 0,
'sortorder' => 0,
'hidefrommenu' => 0,
'haslayout' => 0,
'contentparentid' => 0,
'metaauto' => 0,
);
/**
* The data array of the current row held in the class memory. It should be the same structure as _columns.
*
* @var data
*
* @see Save
* @see Load
* @see LoadMulti
**/
protected $data = array();
/**
* This static variable holds the current instance of this object being loaded.
* So using the getInstance function anywhere will return the very same instance.
*
* @var object Instance
*/
public static $Instance;
/**
* This is a static function that sets up the class instance and stores it to the static variable. It will then return that instantiation in the future.
*
* @return iwp_content Returns the instantiated object
**/
public static function getInstance(){
if(!isset(self::$Instance)){
self::$Instance = new self();
}
return self::$Instance;
}
/**
* This returns an associative array of parent content items from the current ID up to the root with the content id as the array key and content name as the value.
*
* @param integer $catid The ID number of the current content item to get the parents for
*
* @return array An array of the parent content item
*/
function GetParentsArray ($contentid) {
if (!iwp_IsId($contentid)) {
return array();
}
$return = array();
$sql = sprintf("SELECT /* iwp_content::GetParentsArray */ contentid, title FROM " . IWP_TABLE_CONTENT . " WHERE contentid <> " . $contentid . " AND sortcache = LEFT((SELECT sortcache FROM " . IWP_TABLE_CONTENT . " WHERE contentid = " . $contentid . "), CHAR_LENGTH(sortcache)) ORDER BY sortcache");
$result = $this->db->Query($sql);
while ($row = $this->db->Fetch($result)) {
$return[$row['contentid']] = $row['title'];
}
return $return;
}
/**
* This function returns the PermissionOptions variable
*
* @return array The value of $PermissionOptions
*/
public static function GetPermissionOptions () {
return self::$PermissionOptions;
}
/**
* The class contructor. Call the parent constructor then reset the data columns
*
* @return void
**/
public function __construct(){
parent::__construct();
$this->_columns['expirydate'] = GetMysqlDateTime();
$this->_columns['startdate'] = GetMysqlDateTime();
$this->ResetColumns();
}
/**
* This function calls the parent load function to load the data from the database and then calls the LoadCategories function to get all the associated categories with this content item.
*
* @param integer $id The ID number of the content item to load
*
* @return boolean True if the item was loaded, false if it wasn't
*
* @see LoadCategories()
* @see ValidId()
*/
public function Load($id=null) {
if(!parent::Load($id)){
return false;
}
if($this->ValidId()){
$this->LoadCategories();
}
// expand the {siteURL} placeholders which are added in $this->Save()
$fieldsToFilter = array('content', 'summary');
foreach ($fieldsToFilter as $field) {
$this->data[$field] = $this->DecodeSiteURLs($this->data[$field]);
}
return true;
}
/**
* Examines the given HTML content and replaces {siteURL} placeholders with the current URL
*
* @param string $html input html to examine
* @return string modified version of $html with {siteURL} placeholders replaced
*/
public function DecodeSiteURLs ($html) {
// replace {siteURL} and {siteURL}/ with the current appPath
// this used to be replaced with the full siteURL but, see redmine issue #5941
return preg_replace('#{siteURL}/?#i', iwp_config::Get('appPath'), $html);
}
/**
* Examines a given URL and replaces the beginning with {siteURL} if it would match the site's current URL
*
* @param string $url The URL to parse
* @return string The result
*/
public function EncodeSiteURL ($url) {
$url = trim($url);
$siteURL = iwp_urls::getInstance()->ForceSlash(iwp_config::Get('siteURL'), false, true);
$siteURLLength = strlen($siteURL);
$appPath = iwp_config::Get('appPath');
$appPathLength = strlen($appPath);
if ($url == iwp_urls::getInstance()->ForceSlash(iwp_config::Get('siteURL'), false, false) || $url == iwp_urls::getInstance()->ForceSlash($appPath, true, false)) {
// match on entire string without added slashes
$url = '{siteURL}';
} elseif (substr($url, 0, $siteURLLength) == $siteURL) {
// match on asbolute reference
$url = '{siteURL}/'. substr($url, $siteURLLength);
} elseif (substr($url, 0, $appPathLength) == $appPath) {
// match on full reference
$url = '{siteURL}/'. substr($url, $appPathLength);
}
return $url;
}
/**
* Finds instances of the site's current URL (relative or absolute) in the given html string and replaces with a {siteURL} placeholder.
*
* Examines attributes such as href and src (among others). Does it's best to include style attributes too.
*
* @param string $html The string to encode
* @return string The encoded string
*/
public function EncodeSiteURLs ($html) {
$cleaner = new InterspireHTMLCleaner();
$cleaner->LoadHTMLAsDocument($html);
$document = $cleaner->GetDocument();
$body = $cleaner->GetBodyElement($document);
$xpath = $cleaner->LoadXPath($document);
// locate all instances of basic url attributes we can process
// a href
// area href
// img src
// iframe src
// script src
// object codebase (flash embeds)
// embed src (flash embeds)
// param value, where name is src (flash embeds)
$nodes = $xpath->query('//a[@href]/@href|//area[@href]/@href|//img[@src]/@src|//iframe[@src]/@src|//script[@src]/@src|//object[@codebase]/@codebase|//embed[@src]/@src|//param[@name="src" and @value]/@value', $body);
foreach ($nodes as $node) {
$node->parentNode->setAttribute($node->name, $this->EncodeSiteURL($node->textContent));
}
// try looking for urls inside style attributes
$nodes = $xpath->query('//*[@style]/@style', $body);
foreach ($nodes as $node) {
$node->parentNode->setAttribute($node->name, preg_replace("#url\\(([^\\)]*)\\)#ie", "'url('. $"."this->EncodeSiteURL('$1') .')'", $node->textContent));
}
// special case for tinymce's mouseover/out support
$nodes = $xpath->query('//img[@onmouseover]/@onmouseover|//img[@onmouseout]/@onmouseout', $body);
foreach ($nodes as $node) {
$script = $node->textContent;
if (preg_match("#^this\\.src='([^']*)';$#i", $script, $matches, PREG_OFFSET_CAPTURE)) {
$url = $this->EncodeSiteURL($matches[1][0]);
$node->parentNode->setAttribute($node->name, "this.src='". $url ."';");
}
}
return $cleaner->GetBodyContent($body);
}
/**
* This function calls the parent save function to save the current data to the database, then calls the SaveCategories function to save the category associations for this content item.
*
* @return mixed False if the save fail, an integer of the content ID if it succeeded
*
* @see SaveCategories()
* @see ValidId()
*/
public function Save(){
$fieldsToFilter = array('content', 'summary');
foreach ($fieldsToFilter as $field) {
$this->data[$field] = $this->EncodeSiteURLs($this->data[$field]);
}
$return = parent::Save();
if(!$return){
return $return;
}
if($this->ValidId()){
$this->SaveCategories();
}
return $return;
}
/**
* This function saves the current categories array into the database as the category associations for the current content item
*
* @return void
*
* @see $categories
*/
public function SaveCategories(){
$this->db->Query('delete from ' . IWP_TABLE_CATASSOC . ' where contentid='.$this->GetId());
if(is_array($this->categories) && sizeof($this->categories) > 0){
$contentid = $this->GetId();
foreach($this->categories as $k=>$catid){
$Insert = array();
$Insert['contentid'] = $contentid;
$Insert['categoryid'] = $catid;
$this->db->InsertQuery(IWP_TABLE_CATASSOC, $Insert);
}
}
}
public function DeleteContentType ($typeid=null) {
if (is_null($typeid)) {
return false;
}
$typeid = (int)$typeid;
if ($typeid <= 0) {
return false;
}
$this->db->Query('delete from ' . IWP_TABLE_URLS .' where associd IN (select contentid from `'. $this->GetTableName() . '` where `typeid`='. $typeid . ')
AND (
(assoctype="content" AND )
OR
(assoctype="redirect" AND redirecttype="content")
)');
$result = $this->db->Query("delete from `". $this->GetTableName() ."` where `typeid`=". $typeid);
if (!$result) {
return false;
}
}
/**
* The delete function which calls the parent function but also deletes the associations in the URLs table
*
* @return boolean True if successful, false otherwise
*/
public function Delete(){
$id = $this->GetId();
if($id < 1) {
return true;
}
if (!iwp_event::trigger(new iwp_event_content_beforedelete($this))) {
return false;
}
if(!parent::Delete()){
return false;
}
iwp_event::trigger(new iwp_event_content_afterdelete($id, $this));
// remove any additional pages
$this->db->Query('delete from ' . IWP_TABLE_CONTENT .' where parentid='.$id);
return $this->db->Query('delete from ' . IWP_TABLE_URLS .' where (assoctype="content" AND associd='.$id.') OR (assoctype="redirect" AND redirecttype="content" AND associd='.$id.')');
}
/**
* This function loads the category associations for the current content item from the database into the $categories member variable
*
* @return void
*
* @see $categories
*/
public function LoadCategories(){
$q = $this->db->Query('select * from ' . IWP_TABLE_CATASSOC . ' where contentid='.$this->GetId());
$this->categories = array();
while($row = $this->db->Fetch($q)){
$this->categories[] = $row['categoryid'];
}
}
/**
* Returns a granularity list for this class.
*
* @param Integer $total Total will be populated with number of rows found in query (by reference)
* @param String $filter Filter string, optional
* @param Integer $page Page number of records to return, optional
* @return Array List of value/text pairs
*/
public static function getGranularityList (&$total, &$page, $filter = '', $scopeType, $scopeName)
{
$limitStart = ($page * IWP_PERMISSIONGRANULARITEMS_PER_PAGE) - IWP_PERMISSIONGRANULARITEMS_PER_PAGE;
if ($filter) {
$filter = '%'. self::getInstance()->db->Quote($filter) .'%';
$where = sprintf("WHERE (typeid = %d) AND (title LIKE '%s')", (int)$scopeName, $filter);
} else {
$where = sprintf("WHERE (typeid = %d)", (int)$scopeName);
}
$result = self::getInstance()->db->Query(sprintf("SELECT SQL_CALC_FOUND_ROWS contentid AS `value`, title AS `text` FROM %s %s ORDER BY title LIMIT %d, %d", IWP_TABLE_CONTENT, $where, $limitStart, IWP_PERMISSIONGRANULARITEMS_PER_PAGE));
$total = self::getInstance()->db->FetchOne('SELECT found_rows()');
$list = array();
if ($result) {
while ($row = self::getInstance()->db->Fetch($result)) {
$row['value'] = (int)$row['value'];
array_push($list, $row);
}
self::getInstance()->db->FreeResult($result);
}
return $list;
}
/**
* Return this instance's list of categories
*
* @return Array List of categoriy ids
*/
public function GetCategories () {
return $this->categories;
}
/**
* Returns a list of content titles for a supplied list of content IDs
*
* @param Array $ids
* @return array
*/
public static function GetTitleList ($ids)
{
$list = array();
if (count($ids)) {
$me = self::getInstance();
$sql = sprintf("SELECT contentid as `value`, title as `text` FROM %s WHERE contentid IN (%s) ORDER BY `title`", IWP_TABLE_CONTENT, implode(',', $ids));
$result = $me->db->Query($sql);
while ($row = $me->db->Fetch($result)) {
$row['value'] = (int)$row['value'];
array_push($list, $row);
}
}
return $list;
}
public function RegenerateMasterSortingCache() {
iwp_lists::getInstance()->ClearSortingCacheByListId(0);
$sql = "SELECT c.`contentid` as `pkey`, c.`contentparentid` as `parentid` FROM `" . IWP_TABLE_CONTENT . "` as c inner join ". IWP_TABLE_CONTENTTYPES ." as ct on ct.typeid = c.typeid where /*%%visibility%%*/ (1=1) ORDER BY `sortorder` ASC";
$sql = iwp_lists::InsertVisibility($sql, 'content');
iwp_lists::getInstance()->rebuildSortCacheTree($sql, 0);
}
/**
* Return an instance of iwp_contenttypes populated with data for this content's contenttype
*
* @return iwp_contenttypes A singleton instance of iwp_contenttypes
*/
public function GetContentTypeInstance(){
if(!$this->ValidId()){
return;
}
$contentTypeInstance = iwp_contenttypes::getInstance();
if($contentTypeInstance->GetId() != $this->Get('typeid')){
$contentTypeInstance->Load($this->Get('typeid'));
}
return $contentTypeInstance;
}
/**
* This function loads the list of child pages for a content item into an array.
* The resulting array has the page's ID number as the key, and the database row as the value.
*
* @return array The array of child pages
*/
public function GetAdditionalPagesList(){
if(!is_array($this->additionalPages) || sizeof($this->additionalPages) < 1) {
$q = $this->db->Query("select * from ". IWP_TABLE_CONTENT . " where parentid=".$this->GetId()." AND contentid!=".$this->GetId() ." order by `pagesortorder` asc");
$pages = array();
while(($row = $this->db->Fetch($q))) {
$pages[] = $row;
}
$this->additionalPages = $pages;
}
return $this->additionalPages;
}
/**
* This function checks to see if this content item has additional pages or not
*
* @return boolean True if the content item does have additional pages, false otherwise
*/
public function HasAdditionalPages(){
$this->GetAdditionalPagesList();
if(is_array($this->additionalPages) && sizeof($this->additionalPages) > 0){
return true;
}else{
return false;
}
}
/**
* This function checks to see if this content item has a field as defined by its content type
*
* @return boolean True if the content item does have the field, false otherwise
*/
public function HasField($field=null){
if(is_null($field)){
return false;
}
$contentType = $this->GetContentTypeInstance();
if(in_array('field_'.$field, $contentType->flatAvailableFieldsList)) {
return true;
}
return false;
}
/**
* Returns the URL for the currently loaded content item.
*
* @param boolean $full Optional. If set to true, this will return a full url including the domain. Otherwise, only an absolute url for the current domain will be returned.
* @return string|boolean The URL for the currently loaded content item, or false if no content id is loaded.
*/
public function GetUrl ($full = false)
{
if (!$this->GetId()) {
return false;
}
if ($full) {
return iwp_config::Get('siteURL') . $this->urls->GetUrl('content', $this->GetId(), $this->data);
}
return $this->urls->GetURLPrepend(true, false) . $this->urls->GetUrl('content', $this->GetId(), $this->data);
}
/**
* Returns an array containing user data representing the list of authors for this article. If a particular author id in the author field for this content could not be matched against the user list, nothing will be returned for that author.
*
* @return array An array containing a list of user data.
*/
public function GetAuthorList ()
{
return iwp_user::getInstance()->GetAuthorListByIdList($this->Get('author'));
}
/**
* This function updates the URL for the currently loaded content item.
* It optionally takes an arguement for the new URL. If its not set, a new one is generated.
* It updates all old urls to be redirects and inserts the new URL for the content item.
*
* @param string $url Optionally the new URL for the current content item.
*
* @return Doesn't return anything
*/
public function UpdateURL($url=null){
if(!isset($this->ContentType)){
$this->ContentType = iwp_admin_contenttypes::getInstance();
if($this->ContentType->GetId() != $this->Get('typeid')){
$this->ContentType->Load($this->Get('typeid'));
}
}
// we need to update any previous URL and turn it into a redirect
$Update = array();
$Update['redirecttype'] = 'content';
$Update['assoctype'] = 'redirect';
$this->db->UpdateQuery(IWP_TABLE_URLS, $Update, 'assoctype="content" AND associd='.$this->GetId());
// save the new URL
$Insert = array();
if(is_null($url)){
$Insert['urlpath'] = $this->urls->GetUniqueContentUrl($this->GetId(), $this, $this->ContentType);
}else{
$Insert['urlpath'] = $url;
}
$Insert['assoctype'] = 'content';
$Insert['associd'] = $this->GetId();
$this->db->InsertQuery(IWP_TABLE_URLS, $Insert);
}
/**
* Increments this content's view count if it hasn't been viewed in this session.
*
* @param boolean $force Default false. If true, the session check will be bypassed.
* @return boolean Returns true if the view count was incremented. Otherwise false.
*/
public function Hit ($force = false) {
$id = $this->GetId();
if (!$id) {
return false;
}
// increment this session's hit counter for this content, while saving the new hit count value into $hits
// using @ stops php from whinging about an invalid index while automatically creating ContentHits and the $id index if either don't exist
$hits = @++$_SESSION[iwp_session::GetKey()]['ContentHits'][$id];
if ($hits == 1 || $force) {
// first hit this session or we're forcing it - dont' use the ->Save() method because the front end uses this, and we don't want it saving changes that modules may have put in to the loaded data
$this->db->Query('UPDATE ' . $this->GetTableName() . ' SET views = views + 1 WHERE contentid = ' . $this->GetId());
return true;
}
return false;
}
}
/**
* These are the permission options available for this class
*/
iwp_content::$PermissionOptions = array(
'full' => new iwp_permissionoption(true),
'create' => new iwp_permissionoption(true, false, true),
'delete' => new iwp_permissionoption(true),
'edit' => new iwp_permissionoption(true),
'approve' => new iwp_permissionoption(true),
);