Source for file LibertyContent.php
Documentation is available at LibertyContent.php
/* Management of Liberty content
* @author spider <spider@steelsun.com>
// +----------------------------------------------------------------------+
// | Copyright (c) 2004, bitweaver.org
// +----------------------------------------------------------------------+
// | All Rights Reserved. See below for details and a complete list of authors.
// | Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See http://www.gnu.org/copyleft/lesser.html for details
// | For comments, please use phpdocu.sourceforge.net documentation standards!!!
// | -> see http://phpdocu.sourceforge.net/
// +----------------------------------------------------------------------+
// | Authors: spider <spider@steelsun.com>
// +----------------------------------------------------------------------+
* Maximum lengths for database fields
if( !defined( 'BIT_CONTENT_MAX_TITLE_LEN' ) ) {
define( 'BIT_CONTENT_MAX_TITLE_LEN', 160);
define( 'BIT_CONTENT_MAX_LANGUAGE_LEN', 4);
define( 'BIT_CONTENT_MAX_IP_LEN', 39);
define( 'BIT_CONTENT_MAX_FORMAT_GUID_LEN', 16);
if( !defined( 'BIT_CONTENT_DEFAULT_STATUS' ) ) {
define( 'BIT_CONTENT_DEFAULT_STATUS', 50);
//$gBitSystem->getConfig( 'liberty_status_deleted', -999 ) );
//$gBitSystem->getConfig( 'liberty_status_threshold_private', -40 ) );
//$gBitSystem->getConfig( 'liberty_status_threshold_protected', -20 ) );
//$gBitSystem->getConfig( 'liberty_status_threshold_hidden', -10 ) );
require_once( LIBERTY_PKG_PATH. 'LibertyBase.php' );
define( 'LIBERTY_SPLIT_REGEX', "!\.{3}split\.{3}[\t ]*\n?!" );
* Virtual base class (as much as one can have such things in PHP) for all
* derived tikiwiki classes that require database access.
* Content Id if an object has been loaded
* If this content is being viewed within a structure
* Content type GUID for this LibertyContent object
* Content type hash for this LibertyContent object
*Permissions hash specific to the user accessing this LibertyContetn object
* Preferences hash specific to this LibertyContent object - accessed via getPreference/storePreference
* Control permission specific to this LibertyContent type
* Construct an empty LibertyBase object with a blank permissions array
$this->mPrefs = NULL; // init to NULL so getPreference can determine if a load is necessary
// NOTE: we are not assigning anything to mViewContentPerm. if this is empty, we will return TRUE in hasViewPermission()
return !$gBitSystem->isLive();
return parent::isCacheableObject() && !empty( $this->mContentId );
* load Assume a derived class has joined on the liberty_content table, and loaded it's columns already.
function load( $pContentId = NULL, $pPluginParams = NULL ) {
if( !empty( $this->mInfo['content_type_guid'] )) {
global $gLibertySystem, $gBitSystem, $gBitUser;
$this->mInfo['content_type'] = $gLibertySystem->mContentTypes[$this->mInfo['content_type_guid']];
* Verify the core class data required to update the liberty_content table entries
* Verify will build an array [content_store] with all of the required values
* and populate it with the relevent data to create/update the liberty_content
* @param array $pParamHash Array of content data to be stored
* @param array $pParamHash[content_id]
* @param array $pParamHash[user_id]
* @param array $pParamHash[modifier_user_id]
* @param array $pParamHash[created]
* @param array $pParamHash[last_modified]
* @param array $pParamHash[content_type_guid]
* @param array $pParamHash[format_guid]
* @param array $pParamHash[last_hit]
* @param array $pParamHash[event_time]
* @param array $pParamHash[hits]
* @param array $pParamHash[lang_code]
* @param array $pParamHash[title]
* @param array $pParamHash[ip]
* @param array $pParamHash[edit]
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
function verify( &$pParamHash ) {
global $gLibertySystem, $gBitSystem, $gBitLanguage, $gBitUser;
// It is possible a derived class set this to something different
if( empty( $pParamHash['content_type_guid'] ) ) {
if( empty( $pParamHash['user_id'] ) ) {
$pParamHash['user_id'] = $gBitUser->getUserId();
if( !@$this->verifyId( $pParamHash['content_id'] ) ) {
// These should never be updated, only inserted
$pParamHash['content_store']['created'] = !empty( $pParamHash['created'] ) ? $pParamHash['created'] : $gBitSystem->getUTCTime();
// This may get overridden by owner set
$pParamHash['content_store']['user_id'] = $pParamHash['user_id'];
// Set a default status when creating if none is set
// This may get overwritten below
if( empty($pParamHash['content_store']['content_status_id'] ) ){
if( @BitBase::verifyId( $pParamHash['content_id'] )) {
$pParamHash['content_store']['content_id'] = $pParamHash['content_id'];
// Are we allowed to override owner?
if( !empty($pParamHash['owner_id'] ) ) {
if( $gBitUser->isAdmin() || ($gBitSystem->isFeatureActive('liberty_allow_change_owner') && $gBitUser->hasPermission('p_liberty_edit_content_owner') && !empty($pParamHash['owner_id']) && !empty($pParamHash['current_owner_id']) && $pParamHash['owner_id'] != $pParamHash['current_owner_id']) ) {
// If an owner is being set override user_id
$pParamHash['content_store']['user_id'] = $pParamHash['owner_id'];
// Do we need to change the status
if (!empty($pParamHash['content_status_id'])) {
if( $this->hasUserPermission( 'p_liberty_edit_content_status' ) || $gBitUser->hasUserPermission( 'p_liberty_edit_all_status') ) {
if (empty($allStatus[$pParamHash['content_status_id']])) {
$this->mErrors['content_status_id'] = "No such status ID or permission denied.";
$pParamHash['content_store']['content_status_id'] = $pParamHash['content_status_id'];
$pParamHash['field_changed'] = empty( $pParamHash['content_id'] )
|| (!empty($this->mInfo["data"]) && !empty($pParamHash["edit"]) && (md5($this->mInfo["data"]) != md5($pParamHash["edit"])))
|| (!empty($pParamHash["title"]) && !empty($this->mInfo["title"]) && (md5($this->mInfo["title"]) != md5($pParamHash["title"])))
|| (!empty($pParamHash["edit_comment"]) && !empty($this->mInfo["edit_comment"]) && (md5($this->mInfo["edit_comment"]) != md5($pParamHash["edit_comment"])));
// check some lengths, if too long, then truncate
if( !empty( $pParamHash['title'] ) ) {
} elseif( isset ( $pParamHash['title'] ) ) {
$pParamHash['content_store']['title'] = NULL;
// get the lang code from $_REQUEST if it's not set
if( !empty( $pParamHash['lang_code'] ) && in_array( $pParamHash['lang_code'], array_keys( $gBitLanguage->mLanguageList ) ) ) {
$pParamHash['content_store']['lang_code'] = $pParamHash['lang_code'];
} elseif( !empty( $_REQUEST['i18n']['lang_code'] ) && in_array( $_REQUEST['i18n']['lang_code'], array_keys( $gBitLanguage->mLanguageList ) ) ) {
$pParamHash['content_store']['lang_code'] = $_REQUEST['i18n']['lang_code'];
$pParamHash['content_store']['last_modified'] = !empty( $pParamHash['last_modified'] ) ? $pParamHash['last_modified'] : $gBitSystem->getUTCTime();
if( !empty( $pParamHash['event_time'] ) ) {
$pParamHash['content_store']['event_time'] = $pParamHash['event_time'];
// WARNING: Assume WIKI if t
if( !empty( $pParamHash['content_id'] ) ) {
// do NOT allow changing of content_type_guid in update for safety of overridden secondary classes (like BitBook )
unset ( $pParamHash['content_store']['content_type_guid'] );
} elseif( empty( $pParamHash['content_type_guid'] ) ) {
$this->mErrors['content_type'] = tra( 'System Error: Unknown content type' );
$pParamHash['content_store']['content_type_guid'] = $pParamHash['content_type_guid'];
// setup some required defaults if not defined
if( empty( $pParamHash['ip'] ) ) {
if( empty( $_SERVER["REMOTE_ADDR"] ) ) {
$pParamHash['ip'] = '127.0.0.1';
$pParamHash['ip'] = $_SERVER["REMOTE_ADDR"];
$pParamHash['content_store']['ip'] = $pParamHash['ip'];
if( !@$this->verifyId( $pParamHash['modifier_user_id'] ) ) {
$pParamHash['modifier_user_id'] = $gBitUser->getUserId();
$pParamHash['content_store']['modifier_user_id'] = $pParamHash['modifier_user_id'];
if( empty( $pParamHash['format_guid'] ) ) {
$pParamHash['format_guid'] = $gBitSystem->getConfig( 'default_format', 'tikiwiki' );
$pParamHash['content_store']['format_guid'] = $pParamHash['format_guid'];
if( !empty( $pParamHash['hits'] ) ) {
$pParamHash['content_store']['hits'] = $pParamHash['hits'] + 1;
$pParamHash['content_store']['last_hit'] = $gBitSystem->getUTCTime();
if( !empty( $pParamHash['edit'] ) && $func = $gLibertySystem->getPluginFunction( $pParamHash['format_guid'], 'verify_function' ) ) {
$error = $func( $pParamHash );
if( !empty( $pParamHash['content_store']['data'] )) {
$this->filterData( $pParamHash['content_store']['data'], $pParamHash['content_store'], 'prestore' );
// someone has deleted the data entirely - common for fisheye
$pParamHash['content_store']['data'] = NULL;
$pParamHash['content_store']['format_guid'] = $pParamHash['format_guid'];
$pParamHash['content_store']['version'] = 1;
$pParamHash['content_store']['version'] = $this->mInfo['version'] + 1;
if ( ( !(isset ($this->mInfo['no_index']) and $this->mInfo['no_index'] == true ) ) and !isset ($this->mInfo['index_data']) ) {
$this->mInfo['index_data'] = "";
if ( isset ($pParamHash["title"]) ) $this->mInfo['index_data'] .= $pParamHash["title"] . ' ';
if ( isset ($pParamHash["author_name"]) ) $this->mInfo['index_data'] .= $pParamHash["author_name"] . ' ';
if ( isset ($pParamHash["edit"]) ) $this->mInfo['index_data'] .= $pParamHash["edit"];
if( $gBitUser->hasPermission( 'p_liberty_enter_html' ) ) {
$prefs[] = 'content_enter_html';
foreach( $prefs as $pref ) {
if( !empty( $pParamHash['preferences'][$pref] ) ) {
$pParamHash['preferences_store'][$pref] = $pParamHash['preferences'][$pref];
$pParamHash['preferences_store'][$pref] = NULL;
$pParamHash['data_store']['summary'] = !empty( $pParamHash['summary'] ) ? $pParamHash['summary'] : NULL ;
// call verify service to see if any services have errors
* Create a new content object or update an existing one
* @param array Array of content data to be stored <br>
* See verify for details of the values required
function store( &$pParamHash ) {
$this->mDb->StartTrans();
if( !@$this->verifyId( $pParamHash['content_id'] ) ) {
// make sure some variables are stuff in case services need getObjectType, mContentId, etc...
$this->mContentId = $pParamHash['content_id'] = $pParamHash['content_store']['content_id'] = $this->mDb->GenID( 'liberty_content_id_seq' );
$result = $this->mDb->associateInsert( $table, $pParamHash['content_store'] );
$this->mLogs['content_store'] = "Created";
if( !empty( $pParamHash['content_store']['title'] ) && !empty( $this->mInfo['title'] ) && $pParamHash['content_store']['title'] != $this->mInfo['title'] ) {
$this->mLogs['rename_page'] = "Renamed from {$this->mInfo['title']} to { $pParamHash['content_store']['title']}. ";
$result = $this->mDb->associateUpdate( $table, $pParamHash['content_store'], array("content_id" => $pParamHash['content_id'] ) );
$this->mLogs['content_store'] = "Updated";
if( !empty( $pParamHash['force_history'] ) || ( empty( $pParamHash['minor'] ) && $this->getField( 'version' ) && $pParamHash['field_changed'] )) {
if( empty( $pParamHash['has_no_history'] ) ) {
//$mailEvents = 'wiki_page_changes';
// Call the formatter's save
if( !empty( $pParamHash['content_store']['data'] )) {
if( $func = $gLibertySystem->getPluginFunction( $pParamHash['format_guid'], 'store_function' ) ) {
$ret = $func( $pParamHash );
// post store filter - this is needed to deal with filters that need the content_id on the first save
$this->filterData( $pParamHash['content_store']['data'], $pParamHash['content_store'], 'poststore' );
foreach( $pParamHash['data_store'] AS $dataType => $data ) {
// store content preferences
if( @is_array( $pParamHash['preferences_store'] ) ) {
foreach( $pParamHash['preferences_store'] as $pref => $value ) {
// store hits and last hit
if( !empty( $pParamHash['content_store']['hits'] ) ) {
$this->setHits($pParamHash['content_store']['hits'], $pParamHash['content_store']['last_hit']);
// store any messages in the logs
$this->mDb->CompleteTrans();
* Delete comment entries relating to the content object
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
// Delete all comments associated with this piece of content
$query = "SELECT `comment_id` FROM `". BIT_DB_PREFIX. "liberty_comments` WHERE `root_id` = ?";
if( $commentIds = $this->mDb->getCol($query, array( $this->mContentId ) ) ) {
foreach ($commentIds as $commentId) {
* Delete content object and all related records
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
global $gBitSystem, $gLibertySystem;
$this->mDb->StartTrans();
// services, filters and cache
if( $this->getField( 'format_guid' ) && $func = $gLibertySystem->getPluginFunction( $this->getField( 'format_guid' ), 'expunge_function' ) ) {
// remove favorites - this probably should be a content_expunge_function in users
$this->mDb->query( "DELETE FROM `". BIT_DB_PREFIX. "users_favorites_map` WHERE `favorite_content_id`=?", array( $this->mContentId ) );
// remove entries in the history
// Remove individual permissions for this object if they exist
$query = "delete from `". BIT_DB_PREFIX. "liberty_content_permissions` where `content_id`=?";
$result = $this->mDb->query( $query, array( $this->mContentId ) );
// it's not this simple. what about orphans? needs real work. :( xoxo - spider
// $query = "DELETE FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `content_id` = ?";
// $result = $this->mDb->query( $query, array( $this->mContentId ) );
// Remove any queued data processing (images, movies, etc.)
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_process_queue` WHERE `content_id` = ?";
$result = $this->mDb->query( $query, array( $this->mContentId ) );
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_data` WHERE `content_id` = ?";
$result = $this->mDb->query( $query, array( $this->mContentId ) );
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_hits` WHERE `content_id` = ?";
$result = $this->mDb->query( $query, array( $this->mContentId ) );
// Remove content preferences
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_prefs` WHERE `content_id` = ?";
$result = $this->mDb->query( $query, array( $this->mContentId ) );
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_links` WHERE `to_content_id` = ? or `from_content_id` = ?";
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content` WHERE `content_id` = ?";
$result = $this->mDb->query( $query, array( $this->mContentId ) );
$this->mLogs['content_expunge'] = "Deleted";
$this->mDb->CompleteTrans();
* storeAliases will store aliases to a given content item
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
if( $this->isValid() && isset ( $pParamHash['alias_string']) ) {
$trimmedAliases = trim( $pParamHash['alias_string'] );
if( !empty( $trimmedAliases ) && $aliases = explode( "\n", $trimmedAliases ) ) {
foreach( $aliases as $a ) {
$this->mDb->query( "INSERT INTO `". BIT_DB_PREFIX. "liberty_aliases` (`content_id`, `alias_title`) VALUES (?,?)", array( $this->mContentId, trim( $a ) ) );
* storeHistory will store the previous data into the history table for reference
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
"version" => $this->getField( "version" ),
"last_modified" => $this->getField( "last_modified" ),
"user_id" => $this->getField( "modifier_user_id" ),
"summary" => $this->getField( "summary" ),
"history_comment" => (string) substr( $this->getField( "edit_comment" ), 0, 200 ),
"format_guid" => $this->getField( "format_guid", $gBitSystem->getConfig( "default_format", "tikiwiki" )),
$this->mDb->associateInsert( BIT_DB_PREFIX. "liberty_content_history", $storeHash );
* Get count of the number of historic records for the page
SELECT COUNT(*) AS `hcount`
$ret = $rs->fields['hcount'];
* Get complete set of historical data in order to display a given wiki page version
* @param array $max_records
* @return array of mInfo data
function getHistory( $pVersion= NULL, $pUserId= NULL, $pOffset = 0, $max_records = - 1 ) {
$this->getServicesSql( 'content_list_history_sql_function', $selectSql, $joinSql, $whereSql, $bindVars );
if( @BitBase::verifyId( $pUserId ) ) {
$whereSql .= ' th.`user_id`=? ';
$whereSql .= ' th.`content_id`=? ';
if( !empty( $pVersion ) ) {
$versionSql = ' AND th.`version`=? ';
$query = "SELECT COUNT(*) AS `hcount`
$cant = $rs->fields['hcount'];
# Check for offset out of range
} elseif ( $pOffset > $cant ) {
$lastPageNumber = ceil ( $cant / $max_records ) - 1;
$pOffset = $max_records * $lastPageNumber;
$query = "SELECT lc.`title`, th.*,
uue.`login` AS modifier_user, uue.`real_name` AS modifier_real_name,
uuc.`login` AS creator_user, uuc.`real_name` AS creator_real_name
FROM `". BIT_DB_PREFIX. "liberty_content_history` th INNER JOIN `". BIT_DB_PREFIX. "liberty_content` lc ON (lc.`content_id` = th.`content_id`)
LEFT JOIN `". BIT_DB_PREFIX. "users_users` uue ON (uue.`user_id` = th.`user_id`)
LEFT JOIN `". BIT_DB_PREFIX. "users_users` uuc ON (uuc.`user_id` = lc.`user_id`)
WHERE $whereSql $versionSql order by th.`version` desc";
$result = $this->mDb->query( $query, $bindVars, $max_records, $pOffset );
$aux['creator'] = (isset ( $aux['creator_real_name'] ) ? $aux['creator_real_name'] : $aux['creator_user'] );
$aux['editor'] = (isset ( $aux['modifier_real_name'] ) ? $aux['modifier_real_name'] : $aux['modifier_user'] );
//array_push( $ret, $aux );
// Temporary patch to get a $pListHash array for the output
// this needs to be tidied on the input side
// TODO: update this to work like newer getList methods
$pListHash["data"] = $data;
$pListHash["cant"] = $cant;
$pListHash["max_records"] = $max_records;
$pListHash["offset"] = $pOffset;
$pListHash["find"] = NULL;
$pListHash["sort_mode"] = NULL;
* Removes last version of the page (from pages) if theres some
* version in the liberty_content_history then the last version becomes the actual version
* @param string $pComment
$query = "select * from `". BIT_DB_PREFIX. "liberty_content_history` where `content_id`=? order by ". $this->convertSortMode("last_modified_desc");
$result = $this->mDb->query($query, array( $this->mContentId ) );
if ($result->numRows()) {
$res = $result->fetchRow();
$this->remove_all_versions($page);
$action = "Removed last version";
$t = $gBitSystem->getUTCTime();
$query = "insert into `". BIT_DB_PREFIX. "liberty_action_log`( `log_message`, `content_id`, `last_modified`, `user_id`, `ip`, `error_message`) values( ?, ?, ?, ?, ?, ?)";
$result = $this->mDb->query( $query, array( $action, $this->mContentId, $t, ROOT_USER_ID, $_SERVER["REMOTE_ADDR"], $pComment ));
* Roll back to a specific version of a page
* @param pVersion Version number to roll back to
* @param pComment Comment text to be added to the action log
* @return TRUE if completed successfully
global $gBitUser,$gBitSystem;
$this->mDb->StartTrans();
// JHT - cache invalidation appears to be handled by store function - so don't need to do it here
$query = "select lch.*, lch.`user_id` AS modifier_user_id, lch.`data` AS `edit` from `". BIT_DB_PREFIX. "liberty_content_history` lch where lch.`content_id`=? and lch.`version`=?";
if( $res = $this->mDb->getRow($query,array( $this->mContentId, $pVersion ) ) ) {
$res['edit_comment'] = 'Rollback to version '. $pVersion. ' by '. $gBitUser->getDisplayName();
$res['edit_comment'] .= ": $pComment";
// JHT 2005-06-19_15:22:18
// set ['force_history'] to
// make sure we don't destory current content without leaving a copy in history
// if rollback can destroy the current page version, it can be used
$res['force_history'] = 1;
// JHT 2005-10-16_22:21:10
// title must be set or store fails
// we use current page name
if( $this->store( $res ) ) {
$this->mDb->CompleteTrans();
$this->mDb->RollbackTrans();
* Removes a specific version of a page
* @param pVersion Version number to roll back to
* @param pComment Comment text to be added to the action log
* @return TRUE if completed successfully
$this->mDb->StartTrans();
$versionSql = " and `version`=? ";
$hasRows = $this->mDb->getOne( "SELECT COUNT(`version`) FROM `". BIT_DB_PREFIX. "liberty_content_history` WHERE `content_id`=? $versionSql ", $bindVars );
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_history` WHERE `content_id`=? $versionSql ";
$result = $this->mDb->query( $query, $bindVars );
$action = "Removed version $pVersion";
$t = $gBitSystem->getUTCTime();
$query = "INSERT INTO `". BIT_DB_PREFIX. "liberty_action_log` (`log_message`,`content_id`,`last_modified`,`user_id`,`ip`,`error_message`) VALUES (?,?,?,?,?,?)";
$result = $this->mDb->query($query,array($action,$this->mContentId,$t,$gBitUser->mUserId,$_SERVER["REMOTE_ADDR"],$pComment));
$this->mDb->CompleteTrans();
$keys = array_merge( array( 'content_type_guid', 'title', 'uri', 'url', 'content_id' ), $this->invokeServices( 'content_export_keys_function', $pList ) );
foreach( $pList as $key=> $hash ) {
foreach( $keys as $field ) {
if( isset ( $hash[$field] ) ) {
$ret[$key][$field] = $hash[$field];
$ret[$key]['content_id'] = $hash['content_id'];
$ret[$key]['date_created'] = date( DateTime::W3C, $hash['created'] );
$ret[$key]['date_last_modified'] = date( DateTime::W3C, strtotime( $hash['last_modified'] ) );
* Create an export hash from the data
'date_created' => date( DateTime::W3C, $this->getField('created') ),
'date_last_modified' => date( DateTime::W3C, $this->getField('last_modified') ),
* Check mContentId to establish if the object has been loaded with a valid record
* Check permissions to establish if user has permission to view the object
* Should be provided by the decendent package
* Check permissions to establish if user has permission to edit the object
* Should be provided by the decendent package
* Check permissions to establish if user has permission to admin the object
* That would include permission to delete an object or change it's permissions
* Should be provided by the decendent package
* Check user_id to establish if the object that has been loaded was created by the current user
* @param $pParamHash optionally pass in the hash to check against
* @return TRUE if user owns the content
function isOwner( $pParamHash = NULL ) {
if( @BitBase::verifyId( $pParamHash['user_id'] ) ) {
$user_id = $pParamHash['user_id'];
$user_id = $this->mInfo['user_id'];
* Check if content matches content type GUID - must also be a valid content object, it will not work for generic content class
return( $this->isValid() && !empty( $this->mInfo['content_type_guid'] ) && $this->mInfo['content_type_guid'] == $pContentGuid );
* Check permissions to establish if user has permission to access the object
* Set up access to services used by the object
// Invoke any services store functions such as categorization or access control
if( $serviceFunctions = $gLibertySystem->getServiceValues( $pServiceFunction ) ) {
foreach ( $serviceFunctions as $func ) {
if( $errors = $func( $this, $pFunctionParam ) ) {
* check if a service is active for this content type
* requires package LCConfig
* provisional method until LCConfig package is integrated into the core
$ret = TRUE; // we return true by default to preserve legacy service opperation which has no content type preferences
if( $gBitSystem->isPackageActive( 'lcconfig' ) ){
// LCConfig is a singleton class
$LCConfig = LCConfig::getInstance();
// LCConfig negates services by content type
// if result is not 'n' then service should apply to this content type
if( $LCConfig->getConfig( 'service_'. $pServiceGuid, $this->mContentTypeGuid ) == 'n' ){
* check if a service is required for this content type
* requires package LCConfig
* provisional method until LCConfig package is integrated into the core
$ret = TRUE; // we return true by default to preserve legacy service opperation which has no content type preferences
if( $gBitSystem->isPackageActive( 'lcconfig' ) ){
// LCConfig is a singleton class
$LCConfig = LCConfig::getInstance();
return ( $LCConfig->getConfig( 'service_'. $pServiceGuid, $this->mContentTypeGuid ) == 'required' );
* Default liberty sql for joining a content object table to liberty.
* We are proposing a new way of building queries here where we build up everything in a hash with implicit AND over all
* where clauses and then do an array_merge and concatenation in a single function at the end. See convertQueryHash for details.
* This is an example current, and would be invoked in getList
* $queryHash = array('summary', 'users', 'hits', 'avatar', 'primary'), array('select' => array('sql' => $selectSql), 'join' => array('sql' => $joinSql), 'where' => array('sql' => $whereSql, 'var' => $bindVars ));
* $this->getLibertySql( 'bp.`content_id`', $queryHash);
function getLibertySql( $pJoinColumn, &$pQueryHash, $pJoins = NULL, $pServiceFunction = NULL, $pObject = NULL, $pParamHash = NULL ) {
$pQueryHash['select']['sql'][] = "lc.*";
$pQueryHash['join']['sql'][] = "
INNER JOIN `". BIT_DB_PREFIX. "liberty_content` lc ON( lc.`content_id` = $pJoinColumn )";
if( empty( $pJoins ) || in_array( 'summary', $pJoins )) {
$pQueryHash['select']['sql'][] = "lcds.`data` AS `summary`";
$pQueryHash['join']['sql'][] = "
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_data` lcds ON( lc.`content_id` = lcds.`content_id` AND lcds.`data_type` = ? )";
$pQueryHash['join']['var'][] = 'summary';
if( empty( $pJoins ) || in_array( 'hits', $pJoins )) {
$pQueryHash['select']['sql'][] = "lch.`hits`, lch.`last_hit`";
$pQueryHash['join']['sql'][] = "
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_hits` lch ON( lc.`content_id` = lch.`content_id` )";
if( empty( $pJoins ) || in_array( 'users', $pJoins )) {
$pQueryHash['select']['sql'][] = "
uu.`email` AS creator_email, uu.`login` AS creator_user, uu.`real_name` AS creator_real_name,
uue.`email` AS modifier_email, uue.`login` AS modifier_user, uue.`real_name` AS modifier_real_name";
$pQueryHash['join']['sql'][] = "
INNER JOIN `". BIT_DB_PREFIX. "users_users` uu ON( uu.`user_id` = lc.`user_id` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_users` uue ON( uue.`user_id` = lc.`modifier_user_id` )";
if( empty( $pJoins ) || in_array( 'avatar', $pJoins )) {
$pQueryHash['select']['sql'][] = "ulf.`file_name` AS `avatar_file_name`, ulf.`mime_type` AS `avatar_mime_type`, ula.`attachment_id` AS `avatar_attachment_id`";
$pQueryHash['join']['sql'][] = "
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_attachments` ula ON( uu.`user_id` = ula.`user_id` AND ula.`attachment_id` = uu.`avatar_attachment_id` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_files` ulf ON( ulf.`file_id` = ula.`foreign_id` )";
if( empty( $pJoins ) || in_array( 'primary', $pJoins )) {
$pQueryHash['select']['sql'][] = "pla.`attachment_id` AS `primary_attachment_id`, plf.`file_name` AS `primary_file_name`, plf.`mime_type` AS `primary_mime_type`";
$pQueryHash['join']['sql'][] = "
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_attachments` pla ON( pla.`content_id` = lc.`content_id` AND pla.`is_primary` = 'y' )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_files` plf ON( plf.`file_id` = pla.`foreign_id` )";
if( !empty( $pServiceFunction )) {
$this->getServicesSql2( $pServiceFunction, $pQueryHash, $pObject, $pParamHash );
* @param array $pServiceFunction
* @param array $pQueryHash
* @param array $pParamHash
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
* @TODO this function still contains legacy code.
* @TODO rename this function to getServicesSql has been weened out
function getServicesSql2( $pServiceFunction, &$pQueryHash, $pObject = NULL, $pParamHash = NULL ) {
if( $loadFuncs = $gLibertySystem->getServiceValues( $pServiceFunction ) ) {
// TODO: clear out this legacy code
$pQueryHash['service_select_sql'] = $pQueryHash['service_join_sql'] = $pQueryHash['service_where_sql'] = '';
foreach( $loadFuncs as $func ) {
if( !empty( $pObject ) && is_object( $pObject )) {
$queryHash = $func( $pObject, $pParamHash );
$queryHash = $func( $this, $pParamHash );
// work out if we're using the old services sql method or the new one
if( !empty( $queryHash['select'] ) || !empty( $queryHash['from'] ) || !empty( $queryHash['join'] ) || !empty( $queryHash['where'] )) {
// we're using the new method
// TODO: clean out this legacy code {{{
// old method: warn the developer
//deprecated( 'This service is still using the old LibertyContent::getServicesSql() method. Please update the service to use the new SQL hash method' );
if( !empty( $queryHash['select_sql'] )) {
$pQueryHash['service_select_sql'] .= $queryHash['select_sql'];
if( !empty( $queryHash['join_sql'] )) {
$pQueryHash['service_join_sql'] .= $queryHash['join_sql'];
if( !empty( $queryHash['where_sql'] )) {
$pQueryHash['service_where_sql'] .= $queryHash['where_sql'];
if( !empty( $queryHash['bind_vars'] )) {
if ( is_array( $pQueryHash['service_bind_vars'] )) {
$pQueryHash ['service_bind_vars']= array_merge( $pQueryHash['service_bind_vars'], $queryHash['bind_vars'] );
$pQueryHash['service_bind_vars'] = $queryHash['bind_vars'];
* Convert a built up pQueryHash into a single query string and set of bind variables.
* A pQueryHash is an array with required keys select and from, and optional keys join, where and order.
* Each key other than order should be an array with an 'sql' key which points to an array with statements.
* Statements should not include the keywords to start them excluding join statements nor should they
* include trailing delimeters such as commas as the conversion adds these where required.
* All where statments are automatically ANDed together.
* Each key other than order can optionally have a 'vars' key which points to an array with bind variables.
* The order key can either be an array or a single value. convertSortmode is automatically called on each order
* statement and built into the ORDER BY clause with delimeters where required.
* @return Results come back in $pQueryHash['query'] $pQueryHash['bind_vars'] and $pQueryHash['query_count'] if requested
* @TODO this function still contains legacy code.
// initiate some variables
if( empty( $pQueryHash['query'] )) {
$pQueryHash['query'] = '';
if( empty( $pQueryHash['query_count'] )) {
$pQueryHash['query_count'] = '';
if( empty( $pQueryHash['bind_vars'] )) {
$pQueryHash['bind_vars'] = array();
// Build up all the parts of the query
$queryParts = array( 'select', 'from', 'join', 'where' );
foreach( $queryParts as $part ) {
if( !empty( $pQueryHash[$part] ) && !empty( $pQueryHash[$part]['sql'] )) {
// Add the required keyword -- joins include their own
$pQueryHash['query_count'] .= strtoupper( " $part " );
// Add the count for the count query
if( $pCountQuery && $part == 'select' ) {
$pQueryHash['query_count'] .= 'COUNT( ';
foreach( $pQueryHash[$part]['sql'] as $sql ) {
// WHERE clauses have an implicit AND over all terms
$pQueryHash['query'] .= " AND ";
$pQueryHash['query_count'] .= " AND ";
} elseif( $part == 'select' || $part == 'from' ) {
$pQueryHash['query'] .= ", ";
$pQueryHash['query_count'] .= ", ";
$pQueryHash['query'] .= $sql;
$pQueryHash['query_count'] .= $sql;
// Close the count for the count query
if( $pCountQuery && $part == 'select' ) {
$pQueryHash['query_count'] .= ' )';
if( !empty( $pQueryHash[$part]['var'] )) {
$pQueryHash['bind_vars'] = array_merge( $pQueryHash['bind_vars'], $pQueryHash[$part]['var'] );
// TODO: clean out this legacy code {{{
// append old style serivce sql arguments
// since we don't allow bind_vars in the old services style, we can append everything here and then later on add the bind vars
if( !empty( $pQueryHash['service_'. $part. '_sql'] )) {
$pQueryHash['query'] .= $pQueryHash['service_'. $part. '_sql'];
$pQueryHash['query_count'] .= $pQueryHash['service_'. $part. '_sql'];
// TODO: clean out this legacy code {{{
// append legacy service bind vars
if( !empty( $pQueryHash['service_bind_vars'] )) {
$pQueryHash['bind_vars'] = array_merge( $pQueryHash['bind_vars'], $pQueryHash['service_bind_vars'] );
// Order can be a single value or an array of values all of which get passed to convertSortmode
if( !empty( $pQueryHash['order'] )) {
foreach( $pQueryHash['order'] as $order ) {
$pQueryHash['query'] .= ', ';
$pQueryHash['query'] .= ' ORDER BY ';
$pQueryHash['query'] .= $gBitSystem->mDb->convertSortmode( $order );
$pQueryHash['query'] .= ' ORDER BY '. $gBitSystem->mDb->convertSortmode( $pQueryHash['order'] );
* Set up SQL strings for services used by the object
* TODO: set this function deprecated and eventually nuke it
function getServicesSql( $pServiceFunction, &$pSelectSql, &$pJoinSql, &$pWhereSql, &$pBindVars, $pObject = NULL, &$pParamHash = NULL ) {
//deprecated( 'You package is calling the deprecated LibertyContent::getServicesSql() method. Please update your code to use LibertyContent::getLibertySql' );
if( $loadFuncs = $gLibertySystem->getServiceValues( $pServiceFunction ) ) {
foreach( $loadFuncs as $func ) {
if( !empty( $pObject ) && is_object( $pObject ) ) {
$loadHash = $func( $pObject, $pParamHash );
$loadHash = $func( $this, $pParamHash );
if( !empty( $loadHash['select_sql'] ) ) {
$pSelectSql .= $loadHash['select_sql'];
if( !empty( $loadHash['join_sql'] ) ) {
$pJoinSql .= $loadHash['join_sql'];
if( !empty( $loadHash['where_sql'] ) ) {
$pWhereSql .= $loadHash['where_sql'];
if( !empty( $loadHash['bind_vars'] ) ) {
$pBindVars = array_merge( $pBindVars, $loadHash['bind_vars'] );
$pBindVars = $loadHash['bind_vars'];
// -------------------------------- Content Permission Functions
* Check to see if the loaded content has individually assigned permissions
* @return Number of custom assigned permissions set for the loaded content item
$ret = $this->mDb->getOne( "SELECT COUNT(`perm_name`) FROM `". BIT_DB_PREFIX. "liberty_content_permissions` WHERE `content_id` = ?", array( $this->mContentId ));
* getContentPermissionsSql
* @param array $pPermName
* @param array $pSelectSql
* @param array $pWhereSql
* @param array $pBindVars
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_permissions` lcperm ON (lc.`content_id`=lcperm.`content_id`)
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_roles_map` urm ON (urm.`role_id`=lcperm.`role_id`) ";
$pWhereSql .= " OR (lcperm.perm_name=? AND (urm.user_id=? OR urm.user_id=?)) ";
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_permissions` lcperm ON (lc.`content_id`=lcperm.`content_id`)
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_groups_map` ugm ON (ugm.`group_id`=lcperm.`group_id`) ";
$pWhereSql .= " OR (lcperm.perm_name=? AND (ugm.user_id=? OR ugm.user_id=?)) ";
$pBindVars[] = $pPermName;
$pBindVars[] = $gBitUser->mUserId;
* getContentListPermissionsSql
* @param array $pPermName
* @param array $pSelectSql
* @param array $pWhereSql
* @param array $pBindVars
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_permissions` lcperm ON (lc.`content_id`=lcperm.`content_id`)
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_roles_map` urm ON (urm.`role_id`=lcperm.`role_id`) ";
$pWhereSql .= " AND ( lcperm.perm_name IS NULL OR ( lcperm.perm_name=? AND urm.user_id=? AND ( (lcperm.is_revoked !=? OR lcperm.is_revoked IS NULL) OR lc.`user_id`=? ) ) )";
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_permissions` lcperm ON (lc.`content_id`=lcperm.`content_id`)
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_groups_map` ugsm ON (ugsm.`group_id`=lcperm.`group_id`) ";
$pWhereSql .= " AND ( lcperm.perm_name IS NULL OR ( lcperm.perm_name=? AND ugsm.user_id=? AND ( (lcperm.is_revoked !=? OR lcperm.is_revoked IS NULL) OR lc.`user_id`=? ) ) )";
$pBindVars[] = $pPermName;
$pBindVars[] = $gBitUser->mUserId;
$pBindVars[] = $gBitUser->mUserId;
* Check is a user has permission to access the object
* @param integer User Identifier
* @param integer Content Itentifier
* @param string Content Type GUID
* @param string Name of the permission
* @return bool true if access is allowed
// content admin shortcut
$selectSql = ''; $joinSql = ''; $whereSql = '';
if( !empty( $pParamHash['content_id'] ) ) {
$bindVars[] = $pParamHash['content_id'];
if( @$this->verifyId( $pParamHash['user_id'] ) ) {
$whereSql .= " AND lc.`user_id` = ? ";
$bindVars[] = $pParamHash['user_id'];
if( !empty( $pParamHash['group_id'] ) ) {
$whereSql .= " AND lcperm.`group_id` = ? ";
$bindVars[] = $pParamHash['group_id'];
if( !empty( $pParamHash['role_id'] ) ) {
$whereSql .= " AND lcperm.`role_id` = ? ";
$bindVars[] = $pParamHash['role_id'];
if( !empty( $whereSql ) ) {
$query = "SELECT COUNT(*)
WHERE lc.`content_id`=? AND ( $whereSql $permWhereSql ) ";
$ret = $this->mDb->getOne( $query, $bindVars );
return( !empty( $ret ) );
* Load all permissions assigned to a given object.
* This function is mainly used to fetch a list of custom permissions of a given content item.
SELECT lcperm.`perm_name`, lcperm.`is_revoked`, ur.`role_id`, ur.`role_name`, up.`perm_desc`
INNER JOIN `". BIT_DB_PREFIX. "users_roles` ur ON( lcperm.`role_id`=ur.`role_id` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_permissions` up ON( up.`perm_name`=lcperm.`perm_name` )
WHERE lcperm.`content_id` = ?";
SELECT lcperm.`perm_name`, lcperm.`is_revoked`, ug.`group_id`, ug.`group_name`, up.`perm_desc`
INNER JOIN `". BIT_DB_PREFIX. "users_groups` ug ON( lcperm.`group_id`=ug.`group_id` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_permissions` up ON( up.`perm_name`=lcperm.`perm_name` )
WHERE lcperm.`content_id` = ?";
$perms = $this->mDb->getAll( $query, array( $this->mContentId ));
foreach( $perms as $perm ) {
$ret[$perm[$team]][$perm['perm_name']] = $perm;
* Get a list of content with permissions
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
SELECT lcperm.`perm_name`, lc.`title`, lc.`content_id`, lc.`content_type_guid`, lcperm.`is_revoked`, ur.`role_id`, ur.`role_name`, up.`perm_desc`
INNER JOIN `". BIT_DB_PREFIX. "users_roles` ur ON( lcperm.`role_id`=ur.`role_id` )
INNER JOIN `". BIT_DB_PREFIX. "liberty_content` lc ON( lcperm.`content_id`=lc.`content_id` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_permissions` up ON( up.`perm_name`=lcperm.`perm_name` )
ORDER BY ". $gBitSystem->mDb->convertSortmode( 'content_type_guid_asc' ). ", ". $gBitSystem->mDb->convertSortmode( 'title_asc' );
SELECT lcperm.`perm_name`, lc.`title`, lc.`content_id`, lc.`content_type_guid`, lcperm.`is_revoked`, ug.`group_id`, ug.`group_name`, up.`perm_desc`
INNER JOIN `". BIT_DB_PREFIX. "users_groups` ug ON( lcperm.`group_id`=ug.`group_id` )
INNER JOIN `". BIT_DB_PREFIX. "liberty_content` lc ON( lcperm.`content_id`=lc.`content_id` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_permissions` up ON( up.`perm_name`=lcperm.`perm_name` )
ORDER BY ". $gBitSystem->mDb->convertSortmode( 'content_type_guid_asc' ). ", ". $gBitSystem->mDb->convertSortmode( 'title_asc' );
$perms = $gBitSystem->mDb->getAll( $query );
foreach( $perms as $perm ) {
$ret[$perm['content_type_guid']][$perm['content_id']][] = $perm;
* Expunge Content Permissions
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_permissions` WHERE `content_id` = ?";
* Function that determines if this content specified permission for the current gBitUser, and will throw a fatal error if not.
* @param string Name of the permission to check
* @param string Message if permission denigned
$gBitSystem->fatalPermission( $pPermName, $pFatalMessage );
* Function that determines if this content specified permission for the current gBitUser.
* Assigned content perms override the indvidual global perms, so the result is the union of the global permission set + overridden individual content perms
* @param string Name of the permission to check
* @param string Check access control service if available
* @param string return default user permission setting when no content perms are set
* @return bool true if user has permission to access file
// return default user permission setting when no content is loaded
$ret = $gBitUser->hasPermission( $pPermName );
} elseif( !$gBitUser->isRegistered() || !( $ret = $this->isOwner() || $ret = $gBitUser->isAdmin() )) {
if( $pVerifyAccessControl ) {
if ( !empty( $checkPerms ) ) {
// Do they have the admin permission or the one we want?
} elseif ( !empty( $checkPerms[$pPermName] ) ) {
// return default user permission setting when no content perms are set
$ret = $gBitUser->hasPermission( $pPermName );
* Determine if current user has the ability to administer this type of content
* @return bool True if user has this type of content administration permission
// === verifyAdminPermission
* This code was duplicated _EVERYWHERE_ so here is an easy template to cut that down.
* It will verify if a given user has a given $permission and if not, it will display the error template and die()
* @param $pVerifyAccessControl check access control service if available
* @return TRUE if permitted, method will fatal out if not
* Determine if current user has the ability to delete/expunge this type of content
* @return bool True if user has this type of content expunge permission
// === verifyExpungePermission
* It will verify if a given user has a given $permission and if not, it will display the error template and die()
* @param $pVerifyAccessControl check access control service if available
* @return TRUE if permitted, method will fatal out if not
* Determine if current user has the ability to edit this type of content
* @return bool True if user has this type of content administration permission
* Deprecated, use hasUpdatePermission
* @return bool True if user has this type of content administration permission
deprecated( "LibertyContent::hasEditPermission has been replaced with LibertyContent::hasUpdatePermission and pCheckGlobal has been change to always be the case" );
// === verifyUpdatePermission
* This code was duplicated _EVERYWHERE_ so here is an easy template to cut that down.
* It will verify if a given user has a given $permission and if not, it will display the error template and die()
* @param $pVerifyAccessControl check access control service if available
* @return TRUE if permitted, method will fatal out if not
// === verifyEditPermission
* Deprecated, use verifyUpdatePermission
deprecated( "LibertyContent::verifyEditPermission has been replaced with LibertyContent::verifyUpdatePermission and pCheckGlobal has been change to always be the case" );
* Determine if current user has the ability to craete this type of content
* @return bool True if user has this type of content administration permission
// === verifyCreatePermission
* Determine if current user has the ability to create this type of content
* Note this will always return FALSEif the content isValid
* @return bool True if user has this type of content administration permission
* Determine if current user has the ability to view this type of content
* Note that this will always return TRUE if you haven't set the mViewContentPerm in your class
* @return bool True if user has this type of content administration permission
// === verifyViewPermission
* This code was duplicated _EVERYWHERE_ so here is an easy template to cut that down.
* It will verify if a given user has a given $permission and if not, it will display the error template and die()
* @param $pVerifyAccessControl check access control service if available
* @return TRUE if permitted, method will fatal out if not
* Determine if current user has the ability to post comments to this type of content
* @return bool True if user has this type of content administration permission
return( $this->hasUserPermission( 'p_liberty_post_comments', $pVerifyAccessControl ));
// === verifyPostCommentsPermission
* It will verify if a given user has a given $permission and if not, it will display the error template and die()
* @param $pVerifyAccessControl check access control service if available
* @return TRUE if permitted, method will fatal out if not
if( $this->hasPostCommentPermission( $pVerifyAccessControl ) ) {
$gBitSystem->fatalPermission( 'p_liberty_post_comments' );
* Get specific permissions for the specified user for this content
* @return Array of all permissions for the current user joined with perms
* for the current content. This should handle cases where
* non-default permissions is assigned, default permission is
* removed, and duplicate default permissions where one team's perm
* is revoked, but another is still permitted. If the permission is
* revoked, is_revoked will be set to 'y'
$userId = $gBitUser->mUserId;
// Prevent null entires when creating database
// get the default permissions for specified user
SELECT urp.`perm_name` as `hash_key`, 1 as `role_perm`, urp.`perm_name`, urp.`perm_value`, urp.`role_id`
LEFT JOIN `". BIT_DB_PREFIX. "users_role_permissions` urp ON(urm.`role_id`=urp.`role_id`)
LEFT JOIN `". BIT_DB_PREFIX. "liberty_content_permissions` lcp ON(lcp.`role_id`=urm.`role_id` AND lcp.`content_id`=? AND urp.`perm_name`=lcp.`perm_name`)
WHERE (urm.`user_id`=? OR urm.`user_id`=?) AND lcp.`perm_name` IS NULL";
SELECT ugp.`perm_name` as `hash_key`, 1 as `group_perm`, ugp.`perm_name`, ugp.`perm_value`, ugp.`group_id`
LEFT JOIN `". BIT_DB_PREFIX. "users_group_permissions` ugp ON(ugm.`group_id`=ugp.`group_id`)
LEFT JOIN `". BIT_DB_PREFIX. "liberty_content_permissions` lcp ON(lcp.`group_id`=ugm.`group_id` AND lcp.`content_id`=? AND ugp.`perm_name`=lcp.`perm_name`)
WHERE (ugm.`user_id`=? OR ugm.`user_id`=?) AND lcp.`perm_name` IS NULL";
SELECT lcp.`perm_name` AS `hash_key`, lcp.*
INNER JOIN `". BIT_DB_PREFIX. "users_roles_map` urm ON(lcp.role_id=urm.role_id)
LEFT JOIN `". BIT_DB_PREFIX. "users_role_permissions` urp ON(urm.role_id=urp.role_id AND urp.role_id!=lcp.role_id AND urp.perm_name=lcp.perm_name)
WHERE lcp.content_id=? AND (urm.user_id=? OR urm.user_id=?) AND lcp.is_revoked IS NULL";
SELECT lcp.`perm_name` AS `hash_key`, lcp.*
INNER JOIN `". BIT_DB_PREFIX. "users_groups_map` ugm ON(lcp.group_id=ugm.group_id)
LEFT JOIN `". BIT_DB_PREFIX. "users_group_permissions` ugp ON(ugm.group_id=ugp.group_id AND ugp.group_id!=lcp.group_id AND ugp.perm_name=lcp.perm_name)
WHERE lcp.content_id=? AND (ugm.user_id=? OR ugm.user_id=?) AND lcp.is_revoked IS NULL";
$nonDefaultPerms = array();
* Store a permission for the object that has been loaded in the permission database
* Any old copy of the permission is deleted prior to loading the new copy
* @param integer Group Identifier
* @param string Name of the permission
* @param integer Content Itentifier
* @return bool true ( will not currently report a failure )
function storePermission( $pTeamId, $pPermName, $pIsRevoked= FALSE, $pContentId= NULL ){
$pContentId = $pContentId == NULL? $this->mContentId: $pContentId;
if( @BitBase::verifyId( $pGroupId ) && !empty( $pPermName ) && @BitBase::verifyId( $pContentId ) ) {
'perm_name' => $pPermName,
'content_id' => $pContentId,
$storeHash['role_id'] = $pTeamId;
$storeHash['group_id'] = $pTeamId;
// check to see if this is an exclusion
$storeHash['is_revoked'] = 'y';
$ret = $this->mDb->associateInsert( BIT_DB_PREFIX. "liberty_content_permissions", $storeHash );
* Remove a permission to access the content
* @param integer Group Identifier
* @param string Name of the permission
* @return bool true ( will not currently report a failure )
$pContentId = $pContentId == NULL? $this->mContentId: $pContentId;
if( @BitBase::verifyId( $pTeamId ) && !empty( $pPermName ) && @BitBase::verifyId( $pContentId ) ) {
WHERE `$team` = ? and `content_id` = ? and `perm_name` = ?";
$bindVars = array( $pTeamId, $pContentId, $pPermName );
$result = $this->mDb->query( $query, $bindVars );
* Check to see if this permission is already in the global permissions table.
* @param array $pPermName
* @return TRUE if present, FALSE if not
if( @BitBase::verifyId( $pTeamId ) && !empty( $pPermName )) {
$query = "SELECT `perm_name` FROM `". BIT_DB_PREFIX. "users_role_permissions` WHERE `role_id` = ? AND `perm_name` = ?";
$query = "SELECT `perm_name` FROM `". BIT_DB_PREFIX. "users_group_permissions` WHERE `group_id` = ? AND `perm_name` = ?";
return( $this->mDb->getOne( $query, array( $pTeamId, $pPermName )) == $pPermName );
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Preferences Functions
* Returns the content preferences value for the passed in key.
* @param string Hash key for the mPrefs value
* @param string Default value to return if the preference is empty
* @param int Optional content_id for arbitrary content preference
function getPreference( $pPrefName, $pPrefDefault= NULL, $pContentId = NULL ) {
if( $pContentId && !empty( $pPrefName )) {
// Get a user preference for an arbitrary user
$sql = "SELECT `pref_value` FROM `". BIT_DB_PREFIX. "liberty_content_prefs` WHERE `content_id`=? AND `pref_name`=?";
if( !$ret = $gBitDb->getOne( $sql, array( $pContentId, $pPrefName ) ) ) {
if( isset ( $this->mPrefs ) && isset ( $this->mPrefs[$pPrefName] ) ) {
$ret = $this->mPrefs[$pPrefName];
* loadPreferences of the currently loaded object or pass in to get preferences of a specific content_id
* @param numeric $pContentId content_id of the item we want the prefs from (optional)
* @return array of preferences if $pContentId is set or pass preferences on to $this->mPrefs
if( @BitBase::verifyId( $pContentId )) {
return $gBitSystem->mDb->getAssoc( "SELECT `pref_name`, `pref_value` FROM `". BIT_DB_PREFIX. "liberty_content_prefs` WHERE `content_id`=?", array( $pContentId ));
// If no results, getAssoc will return an empty array (ie not a true NULL value) so getPreference can tell we have attempted a load
$this->mPrefs = @$this->mDb->getAssoc( "SELECT `pref_name`, `pref_value` FROM `". BIT_DB_PREFIX. "liberty_content_prefs` WHERE `content_id`=?", array( $this->mContentId ));
* Set a hash value in the mPrefs hash. This does *NOT* store the value in the database. It does no checking for existing or duplicate values. the main point of this function is to limit direct accessing of the mPrefs hash. I will probably make mPrefs private one day.
* @param string Hash key for the mPrefs value
* @param string Value for the mPrefs hash key
$this->mPrefs[$pPrefName] = $pPrefValue;
* Saves a preference to the liberty_content_prefs database table with the given pref name and value. If the value is NULL, the existing value will be delete and the value will not be saved. However, a zero will be stored. This will update the mPrefs hash.
* @param string Hash key for the mPrefs value
* @param string Value for the mPrefs hash key
$query = "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_prefs` WHERE `content_id`=? AND `pref_name`=?";
$bindvars = array( $this->mContentId, $pPrefName );
$result = $this->mDb->query($query, $bindvars);
$query = "INSERT INTO `". BIT_DB_PREFIX. "liberty_content_prefs` (`content_id`,`pref_name`,`pref_value`) VALUES(?, ?, ?)";
$bindvars[] = substr( $pPrefValue, 0, 250 );
$result = $this->mDb->query( $query, $bindvars );
$this->mPrefs[$pPrefName] = $pPrefValue;
$this->mPrefs[$pPrefName] = $pPrefValue;
* Register the content type for reference
* @param string Content Type GUID
* @param array Array of content type data
* Populates the mType array with the following entries
* string content_type_guid
$gLibertySystem->registerContentType( $pContentGuid, $pTypeParams );
$this->mType = $pTypeParams;
* Increment the content item hit flag by 1
* @return bool true ( will not currently report a failure )
global $gBitUser,$gBitSystem;
if( empty( $_REQUEST['post_comment_submit'] ) && empty( $_REQUEST['post_comment_request'] ) ) {
if( @BitBase::verifyId( $this->mContentId ) && (( $gBitUser->isRegistered() && !$this->isOwner() ) || ( $gBitUser->getField( 'user_id' ) == ANONYMOUS_USER_ID )) && ( $gBitSystem->isFeatureActive( 'users_count_admin_pageviews' ) || !$gBitUser->isAdmin() ) ) {
if( $this->mDb->getOne( "SELECT `content_id` FROM `". BIT_DB_PREFIX. "liberty_content_hits` WHERE `content_id`=?", array( $this->mContentId ))) {
$query = "UPDATE `". BIT_DB_PREFIX. "liberty_content_hits` SET `hits`=`hits`+1, `last_hit`= ? WHERE `content_id` = ?";
$query = "INSERT INTO `". BIT_DB_PREFIX. "liberty_content_hits` ( `hits`, `last_hit`, `content_id` ) VALUES ( ?,?,? )";
$bindVars[] = $gBitSystem->getUTCTime();
$this->mDb->StartTrans();
$result = $this->mDb->query( $query, $bindVars );
$this->mDb->CompleteTrans();
* @return bool true ( will not currently report a failure )
function setHits($pHits, $pLastHit= 0) {
$query = "UPDATE `". BIT_DB_PREFIX. "liberty_content_hits` SET `hits`= ?, `last_hit`= ? WHERE `content_id` = ?";
$result = $this->mDb->query( $query, array( $pHits, $pLastHit, $this->mContentId ) );
$affected_rows = $this->mDb->Affected_Rows();
$query = "INSERT INTO `". BIT_DB_PREFIX. "liberty_content_hits` ( `hits`, `last_hit`, `content_id` ) VALUES (?,?,?)";
$result = $this->mDb->query( $query, array( $pHits, $pLastHit, $this->mContentId ) );
* @return bool true ( will not currently report a failure )
$query = "SELECT `hits`,`last_hit` FROM `". BIT_DB_PREFIX. "liberty_content_hits` where `content_id` = ?";
$this->mInfo['hits'] = $row['hits'];
$this->mInfo['last_hit'] = $row['last_hit'];
* Create the generic title for a content item
* This will normally be overwriten by extended classes to provide
* an appropriate title string
* @param array pHash type hash of data to be used to provide base data
* @return string Descriptive title for the page
if( !empty( $pHash['title'] ) ) {
} elseif( $pDefault && !empty( $pHash['content_name'] ) ) {
$ret = $pHash['content_name'];
* Create the generic title for a content item
* This will normally be overwriten by extended classes to provide
* an appropriate title string
* @return string Descriptive title for the page
$ret = self::getTitleFromHash( $this->mInfo );
* Attempt to create a brief description of this object, most useful for <meta name="description" />
* @return array list of aliases
// 250 to 300 is max description
$ret = substr( $text, 0, 250 );
if( empty($ret) ) { $ret = 'Navigation of the site '. $this->getTitle(); }
* Attempt to create a collection of relevant words about this object, most useful for <meta name="keywords" />
* @return array list of aliases
* Get array of aliases for this content object
* @return array list of aliases
$ret = $this->mDb->getCol( "SELECT `alias_title` FROM `". BIT_DB_PREFIX. "liberty_aliases` lal INNER JOIN `". BIT_DB_PREFIX. "liberty_content` lc ON(lal.`content_id`=lc.`content_id`) WHERE lal.`content_id`=? ", array( $this->mContentId ) );
* Access a content item type GUID
* @return string content_type_guid for the content
if( isset ( $this->mInfo['content_type_guid'] ) ) {
$ret = $this->mInfo['content_type_guid'];
} elseif( $this->mType['content_type_guid'] ) {
// unloaded content might have this
$ret = $this->mType['content_type_guid'];
* Get the display name of the content type
* @param boolean $pPlural true will return the plural form of the content type display name
* @return string the display name of the content type
return $gLibertySystem->getContentTypeName( $this->getContentType(), $pPlural );
* getContentTypeDescription
* @param array $pContentType
* @return TRUE on success, FALSE on failure
deprecated( 'You are calling the deprecated method getContentTypeDescription, use getContentTypeName( $pPlural )' );
if( is_null( $pContentType ) ) {
$pContentType = $this->getContentType();
return $gLibertySystem->getContentTypeDescription( $pContentType );
* Access a content item content_id
* @return string content_type_guid for the object
* Return content type description for this content object.
* @return string content_type_guid description for the object
deprecated( 'You are calling the deprecated method getContentDescription, use getContentTypeName( $pPlural )' );
* returns a path to the template type requested
* this is intended for package override. while not a requirement please use a naming convention of center_<action>_<content_type_guid>.tpl for new tpls
* @param string $pAction the type of template. common types are view and list
$ret = "bitpackage:liberty/center_". $pAction. "_generic.tpl";
* Pure virtual function that returns the include file that should render a page of content of this type
* @return the fully specified path to file to be included
* Pure virtual function that returns link to display a piece of content
* @param string $pLinkText Text for the link unless overriden by object title
* @param array $pMixed different possibilities depending on derived class
* @param string $pAnchor anchor string e.g.: #comment_123
* @return string Formated html the link to display the page.
function getDisplayLink( $pLinkText= NULL, $pMixed= NULL, $pAnchor= NULL ) {
if( empty( $pMixed ) && !empty( $this->mInfo )) {
if( empty( $pLinkText )) {
if( !empty( $pMixed['title'] )) {
$pLinkText = $pMixed['title'];
} elseif( !empty( $pMixed['content_name'] ) ) {
$pLinkText = "[ ". $pMixed['content_name']. " ]";
if( empty( $pLinkText )) {
$pLinkText = "[ ". tra( "No Title" ). " ]";
// we add some more info to the title of the link
if( !empty( $pMixed['created'] )) {
$gBitSmarty->loadPlugin( 'smarty_modifier_bit_short_date' );
// finally we are ready to create the full link
if( !empty( $pMixed['content_id'] )) {
* Not-so-pure virtual function that returns fully qualified URI to a piece of content
* @param string Text for DisplayLink function
* @param array different possibilities depending on derived class
* @return string Formated URL address to display the page.
return BIT_ROOT_URI. substr( static::getDisplayUrlFromHash( $this->mInfo ), strlen( BIT_ROOT_URL ) );
* Not-so-pure virtual function that returns fully qualified URI to a piece of content
* @param string Text for DisplayLink function
* @param array different possibilities depending on derived class
* @return string Formated URL address to display the page.
return BIT_ROOT_URI. substr( static::getDisplayUrlFromHash( $pParamHash ), strlen( BIT_ROOT_URL ) );
* Not-so-pure virtual function that returns Request_URI to a piece of content
* @param array $pMixed a hash of params to add to the url
* @return string Formated URL address to display the page.
if( @static::verifyId( $pParamHash['content_id'] ) ) {
$ret = BIT_ROOT_URL. 'index.php?content_id='. $pParamHash['content_id'];
* Returns Request URL to a piece of content
if( !empty( $this ) && $this->isValid() ) {
$ret = static::getDisplayUrlFromHash( $this->mInfo );
* Returns the create/edit url to a piece of content
* @param number $pContentId a valid content id
* @param array $pMixed a hash of params to add to the url
function getEditUrl( $pContentId = NULL, $pMixed = NULL ){
$package = $gLibertySystem->mContentTypes[$this->mType['content_type_guid']]['handler_package'];
if( @BitBase::verifyId( $pContentId ) ) {
$ret = $packagePath. 'edit.php?content_id='. $pContentId;
$ret = $packagePath. 'edit.php?content_id='. $this->mContentId;
$ret = $packagePath. 'edit.php'. (!empty( $pMixed )? "?": "");
foreach( $pMixed as $key => $value ){
if( $key != "content_id" || ( $key == "content_id" && @BitBase::verifyId( $value ) ) ) {
$ret .= (isset ($amp)? "&": ""). $key. "=". $value;
* Not-so-pure virtual function that returns Request_URI to the preview.
* @param string Text for DisplayLink function
* @param array different possibilities depending on derived class
* @return string Formated URL address to display the page.
if( @BitBase::verifyId( $pContentId ) ) {
} elseif( @BitBase::verifyId( $pMixed['content_id'] ) ) {
* Not-so-pure virtual function that returns Request_URI to a content's thumbnail representation. It is up to the derived content what exactly this means
* If not implemented in the content's class, this class will return NULL, which is an acceptable case meaning no thumbnail is available.
* FisheyeGallery, BitUser might return pictures, BitArticle might return the article topic image, etc.
* @param string Size of the url to return - should be a standard thumbnail size such as 'icon', 'avatar', 'small', 'medium', or 'large'
* @param int optional contentId tp generate the thumbnail, if empty, the mContentId variable should be used
* @param int optional secondary id, such as user_id or products_id, etc
* @return string Formated URL address to display the page.
public function getThumbnailUrl( $pSize = 'small', $pSecondaryId = NULL, $pDefault= TRUE ) {
public static function getThumbnailUrlFromHash( &$pMixed, $pSize = 'small', $pSecondaryId = NULL, $pDefault= TRUE ) {
if( !empty( $pMixed['content_type']['handler_package'] ) ) {
$pkgName = $pMixed['content_type']['handler_package'];
if( file_exists( $pkgPath. 'icons/pkg_'. $pkgName. '.png' ) ) {
$ret = static::getThumbnailUrlFromHash( $pMixed, $pSize );
// Check to make sure we don't have an absolute URI already, which could be the case for custom classes
if( strpos( $ret, 'http' ) !== 0 ) {
$ret = static::getThumbnailUrlFromHash( $pMixed, $pSize );
// Check to make sure we don't have an absolute URI already, which could be the case for custom classes
if( strpos( $ret, 'http' ) !== 0 ) {
* Validate inbound sort_mode parameter
* @param pParamHash hash of parameters for any getList() function
* @return the link to display the page.
* Validate inbound sort_mode parameter
* @param pParamHash hash of parameters for any getList() function
* @return the link to display the page.
public function convertSortMode( &$pSortMode, $pDefault= 'last_modified_desc' ) {
$sortHash = static::getSortModeFields();
$baseSortMode = preg_replace( '/^.*\./', '', $baseSortMode );
if( !in_array( $baseSortMode, $sortHash ) ) {
return $this->mDb->convertSortmode( $pSortMode );
* Liberty override to stuff content_status_id and prepares parameters with default values for any getList function
* @param pParamHash hash of parameters for any getList() function
* @return the link to display the page.
if( $gBitUser->isAdmin() ) {
$pListHash['min_content_status_id'] = - 9999;
$pListHash['min_content_status_id'] = - 999;
$pListHash['min_content_status_id'] = - 99;
$pListHash['min_content_status_id'] = 1;
if( empty( $pListHash['query_cache_time'] ) ) {
$pListHash['query_cache_time'] = 0;
// if sort_mode is not set then use last_modified_desc
if( !empty( $pListHash['sort_mode'] )) {
if( is_string( $pListHash['sort_mode'] ) && strpos( $pListHash['sort_mode'], 'hits_' ) === 0 ) {
// if sort mode is hits_*, then assume liberty content
$pListHash['sort_mode'] = 'lch.'. $pListHash['sort_mode'];
} elseif( is_array( $pListHash['sort_mode'] )) {
foreach( $pListHash['sort_mode'] as $key => $mode ) {
if( strpos( $mode, 'hits_' ) === 0 ) {
$pListHash['sort_mode'][$key] = 'lch.'. $mode;
// if sort_mode is not set then use last_modified_desc
$pListHash['sort_mode'] = 'last_modified_desc';
return parent::prepGetList( $pListHash );
* Get a list of users who have created entries in the content table
* @param array hash of parameters ( content_type_guid will limit list to a single content type
* @return - none the hash is updated via the reference
if( !empty( $pListHash['content_type_guid'] ) ) {
$mid .= ' AND lc.`content_type_guid`=? ';
$bindVars[] = $pListHash['content_type_guid'];
$query = "SELECT DISTINCT(uu.`user_id`) AS hash_key, uu.`user_id`, SUM( lch.`hits` ) AS `ag_hits`, uu.`login`, uu.`real_name`
ON (`lc`.`content_id` = `lch`.`content_id`)
GROUP BY uu.`user_id`, uu.`login`, uu.`real_name`
ORDER BY `ag_hits` DESC";
$result = $this->mDb->query( $query, $bindVars, $pListHash['max_records'], $pListHash['offset'] );
while( $aux = $result->fetchRow() ) {
* Get a list of content ranked by certain criteria set in $pListHash['sort_mode']
* @param array hash of parameters ( content_type_guid will limit list to a single content type
$pListHash['sort_mode'] = !empty( $pListHash['sort_mode'] ) ? $pListHash['sort_mode'] : 'hits_desc';
if( $pListHash['sort_mode'] == 'top_authors' ) {
$ret['data'] = $gBitUser->getAuthorList( $pListHash );
$ret['data'] = $libertyContent->getContentList( $pListHash );
$ret['title'] = !empty( $pListHash['title'] ) ? $pListHash['title'] : tra( "Content Ranking" );
$ret['attribute'] = !empty( $pListHash['attribute'] ) ? $pListHash['attribute'] : tra( "Hits" );
* Get a list of all content
* @param string $pListHash['content_type_guid'] Content GUID to limit the list to
* @param integer $pListHash['max_records'] Number of the first record to access ( used to page the list )
* @param integer $pListHash['offset'] Number of records to return
* @param string $pListHash['sort_mode'] Name of the field to sort by ( extended by _asc or _desc for sort direction )
* @param array $pListHash['find'] List of text elements to filter the results by
* @param integer $pListHash[''] User ID - If set, then only the objcets created by that user will be returned
* $pListHash['last_modified'] date - modified since
* $pListHash['end_date'] date - modified before
* @return array An array of mInfo type arrays of content objects
global $gLibertySystem, $gBitSystem, $gBitUser, $gBitSmarty;
$hashSql = array('select'=> array(), 'join'=> array(),'where'=> array() );
$hashBindVars = array('select'=> array(), 'where'=> array(), 'join'=> array());
if( !empty( $pListHash['content_type_guid'] ) && is_array( $pListHash['content_type_guid'] )) {
foreach( $pListHash['content_type_guid'] as $contentTypeGuid ) {
$this->getFilter( $contentTypeGuid, $hashSql, $hashBindVars, $pListHash );
} elseif( !empty( $pListHash['content_type_guid'] )) {
$this->getFilter( $pListHash['content_type_guid'], $hashSql, $hashBindVars, $pListHash );
if( !empty( $hashSql['select'] )) {
$selectSql = ','. implode( ',', $hashSql['select'] );
$joinSql = implode( ' ', $hashSql['join'] );
if( empty( $hashBindVars['join'] )) {
$bindVars = $hashBindVars['join'];
$this->getServicesSql( 'content_list_sql_function', $selectSql, $joinSql, $whereSql, $bindVars, NULL, $pListHash );
if( $pListHash['sort_mode'] == 'size_desc' ) {
$pListHash['sort_mode'] = 'wiki_page_size_desc';
if( $pListHash['sort_mode'] == 'size_asc' ) {
$pListHash['sort_mode'] = 'wiki_page_size_asc';
if( in_array( $pListHash['sort_mode'], $sortHash ) ) {
$old_offset = $pListHash['offset'];
$old_max_records = $pListHash['max_records'];
$old_sort_mode = $pListHash['sort_mode'];
$pListHash['sort_mode'] = 'modifier_user_desc';
$pListHash['offset'] = 0;
$pListHash['max_records'] = - 1;
if( is_array( $pListHash['find'] ) ) { // you can use an array of titles
$bindVars = array_merge( $pListHash['find'], $pListHash['find'] );
} elseif( !empty( $pListHash['find'] ) && is_string( $pListHash['find'] ) ) { // or a string
$whereSql .= " AND UPPER(lc.`title`) like ? ";
$bindVars[] = ( '%' . strtoupper( $pListHash['find'] ) . '%' );
if( !empty( $pListHash['content_id_list'] ) ) { // you can use an array of titles
$whereSql .= " AND lc.`content_id` IN ( ". implode( ',',array_fill( 0,count( $pListHash['content_id_list'] ),'?' ) ). ") ";
$bindVars = array_merge( $bindVars, $pListHash['content_id_list'] );
// this is necessary to display useful information in the liberty RSS feed
if( !empty( $pListHash['include_data'] ) ) {
$selectSql .= ", lc.`data`, lc.`format_guid`";
// if we want the primary attachment for each object
if( $gBitSystem->isFeatureActive( 'liberty_display_primary_attach' ) ){
$selectSql .= ', lfp.`file_name`, lfp.`mime_type`, la.`attachment_id`, ';
$joinSql .= "LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_attachments` la ON( la.`content_id` = lc.`content_id` AND la.`is_primary` = 'y' )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_files` lfp ON( lfp.`file_id` = la.`foreign_id` )";
// Allow selection based on arbitrary time limits -- used in calendar
// TODO: We should replace usages of from_date and until_date with this generic setup and depricate those
if( !empty( $pListHash['time_limit_column'] )) {
if( empty( $pListHash['time_limit_table'] ) ) {
$pListHash['time_limit_table'] = 'lc.';
if( !empty( $pListHash['time_limit_start'] ) ) {
$whereSql .= " AND ". $pListHash['time_limit_table']. "`". $pListHash['time_limit_column']. "` >= ? ";
$bindVars[] = $pListHash['time_limit_start'];
if( !empty( $pListHash['time_limit_stop'] ) ) {
$whereSql .= " AND ". $pListHash['time_limit_table']. "`". $pListHash['time_limit_column']. "` <= ? ";
$bindVars[] = $pListHash['time_limit_stop'];
if( @$this->verifyId( $pListHash['user_id'] ) ) {
$whereSql .= " AND lc.`user_id` = ? ";
$bindVars[] = $pListHash['user_id'];
if( @$this->verifyId( $pListHash['link_content_id'] ) ){
$joinSql .= " INNER JOIN `". BIT_DB_PREFIX. "liberty_content_links` lclk ON ( lc.`content_id` = lclk.`to_content_id` )";
$whereSql .= " AND lclk.`from_content_id` = ? ";
$bindVars[] = (int) $pListHash['link_content_id'];
if( $gBitSystem->isFeatureActive( 'liberty_display_status' ) && $gBitUser->hasPermission( 'p_liberty_view_all_status' )) {
$selectSql .= ", lcs.`content_status_id`, lcs.`content_status_name`";
$joinSql .= " LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_status` lcs ON ( lc.`content_status_id` = lcs.`content_status_id` )";
if( !empty( $pListHash['content_status_id'] )) {
if( $pListHash['content_status_id'] == 'not_available' ) {
$whereSql .= " AND lcs.`content_status_id` <> ? ";
$whereSql .= " AND lcs.`content_status_id` = ? ";
$bindVars[] = (int) $pListHash['content_status_id'];
// join on specific content_type_guids
if( !empty( $pListHash['content_type_guid'] ) && is_string( $pListHash['content_type_guid'] ) ) {
$whereSql .= ' AND lc.`content_type_guid`=? ';
$bindVars[] = $pListHash['content_type_guid'];
} elseif( !empty( $pListHash['content_type_guid'] ) && is_array( $pListHash['content_type_guid'] ) ) {
$whereSql .= " AND lc.`content_type_guid` IN ( ". implode( ',',array_fill ( 0, count( $pListHash['content_type_guid'] ),'?' ) ). " )";
$bindVars = array_merge( $bindVars, $pListHash['content_type_guid'] );
// exclude by content_type_guids
if( !empty( $pListHash['exclude_content_type_guid'] ) && is_string( $pListHash['exclude_content_type_guid'] ) ) {
$whereSql .= " AND lc.`content_type_guid` != ?";
$bindVars[] = $pListHash['exclude_content_type_guid'];
} elseif( !empty( $pListHash['exclude_content_type_guid'] ) && is_array( $pListHash['exclude_content_type_guid'] ) ) {
$whereSql .= " AND lc.`content_type_guid` NOT IN ( ". implode( ',',array_fill ( 0, count( $pListHash['exclude_content_type_guid'] ),'?' ) ). " )";
$bindVars = array_merge( $bindVars, $pListHash['exclude_content_type_guid'] );
// only display content modified more recently than this (UTC timestamp)
if( !empty( $pListHash['from_date'] ) ) {
$whereSql .= ' AND lc.`last_modified` >= ?';
$bindVars[] = $pListHash['from_date'];
// only display content modified before this (UTC timestamp)
if( !empty( $pListHash['until_date'] ) ) {
$whereSql .= ' AND lc.`last_modified` <= ?';
$bindVars[] = $pListHash['until_date'];
// Should results be hashed or sequential indexed
if( !empty( $pListHash['hash_key'] ) ) {
$hashKeySql = $pListHash['hash_key']. ' AS `hash_key`, ';
if( $gBitSystem->isPackageActive( 'gatekeeper' ) ) {
if( $gBitSystem->isPackageActive( 'fisheye' ) ) {
// This is really ugly to have in here, and really would be better off somewhere else.
// However, because of the specific nature of the current implementation of fisheye galleries, I am afraid
// this is the only place it can go to properly enforce gatekeeper protections. Hopefully a new content generic
// solution will be available in ReleaseTwo - spiderr
if( $this->mDb->isAdvancedPostgresEnabled() ) {
// $joinSql .= " LEFT OUTER JOIN `".BIT_DB_PREFIX."fisheye_gallery_image_map` fgim ON (fgim.`item_content_id`=lc.`content_id`)";
$whereSql .= " AND (SELECT ls.`security_id` FROM connectby('fisheye_gallery_image_map', 'gallery_content_id', 'item_content_id', 'item_content_id', text( lc.`content_id` ), 0, '/') AS t(`cb_gallery_content_id` int, `cb_item_content_id` int, level int, branch text, pos int), `". BIT_DB_PREFIX. "gatekeeper_security_map` cgm, `". BIT_DB_PREFIX. "gatekeeper_security` ls
WHERE ls.`security_id`=cgm.`security_id` AND cgm.`content_id`=`cb_gallery_content_id` LIMIT 1) IS NULL";
'modifier_real_name_desc',
'modifier_real_name_asc',
'creator_real_name_desc',
if( in_array( $pListHash['sort_mode'], $sortHash ) ) {
} elseif( !empty( $pListHash['order_table'] ) ) {
$orderTable = $pListHash['order_table'];
} elseif( !empty( $pListHash['sort_mode'] ) && strtolower( substr( $pListHash['sort_mode'], 0, 4 ) ) == 'hits' ) {
} elseif( strpos( $pListHash['sort_mode'], '.' ) ) {
// do not specifiy orderTable of sort_mode already has a . in it
if (!empty($hashSql['where'])) {
$whereSql .= ' AND '. implode(' ', $hashSql['where']);
if (!empty($hashBindVars['where'])) {
$bindVars = array_merge($bindVars, $hashBindVars['where']);
$whereSql = preg_replace( '/^[\s]*AND\b/i', 'WHERE ', $whereSql );
// If sort mode is versions then offset is 0, max_records is -1 (again) and sort_mode is nil
// If sort mode is links then offset is 0, max_records is -1 (again) and sort_mode is nil
// If sort mode is backlinks then offset is 0, max_records is -1 (again) and sort_mode is nil
uue.`login` AS `modifier_user`,
uue.`real_name` AS `modifier_real_name`,
uue.`user_id` AS `modifier_user_id`,
uuc.`login` AS `creator_user`,
uuc.`real_name` AS `creator_real_name`,
uuc.`user_id` AS `creator_user_id`,
INNER JOIN `". BIT_DB_PREFIX. "users_users` uuc ON (lc.`user_id`=uuc.`user_id`)
INNER JOIN `". BIT_DB_PREFIX. "users_users` uue ON (lc.`modifier_user_id`=uue.`user_id`)
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_hits` lch ON( lc.`content_id` = lch.`content_id`)
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_data` lcds ON (lc.`content_id` = lcds.`content_id` AND lcds.`data_type`='summary')
INNER JOIN `". BIT_DB_PREFIX. "users_users` uu ON (lc.`modifier_user_id`=uu.`user_id`)
$cant = $this->mDb->getOne( $query_cant, $bindVars );
$pListHash["cant"] = $cant;
# Check for offset out of range
if( $pListHash['offset'] < 0 ) {
$pListHash['offset'] = 0;
} elseif ( $pListHash['offset'] > $pListHash["cant"] ) {
$lastPageNumber = ceil ( $pListHash["cant"] / $pListHash['max_records'] ) - 1;
$pListHash['offset'] = $pListHash['max_records'] * $lastPageNumber;
if( !empty( $hashBindVars['select'] ) ) {
$bindVars = array_merge($hashBindVars['select'], $bindVars);
$result = $this->mDb->query( $query, $bindVars, $pListHash['max_records'], $pListHash['offset'] );
$contentTypes = $gLibertySystem->mContentTypes;
while( $aux = $result->fetchRow() ) {
if( !empty( $contentTypes[$aux['content_type_guid']] ) ) {
// quick alias for code readability
$type = &$contentTypes[$aux['content_type_guid']];
$aux['content_name'] = $type['content_name'];
$aux['creator'] = (isset ( $aux['creator_real_name'] ) ? $aux['creator_real_name'] : $aux['creator_user'] );
$aux['real_name'] = (isset ( $aux['creator_real_name'] ) ? $aux['creator_real_name'] : $aux['creator_user'] );
$aux['editor'] = (isset ( $aux['modifier_real_name'] ) ? $aux['modifier_real_name'] : $aux['modifier_user'] );
$aux['user'] = $aux['creator_user'];
$aux['user_id'] = $aux['creator_user_id'];
// create *one* object for each object *type* to call virtual methods.
if( empty( $type['content_object'] ) ) {
include_once( $gBitSystem->mPackages[$type['handler_package']]['path']. $type['handler_file'] );
$type['content_object'] = new $type['handler_class']();
if( !empty( $gBitSystem->mPackages[$type['handler_package']] ) ) {
// here we provide getDisplay(Link|Url) with user-specific information that we get the correct links to display in pages
$userInfo = $gBitUser->getUserInfo( array( 'content_id' => $aux['content_id'] ));
$aux['title'] = $type['content_object']->getTitleFromHash( $userInfo );
$aux['display_link'] = $type['content_object']->getDisplayLink( $userInfo['login'], $userInfo );
$aux['display_url'] = $type['content_object']->getDisplayUrl( $userInfo['login'] );
$aux['title'] = $type['content_object']->getTitleFromHash( $aux );
$aux['display_link'] = $type['content_object']->getDisplayLink( $aux['title'], $aux );
* @TODO standardize getDisplayUrl params
* nice try, but you can't do this because individual classes have gone off the reservation changing the params they accept
* for distributed packages we need to enforce that method overrides all take the same basic params.
// $aux['display_url'] = $type['content_object']->getDisplayUrl( NULL, $aux );
$aux['display_url'] = BIT_ROOT_URL. "index.php?content_id=". $aux['content_id'];
if( !empty( $pListHash['thumbnail_size'] ) ) {
$aux['content_object'] = new $type['handler_class']( NULL, $aux['content_id'] );
if( $aux['content_object']->load( FALSE ) ) {
$aux['thumbnail_url'] = $aux['content_object']->getThumbnailUrl( $pListHash['thumbnail_size'] );
* @TODO standardize use of thumbnail_url and provision for hash of thumbnail sizes
* We have a bit of a mess with the use of thumbnail_url where sometimes it is a hash of sizes, and sometimes it is a single size
* we should standardize the param and what kind of value it returns, and if we need both types then have two params.
* This ultimately might need to be more sophisticated to deal with different mime types.
if( $gBitSystem->isFeatureActive( 'liberty_display_primary_attach' ) ) {
if( isset ( $aux['hash_key'] ) ) {
$ret[$aux['hash_key']] = $aux;
// If sortmode is versions, links or backlinks sort using the ad-hoc function and reduce using old_offse and old_max_records
if( $old_sort_mode == 'versions_asc' && !empty( $ret['versions'] ) ) {
usort( $ret, 'compare_versions' );
if( $old_sort_mode == 'versions_desc' && !empty( $ret['versions'] ) ) {
usort( $ret, 'r_compare_versions' );
if( $old_sort_mode == 'links_desc' && !empty( $ret['links'] ) ) {
usort( $ret, 'compare_links' );
if( $old_sort_mode == 'links_asc' && !empty( $ret['links'] ) ) {
usort( $ret, 'r_compare_links' );
if( $old_sort_mode == 'backlinks_desc' && !empty( $ret['backlinks'] ) ) {
usort( $ret, 'compare_backlinks' );
if( $old_sort_mode == 'backlinks_asc' && !empty( $ret['backlinks'] ) ) {
usort( $ret, 'r_compare_backlinks' );
$ret = array_slice( $ret, $old_offset, $old_max_records );
* Get a list of all structures this content is a member of
$structures_added = array();
$query = 'SELECT ls.*, lc.`title`, tcr.`title` AS `root_title`
INNER JOIN `'. BIT_DB_PREFIX. 'liberty_structures` tsr ON( tsr.`structure_id`=ls.`root_structure_id` )
INNER JOIN `'. BIT_DB_PREFIX. 'liberty_content` tcr ON( tsr.`content_id`=tcr.`content_id` )
WHERE lc.`content_id`=ls.`content_id` AND ls.`content_id`=?';
if( $result = $this->mDb->query( $query,array( $this->mContentId ) ) ) {
while ($res = $result->fetchRow()) {
* Splits content either at the ...split... or at the
* length specified if no manual split is in the content.
* @param pParseHash a hash with 'data' in it and any
* arguments to the parser as required
* @param pLength the length to split at if no ...split... is present
* @param pForceLength force split at length (default false)
* @return parsed data cut at LIBERTY_SPLIT_REGEX or at $pLength
function parseSplit( $pParseHash, $pLength = 500, $pForceLength = FALSE ) {
global $gLibertySystem, $gBitSystem;
// Indicate that we are parsing split data. This will clean up the HTML better and avoid pre / post filters
$pParseHash['split_parse'] = TRUE;
// copy data that we can compare strings later on
$res['data'] = $pParseHash['data'];
// allways set the cache extension to description if it's not set manually
$pParseHash['cache_extension'] = !empty( $pParseHash['cache_extension'] ) ? $pParseHash['cache_extension'] : 'desc';
// split data according to user specifications
// this has been manually split
$res['man_split'] = TRUE;
$pParseHash['data'] = $parts[0];
// Include length in cache file
$pParseHash['cache_extension'] .= '.'. $pLength;
$pParseHash['data'] = substr( $res['data'], 0, $pLength );
// snip off a broken tag at the end if there is one
$pParseHash['data'] = preg_replace( '!<[a-zA-Z/][^>]*?$!', '', $pParseHash['data'] );
// set 'has_more' and remove cache_extension if we don't need it
if( !( $res['has_more'] = ( $res['data'] != $pParseHash['data'] ))) {
$pParseHash['cache_extension'] = NULL;
if( !empty( $pParseHash['data'] )) {
// parse data and run it through postsplit filter
if( $parsed = $this->parseData( $pParseHash )) {
// parsing split content can break stuff so we remove trailing junk
$res['parsed'] = $res['parsed_description'] = preg_replace( '!((<br\b[^>]*>)*\s*)*$!si', '', $parsed );
// we append '...' when the split was generated automagically
if( empty( $res['man_split'] ) && !empty( $res['has_more'] )) {
$res['parsed_description'] .= '…';
// did we parse an empty page?
$res['parsed'] = $res['parsed_description'] = '';
$res['has_more'] = FALSE;
* Process the raw content blob using the speified content GUID processor
* This is the "object like" method. It should be more object like,
* but for now, we'll just point to the old lib style "parse_data" - XOXO spiderr
* @param pMixed can be a string or a hash - if a string is given, it will be parsed without the use of cache
* @param string pMixed['data'] string to be parsed
* @param int pMixed['content_id'] content_id or the item to be parsed - required for caching and optimal parser performance
* @param boolean pMixed['no_cache'] disable caching
* @param string pMixed['cache_extension'] cache to a separate file. useful for truncated displays of parsed content such as article front page
* @param string pFormatGuid processor to use
* @return string Formated data string
function parseData( $pMixed= NULL, $pFormatGuid= NULL ) {
global $gLibertySystem, $gBitSystem, $gBitUser;
// get the data into place
if( empty( $pMixed ) && !empty( $this->mInfo['data'] ) ) {
$parseHash = $this->mInfo;
if( empty( $parseHash['data'] ) ) {
$parseHash['data'] = $pMixed;
// sanitise parseHash a bit
$parseHash['content_id'] = !empty( $parseHash['content_id'] ) ? $parseHash['content_id'] : NULL;
$parseHash['cache_extension'] = !empty( $parseHash['cache_extension'] ) ? $parseHash['cache_extension'] : NULL;
$parseHash['format_guid'] = !empty( $parseHash['format_guid'] ) ? $parseHash['format_guid'] : $pFormatGuid;
$parseHash['user_id'] = !empty( $parseHash['user_id'] ) ? $parseHash['user_id'] : is_object( $gBitUser ) ? $gBitUser->mUserId : ANONYMOUS_USER_ID;
// Ensure we have a format
if( empty( $parseHash['format_guid'] )) {
$parseHash['format_guid'] = $gBitSystem->getConfig( 'default_format', 'tikiwiki' );
// Handle caching if it is enabled.
if( $gBitSystem->isFeatureActive( 'liberty_cache' ) && !empty( $parseHash['content_id'] ) && empty( $parseHash['no_cache'] ) ) {
if( $cacheFile = LibertyContent::getCacheFile( $parseHash['content_id'], $parseHash['cache_extension'] ) ) {
// Attempt to read cache file
// failed to read from cache.
// Note that we read from cache.
$this->mInfo['is_cached'] = TRUE;
// if $ret is empty, we haven't read anything from cache yet - we need to parse the raw data
if( empty( $ret ) || !empty( $parseAndCache )) {
if( !empty( $parseHash['data'] ) && $parseHash['format_guid'] ) {
// extract and protect ~pp~...~/pp~ and ~np~...~/np~ sections
// some few filters such as stencils need to be before the data plugins
LibertyContent::filterData( $parseHash['data'], $parseHash, 'preplugin' );
// this will handle all liberty data plugins like {code} and {attachment} usage in all formats
// pre parse filter according to what we're parsing - split or full body
$filter = empty( $parseHash['split_parse'] ) ? 'parse' : 'split';
LibertyContent::filterData( $parseHash['data'], $parseHash, 'pre'. $filter );
if( $func = $gLibertySystem->getPluginFunction( $parseHash['format_guid'], 'load_function' ) ) {
if( $ret = $func( $parseHash, $this )) {
// before we cache we insert the protected sections back - currently this is even after the filters.
// this might not be ideal but it allows stuff like ~pp~{maketoc}~/pp~
foreach( $replace as $rep ) {
if( !empty( $parseAndCache )) {
* filterData will apply one of the specified filter stages to the input data
* @param array $pFilterHash array of data that should be filtered
* @param string $pFilterHash[data] is the actual data that needs to be filtered
* @param keyword $pFilterStage specify what filter stage the data is at: pre, post, presplit or postsplit
function filterData( &$pData, &$pFilterHash, $pFilterStage = 'preparse' ) {
if( !empty( $pData ) && $filters = $gLibertySystem->getPluginsOfType( FILTER_PLUGIN )) {
foreach( $filters as $guid => $filter ) {
if( $gLibertySystem->isPluginActive( $guid ) && $func = $gLibertySystem->getPluginFunction( $guid, $pFilterStage. '_function' )) {
$func( $pData, $pFilterHash, ( !empty( $this ) ? $this : NULL ));
* Special parsing for multipage articles
* Temporarily remove <pre>...</pre> sections to protect
* from broke <pre>pre</pre> tags and leave well known <pre>pre</pre>
* behaviour (i.e. type all text inside AS IS w/o
* @param string Data to process
* @return string Extracted pages
preg_match_all("/(<[Pp][Rr][Ee]>)((.|\n)*?)(<\/[Pp][Rr][Ee]>)/", $data, $preparse);
$data = str_replace($preparse[1][$idx] . $pp . $preparse[4][$idx], $key, $data);
$parts = explode(defined('PAGE_SEP') ? PAGE_SEP : "...page...", $data);
* Special parsing for a particular page of a multipage article
* Temporary remove <PRE></PRE> secions to protect
* from broke <PRE> tags and leave well known <PRE>
* behaviour (i.e. type all text inside AS IS w/o
* @param string Data to process
* @param integer Number of page to extract
* @return string Extracted page
preg_match_all("/(<[Pp][Rr][Ee]>)((.|\n)*?)(<\/[Pp][Rr][Ee]>)/", $data, $preparse);
$data = str_replace($preparse[1][$idx] . $pp . $preparse[4][$idx], $key, $data);
$parts = explode(defined('PAGE_SEP') ? PAGE_SEP : "...page...", $data);
if (substr($parts[$i - 1], 1, 5) == "<br/>") {
$ret = substr($parts[$i - 1], 6);
// Replace back <PRE> sections
foreach ($preparsed as $pp) {
$ret = str_replace($pp["key"], "<pre>" . $pp["data"] . "</pre>", $ret);
* convenience function to process a $_REQUEST array
foreach( $pParamHash as $key => $value ){
* Set content related mStructureId
* @param integer Structure ID
if( $this->verifyId( $pStructureId ) ) {
* Check the number of structures that the content object is being used in
* @param integer Structure ID ( If NULL or not supplied check all structures )
* @return integer Number of structures that this content object is located in
$whereSql = ' AND ls.`root_structure_id`=? ';
$query = "SELECT `structure_id` FROM `". BIT_DB_PREFIX. "liberty_structures` ls
WHERE ls.`content_id`=? $whereSql";
$cant = $this->mDb->getOne( $query, $bindVars );
* This is a generic liberty content function to gather indexable words. Override this function
* in your BitPackage.php file if you need to add more indexable words from files other than
* tiki_content and users_users.
if ( $pContentId == 0 ) $pContentId = $this->mContentId;
$sql = "SELECT lc.`title`, lc.`data`, lcds.`data` AS `summary`, uu.`login`, uu.`real_name`
INNER JOIN `" . BIT_DB_PREFIX . "users_users` uu ON uu.`user_id` = lc.`user_id`
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_data` lcds ON (lc.`content_id` = lcds.`content_id` AND lcds.`data_type`='summary')
WHERE lc.`content_id` = ?" ;
$res = $gBitSystem->mDb->getRow($sql, array($pContentId));
if (!(isset ($this->mInfo['no_index']) and $this->mInfo['no_index'] == true)) {
$this->mInfo['index_data'] = $res["title"] . " " . $res["data"] . " " . $res["login"] . " " . $res["real_name"] ;
// -------------------- Cache Funtions -------------------- //
* Check if content has a cache file
* @param array $pContentId Content id of cached item
function isCached( $pContentId = NULL ) {
return( $gBitSystem->getConfig( 'liberty_cache' ) && is_file( LibertyContent::getCacheFile( $pContentId )));
* Get the path where we store liberty cached content
* Get the path to directory where an individual cache item is stored
* @param array $pContentId Content id of cached item
* @return path on success, FALSE on failure
if( @BitBase::verifyId( $pContentId ) ) {
if( $gBitSystem->isFeatureActive( 'liberty_flat_cache' )) {
$subdir = floor( $pContentId / 1000 );
$subdir = $pContentId % 1000;
$path = LibertyContent::getCacheBasePath(). $subdir. '/'. $pContentId. '/';
* Attempts to read from the specified cache file checking if the
* cached data has expired.
* @param the name of the cache file from getCacheFile()
* @return the contents of the cache file or NULL
if( is_file( $pCacheFile ) && ( time() - filemtime( $pCacheFile )) < $gBitSystem->getConfig('liberty_cache') && filesize( $pCacheFile ) > 0 ) {
// get contents from cache file
$h = fopen( $pCacheFile, 'r' );
* Unconditionally writes data to the cache file.
* Does not check for error assuming if write failed that the
* @param the name of the cache file from getCacheFile() to write
* @param the contents to write to the file
// Cowardly refuse to write nothing.
// write parsed contents to cache file
$h = fopen( $pCacheFile, 'w' );
* Get the path to file where an individual cache item is stored
* @param array $pContentId Content id of cached item
* @return filename on success, FALSE on failure
function getCacheFile( $pContentId = NULL, $pCacheExtension = NULL ) {
return( $ret. $pContentId. ( !empty( $pCacheExtension ) ? '.'. $pCacheExtension : '') );
* Delete cache files for a given content item
* @param array $pContentId
* @return TRUE on success, FALSE on failure
if( $gBitSystem->isFeatureActive( 'liberty_cache' ) && @BitBase::verifyId( $pContentId ) ) {
// we need to unlink all files with the same id and any extension
while( FALSE !== ( $file = readdir( $dh ) ) ) {
if( $file != '.' && $file != '..' && ( preg_match( "/^". $pContentId. "$/", $file ) || preg_match( "/^". $pContentId. "\..*/", $file ) ) ) {
* @param array $pContentId
* @return TRUE on success, FALSE on failure
if( $gBitSystem->isFeatureActive( 'liberty_cache' )) {
// make sure that we're in the temp dir at least
// make sure we have a usable cache directory to work with
* @param array $pContentTypeGuid
* @param array $pBindVars
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
* - i think this function is not being used and will hopefully be removed soon - xing - Saturday Jul 07, 2007 19:54:02 CEST
* - it is called in getContentList but I think that services can do what it does now - nick - Sunday Sep 30, 2007
function getFilter( $pContentTypeGuid, &$pSql, &$pBindVars, $pHash = null) {
global $gLibertySystem, $gBitSystem;
foreach ($gLibertySystem->mContentTypes as $type) {
if ($type['content_type_guid'] == $pContentTypeGuid) {
$path = $gBitSystem->mPackages[$type['handler_package']]['path'];//constant(strtoupper($type['handler_package']).'_PKG_PATH');
include_once($path. $type['handler_file']);
$content = new $type['handler_class'];
$content->getFilterSql($pSql, $pBindVars, $pHash);
// -------------------- Action Logging Funtions -------------------- //
* Note: use $gBitSystem throughout that this function can be called statically if needed
* @param array $pParamHash
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
if( $gBitSystem->isFeatureActive( 'liberty_action_log' ) && $this->verifyActionLog( $pParamHash ) ) {
$gBitSystem->mDb->associateInsert( BIT_DB_PREFIX. "liberty_action_log", $pParamHash['action_log_store'] );
* Note: use $gBitSystem throughout that this function can be called statically if needed
* @param array $pParamHash
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
$pParamHash['action_log']['content_id'] = $this->mContentId;
if( !empty( $this->mInfo['title'] ) ) {
$pParamHash['action_log']['title'] = $this->mInfo['title'];
if( empty( $pParamHash['action_log']['log_message'] ) && !empty( $this->mLogs ) ) {
foreach( $this->mLogs as $key => $msg ) {
$pParamHash['action_log']['log_message'] = $log_message;
if( empty( $pParamHash['action_log']['error_message'] ) && !empty( $this->mErrors ) ) {
foreach( $this->mErrors as $key => $msg ) {
$error_message .= "$msg\n";
$pParamHash['action_log']['error_message'] = $error_message;
if( $gBitSystem->isFeatureActive( 'liberty_action_log' ) && static::verifyActionLog( $pParamHash ) ) {
$gBitSystem->mDb->associateInsert( BIT_DB_PREFIX. "liberty_action_log", $pParamHash['action_log_store'] );
* verify the data in the action log is ready for storing
* First checks $pParamHash['action_log'] for information and then the content_store stuff
* Note: use $gBitSystem throughout that this function can be called statically if needed
* @param array $pParamHash
* @return TRUE on success, FALSE on failure
global $gBitUser, $gBitSystem;
// we will set $ret FALSE if there is a problem along the way
// we can't populate mErrors since it would defeat the purpose having errors about the logging system
// content_id isn't strictly needed
if( @BitBase::verifyId( $pParamHash['action_log']['content_id'] ) ) {
$pParamHash['action_log_store']['content_id'] = $pParamHash['action_log']['content_id'];
} elseif( @BitBase::verifyId( $pParamHash['content_id'] ) ) {
$pParamHash['action_log_store']['content_id'] = $pParamHash['content_id'];
// generic information needed in log
if( !empty( $pParamHash['action_log']['user_id'] ) ) {
$pParamHash['action_log_store']['user_id'] = $pParamHash['action_log']['user_id'];
$pParamHash['action_log_store']['user_id'] = $gBitUser->mUserId;
if( !empty( $pParamHash['action_log']['title'] ) ) {
$pParamHash['action_log_store']['title'] = $pParamHash['action_log']['title'];
} elseif( !empty( $pParamHash['content_store']['title'] ) ) {
$pParamHash['action_log_store']['title'] = $pParamHash['content_store']['title'];
if( empty( $pParamHash['action_log']['ip'] ) ) {
if( !empty( $pParamHash['content_store']['ip'] ) ) {
$pParamHash['action_log']['ip'] = $pParamHash['content_store']['ip'];
} elseif( empty( $_SERVER["REMOTE_ADDR"] ) ) {
$pParamHash['action_log']['ip'] = '127.0.0.1';
$pParamHash['action_log']['ip'] = $_SERVER["REMOTE_ADDR"];
$pParamHash['action_log_store']['ip'] = $pParamHash['action_log']['ip'];
$pParamHash['action_log_store']['last_modified'] = $gBitSystem->getUTCTime();
if( empty( $pParamHash['action_log']['log_message'] ) && !empty( $this ) && !empty( $this->mLogs ) ) {
foreach( $this->mLogs as $key => $msg ) {
} elseif( !empty( $pParamHash['action_log']['log_message'] ) ) {
$log_message = $pParamHash['action_log']['log_message'];
if( !empty( $log_message ) ) {
$pParamHash['action_log_store']['log_message'] = substr( $log_message, 0, 250 );
// error message - default is to put in any stuff in mErrors
if( !empty( $pParamHash['action_log']['error_message'] ) ) {
$error_message = $pParamHash['action_log']['error_message'];
// trim down error message
if( !empty( $error_message ) ) {
$pParamHash['action_log_store']['error_message'] = substr( $error_message, 0, 250 );
if( empty( $pParamHash['action_log_store']['error_message'] ) && empty( $pParamHash['action_log_store']['log_message'] )) {
// if we get as far as here, we can
* Get a list of action log entries
* @param array $pListHash List options
* @return List of entries on success, FALSE on failure
$ret = $bindVars = array();
$selectSql = $joinSql = $orderSql = $whereSql = '';
if( !empty( $pListHash['find'] ) ) {
$whereSql .= empty( $whereSql ) ? ' WHERE ' : ' AND ';
$whereSql .= " UPPER( lal.`log_message` ) LIKE ? ";
$bindVars[] = '%'. strtoupper( $pListHash['find'] ). '%';
if( !empty( $pListHash['find_title'] ) ) {
$whereSql .= empty( $whereSql ) ? ' WHERE ' : ' AND ';
$whereSql .= " UPPER( lal.`title` ) LIKE ? ";
$bindVars[] = '%'. strtoupper( $pListHash['find_log'] ). '%';
if( !empty( $pListHash['user_id'] ) ) {
$whereSql .= empty( $whereSql ) ? ' WHERE ' : ' AND ';
$whereSql .= " lal.`user_id` = ? ";
$bindVars[] = $pListHash['user_id'];
if( !empty( $pListHash['content_id'] ) ) {
$whereSql .= empty( $whereSql ) ? ' WHERE ' : ' AND ';
$whereSql .= " lal.`content_id` = ? ";
$bindVars[] = $pListHash['content_id'];
if( !empty( $pListHash['sort_mode'] )) {
if( preg_match( "/^last_modified|^title/", $pListHash['sort_mode'] )) {
$pListHash['sort_mode'] = "lal.". $pListHash['sort_mode'];
$orderSql = " ORDER BY ". $this->convertSortMode( $pListHash['sort_mode'] ). " ";
lc.`content_type_guid`, lc.`created`, lct.`content_name`, lct.`content_name_plural`,
uue.`login` AS modifier_user, uue.`real_name` AS modifier_real_name
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content` lc ON ( lc.`content_id` = lal.`content_id` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_types` lct ON ( lct.`content_type_guid` = lc.`content_type_guid` )
LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_users` uue ON ( uue.`user_id` = lal.`user_id` )
$result = $this->mDb->query( $query, $bindVars, $pListHash['max_records'], $pListHash['offset'] );
while( $aux = $result->fetchRow() ) {
$aux['user'] = $aux['modifier_user'];
$aux['editor'] = ( isset ( $aux['modifier_real_name'] ) ? $aux['modifier_real_name'] : $aux['modifier_user'] );
$query = "SELECT COUNT( lal.`user_id` ) FROM `". BIT_DB_PREFIX. "liberty_action_log` lal $whereSql";
$pListHash['cant'] = $this->mDb->getOne( $query, $bindVars );
* @param array $pTimeSpan Anything older than this timespan will be removed
* @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
if( @BitBase::verifyId( $pTimeSpan ) ) {
$where = "WHERE `last_modified` < ?";
$bindVars[] = $gBitSystem->mServerTimestamp->getUTCTime() - $pTimeSpan;
$this->mDb->query( "DELETE FROM `". BIT_DB_PREFIX. "liberty_action_log` $where", $bindVars );
* getAvailableContentStatus
* @return an array of content_status_id, content_status_names the current
* user can use on this content. Subclases may easily override with return
* LibertyContent::getAvailableContentStatus(-100, 0) for example to restrict to
* only hidden content types.
return( $this->mDb->getAssoc( "SELECT `content_status_id`,`content_status_name` FROM `". BIT_DB_PREFIX. "liberty_content_status` ORDER BY `content_status_id`" ) );
return( $this->mDb->getAssoc( "SELECT `content_status_id`, `content_status_name` FROM `". BIT_DB_PREFIX. "liberty_content_status` WHERE `content_status_id` > ? AND `content_status_id` < ? ORDER BY `content_status_id`", array( $pUserMinimum, $pUserMaximum )));
* getContentStatus will return the content status of the currently loaded content.
* @param array $pContentId Content ID of the content in question
if ( !( $ret = $this->getField( 'content_status_id' ) ) ){
$ret = $this->mDb->getOne( "SELECT `content_status_id` FROM `". BIT_DB_PREFIX. "liberty_content` WHERE `content_id` = ?", array( $pContentId ));
$ret = is_null( $ret ) ? $pDefault : $ret;
* @return true when the content status = -999
return( $this->getField( 'content_status_id' ) <= $gBitSystem->getConfig( 'liberty_status_deleted', - 999 ) );
* @return true when the content status = -999
return( $this->getField( 'content_status_id' ) <= $gBitSystem->getConfig( 'liberty_status_threshold_private', - 40 ) );
* isProtected status test
* @return true when the content status = -20 or content has protection flag set
return( $this->getField( 'content_status_id' ) <= $gBitSystem->getConfig( 'liberty_status_threshold_protected', - 20 ) );
* @return true when the content status = -10
return( $this->getField( 'content_status_id' ) <= $gBitSystem->getConfig( 'liberty_status_threshold_hidden', - 10 ) );
* @param array $pStatusId Status ID if not available in $this->mInfo['content_status_id']
* @return The name of the content status based on the status id of the content
$ret = 'Not a valid content status';
// check to see where we can get the status information from
if( !empty( $this ) && !empty( $this->mInfo['content_status_name'] )) {
return( $this->mInfo['content_status_name'] );
} elseif( is_null( $pStatusId ) && !empty( $this ) && !empty( $this->mInfo['content_status_id'] )) {
$pStatusId = $this->mInfo['content_status_id'];
// fetch from db if needed
if( $ret = $this->mDb->getOne( "SELECT `content_status_name` FROM `". BIT_DB_PREFIX. "liberty_content_status` WHERE `content_status_id` = ?", array( $pStatusId ))) {
* Store Data into liberty_content_data
* @return bool true ( will not currently report a failure )
$this->mDb->query( "DELETE FROM `". BIT_DB_PREFIX. "liberty_content_data` WHERE `content_id`=? AND `data_type`=?", array( $this->mContentId, $pType ) );
if( $this->mDb->getOne( "SELECT `content_id` FROM `". BIT_DB_PREFIX. "liberty_content_data` WHERE `content_id`=? AND `data_type`=?", array( $this->mContentId, $pType ) ) ) {
$query = "UPDATE `". BIT_DB_PREFIX. "liberty_content_data` SET `data`= ? WHERE `content_id` = ? AND `data_type`=?";
$query = "INSERT INTO `". BIT_DB_PREFIX. "liberty_content_data` ( `data`, `content_id`, `data_type` ) VALUES (?,?,?)";
$result = $this->mDb->query( $query, array( $pData, $this->mContentId, $pType ) );
* storeStatus store liberty contenet status
* @param array $pContentStatusId
if( $this->isValid() && $pContentStatusId ) {
return $this->mDb->query( "UPDATE `". BIT_DB_PREFIX. "liberty_content` SET `content_status_id`=? WHERE `content_id`=?", array( $pContentStatusId, $this->mContentId ) );
* isCommentable will check allow_comments in mInfo or if it's set as a preference.
* @return TRUE on success, FALSE on failure
$setting = $this->getField( 'allow_comments' );
return( $setting == TRUE || $setting == 'y' );
* getListingPreview -- Returns a string with a preview of the content.
* @return the preview string
global $gBitSystem, $gContent, $gBitSmarty;
* getPreview -- Returns a string with a preview of the content. Default implementation runs getRenderFile() with $liberty_preview set in the context and gBitSystem set to only render the content.
* @return the preview string
global $gBitSystem, $gContent, $gBitSmarty, $gBitThemes;
// Tell gBitSystem not to do modules and such
$gBitThemes->setFormatHeader( "center_only" );
// Tell the content we are previewing (in case they care)
$gBitSmarty->assign('liberty_preview', true);
$oldGContent = $gContent;
// Return gBitSystem to full render mode
$gBitThemes->setFormatHeader( "html" );
// Clear the preview flag
$gBitSmarty->assign('liberty_preview', false);
$gContent = $oldGContent;
|