summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authormodela bitweaver <spiderr@bitweaver.org>2021-02-02 01:17:09 -0500
committermodela bitweaver <spiderr@bitweaver.org>2021-02-02 01:17:09 -0500
commit4abf677ae556f0cf7e08d377df1e0ffb9041267a (patch)
tree92023c1799a04fe82398beb9436d4e67f7688658 /includes
parent8ad7ef38d30923bb0b8cb5bce381dc4ce87052a4 (diff)
downloadliberty-4abf677ae556f0cf7e08d377df1e0ffb9041267a.tar.gz
liberty-4abf677ae556f0cf7e08d377df1e0ffb9041267a.tar.bz2
liberty-4abf677ae556f0cf7e08d377df1e0ffb9041267a.zip
move _inc and _lib to includes/ and use PKG_INCLUDE_PATH constants
Diffstat (limited to 'includes')
-rw-r--r--includes/calculate_max_upload_inc.php25
-rw-r--r--includes/classes/LibertyAttachable.php418
-rw-r--r--includes/classes/LibertyBase.php127
-rw-r--r--includes/classes/LibertyComment.php708
-rw-r--r--includes/classes/LibertyContent.php3844
-rw-r--r--includes/classes/LibertyMime.php1267
-rw-r--r--includes/classes/LibertyStructure.php1153
-rw-r--r--includes/classes/LibertySystem.php865
-rw-r--r--includes/comments_inc.php368
-rw-r--r--includes/content_history_inc.php86
-rw-r--r--includes/display_content_inc.php17
-rw-r--r--includes/display_structure_inc.php22
-rw-r--r--includes/edit_help_inc.php68
-rw-r--r--includes/edit_storage_inc.php55
-rw-r--r--includes/edit_structure_inc.php126
-rw-r--r--includes/get_content_list_inc.php93
-rw-r--r--includes/help_format_tikiwiki_inc.php239
-rw-r--r--includes/liberty_lib.php939
-rw-r--r--includes/lookup_content_inc.php40
19 files changed, 10460 insertions, 0 deletions
diff --git a/includes/calculate_max_upload_inc.php b/includes/calculate_max_upload_inc.php
new file mode 100644
index 0000000..ee86071
--- /dev/null
+++ b/includes/calculate_max_upload_inc.php
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @version $Header$
+ *
+ * settings that are useful to know about at upload time
+ *
+ * @package liberty
+ * @subpackage functions
+ */
+$postMax = str_replace( 'M', '', ini_get( 'post_max_size' ));
+$uploadMax = str_replace( 'M', '', ini_get( 'upload_max_filesize' ) );
+
+if( $postMax < $uploadMax ) {
+ $uploadMax = $postMax;
+}
+
+/**
+ * calculate user quota
+ */
+if( $gBitSystem->isPackageActive( 'quota' ) ) {
+ require_once( QUOTA_PKG_INCLUDE_PATH.'calculate_quota_inc.php' );
+}
+
+$gBitSmarty->assignByRef( 'uploadMax', $uploadMax );
+?>
diff --git a/includes/classes/LibertyAttachable.php b/includes/classes/LibertyAttachable.php
new file mode 100644
index 0000000..9c365fa
--- /dev/null
+++ b/includes/classes/LibertyAttachable.php
@@ -0,0 +1,418 @@
+<?php
+/**
+ * Management of Liberty Content
+ *
+ * @package liberty
+ * @version $Header$
+ * @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>
+// +----------------------------------------------------------------------+
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyContent.php' );
+
+/**
+ * LibertyAttachable class
+ *
+ * @package liberty
+ */
+class LibertyAttachable extends LibertyContent {
+ public $mContentId;
+ public $mStorage;
+
+ function LibertyAttachable() {
+ parent::__construct();
+ }
+
+ // {{{ =================== Deprecated Methods ====================
+ /**
+ * TODO: This code is old and is not used by any package in the bitweaver CVS anymore.
+ * We will clean up this code as soon as we are sure that noone is using this code.
+ * Please look for the closing tripple '}' brackets to see where this section ends.
+ */
+
+ /**
+ * fully load content and insert any attachments in $this->mStorage
+ * allow an optional content_id to be passed in to ease legacy lib style objects (like blogs, articles, etc.)
+ *
+ * @param array $pContentId
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ * @deprecated deprecated since version 2.1.0-beta
+ */
+
+ /**
+ * TODO: This code is old and is not used by any package in the bitweaver CVS anymore.
+ * We will clean up this code as soon as we migrated all legacy code
+ */
+ function load( $pContentId = NULL, $pPluginParams = NULL ) {
+ //deprecated( "This method has been replaced by a method in LibertyMime. Please try to migrate your code." );
+ // assume a derived class has joined on the liberty_content table, and loaded it's columns already.
+ global $gLibertySystem;
+ $conId = ( @$this->verifyId( $pContentId ) ? $pContentId : $this->mContentId );
+
+ if( @$this->verifyId( $conId ) ) {
+ LibertyContent::load( $conId );
+ $query = "
+ SELECT *
+ FROM `".BIT_DB_PREFIX."liberty_attachments` la
+ WHERE la.`content_id`=? ORDER BY la.`pos` ASC, la.`attachment_id` ASC";
+ if( $result = $this->mDb->query( $query,array( (int)$conId ))) {
+ $this->mStorage = array();
+ while( $row = $result->fetchRow() ) {
+ if( $func = $gLibertySystem->getPluginFunction( $row['attachment_plugin_guid'], 'load_function', 'mime' )) {
+ // this dummy is needed for forward compatability with LibertyMime plugins
+ $dummy = array();
+ $this->mStorage[$row['attachment_id']] = $func( $row, $dummy );
+ //$this->mStorage[$row['attachment_id']]['is_primary'] = !empty( $row['primary_attachment_id'] );
+ } else {
+ print "No load_function for ".$row['attachment_plugin_guid']." ".$gLibertySystem->mPlugins[$row['attachment_plugin_guid']];
+ }
+ }
+ }
+ }
+ return( TRUE );
+ }
+
+ /* store -- Stores any attachments
+ *
+ * pass $pParamHash['liberty_attachable']['skip_content_store'] == TRUE
+ * to avoid the underlying content store and simply store the attachments.
+ *
+ * verify() will shove things to store into $pParamHash['STORAGE'] to be
+ * gobbled up in this function.
+ *
+ * pass $pParamHash['liberty_attachable']['auto_primary'] == FALSE to turn off the auto
+ * primary on first attachment feature for a content type.
+ *
+ * @param hash $pParamHash The hash of arguments
+ *
+ * @deprecated deprecated since version 2.1.0-beta
+ */
+
+ /**
+ * TODO: This code is old and is not used by any package in the bitweaver CVS anymore.
+ * We will clean up this code as soon as we migrated all legacy code
+ */
+ function store( &$pParamHash ) {
+ //deprecated( "This method has been replaced by a method in LibertyMime. Please try to migrate your code." );
+ global $gLibertySystem, $gBitSystem, $gBitUser;
+ $this->StartTrans();
+ if( LibertyAttachable::verify( $pParamHash ) && ( isset($pParamHash['skip_content_store']) || LibertyContent::store( $pParamHash ) ) ) {
+
+ if(!empty( $pParamHash['STORAGE'] ) && count( $pParamHash['STORAGE'] ) ) {
+ foreach( array_keys( $pParamHash['STORAGE'] ) as $guid ) {
+ $storeRows = &$pParamHash['STORAGE'][$guid]; // short hand variable assignment
+ // If it is empty then nothing more to do. Avoid error in foreach.
+ if (empty($storeRows)) {
+ continue;
+ }
+ foreach( $storeRows as $key => $value ) {
+ $storeRow = &$pParamHash['STORAGE'][$guid][$key];
+ $storeRow['plugin_guid'] = $guid;
+
+ if (!@BitBase::verifyId($pParamHash['content_id'])) {
+ $storeRow['content_id'] = NULL;
+ } else {
+ $storeRow['content_id'] = $pParamHash['content_id']; // copy in content_id
+ }
+
+ if (!empty($pParamHash['user_id'])) {
+ $storeRow['user_id'] = $pParamHash['user_id']; // copy in the user_id
+ } else {
+ $storeRow['user_id'] = $gBitUser->mUserId;
+ }
+
+ // do we have a verify function for this storage type, and do things verify?
+ $verifyFunc = $gLibertySystem->getPluginFunction( $guid, 'verify_function' );
+ if( $verifyFunc && $verifyFunc( $storeRow ) ) {
+ // For backwards compatibility with a single upload.
+ if( @BitBase::verifyId( $pParamHash['attachment_id'] )) {
+ $storeRow['upload']['attachment_id'] = $storeRow['attachment_id'] = $pParamHash['attachment_id'];
+ } else if ( !isset($storeRow['skip_insert'] ) ) {
+ if ( defined( 'LINKED_ATTACHMENTS' ) && @BitBase::verifyId( $pParamHash['content_id'] ) ) {
+ $storeRow['upload']['attachment_id'] = $storeRow['attachment_id'] = $pParamHash['content_id'];
+ } else {
+ $storeRow['upload']['attachment_id'] = $storeRow['attachment_id'] =
+ defined( 'LINKED_ATTACHMENTS' ) ? $this->mDb->GenID( 'liberty_content_id_seq') : $this->mDb->GenID( 'liberty_attachments_id_seq' );
+ }
+ }
+
+ // if we have uploaded a file, we can take care of that generically
+ if( !empty( $storeRow['upload'] ) && is_array( $storeRow['upload'] ) && !empty( $storeRow['upload']['size'] ) ) {
+ if( empty( $storeRow['upload']['type'] ) ) {
+ $ext = substr( $storeRow['upload']['name'], strrpos( $storeRow['upload']['name'], '.' ) + 1 );
+ $storeRow['upload']['type'] = $gBitSystem->lookupMimeType( $ext );
+ }
+ $storeRow['upload']['dest_branch'] = $this->getStorageBranch( $storeRow['attachment_id'], $storeRow['user_id'], $this->getStorageSubDirName() );
+ if (!empty( $pParamHash['thumbnail_sizes'] ) ) {
+ $storeRow['upload']['thumbnail_sizes'] = $pParamHash['thumbnail_sizes'];
+ }
+ $storagePath = liberty_process_upload( $storeRow['upload'] );
+ // We're gonna store to local file system & liberty_files table
+ if( empty( $storagePath ) ) {
+ $this->mErrors['file'] = tra( "Could not store file" ).": ".$storeRow['upload']['name'].'.';
+ $storeRow['attachment_id'] = NULL;
+ $storeRow['upload']['attachment_id'] = NULL;
+ } else {
+ $storeRow['upload']['dest_file_path'] = $storagePath;
+ }
+ }
+
+ if( @BitBase::verifyId( $storeRow['attachment_id'] ) && $storeFunc = $gLibertySystem->getPluginFunction( $storeRow['plugin_guid'], 'store_function' )) {
+ $this->mStorage = $storeFunc( $storeRow );
+ }
+
+ // don't insert if we already have an entry with this attachment_id
+ if( @BitBase::verifyId( $storeRow['attachment_id'] ) && !isset( $storeRow['skip_insert'] ) && !LibertyMime::loadAttachment( $storeRow['attachment_id'] )) {
+ $sql = "INSERT INTO `".BIT_DB_PREFIX."liberty_attachments` ( `content_id`, `attachment_id`, `attachment_plugin_guid`, `foreign_id`, `user_id` ) VALUES ( ?, ?, ?, ?, ? )";
+ $rs = $this->mDb->query( $sql, array( $storeRow['content_id'], $storeRow['attachment_id'], $storeRow['plugin_guid'], (int)$storeRow['foreign_id'], $storeRow['user_id'] ) );
+ }
+ }
+ }
+ }
+ }
+
+ // set the primary attachment id
+ $this->setPrimaryAttachment(
+ $pParamHash['liberty_attachments']['primary'],
+ $pParamHash['content_id'],
+ empty( $pParamHash['liberty_attachments']['auto_primary'] ) || $pParamHash['liberty_attachments']['auto_primary'] ? TRUE : FALSE
+ );
+ }
+ $this->CompleteTrans();
+
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * verifyAttachment
+ *
+ * @param array $pParamHash
+ * @param array $pFile
+ * @param array $pKey
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ * @deprecated deprecated since version 2.1.0-beta
+ */
+ function verifyAttachment( &$pParamHash, $pFile, $pKey ) {
+ //deprecated( "This method has been replaced by a method in LibertyMime. Please try to migrate your code." );
+ global $gBitSystem, $gBitUser, $gLibertySystem;
+
+ if( !empty( $pFile ) && !empty( $pFile['size'] ) ) {
+ if( empty( $pParamHash['storage_guid'] )) {
+ // only file format storage available at present
+ $pParamHash['storage_guid'] = $storageGuid = PLUGIN_GUID_BIT_FILES;
+ } else {
+ $storageGuid = $pParamHash['storage_guid'];
+ }
+
+ if( !empty( $pFile['size'] ) ) {
+ $this->extractMetaData( $pParamHash, $pFile );
+ // meta data may be stupid and have stuffed title with all spaces
+ if( !empty( $pParamHash['title'] ) ) {
+ $pParamHash['title'] = trim( $pParamHash['title'] );
+ }
+
+ // let's add a default title
+ if( empty( $pParamHash['title'] ) && !empty( $pFile['name'] ) ) {
+ if( preg_match( '/^[A-Z]:\\\/', $pFile['name'] ) ) {
+ // MSIE shit file names if passthrough via gigaupload, etc.
+ // basename will not work - see http://us3.php.net/manual/en/function.basename.php
+ $tmp = preg_split("#[\\\]#",$pFile['name']);
+ $defaultName = $tmp[count($tmp) - 1];
+ } elseif( strpos( '.', $pFile['name'] ) ) {
+ list( $defaultName, $ext ) = explode( '.', $pFile['name'] );
+ } else {
+ $defaultName = $pFile['name'];
+ }
+ $pParamHash['title'] = str_replace( '_', ' ', substr( $defaultName, 0, strrpos( $defaultName, '.' ) ) );
+ }
+
+
+ if ( !is_windows() ) {
+ list( $pFile['name'], $pFile['type'] ) = $gBitSystem->verifyFileExtension( $pFile['tmp_name'], $pFile['name'] );
+ } else {
+ //$pFile['type'] = $gBitSystem->verifyMimeType( $pFile['tmp_name'] );
+ }
+ // clean out crap that can make life difficult in server maintenance
+ $cleanedBaseName = preg_replace( '/[&\%:\/\\\]/', '', substr( $pFile['name'], 0, strrpos( $pFile['name'], '.' ) ) );
+ $pFile['dest_base_name'] = $cleanedBaseName;
+ $pFile['source_file'] = $pFile['tmp_name'];
+ // lowercase all file extensions
+ $pFile['name'] = $cleanedBaseName.strtolower( substr( $pFile['name'], strrpos( $pFile['name'], '.' ) ) );
+ if (!isset($pParamHash['STORAGE'][$storageGuid])) {
+ $pParamHash['STORAGE'][$storageGuid] = array();
+ }
+ $pParamHash['STORAGE'][$storageGuid][$pKey] = array('upload' => &$pFile);
+ }
+ }
+ }
+
+ /**
+ * verify - standard API method, with a twist. It will gobble up anything in $_FILES if available, unless an array of arrays is passed in to $pParamHash['_files_override']
+ *
+ * @access private
+ * @author Christian Fowler<spider@steelsun.com>
+ * @param $pParamHash
+ * @return FALSE if errors were present, TRUE meaning object is ready to store
+ * @deprecated deprecated since version 2.1.0-beta
+ */
+ function verify( &$pParamHash ) {
+ //deprecated( "This method has been replaced by a method in LibertyMime. Please try to migrate your code." );
+ global $gBitSystem, $gBitUser;
+ // check to see if we have any files to upload
+ if( isset( $pParamHash['_files_override'] ) ) {
+ // we have been passed in a manually stuffed files attachment, such as a custom uploader would have done.
+ // process this, and skip over $_FILES
+ $uploads = $pParamHash['_files_override'];
+ } elseif( !empty( $_FILES ) ) {
+ // we have some _FILES hanging around we will gobble up. This is inherently dagnerous chewing up a _FILES like this as
+ // it can cause premature storing of a _FILE if you are trying to store multiple pieces of content at once.
+ foreach( $_FILES as $key => $file ) {
+ if( !empty( $file['name'] )) {
+ $uploads[$key] = $file;
+ }
+ }
+ }
+
+ // don't check for p_liberty_attach_attachments permission on bitpermuser class so registration with avatar upload works
+ if( strtolower( get_class( $this )) == 'bitpermuser' ) {
+ $pParamHash['no_perm_check'] = TRUE;
+ }
+
+ // check for the required permissions to upload a file to the liberty attachments area
+ if( !empty( $uploads ) && empty( $pParamHash['no_perm_check'] )) {
+ if( !$gBitUser->hasPermission( 'p_liberty_attach_attachments' )) {
+ $this->mErrors['permission'] = tra( 'You do not have permission to upload attachments.' );
+ }
+ }
+
+ if( !empty( $pParamHash['attachment_id'] ) && !$this->verifyId( $pParamHash['attachment_id'] ) ) {
+ $this->mErrors['file'] = tra('System Error: Non-numeric storage_id.');
+ }
+
+ if( empty( $pParamHash['user_id'] ) ) {
+ // storage is always owned by the user that uploaded it!
+ // er... or at least admin if somehow we have a NULL mUserId - anon uploads maybe?
+ $pParamHash['user_id'] = @$this->verifyId( $gBitUser->mUserId ) ? $gBitUser->mUserId : ROOT_USER_ID;
+ }
+ if( empty( $pParamHash['process_storage'] ) ) {
+ $pParamHash['process_storage'] = NULL;
+ }
+
+ if( empty( $pParamHash['subdir'] ) ) {
+ $pParamHash['subdir'] = 'files';
+ }
+
+ if( !empty( $uploads ) ) {
+ foreach( array_keys( $uploads ) as $f ) {
+ $this->verifyAttachment( $pParamHash, $uploads[$f], $f );
+ }
+ }
+
+ // primary attachment. Allow 'none' to clear the primary.
+ if( !@BitBase::verifyId( $pParamHash['liberty_attachments']['primary'] ) && ( empty( $pParamHash['liberty_attachments']['primary'] ) || $pParamHash['liberty_attachments']['primary'] != 'none' ) ) {
+ $pParamHash['liberty_attachments']['primary'] = NULL;
+ }
+
+ // if we have an error we get them all by checking parent classes for additional errors
+ if( count( $this->mErrors ) > 0 ){
+ parent::verify( $pParamHash );
+ }
+
+ return ( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * extractMetaData extract meta data from images
+ *
+ * @param array $pParamHash
+ * @param array $pFile
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ * @deprecated deprecated since version 2.1.0-beta
+ */
+ function extractMetaData( &$pParamHash, &$pFile ) {
+ //deprecated( "This method has been replaced by a method in LibertyMime. Please try to migrate your code." );
+ // Process a JPEG , jpeg_metadata_tk REQUIRES short_tags because that is the way it was written. feel free to fix something. XOXO spiderr
+ if( ini_get( 'short_open_tag' ) && function_exists( 'exif_read_data' ) && !empty( $pFile['tmp_name'] ) && strpos( strtolower($pFile['type']), 'jpeg' ) !== FALSE ) {
+ $exifHash = @exif_read_data( $pFile['tmp_name'], 0, true);
+
+ // Change: Allow this example file to be easily relocatable - as of version 1.11
+ require_once UTIL_PKG_INCLUDE_PATH.'jpeg_metadata_tk/JPEG.php';
+ require_once UTIL_PKG_INCLUDE_PATH.'jpeg_metadata_tk/JFIF.php';
+ require_once UTIL_PKG_INCLUDE_PATH.'jpeg_metadata_tk/PictureInfo.php';
+ require_once UTIL_PKG_INCLUDE_PATH.'jpeg_metadata_tk/XMP.php';
+ require_once UTIL_PKG_INCLUDE_PATH.'jpeg_metadata_tk/EXIF.php';
+
+ // Retrieve the header information from the JPEG file
+ $jpeg_header_data = get_jpeg_header_data( $pFile['tmp_name'] );
+
+ // Retrieve EXIF information from the JPEG file
+ $Exif_array = get_EXIF_JPEG( $pFile['tmp_name'] );
+
+ // Retrieve XMP information from the JPEG file
+ $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) );
+
+ // Retrieve Photoshop IRB information from the JPEG file
+ $IRB_array = get_Photoshop_IRB( $jpeg_header_data );
+ if( !empty( $exifHash['IFD0']['Software'] ) && preg_match( '/photoshop/i', $exifHash['IFD0']['Software'] ) ) {
+ require_once UTIL_PKG_INCLUDE_PATH.'jpeg_metadata_tk/Photoshop_File_Info.php';
+ // Retrieve Photoshop File Info from the three previous arrays
+ $psFileInfo = get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array );
+
+ if( !empty( $psFileInfo['headline'] ) ) {
+ if( empty( $pParamHash['title'] ) ) {
+ $pParamHash['title'] = $psFileInfo['headline'];
+ } elseif( empty( $pParamHash['edit'] ) && !$this->getField( 'data' ) && $pParamHash['title'] != $psFileInfo['headline'] ) {
+ $pParamHash['edit'] = $psFileInfo['headline'];
+ }
+ }
+ if( !empty( $psFileInfo['caption'] ) ) {
+ if( empty( $pParamHash['title'] ) ) {
+ $pParamHash['title'] = $psFileInfo['caption'];
+ } elseif( empty( $pParamHash['edit'] ) && !$this->getField( 'data' ) && $pParamHash['title'] != $psFileInfo['caption'] ) {
+ $pParamHash['edit'] = $psFileInfo['caption'];
+ }
+ }
+ }
+
+ if( !empty( $exifHash['EXIF']['DateTimeOriginal'] ) ) {
+ $pParamHash['event_time'] = strtotime( $exifHash['EXIF']['DateTimeOriginal'] );
+ }
+
+ if( !empty( $exifHash['IFD0']['ImageDescription'] ) ) {
+ if( empty( $pParamHash['title'] ) ) {
+ $pParamHash['title'] = $exifHash['IFD0']['ImageDescription'];
+ } elseif( empty( $pParamHash['edit'] ) && !$this->getField( 'data' ) && $pParamHash['title'] != $exifHash['IFD0']['ImageDescription'] ) {
+ $pParamHash['edit'] = $exifHash['IFD0']['ImageDescription'];
+ }
+ }
+ }
+ }
+
+ // }}}
+
+
+}
+
+// FIXME: this is really dirty and needs to go away from here
+// make sure LibertyMime is available during this transition phase
+// we need to call this down here since LM extends LA and can't be included before LA is available
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyMime.php' );
+
+/* vim: :set fdm=marker : */
+?>
diff --git a/includes/classes/LibertyBase.php b/includes/classes/LibertyBase.php
new file mode 100644
index 0000000..1825827
--- /dev/null
+++ b/includes/classes/LibertyBase.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Base class for Management of Liberty Content
+ *
+ * @package liberty
+ * @version $Header$
+ * @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>
+// +----------------------------------------------------------------------+
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_INCLUDE_PATH.'liberty_lib.php' );
+require_once( KERNEL_PKG_CLASS_PATH.'BitBase.php' );
+
+/**
+ * Virtual base class (as much as one can have such things in PHP) for all
+ * derived bitweaver classes that manage content.
+ *
+ * @package liberty
+ */
+class LibertyBase extends BitBase {
+
+ /**
+ * Constructor building on BitBase object
+ *
+ * Object need to init the database connection early
+ * Database will be linked via a previously activated BitDb object
+ * which will provide the mDb pointer to that database
+ */
+ function __construct() {
+ parent::__construct();
+ }
+
+ /**
+ * given a content_type_guid this will return an object of the proper type
+ *
+ * @param the content type to be loaded
+ */
+ function getLibertyClass($pContentTypeGuid) {
+ // We can abuse getLibertyObject to do the work
+ $ret = LibertyBase::getLibertyObject('1', $pContentTypeGuid, FALSE);
+ // Make sure we don't have a content_id set though.
+ unset($ret->mContentId);
+ return $ret;
+ }
+
+ /**
+ * Given a content_id, this will return and object of the proper type
+ *
+ * @param integer content_id of the object to be returned
+ * @param string optional content_type_guid of pConId. This will save a select if you happen to have this info. If not, this method will look it up for you.
+ * @param call load on the content. Defaults to true.
+ * @returns object of the appropriate content type class
+ */
+ public static function getLibertyObject( $pContentId, $pContentTypeGuid=NULL, $pLoadFromCache = TRUE ) {
+ $ret = NULL;
+ global $gLibertySystem, $gBitUser, $gBitSystem;
+
+ if( static::verifyId( $pContentId ) ) {
+ // remove non integer bits from structure_id and content_id requests
+ // can happen with period's at the end of url's that are email'ed around
+ $typeClass = NULL;
+ $pContentId = preg_replace( '/[\D]/', '', $pContentId );
+ if( empty( $pContentTypeGuid ) ) {
+ $pContentTypeGuid = $gLibertySystem->mDb->getOne( "SELECT `content_type_guid` FROM `".BIT_DB_PREFIX."liberty_content` WHERE `content_id`=?", array( $pContentId ), NULL, NULL, 3600 );
+ }
+ if( !empty( $pContentTypeGuid ) && isset( $gLibertySystem->mContentTypes[$pContentTypeGuid] ) ) {
+ $typeClass = $gLibertySystem->getContentClassName( $pContentTypeGuid );
+ }
+ if( $pLoadFromCache && ($ret = static::loadFromCache( $pContentId, $typeClass )) ) {
+ $ret->mCacheObject = TRUE;
+ } else {
+ if( $typeClass ) {
+ $creator = new $typeClass();
+ $ret = $creator->getNewObject( $typeClass, $pContentId, $pLoadFromCache );
+ $ret->setCacheableObject( FALSE );
+ $ret->clearFromCache();
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Simple method to create a given class with a specified primary_id. The purpose of a method is to allow for derived classes to override as necessary.
+ *
+ * @param string class to be created
+ * @param integer id from the secondary table of the object to be returned
+ * @param call load on the content. Defaults to true.
+ * @returns object of the appropriate content type class
+ */
+ public static function getNewObjectById( $pClass, $pPrimaryId, $pLoadFromCache=TRUE ) {
+ if( $ret = new $pClass( $pPrimaryId ) ) {
+ $ret->load();
+ }
+ return $ret;
+ }
+
+ /**
+ * Simple method to create a given class with a specified content_id. The purpose of a method is to allow for derived classes to override as necessary.
+ *
+ * @param string class to be created
+ * @param integer content_id of the object to be returned
+ * @param call load on the content. Defaults to true.
+ * @returns object of the appropriate content type class
+ */
+ public static function getNewObject( $pClass, $pContentId, $pLoadFromCache=TRUE ) {
+ if( $ret = new $pClass( NULL, $pContentId ) ) {
+ $ret->load();
+ }
+ return $ret;
+ }
+}
+
+?>
diff --git a/includes/classes/LibertyComment.php b/includes/classes/LibertyComment.php
new file mode 100644
index 0000000..db2f82d
--- /dev/null
+++ b/includes/classes/LibertyComment.php
@@ -0,0 +1,708 @@
+<?php
+/**
+ * Management of Liberty Content
+ *
+ * @package liberty
+ * @version $Header$
+ * @author spider <spider@steelsun.com>
+ */
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyMime.php' );
+
+define( 'BITCOMMENT_CONTENT_TYPE_GUID', 'bitcomment' );
+
+/**
+ * Handles all comments which are actual content objects
+ *
+ * @package liberty
+ */
+
+class LibertyComment extends LibertyMime {
+ public $mCommentId;
+
+ function __construct($pCommentId = NULL, $pContentId = NULL, $pInfo = NULL) {
+ parent::__construct();
+ $this->registerContentType( BITCOMMENT_CONTENT_TYPE_GUID, array(
+ 'content_type_guid' => BITCOMMENT_CONTENT_TYPE_GUID,
+ 'content_name' => 'Comment',
+ 'handler_class' => 'LibertyComment',
+ 'handler_package' => 'liberty',
+ 'handler_file' => 'LibertyComment.php',
+ 'maintainer_url' => 'http://www.bitweaver.org'
+ ) );
+ $this->mCommentId = (int)$pCommentId;
+ $this->mContentId = (int)$pContentId;
+ $this->mInfo = $pInfo;
+ $this->mContentTypeGuid = BITCOMMENT_CONTENT_TYPE_GUID;
+ $this->mAdminContentPerm = 'p_liberty_admin_comments';
+ $this->mRootObj = NULL;
+
+ if ($this->mCommentId || $this->mContentId) {
+ $this->loadComment();
+ }
+ }
+
+
+ public function __sleep() {
+ return array_merge( parent::__sleep(), array( 'mCommentId', 'mRootObj' ) );
+ }
+
+ function loadComment() {
+ global $gBitSystem, $gBitUser;
+ if (!$this->verifyId($this->mCommentId) && !$this->verifyId($this->mContentId)) {
+ return NULL;
+ }
+
+ if ($this->mCommentId) {
+ $mid = 'WHERE lcom.`comment_id` = ?';
+ $bindVars = array($this->mCommentId);
+ } else {
+ $mid = 'WHERE lc.`content_id` = ?';
+ $bindVars = array($this->mContentId);
+ }
+
+ $joinSql = $selectSql = $whereSql = '';
+ $this->getServicesSql( 'content_load_sql_function', $selectSql, $joinSql, $whereSql, $bindVars, $this );
+
+ $sql = "SELECT lcom.*, lc.*, uu.`email`, uu.`real_name`, uu.`login` $selectSql
+ FROM `".BIT_DB_PREFIX."liberty_comments` lcom
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (lcom.`content_id` = lc.`content_id`)
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."users_users` uu ON (lc.`user_id` = uu.`user_id`) $joinSql
+ $mid $whereSql";
+ if( $row = $this->mDb->getRow( $sql, $bindVars ) ) {
+ $this->mInfo = $row;
+ $this->mContentId = $row['content_id'];
+ $this->mCommentId = $row['comment_id'];
+
+ // call parent load for attachment data like other Mime derived classes, only need it if feature is active or admin
+ if( $gBitSystem->isFeatureActive( 'comments_allow_attachments' ) || $gBitUser->isAdmin() ){
+ LibertyMime::load();
+ }
+ }
+ return count($this->mInfo);
+ }
+
+ function verifyComment(&$pParamHash) {
+ global $gBitUser, $gBitSystem;
+
+ /* should be unnecessary
+ if( !empty( $_REQUEST['format_guid'] )) {
+ $storeRow['format_guid'] = $_REQUEST['format_guid'];
+ }
+ */
+
+ $pParamHash['content_id'] = (@BitBase::verifyId($this->mContentId) ? $this->mContentId : NULL);
+
+ if( empty( $pParamHash['root_id'] ) && !empty( $pParamHash['comments_parent_id'] ) ) {
+ $pParamHash['root_id'] = $pParamHash['comments_parent_id'];
+ }
+
+ if (!$pParamHash['root_id']) {
+ $this->mErrors['root_id'] = "Missing root id for comment";
+ }
+
+ if( empty( $pParamHash['parent_id'] ) ){
+ $pParamHash['parent_id'] = (@BitBase::verifyId($this->mInfo['parent_id']) ? $this->mInfo['parent_id'] : (!@BitBase::verifyId($pParamHash['post_comment_reply_id']) ? $pParamHash['comments_parent_id'] : $pParamHash['post_comment_reply_id']));
+ }
+
+ if (!$pParamHash['parent_id']) {
+ $this->mErrors['parent_id'] = "Missing parent id for comment";
+ }
+
+ if (empty($pParamHash['anon_name'])) {
+ $pParamHash['anon_name']=null;
+ }
+
+ if( !@$gBitUser->verifyCaptcha( $pParamHash['captcha'] ) ) {
+ $this->mErrors['store'] = tra( 'Incorrect validation code' );
+ }
+
+ if( !empty( $pParamHash['comment_title'] ) ){
+ $pParamHash['title'] = $pParamHash['comment_title'];
+ }
+
+ if( !empty( $pParamHash['comment_data'] ) ){
+ $pParamHash['edit'] = $pParamHash['comment_data'];
+ }
+
+ if( empty( $pParamHash['edit'] ) ) {
+ $this->mErrors['store'] = tra( 'Your comment was empty.' );
+ } elseif( !$gBitUser->hasPermission( 'p_liberty_trusted_editor' ) && ($linkCount = preg_match_all( '/http\:\/\//', $pParamHash['edit'], $links )) > $gBitSystem->getConfig( 'liberty_unstrusted_max_http_in_content', 0 ) ) {
+ $this->mErrors['store'] = tra( 'Links are not allowed.' );
+ } else {
+ $dupeQuery = "SELECT `data` FROM `".BIT_DB_PREFIX."liberty_content` lc INNER JOIN `".BIT_DB_PREFIX."liberty_comments` lcom ON (lc.`content_id`=lcom.`content_id`) WHERE `user_id`=? AND `content_type_guid`='".BITCOMMENT_CONTENT_TYPE_GUID."' AND `ip`=? AND lcom.`root_id`=? ORDER BY `created` DESC";
+ if( $lastPostData = $this->mDb->getOne( $dupeQuery, array( $gBitUser->mUserId, $_SERVER['REMOTE_ADDR'], $pParamHash['root_id'] ) ) ) {
+ if( empty( $this->mCommentId ) && trim( $lastPostData ) == trim( $pParamHash['edit'] ) ) {
+ $this->mErrors['store'] = tra( 'Duplicate comment.' );
+ }
+ }
+ }
+
+ // verify attachments are allowed on comments
+ if( ( isset( $pParamHash['_files_override'] ) || !empty( $_FILES ) ) && !$gBitSystem->isFeatureActive( 'comments_allow_attachments' ) ) {
+ $this->mErrors['comment_attachments'] = tra( 'Files can not be uploaded with comments.' );
+ }
+
+ // if we have an error we get them all by checking parent classes for additional errors
+ if( count( $this->mErrors ) > 0 ){
+ parent::verify( $pParamHash );
+ }
+
+ return (count($this->mErrors) == 0);
+ }
+
+ function storeComment( &$pParamHash ) {
+ $this->StartTrans();
+ if( $this->verifyComment($pParamHash) && LibertyMime::store( $pParamHash ) ) {
+ if (!$this->mCommentId) {
+ $this->mCommentId = $this->mDb->GenID( 'liberty_comment_id_seq');
+
+ if (!empty($pParamHash['parent_id'])) {
+ $parentComment = new LibertyComment(NULL,$pParamHash['parent_id']);
+ }
+ $parent_sequence_forward = '';
+ $parent_sequence_reverse = '';
+ if (!empty($parentComment->mInfo['thread_forward_sequence'])) {
+ $parent_sequence_forward = $parentComment->mInfo['thread_forward_sequence'];
+ $parent_sequence_reverse = $parentComment->mInfo['thread_reverse_sequence'];
+ }
+ // if nesting level > 25 deep, put it on level 25
+ if (strlen($parent_sequence_forward) > 10*24) {
+ $parent_sequence_forward = substr($parent_sequence_forward,0,10*24);
+ }
+
+ $this->mInfo['thread_forward_sequence'] = $parent_sequence_forward . sprintf("%09d.",$this->mCommentId);
+ $this->mInfo['thread_reverse_sequence'] = strtr($parent_sequence_forward . sprintf("%09d.",$this->mCommentId),
+ '0123456789', '9876543210');
+
+ $sql = "INSERT INTO `".BIT_DB_PREFIX."liberty_comments` (`comment_id`, `content_id`, `parent_id`, `root_id`, `anon_name`, `thread_forward_sequence`, `thread_reverse_sequence`) VALUES (?,?,?,?,?,?,?)";
+
+ $this->mDb->query($sql, array($this->mCommentId, $pParamHash['content_id'], $pParamHash['parent_id'],
+ $pParamHash['root_id'], $pParamHash['anon_name'],
+ $this->mInfo['thread_forward_sequence'], $this->mInfo['thread_reverse_sequence']));
+ $this->mInfo['parent_id'] = $pParamHash['parent_id'];
+ $this->mInfo['content_id'] = $pParamHash['content_id'];
+ $this->mInfo['root_id'] = $pParamHash['root_id'];
+ $this->mContentId = $pParamHash['content_id'];
+ } else {
+ $sql = "UPDATE `".BIT_DB_PREFIX."liberty_comments` SET `parent_id` = ?, `content_id`= ? WHERE `comment_id` = ?";
+ $this->mDb->query($sql, array($pParamHash['parent_id'], $pParamHash['content_id'], $this->mCommentId));
+ $this->mInfo['parent_id'] = $pParamHash['parent_id'];
+ $this->mInfo['content_id'] = $pParamHash['content_id'];
+ $this->mContentId = $pParamHash['content_id'];
+ }
+ $this->invokeServices( 'comment_store_function', $pParamHash );
+
+ }
+
+ $this->CompleteTrans();
+ return (count($this->mErrors) == 0);
+ }
+
+
+ // This is a highly specialized method only used for emailing list synchronization. If you don't know anything about this, just move along and live in bliss
+ // (Hint: see mailing list integreation in boards)
+ function storeMessageId( $pMessageId ) {
+ if( $this->isValid() ) {
+ $this->mDb->query( "UPDATE `".BIT_DB_PREFIX."liberty_comments` SET `message_guid`=? WHERE `content_id`=?", array( $pMessageId, $this->mContentId ) );
+ }
+ }
+
+ // delete a single comment
+ function deleteComment() {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $this->StartTrans();
+
+ if( $gBitSystem->isPackageActive( 'boards' ) ) {
+ // due to foreign key constraints, this has to go in the base class of BitBoardPost
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."boards_posts` WHERE `comment_id` = ?";
+ $rs = $this->mDb->query($sql, array($this->mCommentId ) );
+// $query = "DELETE FROM `".BIT_DB_PREFIX."boards_topics` WHERE `parent_id` = ?";
+// $result = $this->mDb->query( $query, array( $this->getField( 'content_id' ) ) );
+ }
+
+
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."liberty_comments` WHERE `comment_id` = ?";
+ $rs = $this->mDb->query($sql, array($this->mCommentId));
+
+ /*
+ * TODO: figureout why this is even here. Mime should handle this and it needs to pass in mandatory attachmentId
+ * Slated to delete for now - wjames5
+ */
+ /*
+ if (method_exists($this,'expungeMetaData')) {
+ $this->expungeMetaData();
+ }
+ */
+
+ if( LibertyMime::expunge() ) {
+ $ret = TRUE;
+ $this->CompleteTrans();
+ } else {
+ $this->mDb->RollbackTrans();
+ }
+ }
+ return $ret;
+ }
+
+ //delete the comment and all of its children
+ //it should be possible to do this in a single query using the materialized path
+ //this is the code from the old function which needs to be revised
+ // 1) change name
+ // 2) use materialized path to cut query count and eliminate recursion
+
+ function expunge() {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $this->StartTrans();
+ $sql = "SELECT `comment_id` FROM `".BIT_DB_PREFIX."liberty_comments` WHERE `parent_id` = ?";
+ $rows = $this->mDb->getAll($sql, array($this->mContentId));
+
+ foreach ($rows as $row) {
+ $comment = new LibertyComment($row['comment_id']);
+ $comment->expunge();
+ }
+
+ if( $gBitSystem->isPackageActive( 'boards' ) ) {
+ // due to foreign key constraints, this has to go in the base class of BitBoardPost
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."boards_posts` WHERE `comment_id` = ?";
+ $rs = $this->mDb->query($sql, array($this->mCommentId ) );
+ $query = "DELETE FROM `".BIT_DB_PREFIX."boards_topics` WHERE `parent_id` = ?";
+ $result = $this->mDb->query( $query, array( $this->getField( 'content_id' ) ) );
+ }
+
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."liberty_comments` WHERE `comment_id` = ?";
+ $rs = $this->mDb->query($sql, array($this->mCommentId));
+
+ /*
+ * TODO: figureout why this is even here. Mime should handle this and it needs to pass in mandatory attachmentId
+ * Slated to delete for now - wjames5
+ */
+ /*
+ if (method_exists($this,'expungeMetaData')) {
+ $this->expungeMetaData();
+ }
+ */
+
+ if( LibertyMime::expunge() ) {
+ $ret = TRUE;
+ $this->CompleteTrans();
+ } else {
+ $this->mDb->RollbackTrans();
+ }
+ }
+ return $ret;
+ }
+
+ function userCanEdit() {
+ global $gBitUser, $gBitSystem;
+ $ret = FALSE;
+
+ // check the allowed edit time limit - we'll use it later
+ $withinEditTime = FALSE;
+ if ( $gBitSystem->getConfig( 'comments_edit_minutes', 60 ) * 60 + $this->getField( 'created' ) > time() ) {
+ $withinEditTime = TRUE;
+ }
+ if( $gBitUser->isRegistered() ) {
+ /* get the hash of the users perms rather than call hasUserPermission which
+ * always returns true for owner which interferes with trying to time limit editing
+ */
+ $checkPerms = $this->getUserPermissions();
+ $ret = ( !empty( $checkPerms['p_liberty_edit_comments'] ) ||
+ !empty( $checkPerms['p_liberty_admin_comments'] ) ||
+ $gBitUser->hasPermission( 'p_liberty_admin_comments' ) ||
+ ( $gBitUser->mUserId == $this->mInfo['user_id'] && $withinEditTime )
+ );
+ } elseif( $this->mInfo['user_id'] == ANONYMOUS_USER_ID ) {
+ $ret = (($_SERVER['REMOTE_ADDR']==$this->mInfo['ip']) && $withinEditTime );
+ }
+ return $ret;
+ }
+
+ function userCanUpdate( $pRootContent=NULL ) {
+ return( $this->userCanEdit() || ($pRootContent && ($pRootContent->hasUserPermission( 'p_liberty_edit_comments' ) || $pRootContent->hasUserPermission( 'p_liberty_admin_comments' ))) );
+ }
+
+ /**
+ * @param pLinkText name of
+ * @param pParamHash different possibilities depending on derived class
+ * @return the link to display the page.
+ */
+ public static function getDisplayUrlFromHash( &$pParamHash ) {
+ $ret = NULL;
+ if( @BitBase::verifyId( $pParamHash['root_id'] ) && $viewContent = LibertyBase::getLibertyObject( $pParamHash['root_id'] ) ) {
+ // pass in cooment hash to the url func incase the root package needs to do something fancy
+ $viewContent->mInfo['comment'] = $pParamHash;
+ $ret = $viewContent->getDisplayUrl().( @static::verifyId( $pParamHash['content_id'] ) ? "#comment_".$pParamHash['content_id'] : '' );
+ } elseif( @BitBase::verifyId( $pParamHash['content_id'] ) ) {
+ $ret = parent::getDisplayUrlFromHash( $pParamHash );
+ $ret .= "#comment_{$pParamHash['content_id']}";
+ }
+
+ return( $ret );
+ }
+
+ //generate a URL to directly access and display a single comment and the associated root content
+ public static function getDirectUrlFromHash( $pParamHash=NULL ) {
+ if( empty( $pParamHash ) ) {
+ $pParamHash = &$this->mInfo;
+ }
+ $ret = NULL;
+ if( !empty( $pParamHash['root_id'] ) && $viewContent = LibertyBase::getLibertyObject( $pParamHash['root_id'] ) ) {
+ $ret = $viewContent->getDisplayUrl();
+ if ( strstr($ret, '?') ) {
+ $ret .= "&";
+ }
+ else {
+ $ret .= "?";
+ }
+ $ret .= "view_comment_id=" . $pParamHash['content_id'] . "#comment_".$pParamHash['content_id'];
+ }
+ return ( $ret );
+ }
+
+ public static function getDisplayLinkFromHash( &$pParamHash, $pLinkText=NULL, $pAnchor=NULL ) {
+ $anchor = '';
+ // Override default title with something comment centric
+ if( empty( $pLinkText ) ) {
+ $pLinkText = tra( 'Comment' );
+ }
+ if( @BitBase::verifyId( $pParamHash['content_id'] )) {
+ $anchor = "&view_comment_id=".$pParamHash['content_id']."#comment_{$pParamHash['content_id']}";
+ }
+ return parent::getDisplayLinkFromHash( $pParamHash, $pLinkText, $anchor );
+ }
+
+ function getList( &$pParamHash ) {
+ global $gBitSystem, $gLibertySystem;
+ if ( !isset( $pParamHash['sort_mode']) or $pParamHash['sort_mode'] == '' ){
+ $pParamHash['sort_mode'] = 'created_desc';
+ }
+ if( empty( $pParamHash['max_records'] ) ) {
+ $pParamHash['max_records'] = $gBitSystem->getConfig( 'max_records' );
+ }
+ LibertyContent::prepGetList( $pParamHash );
+ $sort_mode = $this->mDb->convertSortmode($pParamHash['sort_mode']);
+
+ $joinSql = $whereSql = $selectSql = '';
+ $bindVars = $ret = array();
+
+ $pParamHash['include_comments'] = TRUE;
+ $this->getServicesSql( 'content_list_sql_function', $selectSql, $joinSql, $whereSql, $bindVars, NULL, $pParamHash );
+
+ if ( !empty( $pParamHash['root_content_type_guid'] ) ) {
+ if( is_string( $pParamHash['root_content_type_guid'] ) ) {
+ $pParamHash['root_content_type_guid'] = array( $pParamHash['root_content_type_guid'] );
+ } elseif( is_array( $pParamHash['root_content_type_guid'] ) ) {
+ $contentTypes = array_keys( $gLibertySystem->mContentTypes );
+ $max = count( $pParamHash['root_content_type_guid'] );
+ $guidSql = '';
+ for( $i = 0; $i < $max; $i++ ) {
+ if( in_array( $pParamHash['root_content_type_guid'][$i], $contentTypes ) ) {
+ if( strlen( $guidSql ) ) {
+ $guidSql .= ' OR ';
+ }
+ $guidSql .= " rlc.`content_type_guid`=? ";
+ $bindVars[] = $pParamHash['root_content_type_guid'][$i];
+ }
+ }
+ $whereSql .= " AND ( $guidSql )";
+ }
+ }
+
+ if ( !empty( $pParamHash['content_type_guid'] ) ) {
+ $whereSql .= " AND rlc.`content_type_guid`=? ";
+ $bindVars[] = $pParamHash['content_type_guid'];
+ }
+
+ if ( !empty( $pParamHash['user_id'] ) ) {
+ $whereSql .= " AND ptc.`user_id`=? ";
+ $bindVars[] = $pParamHash['user_id'];
+ }
+
+ if ( !empty( $pParamHash['created_ge'] ) ) {
+ $whereSql .= " AND lc.`created`>=? ";
+ $bindVars[] = $pParamHash['created_ge'];
+ }
+
+ // left outer join on root so updater works
+
+ $query = "SELECT
+ lcom.`comment_id`,
+ lc.`content_id`,
+ lcom.`parent_id`,
+ lcom.`anon_name`,
+ lcom.`root_id`,
+ lc.`title` AS `content_title`,
+ rlc.`title` AS `root_content_title`,
+ lc.`created`,
+ lc.`data`,
+ lc.`last_modified` as `last_modified`,
+ lc.`title` as `title`,
+ ptc.`content_type_guid` as `parent_content_type_guid`,
+ rlc.`content_type_guid` as `root_content_type_guid`,
+ lc.`content_type_guid`,
+ uu.`login` AS `creator_user`,
+ uu.`login`,
+ uu.`real_name`,
+ uu.`user_id`
+ $selectSql
+ FROM `".BIT_DB_PREFIX."liberty_comments` lcom
+ INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON( lcom.`content_id`=lc.`content_id` )
+ INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON( uu.`user_id`=lc.`user_id`)
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` rlc ON( rlc.`content_id`=lcom.`root_id` )
+ $joinSql, `".BIT_DB_PREFIX."liberty_content` ptc
+ WHERE lcom.`parent_id`=ptc.`content_id` $whereSql
+ ORDER BY $sort_mode";
+ if( $result = $this->mDb->query( $query, $bindVars, $pParamHash['max_records'], $pParamHash['offset'] )) {
+ while( $row = $result->FetchRow() ) {
+ $row['display_link'] = $this->getDisplayLink( $row['content_title'], $row );
+ $row['display_url'] = static::getDisplayUrlFromHash( $row );
+ $row['direct_url'] = static::getDirectUrlFromHash( $row );
+ if (!empty($pParamHash['parse'])) {
+ $row['parsed_data'] = self::parseDataHash( $row );
+ }
+ $ret[] = $row;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Fill title with date if available
+ *
+ * This will normally be overwriten by extended classes to provide
+ * an appropriate title title string
+ * @param array mInfo type hash of data to be used to provide base data
+ * @return string Descriptive title for the object
+ */
+ public static function getTitleFromHash( &$pHash, $pDefault=TRUE ) {
+ global $gBitSmarty;
+ $ret = NULL;
+ if( !empty( $pHash['title'] ) ) {
+ $ret = $pHash['title'];
+ } elseif( !empty( $pHash['created'] ) ) {
+ $gBitSmarty->loadPlugin( 'smarty_modifier_bit_short_date' );
+ $ret = smarty_modifier_bit_short_date( $pHash['created'] );
+ } elseif( !empty( $pHash['content_name'] ) ) {
+ $ret = $pHash['content_name'];
+ }
+ return $ret;
+ }
+
+
+ function getNumComments($pContentId = NULL) {
+ $bindVars = NULL;
+ if (!$pContentId && $this->mContentId) {
+ $mid = '=?';
+ $bindVars = array($this->mContentId);
+ } elseif (is_array($pContentId)) {
+ $mid = 'in ('.implode(',', array_fill(0, count( $pContentId ), '?')).')';
+ $bindVars = $pContentId;
+ } elseif ($pContentId) {
+ $mid = '=?';
+ $bindVars = array($pContentId);
+ }
+ $commentCount = 0;
+
+ $joinSql = $selectSql = $whereSql = '';
+
+ /* brute force call to liberty_content_list_sql
+ * for status enforcement
+ *
+ * here we call liberty_content_list_sql which has a
+ * restriction to enforce content_status_id. we could
+ * have called the full list_sql service, but that
+ * would be overkill for just getting a count.
+ */
+ if ( !is_array($pContentId) ){
+ $sqlHash = liberty_content_list_sql( $this, NULL );
+ if( !empty( $sqlHash['select_sql'] ) ) {
+ $selectSql .= $sqlHash['select_sql'];
+ }
+ if( !empty( $sqlHash['join_sql'] ) ) {
+ $joinSql .= $sqlHash['join_sql'];
+ }
+ if( !empty( $sqlHash['where_sql'] ) ) {
+ $whereSql .= $sqlHash['where_sql'];
+ }
+ if( !empty( $sqlHash['bind_vars'] ) ) {
+ if ( is_array( $bindVars ) ) {
+ $bindVars = array_merge( $bindVars, $sqlHash['bind_vars'] );
+ } else {
+ $bindVars = $sqlHash['bind_vars'];
+ }
+ }
+ }
+
+ if ($bindVars) {
+ $sql = "SELECT count(*) as comment_count $selectSql
+ FROM `".BIT_DB_PREFIX."liberty_comments` lcom
+ INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (lcom.`content_id` = lc.`content_id`) $joinSql
+ WHERE lcom.`root_id` $mid $whereSql";
+ $commentCount = $this->mDb->getOne($sql, $bindVars);
+ }
+ return $commentCount;
+ }
+
+
+ // used for direct access to view a single comment
+ // see usage in: liberty/comments_inc.php
+ // there ought to be a better way to do this...
+ function getNumComments_upto($pCommentId = NULL, $pContentId = NULL) {
+
+ $comment = new LibertyComment($pCommentId, $pContentId);
+
+ #assume flat mode
+ $comment_fields = $comment->mInfo;
+ $created = $comment_fields['created'];
+ $contentId = $comment_fields['root_id'];
+
+ $commentCount = 0;
+ if ($contentId) {
+ $sql = "SELECT count(*)
+ FROM `".BIT_DB_PREFIX."liberty_comments` tc LEFT OUTER JOIN
+ `".BIT_DB_PREFIX."liberty_content` tcn
+ ON (tc.`content_id` = tcn.`content_id`)
+ where tc.`root_id` =? and `created` < ?";
+ $commentCount = $this->mDb->getOne($sql, array($contentId, $created));
+ }
+ return $commentCount;
+ }
+
+ // Returns a hash containing the comment tree of comments related to this content
+ function getComments( $pContentId = NULL, $pMaxComments = NULL, $pOffset = NULL, $pSortOrder = NULL, $pDisplayMode = NULL ) {
+ if( $pDisplayMode != "flat" ) {
+ if ($pSortOrder == "commentDate_asc") {
+ $pSortOrder = 'thread_asc';
+ } else {
+ $pSortOrder = 'thread_desc';
+ }
+ }
+
+ $contentId = NULL;
+ $ret = array();
+ if (!$pContentId && $this->mContentId) {
+ $contentId = $this->mContentId;
+ } elseif ($pContentId) {
+ $contentId = $pContentId;
+ }
+
+ $mid = "";
+
+ $sort_order = "ASC";
+ $mid = 'created ASC';
+ if (!empty($pSortOrder)) {
+ if ($pSortOrder == 'commentDate_desc') {
+ $mid = 'created DESC';
+ } else if ($pSortOrder == 'commentDate_asc') {
+ $mid = 'created ASC';
+ } elseif ($pSortOrder == 'thread_asc') {
+ $mid = 'thread_forward_sequence ASC';
+ // thread newest first is harder...
+ } elseif ($pSortOrder == 'thread_desc') {
+ $mid = 'thread_reverse_sequence ASC';
+ } else {
+ $mid = $this->mDb->convertSortmode( $pSortOrder );
+ }
+ }
+ $mid = 'order by ' . $mid;
+
+ $bindVars = array();
+ if (is_array( $pContentId ) ) {
+ $mid2 = 'in ('.implode(',', array_fill(0, count( $pContentId ), '?')).')';
+ $bindVars = $pContentId;
+ $select1 = ', lcp.`content_type_guid` as parent_content_type_guid, lcp.`title` as parent_title ';
+ $join1 = " LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` lcp ON (lcp.`content_id` = lcom.`parent_id`) ";
+ } elseif ($pContentId) {
+ $mid2 = '=?';
+ $bindVars = array( $pContentId );
+ $select1 = '';
+ $join1 = '';
+ }
+
+ $joinSql = $selectSql = $whereSql = '';
+ $pListHash = array( 'content_id' => $contentId, 'max_records' => $pMaxComments, 'offset'=>$pOffset, 'sort_mode'=> $pSortOrder, 'display_mode' => $pDisplayMode, 'has_comment_view_perm' => TRUE );
+ $this->getServicesSql( 'content_list_sql_function', $selectSql, $joinSql, $whereSql, $bindVars, $this, $pListHash );
+
+ if ($pContentId) {
+ $sql = "SELECT lcom.`comment_id`, lcom.`parent_id`, lcom.`root_id`, lcom.`thread_forward_sequence`, lcom.`thread_reverse_sequence`, lcom.`anon_name`, lc.*, uu.`email`, uu.`real_name`, uu.`login` $selectSql $select1
+ FROM `".BIT_DB_PREFIX."liberty_comments` lcom
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (lcom.`content_id` = lc.`content_id`)
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."users_users` uu ON (lc.`user_id` = uu.`user_id`) $joinSql $join1
+ WHERE lcom.root_id $mid2 $whereSql $mid";
+ $flat_comments = array();
+ if( $result = $this->mDb->query( $sql, $bindVars, $pMaxComments, $pOffset ) ) {
+ while( $row = $result->FetchRow() ) {
+ $row['parsed_data'] = self::parseDataHash( $row );
+ $row['level'] = substr_count ( $row['thread_forward_sequence'], '.' ) - 1;
+ $c = new LibertyComment();
+ $c->mInfo=$row;
+ $c->mRootObj = $this->getRootObj();
+ $row['is_editable'] = $c->userCanUpdate( $c->mRootObj );
+
+ global $gBitSystem;
+ if( $gBitSystem->isFeatureActive( 'comments_allow_attachments' ) ){
+ // get attachments for each comment
+ global $gLibertySystem;
+ $query = "SELECT * FROM `".BIT_DB_PREFIX."liberty_attachments` la WHERE la.`content_id`=? ORDER BY la.`pos` ASC, la.`attachment_id` ASC";
+ if( $result2 = $this->mDb->query( $query,array( (int)$row['content_id'] ))) {
+ while( $row2 = $result2->fetchRow() ) {
+ if( $func = $gLibertySystem->getPluginFunction( $row2['attachment_plugin_guid'], 'load_function', 'mime' )) {
+ // we will pass the preferences by reference that the plugin can easily update them
+ if( empty( $row['storage'][$row2['attachment_id']] )) {
+ $row['storage'][$row2['attachment_id']] = array();
+ }
+ $row['storage'][$row2['attachment_id']] = $func( $row2, $row['storage'][$row2['attachment_id']] );
+ } else {
+ print "No load_function for ".$row2['attachment_plugin_guid'];
+ }
+ }
+ }
+ // end get attachements for each comment
+ }
+
+ $flat_comments[$row['content_id']] = $row;
+ }
+ }
+
+ # now select comments wanted
+ $ret = $flat_comments;
+
+ }
+ return $ret;
+ }
+
+ function getQuoted() {
+ $data = $this->mInfo['data'];
+ $pattern = '/\{quote .*\}(.*)\{\/quote\}/i';
+ $replacement = '';
+ $data = preg_replace($pattern, $replacement, $data);
+ return '{quote format_guid="'.$this->mInfo['format_guid'].'" comment_id="'.$this->mCommentId.'" user="'.$this->mInfo['login'].'"}'.trim($data).'{/quote}';
+ }
+
+ // Basic formatting for quoting comments
+ function quoteComment($commentData) {
+ $ret = '> '.$commentData;
+ $ret = eregi_replace("\n", "\n>", $ret);
+ return $ret;
+ }
+
+ function getRootObj(){
+ if ( !is_object( $this->mRootObj ) && !empty( $this->mInfo['root_id'] ) ){
+ if ( $obj = LibertyBase::getLibertyObject( $this->mInfo['root_id'] ) ) {
+ $this->mRootObj = $obj;
+ }
+ }
+ return $this->mRootObj;
+ }
+}
+
+?>
diff --git a/includes/classes/LibertyContent.php b/includes/classes/LibertyContent.php
new file mode 100644
index 0000000..4da4f1b
--- /dev/null
+++ b/includes/classes/LibertyContent.php
@@ -0,0 +1,3844 @@
+<?php
+/**
+/* Management of Liberty content
+*
+* @package liberty
+* @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 ) );
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_CLASS_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.
+ *
+ * @package liberty
+ */
+class LibertyContent extends LibertyBase implements BitCacheable {
+ /**
+ * Content Id if an object has been loaded
+ * @public
+ */
+ public $mContentId;
+
+ /**
+ * If this content is being viewed within a structure
+ * @public
+ */
+ public $mStructureId;
+
+ /**
+ * Content type GUID for this LibertyContent object
+ * @public
+ */
+ public $mContentTypeGuid;
+
+ /**
+ * Content type hash for this LibertyContent object
+ * @public
+ */
+ public $mType;
+
+ /**
+ *Permissions hash specific to the user accessing this LibertyContent object
+ * @public
+ */
+ public $mUserContentPerms;
+
+ /**
+ * Preferences hash specific to this LibertyContent object - accessed via getPreference/storePreference
+ * @private
+ */
+ public $mPrefs = NULL;
+
+ /**
+ * Control permission specific to this LibertyContent type
+ * @private
+ */
+ public $mViewContentPerm;
+ public $mUpdateContentPerm;
+ public $mCreateContentPerm;
+ public $mExpungeContentPerm;
+ public $mAdminContentPerm;
+
+ /**
+ * Construct an empty LibertyBase object with a blank permissions array
+ */
+ function __construct() {
+ parent::__construct();
+ $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()
+ if( empty( $this->mUpdateContentPerm )) {
+ $this->mUpdateContentPerm = 'p_admin_content';
+ }
+
+ if( empty( $this->mCreateContentPerm )) {
+ $this->mCreateContentPerm = 'p_admin_content';
+ }
+
+ if( empty( $this->mExpungeContentPerm )) {
+ $this->mExpungeContentPerm = 'p_admin_content';
+ }
+
+ if( empty( $this->mAdminContentPerm )) {
+ $this->mAdminContentPerm = 'p_admin_content';
+ }
+ }
+
+ public static function isCacheableClass() {
+ global $gBitSystem;
+ // new feature, cache by default in development systems only
+ return !$gBitSystem->isLive();
+ }
+
+
+ public function isCacheableObject() {
+ return parent::isCacheableObject() && !empty( $this->mContentId );
+ }
+
+ public function getCacheKey() {
+ if( $this->isValid() ) {
+ return $this->mContentId;
+ }
+ }
+
+ public function __sleep() {
+ return array_merge( parent::__sleep(), array( 'mContentId', 'mInfo', 'mStructureId', 'mContentTypeGuid', 'mType', 'mUserContentPerms', 'mPrefs', 'mViewContentPerm', 'mUpdateContentPerm', 'mCreateContentPerm', 'mExpungeContentPerm', 'mAdminContentPerm') );
+ }
+
+ /**
+ * load Assume a derived class has joined on the liberty_content table, and loaded it's columns already.
+ *
+ * @access public
+ * @return void
+ */
+ public function load() {
+ if( !empty( $this->mInfo['content_type_guid'] )) {
+ global $gLibertySystem, $gBitSystem, $gBitUser;
+ $this->loadPreferences();
+ $this->mInfo['content_type'] = $gLibertySystem->mContentTypes[$this->mInfo['content_type_guid']];
+ $this->invokeServices( 'content_load_function', $this );
+ }
+ }
+
+ /**
+ * 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
+ * table record
+ *
+ * @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]
+ * @access public
+ * @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'] ) ) {
+ $pParamHash['content_type_guid'] = $this->mContentTypeGuid;
+ }
+
+ if( empty( $pParamHash['user_id'] ) ) {
+ $pParamHash['user_id'] = $gBitUser->getUserId();
+ }
+
+ if( $this->verifyIdParameter( $pParamHash, 'content_id' ) && $pParamHash['content_id'] != $this->mContentId ) {
+ // we have request for a content, but is not the same as this object, something stinky going on.
+ // Unset the pParamHash['content_id'] and let mContentId be used going forward
+ unset( $pParamHash['content_id'] );
+ }
+
+ if( !$this->verifyIdParameter( $pParamHash, 'content_id' ) ) {
+ if( !$this->verifyId( $this->mContentId ) ) {
+ // 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'] ) ){
+ $pParamHash['content_store']['content_status_id'] = $gBitSystem->getConfig('liberty_default_status', BIT_CONTENT_DEFAULT_STATUS);
+ }
+ } else {
+ $pParamHash['content_id'] = $this->mContentId;
+ }
+ }
+
+ if( BitBase::verifyIdParameter( $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') ) {
+ $allStatus = $this->getAvailableContentStatuses();
+ if (empty($allStatus[$pParamHash['content_status_id']])) {
+ $this->mErrors['content_status_id'] = "No such status ID or permission denied.";
+ } else {
+ $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'] ) ) {
+ $pParamHash['content_store']['title'] = substr( preg_replace( '/:space:+/m', ' ', trim( $pParamHash['title'] ) ), 0, BIT_CONTENT_MAX_TITLE_LEN );
+ } 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' );
+ } else {
+ $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';
+ } else {
+ $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( $error ) {
+ $this->mErrors['format'] = $error;
+ }
+ }
+
+ if( !empty( $pParamHash['content_store']['data'] )) {
+ $this->filterData( $pParamHash['content_store']['data'], $pParamHash['content_store'], 'prestore' );
+ } else {
+ // someone has deleted the data entirely - common for fisheye
+ $pParamHash['content_store']['data'] = NULL;
+ }
+ $pParamHash['content_store']['format_guid'] = $pParamHash['format_guid'];
+
+ if( !@BitBase::verifyId( $this->mInfo['version'] ) ) {
+ $pParamHash['content_store']['version'] = 1;
+ } else {
+ $pParamHash['content_store']['version'] = $this->mInfo['version'] + 1;
+ }
+
+ // search related stuff
+ 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"];
+ }
+
+ // content preferences
+ $prefs = array();
+ 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];
+ } else {
+ $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
+ $this->invokeServices( 'content_verify_function', $pParamHash );
+
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * 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 ) {
+ global $gLibertySystem;
+ if( LibertyContent::verify( $pParamHash ) ) {
+ $this->clearFromCache();
+ $this->StartTrans();
+ $table = BIT_DB_PREFIX."liberty_content";
+ if( !$this->verifyIdParameter( $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' );
+ $this->mContentTypeGuid = $this->mInfo['content_type_guid'] = $pParamHash['content_type_guid'];
+ $result = $this->mDb->associateInsert( $table, $pParamHash['content_store'] );
+ $this->mLogs['content_store'] = "Created";
+ } else {
+ 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'] ) ) {
+ $this->storeHistory();
+ }
+ //$action = "Created";
+ //$mailEvents = 'wiki_page_changes';
+ }
+
+ $this->storeAliases( $pParamHash );
+
+ $this->invokeServices( 'content_store_function', $pParamHash );
+
+ // 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' );
+ }
+ LibertyContent::expungeCacheFile( $pParamHash['content_id'] );
+
+ // store data
+ foreach( $pParamHash['data_store'] AS $dataType => $data ) {
+ $this->storeData( $data, $dataType );
+ }
+
+ // store content preferences
+ if( @is_array( $pParamHash['preferences_store'] ) ) {
+ foreach( $pParamHash['preferences_store'] as $pref => $value ) {
+ $this->storePreference( $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->storeActionLog( $pParamHash );
+
+
+ $this->CompleteTrans();
+ }
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * Delete comment entries relating to the content object
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function expungeComments() {
+ require_once( LIBERTY_PKG_CLASS_PATH.'LibertyComment.php' );
+ // 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) {
+ $tmpComment = new LibertyComment($commentId);
+ $tmpComment->expunge();
+ }
+ }
+ return parent::expunge();
+ }
+
+ /**
+ * Delete content object and all related records
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function expunge() {
+ global $gBitSystem, $gLibertySystem;
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $this->StartTrans();
+ $this->expungeComments();
+
+ // services, filters and cache
+ $this->invokeServices( 'content_expunge_function', $this );
+ if( $this->getField( 'format_guid' ) && $func = $gLibertySystem->getPluginFunction( $this->getField( 'format_guid' ), 'expunge_function' ) ) {
+ $func( $this->mContentId );
+ }
+ $this->filterData( $this->mInfo['data'], $this->mInfo, 'expunge' );
+ LibertyContent::expungeCacheFile( $this->mContentId );
+
+ // 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
+ $this->expungeVersion();
+
+ // 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 ) );
+
+ // Remove aliases
+ $this->mDb->query( "DELETE FROM `".BIT_DB_PREFIX."liberty_aliases` WHERE `content_id`=?", array( $this->mContentId ) );
+
+ // Remove structures
+ // 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 ) );
+
+ // Remove data
+ $query = "DELETE FROM `".BIT_DB_PREFIX."liberty_content_data` WHERE `content_id` = ?";
+ $result = $this->mDb->query( $query, array( $this->mContentId ) );
+
+ // Remove hits
+ $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 ) );
+
+ // Remove content links
+ $query = "DELETE FROM `".BIT_DB_PREFIX."liberty_content_links` WHERE `to_content_id` = ? or `from_content_id` = ?";
+ $result = $this->mDb->query( $query, array( $this->mContentId, $this->mContentId ) );
+
+ // Remove content
+ $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->storeActionLog();
+
+ $this->CompleteTrans();
+ $ret = TRUE;
+
+ parent::expunge();
+ }
+ return $ret;
+ }
+
+ /**
+ * storeAliases will store aliases to a given content item
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function storeAliases( $pParamHash ) {
+ $ret = FALSE;
+ if( $this->isValid() && isset( $pParamHash['alias_string']) ) {
+ $this->mDb->query( "DELETE FROM `".BIT_DB_PREFIX."liberty_aliases` WHERE `content_id`=?", array( $this->mContentId ) );
+ $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 ) ) );
+ }
+ }
+ $ret = TRUE;
+ }
+ return $ret;
+ }
+
+ /**
+ * storeHistory will store the previous data into the history table for reference
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function storeHistory() {
+ global $gBitSystem;
+
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $storeHash = array(
+ "content_id" => $this->mContentId,
+ "version" => $this->getField( "version" ),
+ "last_modified" => $this->getField( "last_modified" ),
+ "user_id" => $this->getField( "modifier_user_id" ),
+ "ip" => $this->getField( "ip" ),
+ "data" => $this->getField( "data" ),
+ "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 );
+ $ret = TRUE;
+ }
+ return( $ret );
+ }
+
+ /**
+ * Get count of the number of historic records for the page
+ *
+ * @access public
+ * @return count
+ */
+ function getHistoryCount() {
+ $ret = NULL;
+ if( $this->isValid() ) {
+ $query = "
+ SELECT COUNT(*) AS `hcount`
+ FROM `".BIT_DB_PREFIX."liberty_content_history`
+ WHERE `content_id` = ?";
+ $rs = $this->mDb->query($query, array($this->mContentId));
+ $ret = $rs->fields['hcount'];
+ }
+ return $ret;
+ }
+
+ /**
+ * Get complete set of historical data in order to display a given wiki page version
+ *
+ * @param array $pVersion
+ * @param array $pUserId
+ * @param int $pOffset
+ * @param array $max_records
+ * @access public
+ * @return array of mInfo data
+ */
+ function getHistory( $pVersion=NULL, $pUserId=NULL, $pOffset = 0, $max_records = -1 ) {
+ $ret = NULL;
+ $cant = 0;
+ if( $this->isValid() ) {
+ global $gBitSystem;
+
+ $selectSql = '';
+ $joinSql = '';
+ $whereSql = '';
+ $bindVars = array();
+ $this->getServicesSql( 'content_list_history_sql_function', $selectSql, $joinSql, $whereSql, $bindVars );
+
+ $versionSql = '';
+ if( @BitBase::verifyId( $pUserId ) ) {
+ $bindVars[] = $pUserId;
+ $whereSql .= ' th.`user_id`=? ';
+ } else {
+ $bindVars[] = $this->mContentId;
+ $whereSql .= ' th.`content_id`=? ';
+ }
+ if( BitBase::verifyId( $pVersion ) ) {
+ array_push( $bindVars, $pVersion );
+ $versionSql = ' AND th.`version`=? ';
+ }
+
+ $query = "SELECT COUNT(*) AS `hcount`
+ FROM `".BIT_DB_PREFIX."liberty_content_history`
+ WHERE `content_id` = ?";
+ $rs = $this->mDb->query($query, array($this->mContentId));
+ $cant = $rs->fields['hcount'];
+
+ # Check for offset out of range
+ if ( $pOffset < 0 ) {
+ $pOffset = 0;
+ } 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
+ $selectSql
+ 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`)
+ $joinSql
+ WHERE $whereSql $versionSql order by th.`version` desc";
+
+ $result = $this->mDb->query( $query, $bindVars, $max_records, $pOffset );
+ $data = array();
+ while( !$result->EOF ) {
+ $aux = $result->fields;
+ $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'] );
+ $data[] = $aux;
+ //array_push( $ret, $aux );
+ $result->MoveNext();
+ }
+ }
+ // 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 = array();
+ $pListHash["data"] = $data;
+ $pListHash["cant"] = $cant;
+ $pListHash["max_records"] = $max_records;
+ $pListHash["offset"] = $pOffset;
+ $pListHash["find"] = NULL;
+ $pListHash["sort_mode"] = NULL;
+ LibertyContent::postGetList( $pListHash );
+ return $pListHash;
+ }
+
+ /**
+ * 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
+ * @access public
+ * @return void
+ */
+ function removeLastVersion( $pComment = '' ) {
+ if( $this->isValid() ) {
+ global $gBitSystem;
+ $this->expungeCacheFile($this->mContentId);
+ $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()) {
+ // We have a version
+ $res = $result->fetchRow();
+ $this->rollbackVersion( $res["version"] );
+ $this->expungeVersion( $res["version"] );
+ } else {
+ $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
+ */
+ function rollbackVersion( $pVersion, $pComment = '' ) {
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ global $gBitUser,$gBitSystem;
+ $this->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();
+ if (!empty($pComment)) {
+ $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
+ // maliciously
+ $res['force_history'] = 1;
+ // JHT 2005-10-16_22:21:10
+ // title must be set or store fails
+ // we use current page name
+ $res['title'] = $this->getTitle();
+ if( $this->store( $res ) ) {
+ $ret = TRUE;
+ }
+ $this->CompleteTrans();
+ } else {
+ $this->mDb->RollbackTrans();
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function expungeVersion( $pVersion=NULL, $pComment = '' ) {
+ global $gBitUser;
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $this->StartTrans();
+ $bindVars = array( $this->mContentId );
+ $versionSql = '';
+ if( $pVersion ) {
+ $versionSql = " and `version`=? ";
+ array_push( $bindVars, $pVersion );
+ }
+ $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 );
+ if( $hasRows ) {
+ global $gBitSystem;
+ $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));
+ $ret = TRUE;
+ }
+ $this->CompleteTrans();
+ }
+ return $ret;
+ }
+
+ function exportList( $pList ) {
+ foreach( $pList as $keyId=>$hash ) {
+ $content = static::getLibertyObject( $keyId );
+ $ret[$keyId] = $content->exportHash();
+ }
+ return $ret;
+ }
+
+ /**
+ * Create an export hash from the data
+ *
+ * @access public
+ * @return export data
+ */
+ function exportHash() {
+ $ret = array();
+ if( $this->isValid() ) {
+ $ret = array(
+ 'content_type_guid' => $this->getContentType(),
+ 'content_type' => $this->getContentTypeName(),
+ 'content_id' => $this->mContentId,
+ 'title' => $this->getTitle(),
+ 'display_uri' => $this->getDisplayUri(),
+ 'display_url' => $this->getDisplayUrl(),
+ 'date_created' => date( DateTime::W3C, $this->getField('created') ),
+ 'date_last_modified' => date( DateTime::W3C, $this->getField('last_modified') ),
+ );
+ $keys = $this->invokeServices( 'content_export_keys_function', $pList );
+ foreach( $keys as $field ) {
+ if( $value = $this->getField( $field ) ) {
+ $ret[$field] = $value;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Check mContentId to establish if the object has been loaded with a valid record
+ */
+ function isValid() {
+ return( BitBase::verifyId( $this->mContentId ) );
+ }
+
+ /**
+ * Check permissions to establish if user has permission to view the object
+ * Should be provided by the decendent package
+ */
+ function isViewable($pContentId = NULL) {
+ return( true );
+ }
+
+ /**
+ * Check permissions to establish if user has permission to edit the object
+ * Should be provided by the decendent package
+ */
+ function isEditable() {
+ return( false );
+ }
+
+ /**
+ * 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
+ */
+ function isAdminable($pContentId = NULL) {
+ return( false );
+ }
+
+ /**
+ * 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 ) {
+ global $gBitUser;
+ if( @BitBase::verifyId( $pParamHash['user_id'] ) ) {
+ $user_id = $pParamHash['user_id'];
+ } elseif( $this->isValid() && @$this->verifyId( $this->mInfo['user_id'] ) ) {
+ $user_id = $this->mInfo['user_id'];
+ }
+ return( @BitBase::verifyId( $user_id ) && $user_id != ANONYMOUS_USER_ID && $user_id == $gBitUser->mUserId );
+ }
+
+ /**
+ * Check if content matches content type GUID - must also be a valid content object, it will not work for generic content class
+ */
+ function isContentType( $pContentGuid ) {
+ global $gBitUser;
+ 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
+ */
+ function verifyAccessControl() {
+ if( $this->isValid() ) {
+ $this->invokeServices( 'content_verify_access' );
+ }
+ }
+
+ /**
+ * Set up access to services used by the object
+ */
+ function invokeServices( $pServiceFunction, &$pFunctionParam=NULL ) {
+ global $gLibertySystem;
+ $errors = array();
+ // Invoke any services store functions such as categorization or access control
+ if( $serviceFunctions = $gLibertySystem->getServiceValues( $pServiceFunction ) ) {
+ foreach ( $serviceFunctions as $func ) {
+ if( function_exists( $func ) ) {
+ if( $errors = $func( $this, $pFunctionParam ) ) {
+ $this->mErrors = array_merge( $this->mErrors, $errors );
+ }
+ }
+ }
+ }
+ return $errors;
+ }
+
+ /**
+ * check if a service is active for this content type
+ * requires package LCConfig
+ * provisional method until LCConfig package is integrated into the core
+ */
+ function hasService( $pServiceGuid ){
+ global $gBitSystem;
+ $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' ){
+ $ret = FALSE;
+ }
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * check if a service is required for this content type
+ * requires package LCConfig
+ * provisional method until LCConfig package is integrated into the core
+ */
+ function isServiceRequired( $pServiceGuid ){
+ global $gBitSystem;
+ $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' );
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * 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 );
+ }
+ }
+
+ /**
+ * getServicesSql2
+ *
+ * @param array $pServiceFunction
+ * @param array $pQueryHash
+ * @param array $pObject
+ * @param array $pParamHash
+ * @access public
+ * @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 ) {
+ global $gLibertySystem;
+ 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( function_exists( $func ) ) {
+ if( !empty( $pObject ) && is_object( $pObject )) {
+ $queryHash = $func( $pObject, $pParamHash );
+ } else {
+ $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
+ $pQueryHash = array_merge_recursive( $pQueryHash, $queryHash );
+ } else {
+ // 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'] );
+ } else {
+ $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.
+ */
+ function convertQueryHash( &$pQueryHash, $pCountQuery = FALSE ) {
+ global $gBitSystem;
+
+ // 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
+ if( $part != 'join' ) {
+ $pQueryHash['query'] .= strtoupper( " $part " );
+ if( $pCountQuery ) {
+ $pQueryHash['query_count'] .= strtoupper( " $part " );
+ }
+ }
+
+ // Add the count for the count query
+ if( $pCountQuery && $part == 'select' ) {
+ $pQueryHash['query_count'] .= 'COUNT( ';
+ }
+
+ $first = TRUE;
+ foreach( $pQueryHash[$part]['sql'] as $sql ) {
+ if( !$first ) {
+ // WHERE clauses have an implicit AND over all terms
+ if( $part == 'where' ) {
+ $pQueryHash['query'] .= " AND ";
+ if( $pCountQuery ) {
+ $pQueryHash['query_count'] .= " AND ";
+ }
+ } elseif( $part == 'select' || $part == 'from' ) {
+ $pQueryHash['query'] .= ", ";
+ if( $pCountQuery ) {
+ $pQueryHash['query_count'] .= ", ";
+ }
+ }
+ } else {
+ $first = FALSE;
+ }
+
+ $pQueryHash['query'] .= $sql;
+ if( $pCountQuery ) {
+ $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'];
+ if( $pCountQuery ) {
+ $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'] )) {
+ if( is_array( $pQueryHash['order'] )) {
+ $first = true;
+ foreach( $pQueryHash['order'] as $order ) {
+ if( !$first ) {
+ $pQueryHash['query'] .= ', ';
+ } else {
+ $pQueryHash['query'] .= ' ORDER BY ';
+ $first = false;
+ }
+ $pQueryHash['query'] .= $gBitSystem->mDb->convertSortmode( $order );
+ }
+ } else {
+ $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' );
+ global $gLibertySystem;
+ if( $loadFuncs = $gLibertySystem->getServiceValues( $pServiceFunction ) ) {
+ foreach( $loadFuncs as $func ) {
+ if( function_exists( $func ) ) {
+ if( !empty( $pObject ) && is_object( $pObject ) ) {
+ $loadHash = $func( $pObject, $pParamHash );
+ } else {
+ $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'] ) ) {
+ if ( is_array( $pBindVars ) ) {
+ $pBindVars = array_merge( $pBindVars, $loadHash['bind_vars'] );
+ } else {
+ $pBindVars = $loadHash['bind_vars'];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // -------------------------------- Content Permission Functions
+
+ /**
+ * Check to see if the loaded content has individually assigned permissions
+ *
+ * @access public
+ * @return Number of custom assigned permissions set for the loaded content item
+ */
+ function hasUserPermissions() {
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $ret = $this->mDb->getOne( "SELECT COUNT(`perm_name`) FROM `".BIT_DB_PREFIX."liberty_content_permissions` WHERE `content_id` = ?", array( $this->mContentId ));
+ }
+ return $ret;
+ }
+
+ /**
+ * getContentPermissionsSql
+ *
+ * @param array $pPermName
+ * @param array $pSelectSql
+ * @param array $pJoinSql
+ * @param array $pWhereSql
+ * @param array $pBindVars
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getContentPermissionsSql( $pPermName, &$pSelectSql, &$pJoinSql, &$pWhereSql, &$pBindVars ) {
+ global $gBitUser;
+ if ( defined('ROLE_MODEL') ) {
+ $pJoinSql .= "
+ 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=?)) ";
+ } else {
+ $pJoinSql .= "
+ 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;
+ $pBindVars[] = ANONYMOUS_USER_ID;
+ }
+
+ /**
+ * getContentListPermissionsSql
+ *
+ * @param array $pPermName
+ * @param array $pSelectSql
+ * @param array $pJoinSql
+ * @param array $pWhereSql
+ * @param array $pBindVars
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ public static function getContentListPermissionsSql( $pPermName, &$pSelectSql, &$pJoinSql, &$pWhereSql, &$pBindVars ) {
+ global $gBitUser;
+ if ( defined('ROLE_MODEL') ) {
+ $pJoinSql .= "
+ 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`=? ) ) )";
+ } else {
+ $pJoinSql .= "
+ 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[] = "y";
+ $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
+ */
+ function checkContentPermission( $pParamHash ) {
+ global $gBitUser;
+
+ $ret = FALSE;
+
+ if( !empty( $this->mAdminContentPerm ) && $gBitUser->hasPermission( $this->mAdminContentPerm ) ) {
+ // content admin shortcut
+ $ret = TRUE;
+ } else {
+ $selectSql = ''; $joinSql = ''; $whereSql = '';
+ $bindVars = array();
+
+ if( !empty( $pParamHash['content_id'] ) ) {
+ $bindVars[] = $pParamHash['content_id'];
+ } elseif( $this->isValid() ) {
+ $bindVars[] = $this->mContentId;
+ }
+
+ 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'];
+ }
+
+ $permWhereSql = '';
+ $this->getContentPermissionsSql( $pParamHash['perm_name'], $selectSql, $joinSql, $permWhereSql, $bindVars );
+
+ if( !empty( $whereSql ) ) {
+ $whereSql = preg_replace( '/^[\s]*AND/', ' ', $whereSql );
+ }
+
+ $query = "SELECT COUNT(*)
+ FROM `".BIT_DB_PREFIX."liberty_content` lc $joinSql
+ 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.
+ *
+ * @access public
+ */
+ function getContentPermissionsList() {
+ global $gBitUser;
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ if ( defined('ROLE_MODEL') ) {
+ $query = "
+ SELECT lcperm.`perm_name`, lcperm.`is_revoked`, ur.`role_id`, ur.`role_name`, up.`perm_desc`
+ FROM `".BIT_DB_PREFIX."liberty_content_permissions` lcperm
+ 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` = ?";
+ $team = 'role_id';
+ } else {
+ $query = "
+ SELECT lcperm.`perm_name`, lcperm.`is_revoked`, ug.`group_id`, ug.`group_name`, up.`perm_desc`
+ FROM `".BIT_DB_PREFIX."liberty_content_permissions` lcperm
+ 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` = ?";
+ $team = 'group_id';
+ }
+ $perms = $this->mDb->getAll( $query, array( $this->mContentId ));
+ foreach( $perms as $perm ) {
+ $ret[$perm[$team]][$perm['perm_name']] = $perm;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Get a list of content with permissions
+ *
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ public static function getContentWithPermissionsList() {
+ global $gBitSystem;
+ $ret = array();
+ if ( defined('ROLE_MODEL') ) {
+ $query = "
+ 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`
+ FROM `".BIT_DB_PREFIX."liberty_content_permissions` lcperm
+ 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' );
+ } else {
+ $query = "
+ 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`
+ FROM `".BIT_DB_PREFIX."liberty_content_permissions` lcperm
+ 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;
+ }
+ return $ret;
+ }
+
+ /**
+ * Expunge Content Permissions
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function expungeContentPermissions() {
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $query = "DELETE FROM `".BIT_DB_PREFIX."liberty_content_permissions` WHERE `content_id` = ?";
+ $ret = $this->mDb->query( $query, array( $this->mContentId ));
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function verifyUserPermission( $pPermName, $pFatalMessage = NULL ) {
+ $ret = TRUE;
+ if( $this->isValid() && !$this->hasUserPermission( $pPermName ) ) {
+ global $gBitSystem;
+ $gBitSystem->fatalPermission( $pPermName, $pFatalMessage );
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function hasUserPermission( $pPermName, $pVerifyAccessControl=TRUE ) {
+ global $gBitUser;
+ $ret = FALSE;
+ if( !$this->isValid() ) {
+ // return default user permission setting when no content is loaded
+ $ret = $gBitUser->hasPermission( $pPermName );
+ } elseif( !$gBitUser->isRegistered() || !( $ret = $this->isOwner() || $ret = $gBitUser->isAdmin() )) {
+ if( $gBitUser->isAdmin() || $gBitUser->hasPermission( $this->mAdminContentPerm )) {
+ $ret = TRUE;
+ } else {
+ if( $pVerifyAccessControl ) {
+ $this->verifyAccessControl();
+ }
+ $checkPerms = $this->getUserPermissions();
+ if ( !empty( $checkPerms ) ) {
+ // Do they have the admin permission or the one we want?
+ if ( !empty( $checkPerms[$this->mAdminContentPerm] ) ) {
+ $ret = TRUE;
+ } elseif ( !empty( $checkPerms[$pPermName] ) ) {
+ $ret = TRUE;
+ }
+ } else {
+ // return default user permission setting when no content perms are set
+ $ret = $gBitUser->hasPermission( $pPermName );
+ }
+ }
+ }
+ return( $ret );
+ }
+
+ /**
+ * 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
+ */
+ function hasAdminPermission( $pVerifyAccessControl=TRUE ) {
+ return( $this->hasUserPermission( $this->mAdminContentPerm, $pVerifyAccessControl ) );
+ }
+
+ // === 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
+ * @access public
+ */
+ function verifyAdminPermission( $pVerifyAccessControl=TRUE ) {
+ global $gBitSystem;
+ if( $this->hasAdminPermission( $pVerifyAccessControl ) ) {
+ return TRUE;
+ } else {
+ $gBitSystem->fatalPermission( $this->mAdminContentPerm );
+ }
+ }
+
+ /**
+ * 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
+ */
+ function hasExpungePermission( $pVerifyAccessControl=TRUE ) {
+ return( $this->hasUserPermission( $this->mExpungeContentPerm, $pVerifyAccessControl ) );
+ }
+
+ // === 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
+ * @access public
+ */
+ function verifyExpungePermission( $pVerifyAccessControl=TRUE ) {
+ global $gBitSystem;
+ if( $this->hasExpungePermission( $pVerifyAccessControl ) ) {
+ return TRUE;
+ } else {
+ $gBitSystem->fatalPermission( $this->mExpungeContentPerm );
+ }
+ }
+
+ /**
+ * 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
+ */
+ function hasUpdatePermission( $pVerifyAccessControl=TRUE ) {
+ return( $this->hasUserPermission( $this->mUpdateContentPerm, $pVerifyAccessControl ) );
+ }
+
+ /**
+ * Deprecated, use hasUpdatePermission
+ *
+ * @return bool True if user has this type of content administration permission
+ */
+ function hasEditPermission( $pVerifyAccessControl=TRUE, $pCheckGlobalPerm=TRUE ) {
+ deprecated( "LibertyContent::hasEditPermission has been replaced with LibertyContent::hasUpdatePermission and pCheckGlobal has been change to always be the case" );
+ return( $this->hasUpdatePermission( $pVerifyAccessControl ) );
+ }
+
+ // === 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
+ * @access public
+ */
+ function verifyUpdatePermission( $pVerifyAccessControl=TRUE ) {
+ global $gBitSystem;
+ if( $this->hasUpdatePermission( $pVerifyAccessControl ) ) {
+ return TRUE;
+ } else {
+ $gBitSystem->fatalPermission( $this->mUpdateContentPerm );
+ }
+ }
+
+ // === verifyEditPermission
+ /**
+ * Deprecated, use verifyUpdatePermission
+ */
+ function verifyEditPermission( $pVerifyAccessControl=TRUE, $pCheckGlobalPerm=TRUE ) {
+ deprecated( "LibertyContent::verifyEditPermission has been replaced with LibertyContent::verifyUpdatePermission and pCheckGlobal has been change to always be the case" );
+ $this->verifyUpdatePermission( $pVerifyAccessControl );
+ }
+
+ /**
+ * 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
+ */
+ function hasCreatePermission( $pVerifyAccessControl=TRUE ) {
+ return( $this->hasUserPermission( $this->mCreateContentPerm, $pVerifyAccessControl ) );
+ }
+
+ // === 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
+ **/
+ function verifyCreatePermission( $pVerifyAccessControl=TRUE ) {
+ global $gBitSystem;
+ if( !$this->isValid() && $this->hasCreatePermission( $pVerifyAccessControl ) ) {
+ return TRUE;
+ } else {
+ $gBitSystem->fatalPermission( $this->mCreateContentPerm );
+ }
+ }
+
+ /**
+ * 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
+ */
+ function hasViewPermission( $pVerifyAccessControl=TRUE ) {
+ return( $this->hasUpdatePermission( $pVerifyAccessControl ) || empty( $this->mViewContentPerm ) || $this->hasUserPermission( $this->mViewContentPerm, $pVerifyAccessControl ));
+ }
+
+ // === 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
+ * @access public
+ */
+ function verifyViewPermission( $pVerifyAccessControl=TRUE ) {
+ global $gBitSystem;
+ if( $this->hasViewPermission( $pVerifyAccessControl ) ) {
+ return TRUE;
+ } else {
+ $gBitSystem->fatalPermission( $this->mViewContentPerm );
+ }
+ }
+
+ /**
+ * 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
+ */
+ function hasPostCommentsPermission( $pVerifyAccessControl=TRUE ) {
+ 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
+ * @access public
+ */
+ function verifyPostCommentsPermission( $pVerifyAccessControl=TRUE ) {
+ global $gBitSystem;
+ if( $this->hasPostCommentPermission( $pVerifyAccessControl ) ) {
+ return TRUE;
+ } else {
+ $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'
+ */
+ function getUserPermissions() {
+ global $gBitUser;
+
+ $userId = $gBitUser->mUserId;
+ // Prevent null entires when creating database
+ if( !is_numeric( $userId ) ) $userId = 0;
+ if( !is_numeric( $this->mContentId ) ) $this->mContentId = 0;
+ if( !isset( $this->mUserContentPerms )) {
+ // get the default permissions for specified user
+ if ( defined('ROLE_MODEL') ) {
+ $query = "
+ SELECT urp.`perm_name` as `hash_key`, 1 as `role_perm`, urp.`perm_name`, urp.`perm_value`, urp.`role_id`
+ FROM `".BIT_DB_PREFIX."users_roles_map` urm
+ 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";
+ } else {
+ $query = "
+ SELECT ugp.`perm_name` as `hash_key`, 1 as `group_perm`, ugp.`perm_name`, ugp.`perm_value`, ugp.`group_id`
+ FROM `".BIT_DB_PREFIX."users_groups_map` ugm
+ 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";
+ }
+ if( !$defaultPerms = $this->mDb->getAssoc( $query, array( $this->mContentId, $userId, ANONYMOUS_USER_ID ) ) ) {
+ $defaultPerms = array();
+ }
+ if ( defined('ROLE_MODEL') ) {
+ $query = "
+ SELECT lcp.`perm_name` AS `hash_key`, lcp.*
+ FROM `".BIT_DB_PREFIX."liberty_content_permissions` 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";
+ } else {
+ $query = "
+ SELECT lcp.`perm_name` AS `hash_key`, lcp.*
+ FROM `".BIT_DB_PREFIX."liberty_content_permissions` 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";
+ }
+ if( !$nonDefaultPerms = $this->mDb->getAssoc( $query, array( $this->mContentId, $userId, ANONYMOUS_USER_ID ) ) ) {
+ $nonDefaultPerms = array();
+ }
+
+ $this->mUserContentPerms = array_merge( $defaultPerms, $nonDefaultPerms );
+
+ $this->invokeServices( 'content_user_perms_function' );
+ }
+
+ return $this->mUserContentPerms;
+ }
+
+ /**
+ * 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 ){
+ $ret = FALSE;
+ $pContentId = $pContentId == NULL?$this->mContentId:$pContentId;
+ if( @BitBase::verifyId( $pGroupId ) && !empty( $pPermName ) && @BitBase::verifyId( $pContentId ) ) {
+ $this->removePermission( $pGroupId, $pPermName, $pContentId );
+ $storeHash = array(
+ 'perm_name' => $pPermName,
+ 'content_id' => $pContentId,
+ );
+ if ( defined('ROLE_MODEL') ) {
+ $storeHash['role_id'] = $pTeamId;
+ } else {
+ $storeHash['group_id'] = $pTeamId;
+ }
+ // check to see if this is an exclusion
+ if( $pIsRevoked ) {
+ $storeHash['is_revoked'] = 'y';
+ }
+ $ret = $this->mDb->associateInsert( BIT_DB_PREFIX."liberty_content_permissions", $storeHash );
+ }
+ return $ret;
+ }
+
+ /**
+ * 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 )
+ */
+ function removePermission( $pTeamId, $pPermName, $pContentId=NULL ) {
+ $pContentId = $pContentId == NULL?$this->mContentId:$pContentId;
+ if( @BitBase::verifyId( $pTeamId ) && !empty( $pPermName ) && @BitBase::verifyId( $pContentId ) ) {
+ if ( defined('ROLE_MODEL') ) {
+ $team = 'role_id';
+ } else {
+ $team = 'group_id';
+ }
+ $query = "
+ DELETE FROM `".BIT_DB_PREFIX."liberty_content_permissions`
+ WHERE `$team` = ? and `content_id` = ? and `perm_name` = ?";
+ $bindVars = array( $pTeamId, $pContentId, $pPermName );
+ $result = $this->mDb->query( $query, $bindVars );
+ }
+ return TRUE;
+ }
+
+ /**
+ * Check to see if this permission is already in the global permissions table.
+ *
+ * @param array $pTeamId
+ * @param array $pPermName
+ * @access public
+ * @return TRUE if present, FALSE if not
+ */
+ function isExcludedPermission( $pTeamId, $pPermName ) {
+ if( @BitBase::verifyId( $pTeamId ) && !empty( $pPermName )) {
+ if ( defined('ROLE_MODEL') ) {
+ $query = "SELECT `perm_name` FROM `".BIT_DB_PREFIX."users_role_permissions` WHERE `role_id` = ? AND `perm_name` = ?";
+ } else {
+ $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
+ */
+ public static function getContentPreference( $pContentId, $pPrefName, $pPrefDefault=NULL ) {
+ global $gBitDb;
+ $ret = NULL;
+
+ if( parent::verifyId( $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 ) ) ) {
+ $ret = $pPrefDefault;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * 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 ) {
+ global $gBitDb;
+ $ret = NULL;
+
+ if( $this->isValid() ) {
+ if( is_null( $this->mPrefs ) ) {
+ $this->loadPreferences();
+ }
+ if( isset( $this->mPrefs ) && isset( $this->mPrefs[$pPrefName] ) ) {
+ $ret = $this->mPrefs[$pPrefName];
+ } else {
+ $ret = $pPrefDefault;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * 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)
+ * @access public
+ * @return array of preferences if $pContentId is set or pass preferences on to $this->mPrefs
+ */
+ function loadPreferences( $pContentId = NULL ) {
+ global $gBitSystem;
+ 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 ));
+ } elseif( $this->isValid() ) {
+ // 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
+ */
+ function setPreference( $pPrefName, $pPrefValue ) {
+ $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
+ */
+ function storePreference( $pPrefName, $pPrefValue = NULL ) {
+ $ret = FALSE;
+ if( LibertyContent::isValid() ) {
+ $this->StartTrans();
+ $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);
+ if( !is_null( $pPrefValue )) {
+ $query = "INSERT INTO `".BIT_DB_PREFIX."liberty_content_prefs` (`content_id`,`pref_name`,`pref_value`) VALUES(?, ?, ?)";
+ $bindvars[] = substr( $pPrefValue, 0, 510 );
+ $result = $this->mDb->query( $query, $bindvars );
+ $this->mPrefs[$pPrefName] = $pPrefValue;
+ }
+ $this->mPrefs[$pPrefName] = $pPrefValue;
+ $this->CompleteTrans();
+ $this->clearFromCache();
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ * string
+ */
+ function registerContentType( $pContentGuid, $pTypeParams ) {
+ global $gLibertySystem;
+ $gLibertySystem->registerContentType( $pContentGuid, $pTypeParams );
+ $this->mType = $pTypeParams;
+ }
+
+ /**
+ * Increment the content item hit flag by 1
+ *
+ * @return bool true ( will not currently report a failure )
+ */
+ function addHit() {
+ 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` = ?";
+ } else {
+ $query = "INSERT INTO `".BIT_DB_PREFIX."liberty_content_hits` ( `hits`, `last_hit`, `content_id` ) VALUES ( ?,?,? )";
+ $bindVars[] = 1;
+ }
+ $bindVars[] = $gBitSystem->getUTCTime();
+ $bindVars[] = $this->mContentId;
+ $this->StartTrans();
+ $result = $this->mDb->query( $query, $bindVars );
+ $this->CompleteTrans();
+ }
+ }
+ return TRUE;
+ }
+
+ /**
+ * Set Hits and Last Hit
+ *
+ * @return bool true ( will not currently report a failure )
+ */
+ function setHits($pHits, $pLastHit=0) {
+ if( $this->mContentId && !empty($pHits) ) {
+ $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();
+ if( !$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 TRUE;
+ }
+
+
+ /**
+ * Get Hits and Last Hit
+ *
+ * @return bool true ( will not currently report a failure )
+ */
+ function getHits() {
+ if( $this->mContentId ) {
+ $query = "SELECT `hits`,`last_hit` FROM `".BIT_DB_PREFIX."liberty_content_hits` where `content_id` = ?";
+ $row = $this->mDb->getRow( $query, array( $this->mContentId ) );
+ if ( !empty($row) ) {
+ $this->mInfo['hits'] = $row['hits'];
+ $this->mInfo['last_hit'] = $row['last_hit'];
+ }
+ }
+ return TRUE;
+ }
+
+ /**
+ * Create the generic title for a content item
+ *
+ * This can 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
+ */
+ public static function getTitleFromHash( &$pHash, $pDefault=TRUE ) {
+ $ret = NULL;
+ if( !empty( $pHash['title'] ) ) {
+ $ret = $pHash['title'];
+ } elseif( $pDefault && !empty( $pHash['content_name'] ) ) {
+ $ret = $pHash['content_name'];
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function getTitle() {
+ $ret = NULL;
+ if( $this->isValid() ) {
+ $ret = static::getTitleFromHash( $this->mInfo );
+ }
+ return $ret;
+ }
+
+ /**
+ * Get the time this object was created
+ *
+ * @return int Unix epoch of time object was created
+ */
+ function getTimeCreated() {
+ $ret = NULL;
+ if( $this->isValid() ) {
+ $ret = $this->getField( 'created' );
+ }
+ return $ret;
+ }
+
+ /**
+ * Get the time this object was last modified
+ *
+ * @return int Unix epoch of time object was last modified
+ */
+ function getTimeModified() {
+ $ret = NULL;
+ if( $this->isValid() ) {
+ $ret = $this->getField( 'last_modified' );
+ }
+ return $ret;
+ }
+
+ /**
+ * Attempt to create a brief description of this object, most useful for <meta name="description" />
+ *
+ * @return array list of aliases
+ */
+ function generateDescription() {
+ $ret = NULL;
+ if( $this->isValid() ) {
+ if( $this->getField('summary') ) {
+ $ret = $this->getField('summary');
+ } elseif( $this->getField('data') ) {
+ $text = trim( preg_replace('/\s+/', ' ', strip_tags( $this->getParsedData() ) ) );
+ // 250 to 300 is max description
+ $ret = substr( $text, 0, 250 );
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Attempt to create a collection of relevant words about this object, most useful for <meta name="keywords" />
+ *
+ * @return array list of aliases
+ */
+ function generateKeywords() {
+ $ret = array();
+ if( $this->isValid() ) {
+ }
+ return $ret;
+ }
+
+ /**
+ * Get array of aliases for this content object
+ *
+ * @return array list of aliases
+ */
+ function getAliases( $pUpperCase = FALSE ) {
+ global $gBitSystem;
+ $ret = array();
+ if( $this->isValid() ) {
+ $selectColumn = ( $pUpperCase ? $gBitSystem->mDb->getCaseLessColumn('alias_title') : '`alias_title`' );
+ $ret = $this->mDb->getCol( "SELECT ".$selectColumn." 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 ), BIT_QUERY_CACHE_TIME );
+ }
+ return $ret;
+ }
+
+ /**
+ * Access a content item type GUID
+ *
+ * @return string content_type_guid for the content
+ */
+ function getContentType() {
+ $ret = NULL;
+ if( isset( $this->mInfo['content_type_guid'] ) ) {
+ $ret = $this->mInfo['content_type_guid'];
+ } elseif( $this->mContentTypeGuid ) {
+ // for unloaded classes
+ $ret = $this->mContentTypeGuid;
+ } elseif( $this->mType['content_type_guid'] ) {
+ // unloaded content might have this
+ $ret = $this->mType['content_type_guid'];
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function getContentTypeName( $pPlural=FALSE ){
+ global $gLibertySystem;
+ return $gLibertySystem->getContentTypeName( $this->getContentType(), $pPlural );
+ }
+
+
+ /**
+ * getContentTypeDescription
+ *
+ * @param array $pContentType
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ function getContentTypeDescription( $pContentType=NULL ) {
+ deprecated( 'You are calling the deprecated method getContentTypeDescription, use getContentTypeName( $pPlural )' );
+ return $this->getContentTypeName();
+ /*
+ global $gLibertySystem;
+ 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
+ */
+ function getContentId() {
+ $ret = NULL;
+ if( isset( $this->mContentId ) ) {
+ $ret = $this->mContentId;
+ }
+ return $ret;
+ }
+
+ /**
+ * Return content type description for this content object.
+ *
+ * @return string content_type_guid description for the object
+ */
+ function getContentDescription() {
+ deprecated( 'You are calling the deprecated method getContentDescription, use getContentTypeName( $pPlural )' );
+ return $this->getContentTypeName();
+ }
+
+
+ /**
+ * 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
+ */
+ function getViewTemplate( $pAction ) {
+ $ret = null;
+ switch ( $pAction ){
+ case "view":
+ case "list":
+ $ret = "bitpackage:liberty/center_".$pAction."_generic.tpl";
+ break;
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function getRenderFile() {
+ return LIBERTY_PKG_INCLUDE_PATH.'display_content_inc.php';
+ }
+
+ public function getDisplayLink( $pLinkText=NULL, $pAnchor=NULL ) {
+ return self::getDisplayLinkFromHash( $this->mInfo, $pLinkText, $pAnchor );
+ }
+
+ /**
+ * 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 $pParamHash 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.
+ */
+ public static function getDisplayLinkFromHash( &$pParamHash, $pLinkText=NULL, $pAnchor=NULL ) {
+ global $gBitSmarty;
+ $ret = '';
+
+ if( empty( $pLinkText )) {
+ if( !empty( $pParamHash['title'] )) {
+ $pLinkText = $pParamHash['title'];
+ } elseif( !empty( $pParamHash['content_name'] ) ) {
+ $pLinkText = "[ ".$pParamHash['content_name']." ]";
+ }
+ }
+
+ if( empty( $pLinkText )) {
+ $pLinkText = "[ ".tra( "No Title" )." ]";
+ }
+
+ // we add some more info to the title of the link
+ if( !empty( $pParamHash['created'] )) {
+ $gBitSmarty->loadPlugin( 'smarty_modifier_bit_short_date' );
+ $linkTitle = tra( 'Created' ).': '.smarty_modifier_bit_short_date( $pParamHash['created'] );
+ } else {
+ $linkTitle = $pLinkText;
+ }
+
+ // finally we are ready to create the full link
+ if( !empty( $pParamHash['content_id'] )) {
+ $ret = '<a title="'.htmlspecialchars( $linkTitle ).'" href="'.LibertyContent::getDisplayUrlFromHash( $pParamHash ).$pAnchor.'">'.htmlspecialchars( $pLinkText ).'</a>';
+ }
+ return $ret;
+ }
+
+ /**
+ * 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.
+ */
+ public function getDisplayUri() {
+ if( $this->isValid() ) {
+ 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.
+ */
+ public static function getDisplayUriFromHash( &$pParamHash ) {
+ 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 $pParamHash a hash of params to add to the url
+ * @return string Formated URL address to display the page.
+ */
+ public static function getDisplayUrlFromHash( &$pParamHash ) {
+ $ret = NULL;
+ if( @static::verifyId( $pParamHash['content_id'] ) ) {
+ $ret = BIT_ROOT_URL.'index.php?content_id='.$pParamHash['content_id'];
+ }
+ return $ret;
+ }
+
+ /**
+ * Returns Request URL to a piece of content
+ */
+ public function getDisplayUrl() {
+ $ret = NULL;
+ if( !empty( $this ) && $this->isValid() ) {
+ $ret = static::getDisplayUrlFromHash( $this->mInfo );
+ }
+ return $ret;
+ }
+
+ /**
+ * 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 ){
+ global $gLibertySystem;
+ $package = $gLibertySystem->mContentTypes[$this->mType['content_type_guid']]['handler_package'];
+
+ $pathConst = strtoupper( $package ).'_PKG_URL';
+ if( defined( $pathConst ) ) {
+ $packagePath = constant( $pathConst );
+ }else{
+ $packagePath = BIT_ROOT_URL.$package."/";
+ }
+
+ if( @BitBase::verifyId( $pContentId ) ) {
+ $ret = $packagePath.'edit.php?content_id='.$pContentId;
+ } elseif( $this->isValid() ) {
+ $ret = $packagePath.'edit.php?content_id='.$this->mContentId;
+ } else {
+ $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;
+ }
+ $amp = TRUE;
+ }
+ return $ret;
+ }
+
+
+ /**
+ * 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.
+ */
+ function getPreviewUrl( $pContentId = NULL, $pMixed = NULL ) {
+ if( @BitBase::verifyId( $pContentId ) ) {
+ $ret = LIBERTY_PKG_URL.'preview.php?content_id='.$pContentId;
+ } elseif( @BitBase::verifyId( $pMixed['content_id'] ) ) {
+ $ret = LIBERTY_PKG_URL.'preview.php?content_id='.$pMixed['content_id'];
+ } elseif( $this->isValid() ) {
+ $ret = LIBERTY_PKG_URL.'preview.php?content_id='.$this->mContentId;
+ } else {
+ $ret = '#';
+ }
+ return $ret;
+ }
+
+
+ /**
+ * 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 ) {
+ if( $this->isValid() ) {
+ return $this->getThumbnailUrlFromHash( $this->mInfo, $pSize );
+ }
+ }
+
+
+ public static function getThumbnailUrlFromHash( &$pMixed, $pSize = 'small', $pSecondaryId = NULL, $pDefault=TRUE ) {
+ $ret = '';
+ if( !empty( $pMixed['content_type']['handler_package'] ) ) {
+ $pkgName = $pMixed['content_type']['handler_package'];
+ if( $pkgPath = constant( strtoupper( $pkgName ).'_PKG_PATH' ) ) {
+ if( file_exists( $pkgPath.'icons/pkg_'.$pkgName.'.png' ) ) {
+ $ret = constant( strtoupper( $pkgName ).'_PKG_URL' ).'icons/pkg_'.$pkgName.'.png';
+ }
+ }
+ }
+ return $ret;
+ }
+
+
+ public function getThumbnailUri( $pSize='small' ) {
+ if( $this->isValid() ) {
+ return $this->getThumbnailUriFromHash( $this->mInfo, $pSize );
+ }
+ }
+
+ public static function getThumbnailUriFromHash( &$pMixed, $pSize='small' ) {
+ $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 = STORAGE_HOST_URI.substr( $ret, strlen( BIT_ROOT_URL ) );
+ }
+ return( $ret );
+ }
+
+
+ public function getThumbnailFile( $pSize='small' ) {
+ if( $this->isValid() ) {
+ return $this->getThumbnailFileFromHash( $this->mInfo, $pSize );
+ }
+ }
+
+ public static function getThumbnailFileFromHash( &$pMixed, $pSize='small' ) {
+ $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 = substr( $ret, strlen( BIT_ROOT_URL ) );
+ }
+ return( BIT_ROOT_PATH.$ret );
+ }
+
+
+ /**
+ * Validate inbound sort_mode parameter
+ * @param pParamHash hash of parameters for any getList() function
+ * @return the link to display the page.
+ */
+ public static function getSortModeFields() {
+ return array(
+ 'content_id',
+ 'modifier_user',
+ 'modifier_real_name',
+ 'creator_user',
+ 'creator_real_name',
+ 'title',
+ 'content_type_guid',
+ 'ip',
+ 'last_modified',
+ 'created',
+ );
+ }
+
+ /**
+ * 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 = str_replace( '_asc', '', str_replace( '_desc', '', $pSortMode ) );
+
+ $baseSortMode = preg_replace( '/^.*\./', '', $baseSortMode );
+
+ if( !in_array( $baseSortMode, $sortHash ) ) {
+ $pSortMode = $pDefault;
+ }
+
+ 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.
+ */
+ public static function prepGetList( &$pListHash ) {
+ global $gBitUser;
+ if( $gBitUser->isAdmin() ) {
+ $pListHash['min_content_status_id'] = -9999;
+ } elseif( !empty( $this ) && is_object( $this ) && $this->hasAdminPermission() ) {
+ $pListHash['min_content_status_id'] = -999;
+ } elseif( !empty( $this ) && is_object( $this ) && $this->hasUpdatePermission() ) {
+ $pListHash['min_content_status_id'] = -99;
+ } else {
+ $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;
+ }
+ }
+ }
+ } else {
+ // if sort_mode is not set then use last_modified_desc
+ $pListHash['sort_mode'] = 'last_modified_desc';
+ }
+
+ // Users without permission can only see their own content listing
+ if( $gBitUser->isRegistered() && !$gBitUser->hasPermission( 'p_liberty_list_content' ) ) {
+ $pListHash['user_id'] = $gBitUser->mUserId;
+ }
+
+ 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
+ **/
+ function getAuthorList( &$pListHash ) {
+ $ret = NULL;
+ $mid = '';
+
+ $bindVars = array();
+ if( !empty( $pListHash['content_type_guid'] ) ) {
+ $mid .= ' AND lc.`content_type_guid`=? ';
+ $bindVars[] = $pListHash['content_type_guid'];
+ }
+
+ LibertyContent::prepGetList( $pListHash );
+ $query = "SELECT DISTINCT(uu.`user_id`) AS hash_key, uu.`user_id`, SUM( lch.`hits` ) AS `ag_hits`, uu.`login`, uu.`real_name`
+ FROM `".BIT_DB_PREFIX."liberty_content` lc INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON( uu.`user_id`=lc.`user_id` )
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content_hits` lch
+ ON (`lc`.`content_id` = `lch`.`content_id`)
+ WHERE uu.`user_id` != ".ANONYMOUS_USER_ID." AND lch.`hits` > 0 $mid
+ 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() ) {
+ $ret[] = $aux;
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ * @return - data
+ **/
+ function getContentRanking( $pListHash ) {
+ $pListHash['sort_mode'] = !empty( $pListHash['sort_mode'] ) ? $pListHash['sort_mode'] : 'hits_desc';
+
+ if( $pListHash['sort_mode'] == 'top_authors' ) {
+ global $gBitUser;
+ $ret['data'] = $gBitUser->getAuthorList( $pListHash );
+ } else {
+ include_once( LIBERTY_PKG_CLASS_PATH.'LibertyContent.php' );
+ $libertyContent = new LibertyContent();
+ $ret['data'] = $libertyContent->getContentList( $pListHash );
+ }
+
+ $ret['title'] = !empty( $pListHash['title'] ) ? $pListHash['title'] : tra( "Content Ranking" );
+ $ret['attribute'] = !empty( $pListHash['attribute'] ) ? $pListHash['attribute'] : tra( "Hits" );
+
+ return $ret;
+ }
+
+ /**
+ * 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
+ **/
+ function getContentList( &$pListHash ) {
+ global $gLibertySystem, $gBitSystem, $gBitUser, $gBitSmarty;
+
+ LibertyContent::prepGetList( $pListHash );
+
+ $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'] );
+ } else {
+ $selectSql = '';
+ }
+ $joinSql = implode( ' ', $hashSql['join'] );
+ $whereSql = '';
+ if( empty( $hashBindVars['join'] )) {
+ $bindVars = array();
+ } else {
+ $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';
+ }
+
+ $old_sort_mode = '';
+
+ $sortHash = array(
+ 'versions_desc',
+ 'versions_asc',
+ 'links_asc',
+ 'links_desc',
+ 'backlinks_asc',
+ 'backlinks_desc'
+ );
+
+ 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
+ $whereSql .= " AND lc.`title` IN ( ".implode( ',',array_fill( 0,count( $pListHash['find'] ),'?' ) ).") ";
+ $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` <> ? ";
+ $bindVars[] = 50;
+ } else {
+ $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
+ $hashKeySql = '';
+ 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";
+ }
+ }
+ }
+
+ $sortHash = array(
+ 'content_id_desc',
+ 'content_id_asc',
+ 'modifier_user_desc',
+ 'modifier_user_asc',
+ 'modifier_real_name_desc',
+ 'modifier_real_name_asc',
+ 'creator_user_desc',
+ 'creator_user_asc',
+ 'creator_real_name_desc',
+ 'creator_real_name_asc',
+ );
+
+ if( in_array( $pListHash['sort_mode'], $sortHash ) ) {
+ $orderTable = '';
+ } elseif( !empty( $pListHash['order_table'] ) ) {
+ $orderTable = $pListHash['order_table'];
+ } elseif( !empty( $pListHash['sort_mode'] ) && strtolower( substr( $pListHash['sort_mode'], 0, 4 ) ) =='hits' ) {
+ $orderTable = 'lch.';
+ } elseif( strpos( $pListHash['sort_mode'], '.' ) ) {
+ // do not specifiy orderTable of sort_mode already has a . in it
+ $orderTable = '';
+ } else {
+ $orderTable = 'lc.';
+ }
+
+ 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
+ $query = "
+ SELECT
+ $hashKeySql
+ 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`,
+ lch.`hits`,
+ lch.`last_hit`,
+ lc.`event_time`,
+ lc.`title`,
+ lc.`last_modified`,
+ lc.`content_type_guid`,
+ lc.`ip`,
+ lc.`created`,
+ lc.`content_id`,
+ lcds.`data` AS `summary`
+ $selectSql
+ FROM `".BIT_DB_PREFIX."liberty_content` lc
+ INNER JOIN `".BIT_DB_PREFIX."users_users` uuc ON (lc.`user_id`=uuc.`user_id`)
+ LEFT OUTER 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')
+ $joinSql
+ $whereSql
+ ORDER BY ".$orderTable.$this->convertSortMode($pListHash['sort_mode']);
+
+ $query_cant = "
+ SELECT
+ COUNT(lc.`content_id`)
+ FROM `".BIT_DB_PREFIX."liberty_content` lc
+ $joinSql
+ $whereSql";
+
+ $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'] );
+
+ $ret = array();
+ $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'];
+ if( !empty( $gBitSystem->mPackages[$type['handler_package']] ) ) {
+ if( !class_exists( $type['handler_class'] ) ) {
+ $gLibertySystem->getContentClassName( $aux['content_type_guid'] );
+ }
+ if( class_exists( $type['handler_class'] ) ) {
+ if( $aux['content_type_guid'] == BITUSER_CONTENT_TYPE_GUID ) {
+ // 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['handler_class']::getTitleFromHash( $userInfo );
+ $aux['display_link'] = $type['handler_class']::getDisplayLinkFromHash( $userInfo, $userInfo['login'] );
+ $aux['display_url'] = $type['handler_class']::getDisplayUrlFromHash( $userInfo );
+ } else {
+ $aux['title'] = $type['handler_class']::getTitleFromHash( $aux );
+ $aux['display_link'] = $type['handler_class']::getDisplayLinkFromHash( $aux, $aux['title'] );
+ /**
+ * @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'] = static::getLibertyObject( $aux['content_id'], $aux['content_type_guid'] );
+ 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' ) ) {
+ $aux['thumbnail_urls'] = liberty_fetch_thumbnails( $aux );
+ }
+
+ if( isset( $aux['hash_key'] ) ) {
+ $ret[$aux['hash_key']] = $aux;
+ } else {
+ $ret[] = $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' );
+ }
+
+ if( in_array( $old_sort_mode, array(
+ 'versions_desc',
+ 'versions_asc',
+ 'links_asc',
+ 'links_desc',
+ 'backlinks_asc',
+ 'backlinks_desc'
+ ))) {
+ $ret = array_slice( $ret, $old_offset, $old_max_records );
+ }
+
+ LibertyContent::postGetList( $pListHash );
+ return $ret;
+ }
+
+ /**
+ * Get a list of all structures this content is a member of
+ **/
+ function getStructures() {
+ $ret = array();
+ if( $this->isValid() ) {
+ $structures_added = array();
+ $query = 'SELECT ls.*, lc.`title`, tcr.`title` AS `root_title`
+ FROM `'.BIT_DB_PREFIX.'liberty_content` lc, `'.BIT_DB_PREFIX.'liberty_structures` ls
+ 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()) {
+ $ret[] = $res;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /*
+ * 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;
+
+ if( $pForceLength ) {
+ $res['data'] = preg_replace( LIBERTY_SPLIT_REGEX, '', $res['data'] );
+ }
+
+ // 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
+ if( preg_match( LIBERTY_SPLIT_REGEX, $res['data'] )) {
+ // this has been manually split
+ $res['man_split'] = TRUE;
+ $parts = preg_split( LIBERTY_SPLIT_REGEX, $res['data'] );
+ $pParseHash['data'] = $parts[0];
+ } else {
+ // 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 = self::parseDataHash( $pParseHash, $this )) {
+ // 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'] .= '&hellip;';
+ }
+ }
+ } else {
+ // did we parse an empty page?
+ $res['parsed'] = $res['parsed_description'] = '';
+ $res['has_more'] = FALSE;
+ }
+
+ return $res;
+ }
+
+ public function getParsedData() {
+ if( empty( $this->mInfo['parsed_data'] ) ) {
+ $this->parseData();
+ }
+ return $this->mInfo['parsed_data'];
+ }
+
+ protected function parseData() {
+ // get the data into place
+ $this->mInfo['parsed_data'] = self::parseDataHash( $this->mInfo, $this );
+ }
+
+ /**
+ * 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
+ */
+ public static function parseDataHash( &$pParseHash, $pObject=NULL ) {
+ global $gLibertySystem, $gBitSystem, $gBitUser;
+/*
+ if( !is_array( $pParseHash ) ) {
+ $parseHash['data'] = $pMixed;
+ } elseif( empty( $pParseHash['data'] ) ) {
+ $pParseHash['data'] = '';
+ }
+*/
+ // sanitise pParseHash a bit
+ $pParseHash['content_id'] = !empty( $pParseHash['content_id'] ) ? $pParseHash['content_id'] : NULL;
+ $pParseHash['cache_extension'] = !empty( $pParseHash['cache_extension'] ) ? $pParseHash['cache_extension'] : NULL;
+ $pParseHash['user_id'] = !empty( $pParseHash['user_id'] ) ? $pParseHash['user_id'] : is_object( $gBitUser ) ? $gBitUser->mUserId : ANONYMOUS_USER_ID;
+
+ // Ensure we have a format
+ if( empty( $pParseHash['format_guid'] ) ) {
+ // use system wide default
+ $pParseHash['format_guid'] = $gBitSystem->getConfig( 'default_format', 'tikiwiki' );
+ if( is_a( $pObject, 'LibertyContent' ) && ($objectFormat = $pObject->getField( 'format_guid' ) ) ) {
+ // if pObject has a specified format, use that...
+ $pParseHash['format_guid'] = $objectFormat;
+ }
+ }
+
+ $ret = NULL;
+ // Handle caching if it is enabled.
+ if( $gBitSystem->isFeatureActive( 'liberty_cache' ) && !empty( $pParseHash['content_id'] ) && empty( $pParseHash['no_cache'] ) ) {
+ if( $cacheFile = LibertyContent::getCacheFile( $pParseHash['content_id'], $pParseHash['cache_extension'] ) ) {
+ // Attempt to read cache file
+ if( !( $ret = LibertyContent::readCacheFile( $cacheFile ))) {
+ // failed to read from cache.
+ $parseAndCache = TRUE;
+ } else {
+ // Note that we read from cache.
+ $pParseHash['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( $pParseHash['data'] ) && $pParseHash['format_guid'] ) {
+ $replace = array();
+ // extract and protect ~pp~...~/pp~ and ~np~...~/np~ sections
+ parse_protect( $pParseHash['data'], $replace );
+
+ // some few filters such as stencils need to be before the data plugins
+ self::filterDataHash( $pParseHash['data'], $pParseHash, 'preplugin' );
+
+ // handle all liberty data plugins like {code} and {attachment} usage in all formats
+ parse_data_plugins( $pParseHash['data'], $replace, $pObject, $pParseHash );
+
+ // pre parse filter according to what we're parsing - split or full body
+ $filter = empty( $pParseHash['split_parse'] ) ? 'parse' : 'split';
+ self::filterDataHash( $pParseHash['data'], $pParseHash, 'pre'.$filter );
+
+ if( $func = $gLibertySystem->getPluginFunction( $pParseHash['format_guid'], 'load_function' ) ) {
+ // get the beast parsed
+ if( $ret = $func( $pParseHash, $pObject ) ) {
+ // post parse filter
+ self::filterDataHash( $ret, $pParseHash, 'post'.$filter );
+
+ // before we cache we insert the protected sections back - even after the filters.
+ // might not be ideal but it allows stuff like ~pp~{maketoc}~/pp~
+ $replace = array_reverse( $replace );
+ foreach( $replace as $rep ) {
+ $ret = str_replace( $rep["key"], $rep["data"], $ret );
+ }
+
+ if( !empty( $parseAndCache )) {
+ LibertyContent::writeCacheFile( $cacheFile, $ret );
+ }
+ }
+ }
+ }
+ }
+
+ return $ret;
+ }
+
+ protected function filterData( &$pData, &$pFilterHash, $pFilterStage = 'preparse' ) {
+ self::filterDataHash( $pData, $pFilterHash, $pFilterStage, $this );
+ }
+
+ /**
+ * 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
+ * @access public
+ * @return filtered data
+ */
+ public static function filterDataHash( &$pData, &$pFilterHash, $pFilterStage = 'preparse', $pObject = NULL ) {
+ global $gLibertySystem;
+ if( !empty( $pData ) && ($filters = $gLibertySystem->getPluginsOfType( FILTER_PLUGIN )) ) {
+ foreach( $filters as $guid => $filter ) {
+//vvd( $guid, $gLibertySystem->isPluginActive( $guid ), $pFilterStage, $gLibertySystem->getPluginFunction( $guid, $pFilterStage.'_function' ));//, $pData ); //, $pFilterHash, $pObject );
+ if( $gLibertySystem->isPluginActive( $guid ) && ($func = $gLibertySystem->getPluginFunction( $guid, $pFilterStage.'_function' )) ) {
+ $func( $pData, $pFilterHash, $pObject );
+ }
+ }
+ }
+ }
+
+ /**
+ * 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
+ * any interpretation)
+ * @param string Data to process
+ * @return string Extracted pages
+ */
+ function getNumberOfPages( &$data ) {
+ $preparsed = array();
+
+ preg_match_all("/(<[Pp][Rr][Ee]>)((.|\n)*?)(<\/[Pp][Rr][Ee]>)/", $data, $preparse);
+ $idx = 0;
+
+ foreach (array_unique($preparse[2])as $pp) {
+ $key = md5(BitSystem::genPass());
+
+ $aux["key"] = $key;
+ $aux["data"] = $pp;
+ $preparsed[] = $aux;
+ $data = str_replace($preparse[1][$idx] . $pp . $preparse[4][$idx], $key, $data);
+ $idx = $idx + 1;
+ }
+
+ $parts = explode(defined('PAGE_SEP') ? PAGE_SEP : "...page...", $data);
+ return count($parts);
+ }
+
+ /**
+ * Special parsing for a particular page of a multipage article
+ *
+ * Temporary remove &lt;PRE&gt;&lt;/PRE&gt; secions to protect
+ * from broke &lt;PRE&gt; tags and leave well known &lt;PRE&gt;
+ * behaviour (i.e. type all text inside AS IS w/o
+ * any interpretation)
+ * @param string Data to process
+ * @param integer Number of page to extract
+ * @return string Extracted page
+ */
+ function getPage( &$data, $i ) {
+ $preparsed = array();
+
+ preg_match_all("/(<[Pp][Rr][Ee]>)((.|\n)*?)(<\/[Pp][Rr][Ee]>)/", $data, $preparse);
+ $idx = 0;
+
+ foreach (array_unique($preparse[2])as $pp) {
+ $key = md5(BitSystem::genPass());
+
+ $aux["key"] = $key;
+ $aux["data"] = $pp;
+ $preparsed[] = $aux;
+ $data = str_replace($preparse[1][$idx] . $pp . $preparse[4][$idx], $key, $data);
+ $idx = $idx + 1;
+ }
+
+ // Get slides
+ $parts = explode(defined('PAGE_SEP') ? PAGE_SEP : "...page...", $data);
+
+ if (substr($parts[$i - 1], 1, 5) == "<br/>") {
+ $ret = substr($parts[$i - 1], 6);
+ } else {
+ $ret = $parts[$i - 1];
+ }
+
+ // Replace back <PRE> sections
+ foreach ($preparsed as $pp) {
+ $ret = str_replace($pp["key"], "<pre>" . $pp["data"] . "</pre>", $ret);
+ }
+
+ return $ret;
+ }
+
+ /**
+ * convenience function to process a $_REQUEST array
+ **/
+ function decodeAjaxRequest( &$pParamHash ){
+ foreach( $pParamHash as $key => $value ){
+ if( is_string($value) ){
+ $pParamHash[$key] = htmlspecialchars_decode( $value );
+ }
+ }
+ }
+
+ /**
+ * Set content related mStructureId
+ *
+ * @param integer Structure ID
+ */
+ function setStructure( $pStructureId ) {
+ if( $this->verifyId( $pStructureId ) ) {
+ $this->mStructureId = $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
+ */
+ function isInStructure( $pStructureId=NULL ) {
+ if( $this->isValid() ) {
+ $whereSql = NULL;
+ $bindVars = array( $this->mContentId );
+ if( $pStructureId ) {
+ array_push( $bindVars, $pStructureId );
+ $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 );
+ return $cant;
+ }
+ }
+
+ /**
+ * 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.
+ */
+ function setIndexData( $pContentId = 0 ) {
+ global $gBitSystem ;
+ if ( $pContentId == 0 ) $pContentId = $this->mContentId;
+ $sql = "SELECT lc.`title`, lc.`data`, lcds.`data` AS `summary`, uu.`login`, uu.`real_name`
+ FROM `" . BIT_DB_PREFIX . "liberty_content` lc
+ 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
+ * @access public
+ * @return absolute path
+ */
+ public function isCached( $pContentId = NULL ) {
+ global $gBitSystem;
+ return( $gBitSystem->getConfig( 'liberty_cache' ) && is_file( LibertyContent::getCacheFile( $pContentId )));
+ }
+
+ /**
+ * Get the path where we store liberty cached content
+ *
+ * @access public
+ * @return absolute path
+ */
+ public static function getCacheBasePath() {
+ return str_replace( '//', '/', TEMP_PKG_PATH.LIBERTY_PKG_NAME.'/cache/' );
+ }
+
+ /**
+ * Get the path to directory where an individual cache item is stored
+ *
+ * @param array $pContentId Content id of cached item
+ * @access public
+ * @return path on success, FALSE on failure
+ */
+ public static function getCachePath( $pContentId = NULL ) {
+ global $gBitSystem;
+
+ $ret = FALSE;
+ if( @BitBase::verifyId( $pContentId ) ) {
+ if( $gBitSystem->isFeatureActive( 'liberty_flat_cache' )) {
+ $subdir = floor( $pContentId / 1000 );
+ $path = LibertyContent::getCacheBasePath().$subdir.'/';
+ } else {
+ $subdir = $pContentId % 1000;
+ $path = LibertyContent::getCacheBasePath().$subdir.'/'.$pContentId.'/';
+ }
+ if( is_dir( $path ) || mkdir_p( $path ) ) {
+ $ret = $path;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ public static function readCacheFile( $pCacheFile ) {
+ global $gBitSystem;
+ $ret = NULL;
+ if( is_file( $pCacheFile ) && ( time() - filemtime( $pCacheFile )) < $gBitSystem->getConfig('liberty_cache') && filesize( $pCacheFile ) > 0 ) {
+ // get contents from cache file
+ $h = fopen( $pCacheFile, 'r' );
+ $ret = fread( $h, filesize( $pCacheFile ) );
+ fclose( $h );
+ }
+ return $ret;
+ }
+
+ /**
+ * Unconditionally writes data to the cache file.
+ * Does not check for error assuming if write failed that the
+ * read will as well.
+ *
+ * @param the name of the cache file from getCacheFile() to write
+ * @param the contents to write to the file
+ */
+ public static function writeCacheFile( $pCacheFile, $pData ) {
+ // Cowardly refuse to write nothing.
+ if( !empty( $pData )) {
+ // write parsed contents to cache file
+ $h = fopen( $pCacheFile, 'w' );
+ fwrite( $h, $pData );
+ fclose( $h );
+ }
+ }
+
+ /**
+ * Get the path to file where an individual cache item is stored
+ *
+ * @param array $pContentId Content id of cached item
+ * @access public
+ * @return filename on success, FALSE on failure
+ */
+ public static function getCacheFile( $pContentId = NULL, $pCacheExtension = NULL ) {
+ if( $ret = LibertyContent::getCachePath( $pContentId ) ) {
+ return( $ret.$pContentId.( !empty( $pCacheExtension ) ? '.'.$pCacheExtension : '') );
+ } else {
+ return FALSE;
+ }
+ }
+
+ /**
+ * Delete cache files for a given content item
+ *
+ * @param array $pContentId
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ public static function expungeCacheFile( $pContentId = NULL ) {
+ global $gBitSystem;
+ if( $gBitSystem->isFeatureActive( 'liberty_cache' ) && @BitBase::verifyId( $pContentId ) ) {
+ // we need to unlink all files with the same id and any extension
+ if( $dh = opendir( $cacheDir = LibertyContent::getCachePath( $pContentId ) ) ) {
+ while( FALSE !== ( $file = readdir( $dh ) ) ) {
+ if( $file != '.' && $file != '..' && ( preg_match( "/^".$pContentId."$/", $file ) || preg_match( "/^".$pContentId."\..*/", $file ) ) ) {
+ @unlink( $cacheDir.$file );
+ }
+ }
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Delete liberty cache
+ *
+ * @param array $pContentId
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ public static function expungeCache() {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( $gBitSystem->isFeatureActive( 'liberty_cache' )) {
+ $cacheDir = LibertyContent::getCacheBasePath();
+ // make sure that we're in the temp dir at least
+ if( strstr( $cacheDir, str_replace( '//', '/', TEMP_PKG_PATH ))) {
+ unlink_r( $cacheDir );
+ // make sure we have a usable cache directory to work with
+ $ret = ( is_dir( $cacheDir ) || mkdir_p( $cacheDir ));
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * getFilter
+ *
+ * @param array $pContentTypeGuid
+ * @param array $pSql
+ * @param array $pBindVars
+ * @param array $pHash
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ * @todo
+ * - 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) {
+ if( !empty( $gBitSystem->mPackages[$type['handler_package']]['path'] ) ) {
+ $path = $gBitSystem->mPackages[$type['handler_package']]['path'];//constant(strtoupper($type['handler_package']).'_PKG_PATH');
+ if( file_exists( $path.$type['handler_file'] ) ) {
+ include_once($path.$type['handler_file']);
+ if ( class_exists( $type['handler_class'] ) ) {
+ $content = new $type['handler_class'];
+ if (method_exists($content, 'getFilterSql')) {
+ $content->getFilterSql($pSql, $pBindVars, $pHash);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // -------------------- Action Logging Funtions -------------------- //
+
+ /**
+ * storeActionLog
+ * 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
+ */
+ public static function storeActionLogFromHash( $pParamHash = NULL ) {
+ global $gBitSystem;
+
+ if( $gBitSystem->isFeatureActive( 'liberty_action_log' ) && $this->verifyActionLog( $pParamHash ) ) {
+ $gBitSystem->mDb->associateInsert( BIT_DB_PREFIX."liberty_action_log", $pParamHash['action_log_store'] );
+ }
+ }
+
+ /**
+ * storeActionLog
+ * 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
+ */
+ public function storeActionLog( $pParamHash = NULL ) {
+ global $gBitSystem;
+
+ if( !empty( $this ) && @BitBase::verifyId( $this->mContentId ) ) {
+ $pParamHash['action_log']['content_id'] = $this->mContentId;
+ }
+ if( !empty( $this->mInfo['title'] ) ) {
+ $pParamHash['action_log']['title'] = $this->mInfo['title'];
+ }
+ $log_message = '';
+ if( empty( $pParamHash['action_log']['log_message'] ) && !empty( $this->mLogs ) ) {
+ foreach( $this->mLogs as $key => $msg ) {
+ $log_message .= "$msg";
+ }
+ $pParamHash['action_log']['log_message'] = $log_message;
+ }
+ $error_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
+ */
+ public static function verifyActionLog( &$pParamHash ) {
+ 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
+ $ret = TRUE;
+
+ // 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'];
+ } else {
+ $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'];
+ } else {
+ $ret = FALSE;
+ }
+ // IP of the user
+ 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';
+ } else {
+ $pParamHash['action_log']['ip'] = $_SERVER["REMOTE_ADDR"];
+ }
+ }
+ $pParamHash['action_log_store']['ip'] = $pParamHash['action_log']['ip'];
+ $pParamHash['action_log_store']['last_modified'] = $gBitSystem->getUTCTime();
+
+ // the log message
+ $log_message = '';
+ if( empty( $pParamHash['action_log']['log_message'] ) && !empty( $this ) && !empty( $this->mLogs ) ) {
+ foreach( $this->mLogs as $key => $msg ) {
+ $log_message .= "$msg";
+ }
+ } elseif( !empty( $pParamHash['action_log']['log_message'] ) ) {
+ $log_message = $pParamHash['action_log']['log_message'];
+ }
+
+ // trim down log
+ 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
+ $error_message = '';
+ 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'] )) {
+ $ret = FALSE;
+ }
+ // if we get as far as here, we can
+ return $ret;
+ }
+
+ /**
+ * Get a list of action log entries
+ *
+ * @param array $pListHash List options
+ * @access public
+ * @return List of entries on success, FALSE on failure
+ */
+ function getActionLogs( &$pListHash ) {
+ LibertyContent::prepGetList( $pListHash );
+
+ $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'] )." ";
+ }
+
+ $query = "
+ SELECT lal.*,
+ 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
+ FROM `".BIT_DB_PREFIX."liberty_action_log` lal
+ 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` )
+ $whereSql $orderSql";
+
+ $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'] );
+ $aux['display_name'] = BitUser::getDisplayNameFromHash( $aux );
+ $ret[] = $aux;
+ }
+
+ $query = "SELECT COUNT( lal.`user_id` ) FROM `".BIT_DB_PREFIX."liberty_action_log` lal $whereSql";
+ $pListHash['cant'] = $this->mDb->getOne( $query, $bindVars );
+ LibertyContent::postGetList( $pListHash );
+
+ return $ret;
+ }
+
+ /**
+ * expungeActionLog
+ *
+ * @param array $pTimeSpan Anything older than this timespan will be removed
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function expungeActionLog( $pTimeSpan = NULL ) {
+ global $gBitSystem;
+ $where = '';
+ $bindVars = array();
+ 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 );
+ return TRUE;
+ }
+
+ /**
+ * getAvailableContentStatus
+ *
+ * @access public
+ * @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.
+ */
+ function getAvailableContentStatuses( $pUserMinimum=-100, $pUserMaximum=100 ) {
+ global $gBitUser;
+ if( $gBitUser->hasPermission( 'p_liberty_edit_all_status' )) {
+ return( $this->mDb->getAssoc( "SELECT `content_status_id`,`content_status_name` FROM `".BIT_DB_PREFIX."liberty_content_status` ORDER BY `content_status_id`" ) );
+ } else {
+ 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
+ * @access public
+ * @return Status ID
+ */
+ function getContentStatus( $pDefault = 50, $pContentId = NULL ) {
+ $ret = NULL;
+ if ( @!BitBase::verifyId( $pContentId ) && $this->isValid() ){
+ if ( !( $ret = $this->getField( 'content_status_id' ) ) ){
+ $pContentId = $this->mContentId;
+ }
+ }
+ if( !is_null( $pContentId )) {
+ $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 $ret;
+ }
+
+ /**
+ * isDeleted status test
+ *
+ * @return true when the content status = -999
+ */
+ function isDeleted() {
+ global $gBitSystem;
+ return( $this->getField( 'content_status_id' ) <= $gBitSystem->getConfig( 'liberty_status_deleted', -999 ) );
+ }
+
+ /**
+ * isPrivate status test
+ *
+ * @return true when the content status = -999
+ */
+ function isPrivate() {
+ global $gBitSystem;
+ 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
+ */
+ function isProtected() {
+ global $gBitSystem;
+ return( $this->getField( 'content_status_id' ) <= $gBitSystem->getConfig( 'liberty_status_threshold_protected', -20 ) );
+ }
+
+ /**
+ * isHidden status test
+ *
+ * @return true when the content status = -10
+ */
+ function isHidden() {
+ global $gBitSystem;
+ return( $this->getField( 'content_status_id' ) <= $gBitSystem->getConfig( 'liberty_status_threshold_hidden', -10 ) );
+ }
+
+ /**
+ * isHidden status test
+ *
+ * @return true when the content status = -10
+ */
+ function isPublic() {
+ global $gBitSystem;
+ return( $this->getField( 'content_status_id' ) >= $gBitSystem->getConfig( 'liberty_status_threshold_public', 50 ) );
+ }
+
+ /**
+ * getContentStatusName
+ *
+ * @param array $pStatusId Status ID if not available in $this->mInfo['content_status_id']
+ * @access public
+ * @return The name of the content status based on the status id of the content
+ */
+ function getContentStatusName( $pStatusId = NULL ) {
+ $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( !is_null( $pStatusId )) {
+ if( $ret = $this->mDb->getOne( "SELECT `content_status_name` FROM `".BIT_DB_PREFIX."liberty_content_status` WHERE `content_status_id` = ?", array( $pStatusId ))) {
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Store Data into liberty_content_data
+ *
+ * @return bool true ( will not currently report a failure )
+ */
+ function storeData( $pData, $pType ) {
+ if( $this->mContentId ) {
+ $pData = trim( $pData );
+ if( empty( $pData ) ) {
+ $this->mDb->query( "DELETE FROM `".BIT_DB_PREFIX."liberty_content_data` WHERE `content_id`=? AND `data_type`=?", array( $this->mContentId, $pType ) );
+ } else {
+ 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`=?";
+ } else {
+ $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 ) );
+ }
+ }
+ return TRUE;
+ }
+
+ /**
+ * storeStatus store liberty contenet status
+ *
+ * @param array $pContentStatusId
+ * @access public
+ * @return void
+ */
+ function storeStatus( $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.
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ function isCommentable() {
+ if( $this->getPreference( 'allow_comments' ) == 'y' ) {
+ return TRUE;
+ } else {
+ $setting = $this->getField( 'allow_comments' );
+ return( $setting == TRUE || $setting == 'y' );
+ }
+ }
+
+ /**
+ * getListingPreview -- Returns a string with a preview of the content.
+ * @access public
+ * @return the preview string
+ **/
+ function getListingPreview( $pMixed ) {
+ global $gBitSystem, $gContent, $gBitSmarty;
+ // TODO!
+ return $ret;
+ }
+
+ /**
+ * 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.
+ *
+ * @access public
+ * @return the preview string
+ **/
+ function getPreview() {
+ 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);
+ // Save current gContent
+ $oldGContent = $gContent;
+ // Make us the content
+ $gContent = $this;
+
+ $ret = get_include_contents($this->getRenderFile());
+
+ // Return gBitSystem to full render mode
+ $gBitThemes->setFormatHeader( "html" );
+ // Clear the preview flag
+ $gBitSmarty->assign('liberty_preview', false);
+ // Restore gContent
+ $gContent = $oldGContent;
+
+ return $ret;
+ }
+
+}
diff --git a/includes/classes/LibertyMime.php b/includes/classes/LibertyMime.php
new file mode 100644
index 0000000..041f0d0
--- /dev/null
+++ b/includes/classes/LibertyMime.php
@@ -0,0 +1,1267 @@
+<?php
+/**
+ * Manages liberty Uploads
+ *
+ * @package liberty
+ */
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyContent.php' );
+
+// load the image processor plugin, check for loaded 'gd' since that is the default processor, and config might not be set.
+if( $gBitSystem->isFeatureActive( 'image_processor' ) || extension_loaded( 'gd' ) ) {
+ require_once( LIBERTY_PKG_PATH."plugins/processor.".$gBitSystem->getConfig( 'image_processor','gd' ).".php" );
+}
+
+// maximum size of the 'original' image when converted to jpg
+define( 'MAX_THUMBNAIL_DIMENSION', 20000 );
+
+/**
+ * LibertyMime class
+ *
+ * @package liberty
+ */
+class LibertyMime extends LibertyContent {
+ public $mStoragePrefs = NULL;
+
+ /**
+ * load the attachments for a given content id and then stuff them in mStorage
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ public function load() {
+ global $gLibertySystem;
+ if( @BitBase::verifyId( $this->mContentId )) {
+ // load up the content
+ LibertyContent::load();
+
+ // don't loadAttachmentPreferences() when we are forcing the installer since it breaks the login process before 2.1.0-beta
+ if( !defined( 'INSTALLER_FORCE' ) && !defined( 'LOGIN_VALIDATE' )) {
+ $this->loadAttachmentPreferences();
+ }
+
+ $query = "SELECT * FROM `".BIT_DB_PREFIX."liberty_attachments` la WHERE la.`content_id`=? ORDER BY la.`pos` ASC, la.`attachment_id` ASC";
+ if( $result = $this->mDb->query( $query,array( $this->mContentId ))) {
+ $this->mStorage = array();
+ while( $row = $result->fetchRow() ) {
+ if( !empty( $row['is_primary'] ) ) {
+ // used by edit tpl's among other things
+ $this->mInfo['primary_attachment_id'] = $row['attachment_id'];
+ } elseif( !$this->getField( 'primary_attachment_id' ) && !empty( $row['attachment_id'] ) ) {
+ // primary was not set by the above, default to first row. might be reset by later iterations via if is_primary above
+ $this->mInfo['primary_attachment_id'] = $row['attachment_id'];
+ }
+ if( $func = $gLibertySystem->getPluginFunction( $row['attachment_plugin_guid'], 'load_function', 'mime' )) {
+ // we will pass the preferences by reference that the plugin can easily update them
+ if( empty( $this->mStoragePrefs[$row['attachment_id']] )) {
+ $this->mStoragePrefs[$row['attachment_id']] = array();
+ }
+ $this->mStorage[$row['attachment_id']] = $func( $row, $this->mStoragePrefs[$row['attachment_id']], NULL );
+ } else {
+ print "No load_function for ".$row['attachment_plugin_guid'];
+ }
+ }
+ }
+ }
+ return( TRUE );
+ }
+
+ /**
+ * Store a new upload
+ *
+ * @param array $pStoreHash contains all data to store the gallery
+ * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why
+ * @access public
+ **/
+ public function store( &$pStoreHash ) {
+ global $gLibertySystem;
+ // make sure all the data is in order
+ if( LibertyMime::verify( $pStoreHash ) && ( !empty( $pStoreHash['skip_content_store'] ) || parent::store( $pStoreHash ) ) ) {
+ $this->StartTrans();
+ // files have been uploaded
+ if( !empty( $pStoreHash['upload_store']['files'] ) && is_array( $pStoreHash['upload_store']['files'] )) {
+
+ foreach( $pStoreHash['upload_store']['files'] as $key => $upload ) {
+ // if we don't have an upload, we'll simply update the file settings using the mime plugins
+ if( empty( $upload['tmp_name'] )) {
+ if( @BitBase::verifyId( $upload['attachment_id'] )) {
+ // since the form might have all options unchecked, we need to call the update function regardless
+ // currently i can't think of a better way to get the plugin guid back when $pStoreHash[plugin] is
+ // empty. - xing - Friday Jul 11, 2008 20:21:18 CEST
+ if( !empty( $this->mStorage[$upload['attachment_id']] )) {
+ $attachment = $this->mStorage[$upload['attachment_id']];
+ $data = array();
+ if( !empty( $pStoreHash['plugin'][$upload['attachment_id']][$attachment['attachment_plugin_guid']] )) {
+ $data = $pStoreHash['plugin'][$upload['attachment_id']][$attachment['attachment_plugin_guid']];
+ }
+ if( !$this->updateAttachmentParams( $upload['attachment_id'], $attachment['attachment_plugin_guid'], $data )) {
+ $this->mErrors['attachment_update'] = "There was a problem updating the file settings.";
+ }
+ }
+ }
+ // skip rest of process
+ continue;
+ }
+
+ $storeRow = $pStoreHash['upload_store'];
+ unset( $storeRow['files'] );
+
+ // copy by reference that filetype changes are made in lookupMimeHandler()
+ $storeRow['upload'] = &$upload;
+ if( isset( $pStoreHash['thumbnail'] ) ) {
+ $storeRow['upload']['thumbnail'] = $pStoreHash['thumbnail'];
+ }
+
+ // when content is created the content_id is only available after LibertyContent::store()
+ $storeRow['content_id'] = $pStoreHash['content_id'];
+
+ // let the plugin do the rest
+ $guid = $gLibertySystem->lookupMimeHandler( $upload );
+ $this->pluginStore( $storeRow, $guid, @BitBase::verifyId( $upload['attachment_id'] ));
+
+ // finally, we need to update the original hash with the new values
+ $pStoreHash['upload_store']['files'][$key] = $storeRow;
+ }
+ }
+
+ // some mime plugins might not have file uploads - these plugins will tell us what mime handlers they are using
+ if( !empty( $pStoreHash['mimeplugin'] ) && is_array( $pStoreHash['mimeplugin'] )) {
+ foreach( $pStoreHash['mimeplugin'] as $guid => $storeRow ) {
+ // check to see if we have anything worth storing in the array
+ $plugin_store = FALSE;
+ foreach( array_values( $storeRow ) as $value ) {
+ if( !empty( $value )) {
+ $plugin_store = TRUE;
+ }
+ }
+
+ if( !empty( $plugin_store )) {
+ // when content is created the content_id is only available after LibertyContent::store()
+ $storeRow['content_id'] = $pStoreHash['content_id'];
+ $this->pluginStore( $storeRow, $guid, @BitBase::verifyId( $upload['attachment_id'] ));
+ }
+ }
+ }
+
+ // deal with the primary attachment after we've dealt with all the files
+ $this->setPrimaryAttachment(
+ $pStoreHash['liberty_attachments']['primary'],
+ $pStoreHash['content_id'],
+ empty( $pStoreHash['liberty_attachments']['auto_primary'] ) || $pStoreHash['liberty_attachments']['auto_primary'] ? TRUE : FALSE
+ );
+
+ // Roll back if something went wrong
+ if( empty( $this->mErrors )) {
+ $this->CompleteTrans();
+ } else {
+ $this->mDb->RollbackTrans();
+ }
+ }
+
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * pluginStore will use a given plugin to store uploaded file data
+ *
+ * @param string $pGuid GUID of plugin
+ * @param array $pStoreHash Data to be prcessed and stored by the plugin
+ * @param boolean $pUpdate set to TRUE if this is just an update
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ public function pluginStore( &$pStoreHash, $pGuid, $pUpdate = FALSE ) {
+ global $gLibertySystem;
+ if( !empty( $pStoreHash ) && $verify_function = $gLibertySystem->getPluginFunction( $pGuid, 'verify_function' )) {
+ // pass along a pointer to the content object
+ $pStoreHash['this'] = &$this;
+ // verify the uploaded file using the plugin
+ if( $verify_function( $pStoreHash )) {
+ if( $process_function = $gLibertySystem->getPluginFunction( $pGuid, (( $pUpdate ) ? 'update_function' : 'store_function' ))) {
+ if( !$process_function( $pStoreHash )) {
+ $this->mErrors = array_merge( $this->mErrors, $pStoreHash['errors'] );
+ }
+ } else {
+ $this->mErrors['store_function'] = tra( 'No suitable store function found.' );
+ }
+ } else {
+ $this->mErrors = array_merge( $this->mErrors, $pStoreHash['errors'] );
+ }
+ } else {
+ $this->mErrors['verify_function'] = tra( 'No suitable verify function found.' );
+ }
+
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * Verify content that is about to be stored
+ *
+ * @param array $pStoreHash hash of all data that needs to be stored in the database
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason
+ * @todo If one of the uploaded files is an update, place the attachment_id with the upload hash in $_FILES or in _files_override
+ */
+ public function verify( &$pParamHash ) {
+ global $gBitUser, $gLibertySystem;
+
+ // check to see if we have any files to upload
+ if( isset( $pParamHash['_files_override'] )) {
+ // we have been passed in a manually stuffed files attachment, such as a custom uploader would have done.
+ // process this, and skip over $_FILES
+ $uploads = $pParamHash['_files_override'];
+ } elseif( !empty( $_FILES )) {
+ // we have some _FILES hanging around we will gobble up. This is inherently dagnerous chewing up a _FILES like this as
+ // it can cause premature storing of a _FILE if you are trying to store multiple pieces of content at once.
+ foreach( $_FILES as $key => $file ) {
+ if( !empty( $file['name'] ) || !empty( $file['attachment_id'] )) {
+ $uploads[$key] = $file;
+ }
+ }
+ }
+
+ // verify uploads
+ if( !empty( $uploads ) ) {
+ foreach( array_keys( $uploads ) as $file ) {
+ $pParamHash['upload_store']['files'][$file] = LibertyMime::verifyAttachment( $uploads[$file] );
+ }
+ }
+
+ // don't check for p_liberty_attach_attachments permission on bitpermuser class so registration with avatar upload works
+ if( strtolower( get_class( $this )) == 'bitpermuser' ) {
+ $pParamHash['upload_store']['no_perm_check'] = TRUE;
+ }
+
+ // check for the required permissions to upload a file to the liberty attachments area
+ if( !empty( $uploads ) && empty( $pParamHash['no_perm_check'] )) {
+ if( !$this->hasUserPermission( 'p_liberty_attach_attachments' )) {
+ $this->mErrors['permission'] = tra( 'You do not have permission to upload attachments.' );
+ }
+ }
+
+ // primary attachment. Allow 'none' to clear the primary.
+ if( !@BitBase::verifyId( $pParamHash['liberty_attachments']['primary'] ) && ( empty( $pParamHash['liberty_attachments']['primary'] ) || $pParamHash['liberty_attachments']['primary'] != 'none' ) ) {
+ $pParamHash['liberty_attachments']['primary'] = NULL;
+ }
+
+ // if we have an error we get them all by checking parent classes for additional errors
+ if( count( $this->mErrors ) > 0 ){
+ // check errors of LibertyContent since LibertyMime means to override the parent verify
+ LibertyContent::verify( $pParamHash );
+ }
+
+ return ( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * getThumbnailUrl will fetch the primary thumbnail for a given content. If nothing has been set, it will fetch the last thumbnail it can find.
+ *
+ * @param string $pSize
+ * @param array $pInfoHash
+ * @access public
+ * @return boolean TRUE on success, FALSE on failure - $this->mErrors will contain reason for failure
+ */
+ public function getThumbnailUrl( $pSize='small', $pInfoHash=NULL, $pSecondary=NULL, $pDefault=TRUE ) {
+ $ret = NULL;
+ if( !empty( $pInfoHash ) ) {
+ // do some stuff if we are given a hash of stuff
+ } elseif( $this->isValid() && !empty( $this->mStorage ) ) {
+ foreach( array_keys( $this->mStorage ) as $attachmentId ) {
+ if( !empty( $this->mStorage[$attachmentId]['is_primary'] ) ) {
+ break;
+ }
+ }
+ if( !empty( $this->mStorage[$attachmentId]['thumbnail_url'][$pSize] )) {
+ $ret = $this->mStorage[$attachmentId]['thumbnail_url'][$pSize];
+ }
+ }
+ if( $pDefault && empty( $ret ) ) {
+ $ret = parent::getThumbnailUrl( $pSize, $pInfoHash, $pSecondary );
+ }
+ return $ret;
+ }
+
+ /**
+ * updateAttachmentParams will update attachment parameters
+ *
+ * @param numeric $pAttachmentId attachment_id of the item we want the prefs from (optional)
+ * @param string $pPluginGuid GUID of the plugin that should process the data
+ * @param array $pParamHash Data to be processed by the plugin
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ public function updateAttachmentParams( $pAttachmentId, $pPluginGuid, $pParamHash = array() ) {
+ global $gLibertySystem;
+ $ret = FALSE;
+
+ if( BitBase::verifyId( $pAttachmentId )) {
+ if( !empty( $this ) && !empty( $this->mStorage[$pAttachmentId] )) {
+ $file = $this->mStorage[$pAttachmentId];
+ } else {
+ $file = $this->getAttachment( $pAttachmentId );
+ }
+
+ if( @BitBase::verifyId( $file['attachment_id'] ) && !empty( $pPluginGuid ) && ( $update_function = $gLibertySystem->getPluginFunction( $pPluginGuid, 'update_function', 'mime' ))) {
+ if( $update_function( $file, $pParamHash )) {
+ $ret = TRUE;
+ } else {
+ if( !empty( $file['errors'] )) {
+ $this->mErrors['param_update'] = $file['errors'];
+ } else {
+ $this->mErrors['param_update'] = tra( 'There was an unspecified error while updating the file.' );
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * verifyAttachment will perform a generic check if a file is valid for processing
+ *
+ * @param array $pFile file array from $_FILES
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ public function verifyAttachment( $pFile ) {
+ if( !empty( $pFile['tmp_name'] ) && is_file( $pFile['tmp_name'] ) && empty( $pFile['error'] ) || !empty( $pFile['attachment_id'] )) {
+ return $pFile;
+ }
+ }
+
+ /**
+ * Increment the item hit flag by 1
+ *
+ * @access public
+ * @param numeric $pAttachmentId Attachment ID
+ * @return adodb query result or FALSE
+ * @note we're abusing the hits column for download count.
+ */
+ public static function addDownloadHit( $pAttachmentId = NULL ) {
+ global $gBitUser, $gBitSystem;
+ if( @BitBase::verifyId( $pAttachmentId ) && $attachment = static::loadAttachment( $pAttachmentId )) {
+ if( !$gBitUser->isRegistered() || ( $gBitUser->isRegistered() && $gBitUser->mUserId != $attachment['user_id'] )) {
+ $bindVars = array( $pAttachmentId );
+ if( $gBitSystem->mDb->getOne( "SELECT `attachment_id` FROM `".BIT_DB_PREFIX."liberty_attachments` WHERE `attachment_id` = ? AND `hits` IS NULL", $bindVars )) {
+ $query = "UPDATE `".BIT_DB_PREFIX."liberty_attachments` SET `hits` = 1 WHERE `attachment_id` = ?";
+ } else {
+ $query = "UPDATE `".BIT_DB_PREFIX."liberty_attachments` SET `hits` = `hits`+1 WHERE `attachment_id` = ?";
+ }
+ return $gBitSystem->mDb->query( $query, $bindVars );
+ }
+ }
+ return FALSE;
+ }
+
+ // {{{ =================== Storage Directory Methods ====================
+ function getSourceUrl( $pParamHash=array() ) {
+ $ret = NULL;
+ if( empty( $pParamHash ) && !empty( $this ) ) {
+ $pParamHash = $this->mInfo;
+ }
+ if( $fileName = $this->getParameter( $pParamHash, 'file_name', $this->getField( 'file_name' ) ) ) {
+ $defaultFileName = liberty_mime_get_default_file_name( $fileName, $pParamHash['mime_type'] );
+ if( file_exists( $this->getStoragePath( $pParamHash ).$defaultFileName ) ) {
+ $ret = $this->getStorageUrl( $pParamHash ).$defaultFileName;
+ } else {
+ $ret = $this->getStorageUrl( $pParamHash ).basename( $fileName );
+ }
+ }
+ return $ret;
+ }
+
+ function getSourceFile( $pParamHash=array() ) {
+ $ret = NULL;
+ if( empty( $pParamHash ) && !empty( $this ) ) {
+ $pParamHash = $this->mInfo;
+ }
+ if( $fileName = $this->getParameter( $pParamHash, 'file_name', $this->getField( 'file_name' ) ) ) {
+ $defaultFileName = liberty_mime_get_default_file_name( $fileName, $pParamHash['mime_type'] );
+ $ret = $this->getStoragePath( $pParamHash ).$defaultFileName;
+ if( !file_exists( $ret ) ) {
+ $ret = $this->getStoragePath( $pParamHash ).basename( $fileName );
+ }
+ }
+ return $ret;
+ }
+
+
+ /**
+ * getStoragePath - get path to store files for the feature site_upload_dir. It creates a calculable hierarchy of directories
+ *
+ * @access public
+ * @author Christian Fowler<spider@steelsun.com>
+ * @param $pSubDir any desired directory below the StoragePath. this will be created if it doesn't exist
+ * @param $pCommon indicates not to use the 'common' branch, and not the 'users/.../<user_id>' branch
+ * @param $pRootDir override BIT_ROOT_DIR with a custom absolute path - useful for areas where no we access should be allowed
+ * @return string full path on local filsystem to store files.
+ */
+ function getStoragePath( $pParamHash, $pRootDir=NULL ) {
+ $ret = null;
+
+ if( $branch = liberty_mime_get_storage_branch( $pParamHash ) ) {
+ $ret = ( !empty( $pRootDir ) ? $pRootDir : STORAGE_PKG_PATH ).$branch;
+ mkdir_p($ret);
+ }
+ return $ret;
+ }
+
+
+ function getStorageUrl( $pParamHash ) {
+ return STORAGE_PKG_URL.liberty_mime_get_storage_branch( $pParamHash );
+ }
+
+ /**
+ * getStorageBranch - get url to store files for the feature site_upload_dir. It creates a calculable hierarchy of directories
+ *
+ * @access public
+ * @author Christian Fowler<spider@steelsun.com>
+ * @param $pSubDir any desired directory below the StoragePath. this will be created if it doesn't exist
+ * @param $pUserId indicates the 'users/.../<user_id>' branch or use the 'common' branch if null
+ * @param $pRootDir **deprecated, unused, will be removed in future relase**.
+ * @return string full path on local filsystem to store files.
+ */
+ function getStorageBranch( $pParamHash ) {
+ return liberty_mime_get_storage_branch( $pParamHash );
+ }
+
+ /**
+ * getStorageSubDirName get a filename based on the uploaded file
+ *
+ * @param array $pFileHash File information provided in $_FILES
+ * @access public
+ * @return appropriate sub dir name
+ */
+ function getStorageSubDirName( $pFileHash = NULL ) {
+ if( !empty( $pFileHash['mime_type'] ) && strstr( $pFileHash['mime_type'], "/" )) {
+ $ret = strtolower( preg_replace( "!/.*$!", "", $pFileHash['mime_type'] ));
+ // if we only got 'application' we will use the file extenstion
+ if( $ret == 'application' && !empty( $pFileHash['name'] ) && ( $pos = strrpos( $pFileHash['name'], "." )) !== FALSE ) {
+ $ret = strtolower( substr( $pFileHash['name'], $pos + 1 ));
+ }
+ }
+
+ // append an 's' to not create an image and images dir side by side (legacy reasons)
+ if( empty( $ret ) || $ret == 'image' ) {
+ $ret = 'images';
+ }
+
+ return $ret;
+ }
+
+ /**
+ * validateStoragePath make sure that the file/dir you are trying to delete is valid
+ *
+ * @param array $pPath absolute path to the file/dir we want to validate
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ public static function validateStoragePath( $pPath ) {
+ // file_exists checks for file or directory
+ if( !empty( $pPath ) && $pPath = realpath( $pPath )) {
+ // ensure path sanity
+ if( preg_match( "#^".realpath( STORAGE_PKG_PATH )."/(users|common)/\d+/\d+/\w+/\d+#", $pPath )) {
+ return $pPath;
+ }
+ }
+ }
+
+ // }}}
+
+
+ // {{{ =================== Attachment Methods ====================
+ /**
+ * Get a list of all available attachments
+ *
+ * @param array $pListHash
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getAttachmentList( &$pListHash ) {
+ global $gLibertySystem, $gBitUser, $gBitSystem;
+
+ LibertyContent::prepGetList( $pListHash );
+
+ // initialise some variables
+ $attachments = $ret = $bindVars = array();
+ $whereSql = $joinSql = $selectSql = '';
+
+ // only admin may view attachments from other users
+ if( !$gBitUser->isAdmin() ) {
+ $pListHash['user_id'] = $gBitUser->mUserId;
+ }
+
+ if( !empty( $pListHash['user_id'] ) ) {
+ $whereSql .= empty( $whereSql ) ? ' WHERE ' : ' AND ';
+ $whereSql .= " la.user_id = ? ";
+ $bindVars[] = $pListHash['user_id'];
+ }
+
+ if( !empty( $pListHash['content_id'] ) ) {
+ $whereSql .= empty( $whereSql ) ? ' WHERE ' : ' AND ';
+ $whereSql .= " la.`content_id` = ? ";
+ $selectSql .= " , la.`content_id` ";
+ $bindVars[] = $pListHash['content_id'];
+ }
+ $query = "SELECT la.* $selectSql FROM `".BIT_DB_PREFIX."liberty_attachments` la INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON(la.`user_id` = uu.`user_id`) $joinSql $whereSql";
+ $result = $this->mDb->query( $query, $bindVars, $pListHash['max_records'], $pListHash['offset'] );
+ while( $res = $result->fetchRow() ) {
+ $attachments[] = $res;
+ }
+
+ foreach( $attachments as $attachment ) {
+ if( $loadFunc = $gLibertySystem->getPluginFunction( $attachment['attachment_plugin_guid'], 'load_function', 'mime' )) {
+ /* @$prefs - quick hack to stop LibertyMime plugins from breaking until migration to LibertyMime is complete
+ * see expected arguments of liberty/plugins/mime.default.php::mime_default_load -wjames5
+ */
+ $prefs = array();
+ $ret[$attachment['attachment_id']] = $loadFunc( $attachment, $prefs );
+ }
+ }
+
+ // count all entries
+ $query = "SELECT COUNT(*)
+ FROM `".BIT_DB_PREFIX."liberty_attachments` la
+ INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON(la.`user_id` = uu.`user_id`)
+ $joinSql $whereSql
+ ";
+
+ $pListHash['cant'] = $this->mDb->getOne( $query, $bindVars );
+ $this->postGetList( $pListHash );
+
+ return $ret;
+ }
+
+ /**
+ * Expunges the content deleting attached attachments
+ */
+ function expunge() {
+ if( !empty( $this->mStorage ) && count( $this->mStorage )) {
+ foreach( array_keys( $this->mStorage ) as $i ) {
+ $this->expungeAttachment( $this->mStorage[$i]['attachment_id'] );
+ }
+ }
+ return LibertyContent::expunge();
+ }
+
+ /**
+ * expunge attachment from the database (and file system via the plugin if required)
+ *
+ * @param numeric $pAttachmentId attachment id of the item that should be deleted
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ function expungeAttachment( $pAttachmentId ) {
+ global $gLibertySystem, $gBitUser;
+ $ret = NULL;
+ if( @$this->verifyId( $pAttachmentId ) ) {
+ $sql = "SELECT `attachment_plugin_guid`, `user_id` FROM `".BIT_DB_PREFIX."liberty_attachments` WHERE `attachment_id` = ?";
+ if(( $row = $this->mDb->getRow( $sql, array( $pAttachmentId ))) && ( $this->isOwner( $row ) || $gBitUser->isAdmin() )) {
+ // check if we have the means available to remove this attachment
+ if(( $guid = $row['attachment_plugin_guid'] ) && $expungeFunc = $gLibertySystem->getPluginFunction( $guid, 'expunge_function', 'mime' )) {
+ // --- Do the final cleanup of liberty related tables ---
+
+ // there might be situations where we remove user images including portrait, avatar or logo
+ // This needs to happen before the plugin can do it's work due to constraints
+ $types = array( 'portrait', 'avatar', 'logo' );
+ foreach( $types as $type ) {
+ $sql = "UPDATE `".BIT_DB_PREFIX."users_users` SET `{$type}_attachment_id` = NULL WHERE `{$type}_attachment_id` = ?";
+ $this->mDb->query( $sql, array( $pAttachmentId ));
+ }
+
+ if( $expungeFunc( $pAttachmentId )) {
+ // Delete the attachment meta data, prefs and record.
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."liberty_attachment_meta_data` WHERE `attachment_id` = ?";
+ $this->mDb->query( $sql, array( $pAttachmentId ));
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."liberty_attachment_prefs` WHERE `attachment_id` = ?";
+ $this->mDb->query( $sql, array( $pAttachmentId ));
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."liberty_attachments` WHERE `attachment_id`=?";
+ $this->mDb->query( $sql, array( $pAttachmentId ));
+
+ // Remove attachment from memory
+ unset( $this->mStorage[$pAttachmentId] );
+ $ret = TRUE;
+ }
+ } else {
+ print( "Expunge function not found for this content!" );
+ }
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * loadAttachment will load details of a given attachment
+ *
+ * @param numeric $pAttachmentId Attachment ID of the attachment
+ * @param array $pParams optional parameters that might contain information like display thumbnail size
+ * @access public
+ * @return attachment details
+ */
+ public static function loadAttachment( $pAttachmentId, $pParams = NULL ) {
+ global $gLibertySystem, $gBitSystem;
+ $ret = NULL;
+
+ if( @BitBase::verifyId( $pAttachmentId )) {
+ $query = "SELECT * FROM `".BIT_DB_PREFIX."liberty_attachments` la WHERE la.`attachment_id`=?";
+ if( $result = $gBitSystem->mDb->query( $query, array( (int)$pAttachmentId ))) {
+ if( $row = $result->fetchRow() ) {
+ if( $func = $gLibertySystem->getPluginFunction( $row['attachment_plugin_guid'], 'load_function', 'mime' )) {
+ $sql = "SELECT `pref_name`, `pref_value` FROM `".BIT_DB_PREFIX."liberty_attachment_prefs` WHERE `attachment_id` = ?";
+ $prefs = $gBitSystem->mDb->getAssoc( $sql, array( $pAttachmentId ));
+ $ret = $func( $row, $prefs, $pParams );
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * getAttachment will load details of a given attachment
+ *
+ * @param numeric $pAttachmentId Attachment ID of the attachment
+ * @param array $pParams optional parameters that might contain information like display thumbnail size
+ * @access public
+ * @return attachment details
+ */
+ public function getAttachment( $pAttachmentId, $pParams = NULL ) {
+ global $gLibertySystem, $gBitSystem;
+ $ret = NULL;
+
+ if( @BitBase::verifyId( $pAttachmentId )) {
+ $query = "SELECT * FROM `".BIT_DB_PREFIX."liberty_attachments` la WHERE la.`attachment_id`=?";
+ if( $result = $gBitSystem->mDb->query( $query, array( (int)$pAttachmentId ))) {
+ if( $row = $result->fetchRow() ) {
+ if( $func = $gLibertySystem->getPluginFunction( $row['attachment_plugin_guid'], 'load_function', 'mime' )) {
+ $prefs = array();
+ // if the object is available, we'll copy the preferences by reference to allow the plugin to update them as needed
+ if( !empty( $this ) && !empty( $this->mStoragePrefs[$pAttachmentId] )) {
+ $prefs = &$this->mStoragePrefs[$pAttachmentId];
+ } else {
+ $prefs = static::getAttachmentPreferences( $pAttachmentId );
+ }
+ $ret = $func( $row, $prefs, $pParams );
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * setPrimaryAttachment will set is_primary 'y' for the specified
+ * attachment and will ensure that all others are set to 'n'
+ *
+ * @param mixed $pAttachmentId attachment id of the item we want to
+ * set as the primary attachment. Use 'none' to clear.
+ * @param numeric $pContentId content id we are working with.
+ * @param boolean $pAutoPrimary automatically set primary if there is only
+ * one attachment. Defaults to true.
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ public function setPrimaryAttachment( $pAttachmentId = NULL, $pContentId = NULL, $pAutoPrimary = TRUE ) {
+ global $gBitSystem;
+
+ $ret = FALSE;
+
+ // If we are not given an attachment id but we where told the
+ // content_id and we are supposed to auto set the primary then
+ // figure out which one it is
+ if( !@BitBase::verifyId( $pAttachmentId ) && ( empty( $pAttachmentId ) || $pAttachmentId != 'none' ) && @BitBase::verifyId( $pContentId ) && $pAutoPrimary ) {
+ $query = "
+ SELECT `attachment_id`
+ FROM `".BIT_DB_PREFIX."liberty_content` lc
+ INNER JOIN `".BIT_DB_PREFIX."liberty_attachments` la ON( lc.`content_id` = la.`content_id` )
+ WHERE lc.`content_id` = ?";
+ $pAttachmentId = $this->mDb->getOne( $query, array( $pContentId ));
+ }
+
+ // If we have an attachment_id we'll set it to this
+ if( @BitBase::verifyId( $pAttachmentId )) {
+ // get attachment we want to set primary
+ $attachment = $this->getAttachment( $pAttachmentId );
+
+ // Clear old primary. There can only be one!
+ $this->clearPrimaryAttachment( $attachment['content_id'] );
+
+ // now update the attachment to is_primary
+ $query = "
+ UPDATE `".BIT_DB_PREFIX."liberty_attachments`
+ SET `is_primary` = ? WHERE `attachment_id` = ?";
+ $this->mDb->query( $query, array( 'y', $pAttachmentId ));
+
+ $ret = TRUE;
+ // Otherwise, are we supposed to clear the primary entirely?
+ } elseif( @BitBase::verifyId( $pContentId ) && !empty( $pAttachmentId ) && $pAttachmentId == 'none' ) {
+ // Okay then do the job
+ $this->clearPrimaryAttachment( $pContentId );
+ }
+
+ return $ret;
+ }
+
+ /**
+ * clearPrimaryAttachment will remove the primary flag for all attachments
+ * with the given content_id
+ *
+ * @param numeric the content_id for which primary should be unset.
+ * @return TRUE on succes
+ */
+ function clearPrimaryAttachment( $pContentId ) {
+ $ret = FALSE;
+
+ if( @BitBase::verifyId( $pContentId )) {
+ $query = "
+ UPDATE `".BIT_DB_PREFIX."liberty_attachments`
+ SET `is_primary` = ? WHERE `content_id` = ?";
+ $this->mDb->query( $query, array( NULL, $pContentId ));
+ $ret = TRUE;
+ }
+
+ return $ret;
+ }
+ // }}}
+
+
+ /**
+ * === Attachment Preferences ===
+ */
+
+ /**
+ * Returns the attachment preference 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 getAttachmentPreference( $pAttachmentId, $pPrefName, $pPrefDefault = NULL ) {
+ if( is_null( $this->mStoragePrefs ) ) {
+ $this->loadAttachmentPreferences();
+ }
+
+ $ret = NULL;
+ if( @BitBase::verifyId( $pAttachmentId ) && !empty( $pPrefName )) {
+ if( isset( $this->mStoragePrefs ) && isset( $this->mStoragePrefs[$pAttachmentId][$pPrefName] )) {
+ $ret = $this->mStoragePrefs[$pAttachmentId][$pPrefName];
+ } else {
+ $ret = $pPrefDefault;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Returns the attachment preferences for a given attachment id
+ *
+ * @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 getAttachmentPreferences( $pAttachmentId ) {
+ global $gBitSystem;
+
+ $ret = array();
+ if( BitBase::verifyId( $pAttachmentId ) ) {
+ if( !empty( $this ) && is_subclass_of( $this, "LibertyMime" ) ) {
+ // we're loading from within object
+ if( is_null( $this->mStoragePrefs )) {
+ $this->loadAttachmentPreferences();
+ }
+
+ if( @BitBase::verifyId( $pAttachmentId ) && isset( $this->mStoragePrefs[$pAttachmentId] )) {
+ $ret = $this->mStoragePrefs[$pAttachmentId];
+ }
+ } else {
+ // if the object isn't loaded, we need to get the prefs from the database
+ $sql = "SELECT `pref_name`, `pref_value` FROM `".BIT_DB_PREFIX."liberty_attachment_prefs` WHERE `attachment_id` = ?";
+ $ret = $gBitSystem->mDb->getAssoc( $sql, array( (int)$pAttachmentId ));
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * setAttachmentPreference will set an attachment preferences without storing it in the database
+ *
+ * @param array $pAttachmentId
+ * @param array $pPrefName
+ * @param array $pPrefValue
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function setAttachmentPreference( $pAttachmentId, $pPrefName, $pPrefValue ) {
+ $this->mStoragePrefs[$pAttachmentId][$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.
+ *
+ * @param string Hash key for the prefs value
+ * @param string Value for the prefs hash key
+ */
+ function storeAttachmentPreference( $pAttachmentId, $pPrefName, $pPrefValue = NULL ) {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( @BitBase::verifyId( $pAttachmentId ) && !empty( $pPrefName )) {
+ $query = "DELETE FROM `".BIT_DB_PREFIX."liberty_attachment_prefs` WHERE `attachment_id` = ? AND `pref_name` = ?";
+ $bindvars = array( $pAttachmentId, $pPrefName );
+ $result = $gBitSystem->mDb->query( $query, $bindvars );
+ if( !is_null( $pPrefValue )) {
+ $query = "INSERT INTO `".BIT_DB_PREFIX."liberty_attachment_prefs` (`attachment_id`,`pref_name`,`pref_value`) VALUES(?, ?, ?)";
+ $bindvars[] = substr( $pPrefValue, 0, 250 );
+ $result = $gBitSystem->mDb->query( $query, $bindvars );
+ }
+
+ // this function might be called statically
+ if( !empty( $this ) && $this->isValid() ) {
+ $this->mStoragePrefs[$pAttachmentId][$pPrefName] = $pPrefValue;
+ }
+
+ $ret = TRUE;
+ }
+ return $ret;
+ }
+
+ /**
+ * 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)
+ * @param numeric $pAttachmentId attachment_id of the item we want the prefs from (optional)
+ * @access public
+ * @return array of preferences if $pContentId or $pAttachmentId is set or pass preferences on to $this->mStoragePrefs
+ */
+ function loadAttachmentPreferences( $pContentId = NULL ) {
+ global $gBitSystem;
+
+ if( !@BitBase::verifyId( $pContentId ) && $this->isValid() && @BitBase::verifyId( $this->mContentId )) {
+ $pContentId = $this->mContentId;
+ $store_prefs = TRUE;
+ }
+
+ $ret = array();
+ if( !empty( $this ) && !is_null( $this->mStoragePrefs )) {
+ $ret = $this->mStoragePrefs;
+ } elseif( @BitBase::verifyId( $pContentId )) {
+ $sql = "
+ SELECT lap.`attachment_id`, lap.`pref_name`, lap.`pref_value`
+ FROM `".BIT_DB_PREFIX."liberty_attachment_prefs` lap
+ INNER JOIN `".BIT_DB_PREFIX."liberty_attachments` la ON (la.`attachment_id` = lap.`attachment_id`)
+ WHERE la.`content_id` = ?";
+ $result = $gBitSystem->mDb->query( $sql, array( $pContentId ));
+ if( !empty( $result )) {
+ while( $aux = $result->fetchRow() ) {
+ $ret[$aux['attachment_id']][$aux['pref_name']] = $aux['pref_value'];
+ }
+ }
+ }
+
+ // if neither a content id nor an attachment id are given, we will place the results in mStoragePrefs
+ if( !empty( $store_prefs )) {
+ $this->mStoragePrefs = $ret;
+ } else {
+ return $ret;
+ }
+ }
+
+ /**
+ * expungeAttachmentPreferences will remove all attachment preferences of a given attachmtent
+ *
+ * @param array $pAttachmentId attachemnt we want to remove the prefs for
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ function expungeAttachmentPreferences( $pAttachmentId ) {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( @BitBase::verifyId( $pAttachmentId ) ) {
+ $sql = "DELETE FROM `".BIT_DB_PREFIX."liberty_attachment_prefs` WHERE `attachment_id` = ?";
+ $gBitSystem->mDb->query( $sql, array( $pAttachmentId ));
+ $ret = TRUE;
+ }
+ return $ret;
+ }
+
+ public static function getAttachmentDownloadUrl( $pAttachmentId ) {
+ global $gBitSystem;
+ $ret = NULL;
+ if( BitBase::verifyId( $pAttachmentId ) ) {
+ if( $gBitSystem->isFeatureActive( "pretty_urls" ) || $gBitSystem->isFeatureActive( "pretty_urls_extended" )) {
+ $ret = LIBERTY_PKG_URL."download/file/".$pAttachmentId;
+ } else {
+ $ret = LIBERTY_PKG_URL."download_file.php?attachment_id=".$pAttachmentId;
+ }
+ }
+ return $ret;
+ }
+
+ public function getDownloadUrl() {
+ $ret = "";
+ if( $this->isValid() && $this->getField( 'attachment_id' ) ) {
+ $ret = LibertyMime::getAttachmentDownloadUrl( $this->getField( 'attachment_id' ) );
+ }
+ return $ret;
+ }
+
+ // {{{ =================== Meta Methods ====================
+ /**
+ * storeMetaData
+ *
+ * @param numeric $pAttachmentId AttachmentID the data belongs to
+ * @param string $pType Type of data. e.g.: EXIF, ID3. This will default to "Meta Data"
+ * @param array $pStoreHash Data that needs to be stored in the database in an array. The key will be used as the meta_title.
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ public static function storeMetaData( $pAttachmentId, $pType = "Meta Data", $pStoreHash ) {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( @BitBase::verifyId( $pAttachmentId ) && !empty( $pType ) && !empty( $pStoreHash )) {
+ if( is_array( $pStoreHash )) {
+ foreach( $pStoreHash as $key => $data ) {
+ if( !is_array( $data )) {
+ // store the data in the meta table
+ $meta = array(
+ 'attachment_id' => $pAttachmentId,
+ 'meta_type_id' => LibertyMime::storeMetaId( $pType, 'type' ),
+ 'meta_title_id' => LibertyMime::storeMetaId( $key, 'title' ),
+ );
+
+ // remove this entry from the database if it already exists
+ $gBitSystem->mDb->query( "DELETE FROM `".BIT_DB_PREFIX."liberty_attachment_meta_data` WHERE `attachment_id` = ? AND `meta_type_id` = ? AND `meta_title_id` = ?", $meta );
+
+ // don't insert empty lines
+ if( !empty( $data )) {
+ $meta['meta_value'] = $data;
+ $gBitSystem->mDb->associateInsert( BIT_DB_PREFIX."liberty_attachment_meta_data", $meta );
+ }
+
+ $ret = TRUE;
+ } else {
+ // should we recurse?
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * storeMetaId
+ *
+ * @param string $pDescription Description of meta key. e.g.: Exif, ID3, Album, Artist
+ * @param string $pTable Table data is stored in - either 'type' or 'title'
+ * @access public
+ * @return newly stored ID on success, FALSE on failure
+ */
+ private static function storeMetaId( $pDescription, $pTable = 'type' ) {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( !empty( $pDescription )) {
+ if( !( $ret = LibertyMime::getMetaId( $pDescription, $pTable ))) {
+ $store = array(
+ "meta_{$pTable}_id" => $gBitSystem->mDb->GenID( "liberty_meta_{$pTable}s_id_seq" ),
+ "meta_{$pTable}" => LibertyMime::normalizeMetaDescription( $pDescription ),
+ );
+ $gBitSystem->mDb->associateInsert( BIT_DB_PREFIX."liberty_meta_{$pTable}s", $store );
+ $ret = $store["meta_{$pTable}_id"];
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * getMetaData
+ *
+ * @param numeric $pAttachmentId AttachmentID the data belongs to
+ * @param string $pType Type of data. e.g.: EXIF, ID3.
+ * @param string $pTitle Title of data. e.g.: Artist, Album.
+ * @access public
+ * @return array with meta data on success, FALSE on failure
+ * $note: Output format varies depending on requested data
+ */
+ public static function getMetaData( $pAttachmentId, $pType = NULL, $pTitle = NULL ) {
+ global $gBitSystem;
+ $ret = array();
+ if( @BitBase::verifyId( $pAttachmentId )) {
+ $bindVars = array( $pAttachmentId );
+ $whereSql = "";
+ if( !empty( $pType ) && !empty( $pTitle )) {
+
+ // we have a type and title - only one entry will be returned
+ $bindVars[] = LibertyMime::normalizeMetaDescription( $pType );
+ $bindVars[] = LibertyMime::normalizeMetaDescription( $pTitle );
+
+ $sql = "
+ SELECT lmd.`meta_value`
+ FROM `".BIT_DB_PREFIX."liberty_attachment_meta_data` lmd
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_types` lmtype ON( lmd.`meta_type_id` = lmtype.`meta_type_id` )
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_titles` lmtitle ON( lmd.`meta_title_id` = lmtitle.`meta_title_id` )
+ WHERE lmd.`attachment_id` = ? AND lmtype.`meta_type` = ? AND lmtitle.`meta_title` = ?";
+ $ret = $gBitSystem->mDb->getOne( $sql, $bindVars );
+
+ } elseif( !empty( $pType )) {
+
+ // only type given - return array with all vlues of this type
+ $bindVars[] = LibertyMime::normalizeMetaDescription( $pType );
+
+ $sql = "
+ SELECT lmtitle.`meta_title`, lmd.`meta_value`
+ FROM `".BIT_DB_PREFIX."liberty_attachment_meta_data` lmd
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_types` lmtype ON( lmd.`meta_type_id` = lmtype.`meta_type_id` )
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_titles` lmtitle ON( lmd.`meta_title_id` = lmtitle.`meta_title_id` )
+ WHERE lmd.`attachment_id` = ? AND lmtype.`meta_type` = ?";
+ $ret = $gBitSystem->mDb->getAssoc( $sql, $bindVars );
+
+ } elseif( !empty( $pTitle )) {
+
+ // only title given - return array with all vlues with this title
+ $bindVars[] = LibertyMime::normalizeMetaDescription( $pTitle );
+
+ $sql = "
+ SELECT lmtype.`meta_type`, lmd.`meta_value`
+ FROM `".BIT_DB_PREFIX."liberty_attachment_meta_data` lmd
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_types` lmtype ON( lmd.`meta_type_id` = lmtype.`meta_type_id` )
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_titles` lmtitle ON( lmd.`meta_title_id` = lmtitle.`meta_title_id` )
+ WHERE lmd.`attachment_id` = ? AND lmtitle.`meta_title` = ?";
+ $ret = $gBitSystem->mDb->getAssoc( $sql, $bindVars );
+
+ } else {
+
+ // nothing given - return nested array based on type and title
+ $sql = "
+ SELECT lmd.`attachment_id`, lmd.`meta_value`, lmtype.`meta_type`, lmtitle.`meta_title`
+ FROM `".BIT_DB_PREFIX."liberty_attachment_meta_data` lmd
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_types` lmtype ON( lmd.`meta_type_id` = lmtype.`meta_type_id` )
+ INNER JOIN `".BIT_DB_PREFIX."liberty_meta_titles` lmtitle ON( lmd.`meta_title_id` = lmtitle.`meta_title_id` )
+ WHERE lmd.`attachment_id` = ?";
+
+ $result = $gBitSystem->mDb->query( $sql, $bindVars );
+ while( $aux = $result->fetchRow() ) {
+ $ret[$aux['meta_type']][$aux['meta_title']] = $aux['meta_value'];
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * expungeMetaData will remove the meta data for a given attachment
+ *
+ * @param array $pAttachmentId Attachment ID of attachment
+ * @access public
+ * @return query result
+ */
+ function expungeMetaData( $pAttachmentId ) {
+ global $gBitSystem;
+ if( @BitBase::verifyId( $pAttachmentId )) {
+ return $gBitSystem->mDb->query( "DELETE FROM `".BIT_DB_PREFIX."liberty_attachment_meta_data` WHERE `attachment_id` = ?", array( $pAttachmentId ));
+ }
+ }
+
+ /**
+ * getMetaId
+ *
+ * @param string $pDescription Description of meta key. e.g.: Exif, ID3, Album, Artist
+ * @param string $pTable Table data is stored in - either 'type' or 'title'
+ * @access public
+ * @return meta type or title id on sucess, FALSE on failure
+ */
+ private static function getMetaId( $pDescription, $pTable = 'type' ) {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( !empty( $pDescription ) && ( $pTable == 'type' || $pTable == 'title' )) {
+ $ret = $gBitSystem->mDb->getOne( "SELECT `meta_{$pTable}_id` FROM `".BIT_DB_PREFIX."liberty_meta_{$pTable}s` WHERE `meta_{$pTable}` = ?", array( LibertyMime::normalizeMetaDescription( $pDescription )));
+ }
+ return $ret;
+ }
+
+ /**
+ * getMetaDescription
+ *
+ * @param string $pId ID of type or title we want the description for
+ * @param string $pTable Table data is stored in - either 'type' or 'title'
+ * @access public
+ * @return description on sucess, FALSE on failure
+ */
+ function getMetaDescription( $pId, $pTable = 'type' ) {
+ global $gBitSystem;
+ $ret = FALSE;
+ if( @BitBase::verifyId( $pId )) {
+ $ret = $gBitSystem->mDb->getOne( "SELECT `meta_{$pTable}` FROM `".BIT_DB_PREFIX."liberty_meta_{$pTable}s` WHERE `meta_{$pTable}_id` = ?", array( $pId ));
+ }
+ return $ret;
+ }
+
+ /**
+ * normalizeMetaDescription
+ *
+ * @param string $pDescription Description of meta key. e.g.: Exif, ID3, Album, Artist
+ * @access public
+ * @return normalized meta description that can be used as a guid
+ */
+ public static function normalizeMetaDescription( $pDescription ) {
+ return strtolower( substr( preg_replace( "![^a-zA-Z0-9]!", "", trim( $pDescription )), 0, 250 ));
+ }
+ // }}}
+}
+
+/**
+ * mime_get_storage_sub_dir_name get a filename based on the uploaded file
+ *
+ * @param array $pFileHash File information provided in $_FILES
+ * @access public
+ * @return appropriate sub dir name
+ */
+if( !function_exists( 'liberty_mime_get_storage_sub_dir_name' )) {
+ function liberty_mime_get_storage_sub_dir_name( $pFileHash = NULL ) {
+ if( !empty( $pFileHash['type'] ) ) {
+ // type is from upload file hash
+ $mimeType = $pFileHash['type'];
+ } elseif( !empty( $pFileHash['mime_type'] ) ) {
+ // mime_type is from liberty_files
+ $mimeType = $pFileHash['mime_type'];
+ }
+
+ if( !empty( $mimeType ) && strstr( $mimeType, "/" )) {
+ $ret = strtolower( preg_replace( "!/.*$!", "", $mimeType ));
+ // if we only got 'application' we will use the file extenstion
+ if( $ret == 'application' && !empty( $pFileHash['name'] ) && ( $pos = strrpos( $pFileHash['name'], "." )) !== FALSE ) {
+ $ret = strtolower( substr( $pFileHash['name'], $pos + 1 ));
+ }
+ }
+
+ // append an 's' to not create an image and images dir side by side (legacy reasons)
+ if( empty( $ret ) || $ret == 'image' ) {
+ $ret = 'images';
+ }
+ return $ret;
+ }
+}
+
+/**
+ * liberty_mime_get_storage_branch - get url to store files for the feature site_upload_dir. It creates a calculable hierarchy of directories
+ *
+ * @access public
+ * @author Christian Fowler<spider@steelsun.com>
+ * @param $pParamHash key=>value pairs to determine path. Possible keys in descending directory depth are: 'user_id' indicates the 'users/.../<user_id>' branch or use the 'common' branch if null, 'package' - any desired directory below the StoragePath. this will be created if it doesn't exist, 'sub_dir' - the sub-directory in the package organization directory, this is often a primary id such as attachment_id
+ * @return string full path on local filsystem to store files.
+ */
+if( !function_exists( 'liberty_mime_get_storage_branch' )) {
+ function liberty_mime_get_storage_branch( $pParamHash ) {
+ // *PRIVATE FUNCTION. GO AWAY! DO NOT CALL DIRECTLY!!!
+ global $gBitSystem;
+ $pathParts = array();
+
+
+ if( $pUserId = BitBase::getParameter( $pParamHash, 'user_id' ) ) {
+ $pathParts[] = 'users';
+ $pathParts[] = (int)($pUserId % 1000);
+ $pathParts[] = $pUserId;
+ } elseif( $pAttachmentId = BitBase::getParameter( $pParamHash, 'attachment_id' ) ) {
+ $pathParts[] = 'attachments';
+ $pathParts[] = (int)($pAttachmentId % 1000);
+ $pathParts[] = $pAttachmentId;
+ } else {
+ $pathParts[] = 'common';
+ }
+
+ if( $pPackage = BitBase::getParameter( $pParamHash, 'package' ) ) {
+ $pathParts[] = $pPackage;
+ }
+ // In case $pSubDir is multiple levels deep we'll need to mkdir each directory if they don't exist
+ if( $pSubDir = BitBase::getParameter( $pParamHash, 'sub_dir' ) ){
+ $pSubDirParts = preg_split('#/#',$pSubDir);
+ foreach ($pSubDirParts as $subDir) {
+ $pathParts[] = $subDir;
+ }
+ } else {
+ $pSubDir = liberty_mime_get_storage_sub_dir_name( $pParamHash );
+ }
+
+ $fullPath = implode( '/', $pathParts ).'/';
+ if( BitBase::getParameter( $pParamHash, 'create_dir', TRUE ) ){
+ if( !file_exists( STORAGE_PKG_PATH.$fullPath ) ) {
+ mkdir_p( STORAGE_PKG_PATH.$fullPath );
+ }
+ }
+
+ return $fullPath;
+ }
+}
+
+if( !function_exists( 'liberty_mime_get_storage_url' )) {
+ function liberty_mime_get_storage_url( $pParamHash ) {
+ return STORAGE_PKG_URL.liberty_mime_get_storage_branch( $pParamHash );
+ }
+}
+
+if( !function_exists( 'liberty_mime_get_storage_path' )) {
+ function liberty_mime_get_storage_path( $pParamHash ) {
+ return STORAGE_PKG_PATH.liberty_mime_get_storage_branch( $pParamHash );
+ }
+}
+
+if( !function_exists( 'liberty_mime_get_source_url' )) {
+ function liberty_mime_get_source_url( $pParamHash ) {
+ $fileName = BitBase::getParameter( $pParamHash, 'file_name' );
+ if( empty( $pParamHash['package'] ) ) {
+ $pParamHash['package'] = liberty_mime_get_storage_sub_dir_name( array( 'mime_type' => BitBase::getParameter( $pParamHash, 'mime_type' ), 'name' => $fileName ) );
+ }
+ if( empty( $pParamHash['sub_dir'] ) ) {
+ $pParamHash['sub_dir'] = BitBase::getParameter( $pParamHash, 'attachment_id' );
+ }
+ $defaultFileName = liberty_mime_get_default_file_name( $fileName, $pParamHash['mime_type'] );
+ $fileBranch = liberty_mime_get_storage_branch( $pParamHash );
+ if( file_exists( STORAGE_PKG_PATH.$fileBranch.$defaultFileName ) ) {
+ $ret = STORAGE_PKG_URL.$fileBranch.$defaultFileName;
+ } else {
+ $ret = STORAGE_PKG_URL.$fileBranch.basename( BitBase::getParameter( $pParamHash, 'file_name' ) );
+ }
+ return $ret;
+ }
+}
+
+if( !function_exists( 'liberty_mime_get_source_file' )) {
+ function liberty_mime_get_source_file( $pParamHash ) {
+ $fileName = BitBase::getParameter( $pParamHash, 'file_name' );
+ if( empty( $pParamHash['package'] ) ) {
+ $pParamHash['package'] = liberty_mime_get_storage_sub_dir_name( array( 'mime_type' => BitBase::getParameter( $pParamHash, 'mime_type' ), 'name' => $fileName ) );
+ }
+ if( empty( $pParamHash['sub_dir'] ) ) {
+ $pParamHash['sub_dir'] = BitBase::getParameter( $pParamHash, 'attachment_id' );
+ }
+ $defaultFileName = liberty_mime_get_default_file_name( $fileName, $pParamHash['mime_type'] );
+ $ret = STORAGE_PKG_PATH.liberty_mime_get_storage_branch( $pParamHash ).$defaultFileName;
+ if( !file_exists( $ret ) ) {
+ $ret = STORAGE_PKG_PATH.liberty_mime_get_storage_branch( $pParamHash ).basename( BitBase::getParameter( $pParamHash, 'file_name' ) );
+ }
+ return $ret;
+ }
+}
+
+if( !function_exists( 'liberty_mime_get_default_file_name' )) {
+ function liberty_mime_get_default_file_name( $pFileName, $pMimeType ) {
+ global $gBitSystem;
+ if( $gBitSystem->isFeatureActive( 'liberty_originalize_file_names' ) ) {
+ $ret = 'original.'.$gBitSystem->getMimeExtension( $pMimeType );
+ } else {
+ $ret = $pFileName;
+ }
+ return $ret;
+ }
+}
+
+/* vim: :set fdm=marker : */
+
+?>
diff --git a/includes/classes/LibertyStructure.php b/includes/classes/LibertyStructure.php
new file mode 100644
index 0000000..7cb5755
--- /dev/null
+++ b/includes/classes/LibertyStructure.php
@@ -0,0 +1,1153 @@
+<?php
+/**
+ * Management of Liberty Content
+ *
+ * @package liberty
+ * @author spider <spider@steelsun.com>
+ */
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyBase.php' );
+
+/**
+ * System class for handling the liberty package
+ *
+ * @package liberty
+ */
+class LibertyStructure extends LibertyBase {
+ public $mStructureId;
+
+ function __construct( $pStructureId=NULL, $pContentId=NULL ) {
+ // we need to init our database connection early
+ parent::__construct();
+ $this->mStructureId = $pStructureId;
+ $this->mContentId = $pContentId;
+ $this->load();
+ }
+
+ function load( $pContentId=NULL ) {
+ if( $this->mStructureId || $this->mContentId ) {
+ if( $this->mInfo = $this->getNode( $this->mStructureId, $this->mContentId ) ) {
+ global $gLibertySystem;
+ $this->mStructureId = $this->mInfo['structure_id'];
+ $this->mContentId = $this->mInfo['content_id'];
+ $this->mInfo['content_type'] = $gLibertySystem->mContentTypes[$this->mInfo['content_type_guid']];
+ }
+ }
+ return( $this->mInfo && count( $this->mInfo ) );
+ }
+
+ /**
+ * get the details to a given node
+ *
+ * @param array $pStructureId Structure ID of the node
+ * @param array $pContentId Content ID of the node
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getNode( $pStructureId=NULL, $pContentId=NULL ) {
+ global $gLibertySystem, $gBitSystem;
+ static $sStructureNodeCache;
+ $contentTypes = $gLibertySystem->mContentTypes;
+
+ if( @$this->verifyId( $pStructureId ) ) {
+ if (!empty($sStructureNodeCache['structure_id'][$pStructureId])) {
+ return $sStructureNodeCache['structure_id'][$pStructureId];
+ }
+ $where = ' WHERE ls.`structure_id`=?';
+ $bindVars = array( $pStructureId );
+ } elseif( @$this->verifyId( $pContentId ) ) {
+ if (!empty($sStructureNodeCache['content_id'][$pContentId])) {
+ return $sStructureNodeCache['content_id'][$pContentId];
+ }
+ $where = ' WHERE ls.`content_id`=?';
+ $bindVars = array( $pContentId );
+ }
+
+ $ret = NULL;
+ $query = 'SELECT ls.*, lc.`user_id`, lc.`title`, lc.`content_type_guid`, uu.`login`, uu.`real_name`
+ FROM `'.BIT_DB_PREFIX.'liberty_structures` ls
+ INNER JOIN `'.BIT_DB_PREFIX.'liberty_content` lc ON (ls.`content_id`=lc.`content_id`)
+ LEFT JOIN `'.BIT_DB_PREFIX.'users_users` uu ON ( uu.`user_id` = lc.`user_id` )' . $where;
+
+ if( $result = $this->mDb->query( $query, $bindVars ) ) {
+ $ret = $result->fetchRow();
+ }
+
+ if( !empty( $contentTypes[$ret['content_type_guid']] ) ) {
+ // quick alias for code readability
+ $type = &$contentTypes[$ret['content_type_guid']];
+ if( empty( $type['content_object'] ) && !empty( $gBitSystem->mPackages[$type['handler_package']] ) ) {
+ // create *one* object for each object *type* to call virtual methods.
+ $handlerFile = $gBitSystem->mPackages[$type['handler_package']]['path'].$type['handler_file'];
+ if( file_exists( $handlerFile ) ) {
+ include_once( $handlerFile );
+ if( class_exists( $type['handler_class'] ) ) {
+ $type['content_object'] = new $type['handler_class']();
+ }
+ }
+ }
+ if( !empty( $type['content_object'] ) && is_object( $type['content_object'] ) ) {
+ $ret['title'] = $type['content_object']->getTitleFromHash( $ret );
+ }
+ }
+
+ $sStructureNodeCache['structure_id'][$ret['structure_id']] = $ret;
+ $sStructureNodeCache['content_id'][$ret['content_id']] = $ret;
+
+ return $ret;
+ }
+
+ function hasViewPermission( $pVerifyAccessControl=TRUE ) {
+ $ret = FALSE;
+ if( !empty( $this->mInfo['content_object'] ) && is_a( $this->mInfo['content_object'], 'LibertyContent' ) ) {
+ return( $this->mInfo['content_object']->hasUpdatePermission( $pVerifyAccessControl ) || empty( $this->mInfo['content_object']->mViewContentPerm ) || $this->mInfo['content_object']->hasUserPermission( $this->mInfo['content_object']->mViewContentPerm, $pVerifyAccessControl ));
+ }
+ return $ret;
+ }
+
+ /**
+ * Check if a node is a root node
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function isRootNode() {
+ $ret = FALSE;
+ if( @$this->verifyId( $this->mInfo['structure_id'] ) ) {
+ $ret = $this->mInfo['root_structure_id'] == $this->mInfo['structure_id'];
+ }
+ return $ret;
+ }
+
+ /**
+ * get the title of the root node
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getRootObject() {
+ $ret = NULL;
+ if( !empty( $this->mInfo['root_structure_id'] ) ) {
+ if( $rootHash = $this->mDb->getRow( "SELECT * FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `structure_id` = ?", array( $this->mInfo['root_structure_id'] ) ) ) {
+ $ret = $this->getLibertyObject( $rootHash['content_id'] );
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * get the title of the root node
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getRootTitle() {
+ $ret = NULL;
+ if( isset( $this->mInfo['structure_path'][0]['title'] ) ) {
+ $ret = $this->mInfo['structure_path'][0]['title'];
+ }
+ return $ret;
+ }
+
+ /**
+ * if you only have a structure id and you want to figure out the root structure id, use this
+ *
+ * @param array $pParamHash['structure_id'] is the structure id from which you want to figure out the root structure id
+ * @access public
+ * @return none. updates $pParamHash['root_structure_id'] by reference
+ */
+ function getRootStructureId( &$pParamHash ) {
+ if( @BitBase::verifyId( $pParamHash['root_structure_id'] ) ) {
+ $pParamHash['root_structure_id'] = $pParamHash['root_structure_id'];
+ } elseif( @BitBase::verifyId( $this->mInfo['root_structure_id'] ) ) {
+ $pParamHash['root_structure_id'] = $this->mInfo['root_structure_id'];
+ } elseif( @BitBase::verifyId( $pParamHash['structure_id'] ) ) {
+ $pParamHash['root_structure_id'] = $this->mDb->getOne( "SELECT `root_structure_id` FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `structure_id` = ?", array( $pParamHash['structure_id'] ) );
+ } else {
+ $pParamHash['root_structure_id'] = NULL;
+ }
+ }
+
+ // This is a utility function mainly used for upgrading sites.
+ function setTreeRoot( $pRootId, $pTree ) {
+ foreach( $pTree as $structRow ) {
+ $this->mDb->query( "UPDATE `".BIT_DB_PREFIX."liberty_structures` SET `root_structure_id`=? WHERE `structure_id`=?", array( $pRootId, $structRow["structure_id"] ) );
+ if( !empty( $structRow["sub"] ) ) {
+ $this->setTreeRoot( $pRootId, $structRow["sub"] );
+ }
+ }
+ }
+
+
+ function isValid() {
+ return( $this->verifyId( $this->mStructureId ) );
+ }
+
+ /**
+ * loadNavigation
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function loadNavigation() {
+ if( $this->isValid() ) {
+ $this->mInfo["prev"] = null;
+ // Get structure info for this page
+ if( !$this->isRootNode() && ($prev_structure_id = $this->getPrevStructureNode( $this->mStructureId )) ) {
+ $this->mInfo["prev"] = $this->getNode($prev_structure_id);
+ }
+ $next_structure_id = $this->getNextStructureNode( $this->mStructureId );
+ $this->mInfo["next"] = null;
+ if (isset($next_structure_id)) {
+ $this->mInfo["next"] = $this->getNode( $next_structure_id) ;
+ }
+ $this->mInfo["parent"] = $this->getStructureParentInfo( $this->mStructureId );
+ $this->mInfo["home"] = $this->getNode( $this->mStructureId );
+ }
+ return TRUE;
+ }
+
+ function loadPath() {
+ if( $this->isValid() ) {
+ $this->mInfo['structure_path'] = $this->getPath( $this->mStructureId );
+ }
+ return( !empty( $this->mInfo['structure_path'] ) );
+ }
+
+ /**
+ * This can be used to construct a path from the structure head to the requested page.
+ * @returns an array of page_info arrays.
+ */
+ function getPath( $pStructureId ) {
+ $structure_path = array();
+ $page_info = $this->getNode($pStructureId);
+
+ if ($page_info["parent_id"]) {
+ $structure_path = $this->getPath($page_info["parent_id"]);
+ }
+ $structure_path[] = $page_info;
+ return $structure_path;
+ }
+
+ /**
+ * Get full structure from database
+ * @param $pStructureId structure for which we want structure
+ * @return full structure
+ */
+ function getStructure( &$pParamHash ) {
+ global $gBitSystem, $gLibertySystem;
+ // make sure we have the correct id to get the entire structure
+ LibertyStructure::getRootStructureId( $pParamHash );
+
+ $ret = FALSE;
+
+ if( @BitBase::verifyId( $pParamHash['root_structure_id'] ) ) {
+ // Get all nodes for this structure
+ $query = "SELECT ls.*, lc.`user_id`, lc.`title`, lc.`content_type_guid`, uu.`login`, uu.`real_name`
+ FROM `".BIT_DB_PREFIX."liberty_structures` ls
+ INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON ( ls.`content_id` = lc.`content_id` )
+ INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON ( uu.`user_id` = lc.`user_id` )
+ WHERE ls.`root_structure_id` = ? ORDER BY `pos` ASC";
+ $result = $this->mDb->query( $query, array( $pParamHash['root_structure_id'] ) );
+
+ $subs = array();
+ $row_max = $result->numRows();
+ $contentTypes = $gLibertySystem->mContentTypes;
+ while( $res = $result->fetchRow() ) {
+ $aux = array();
+ $aux = $res;
+ if( !empty( $contentTypes[$res['content_type_guid']] ) ) {
+ // quick alias for code readability
+ $type = &$contentTypes[$res['content_type_guid']];
+ if( empty( $type['content_object'] ) ) {
+ // create *one* object for each object *type* to call virtual methods.
+ $handlerFile = $gBitSystem->mPackages[$type['handler_package']]['path'].$type['handler_file'];
+ if( file_exists( $handlerFile ) ) {
+ include_once( $handlerFile );
+ if( class_exists( $type['handler_class'] ) ) {
+ $type['content_object'] = new $type['handler_class']();
+ }
+ }
+ }
+ if( !empty( $pParamHash['thumbnail_size'] ) ) {
+ $aux['content_object'] = new $type['handler_class']( NULL, $aux['content_id'] );
+ if( $aux['content_object']->load() ) {
+ $aux['thumbnail_url'] = $aux['content_object']->getThumbnailUrl( $pParamHash['thumbnail_size'] );
+ }
+ }
+ if( !empty( $type['content_object'] ) && is_object( $type['content_object'] ) ) {
+ $aux['title'] = $type['content_object']->getTitleFromHash( $aux );
+ }
+ $ret[$aux['structure_id']] = $aux;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Get all structures in $pStructureHash that have a given parent_id
+ * @param $pStructureHash full menu as supplied by '$this->getItemList( $pMenuId );'
+ * @return array of nodes with a given parent_id
+ */
+ function getChildNodes( $pStructureHash, $pParentId = 0 ) {
+ $ret = array();
+ if( !empty( $pStructureHash ) ) {
+ foreach( $pStructureHash as $node ) {
+ if( $node['parent_id'] == $pParentId ) {
+ $ret[] = $node;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Create a usable array from the data in the database from getStructure()
+ * @param $pStructureHash raw structure data from database
+ * @return nicely formatted and cleaned up structure array
+ */
+ function createSubTree( $pStructureHash, $pParentId = 0, $pParentPos = '', $pLevel = 0 ) {
+ $ret = array();
+ // get all child menu Nodes for this structure_id
+ $children = LibertyStructure::getChildNodes( $pStructureHash, $pParentId );
+ $pos = 1;
+ $row_max = count( $children );
+
+ // we need to insert the root structure item first
+ if (!$pLevel && !empty($pStructureHash)) {
+ foreach( $pStructureHash as $node ) {
+ if( ( $pParentId == 0 && $node['structure_id'] == $node['root_structure_id'] ) || $node['structure_id'] == $pParentId) {
+ $aux = $node;
+ $aux["first"] = true;
+ $aux["last"] = true;
+ $aux["pos"] = '';
+ $aux["level"] = $pLevel++;
+ $ret[] = $aux;
+ }
+ }
+ }
+
+ foreach( $children as $node ) {
+ $aux = $node;
+ $aux['level'] = $pLevel;
+ $aux['first'] = ( $pos == 1 );
+ $aux['last'] = FALSE;
+ $aux['has_children'] = FALSE;
+ if( strlen( $pParentPos ) == 0 ) {
+ $aux["pos"] = "$pos";
+ } else {
+ $aux["pos"] = $pParentPos . '.' . "$pos";
+ }
+ $ret[] = $aux;
+ //Recursively add any children
+ $subs = LibertyStructure::createSubTree( $pStructureHash, $node['structure_id'], $aux['pos'], ( $pLevel + 1 ) );
+ if( !empty( $subs ) ) {
+ $r = array_pop( $ret );
+ $r['has_children'] = TRUE;
+ array_push( $ret, $r );
+ $ret = array_merge( $ret, $subs );
+ }
+
+ if( $pos == $row_max ) {
+ $aux['structure_id'] = $node['structure_id'];
+ $aux['first'] = FALSE;
+ $aux['last'] = TRUE;
+ $ret[] = $aux;
+ }
+ $pos++;
+ }
+ return $ret;
+ }
+
+ // get sub tree of $pStructureId
+ function getSubTree( $pStructureId, $pRootTree = FALSE, $pListHash=NULL ) {
+ global $gLibertySystem, $gBitSystem;
+ $ret = array();
+ if( @BitBase::verifyId( $pStructureId ) ) {
+ $pListHash['structure_id'] = $pStructureId;
+ $structureHash = $this->getStructure( $pListHash );
+ $ret = $this->createSubTree( $structureHash, ( ( $pRootTree ) ? $pListHash['root_structure_id'] : $pStructureId ) );
+ }
+ return $ret;
+ }
+
+ /**
+ * getList
+ *
+ * @param array $pListHash
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getList( &$pListHash ) {
+ global $gBitSystem, $gBitUser;
+
+ BitBase::prepGetList( $pListHash );
+
+ if( !empty( $pListHash['find'] ) ) {
+ $findesc = '%' . $pListHash['find'] . '%';
+ $mid = " (`parent_id` is null or `parent_id`=0) and (lc.`title` like ?)";
+ $bindVars=array($findesc);
+ } else {
+ $mid = " (`parent_id` is null or `parent_id`=0) ";
+ $bindVars=array();
+ }
+
+ if( @$this->verifyId( $pListHash['user_id'] ) ) {
+ $mid .= " AND lc.`user_id` = ? ";
+ array_push( $bindVars, $pListHash['user_id'] );
+ }
+
+ if( empty( $pListHash['sort_mode'] ) ) {
+ $pListHash['sort_mode'] = 'last_modified_desc';
+ }
+
+ if( !empty( $pListHash['content_type_guid'] ) ) {
+ $mid .= " AND lc.`content_type_guid`=? ";
+ array_push( $bindVars, $pListHash['content_type_guid'] );
+ }
+ $query = "SELECT ls.`structure_id`, ls.`parent_id`, ls.`content_id`, `page_alias`, `pos`, lc.`title`, `data`, `last_modified`, lc.`modifier_user_id`, `ip`, lc.`user_id` AS `creator_user_id`, uu.`login` AS `creator_user`, uu.`real_name` , uu.`email`
+ FROM `".BIT_DB_PREFIX."liberty_structures` ls INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON ( ls.`content_id` = lc.`content_id` ) INNER JOIN `".BIT_DB_PREFIX."users_users` uu ON ( lc.`user_id` = uu.`user_id` )
+ WHERE $mid
+ ORDER BY ".$this->mDb->convertSortmode($pListHash['sort_mode']);
+ $query_cant = "SELECT count(*)
+ FROM `".BIT_DB_PREFIX."liberty_structures` ls INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON ( ls.`content_id` = lc.`content_id` )
+ WHERE $mid";
+ $result = $this->mDb->query($query,$bindVars,$pListHash['max_records'],$pListHash['offset']);
+ $cant = $this->mDb->getOne($query_cant,$bindVars);
+ $ret = array();
+
+ while ($res = $result->fetchRow()) {
+ if( $gBitSystem->isPackageActive( 'bithelp' ) && file_exists(BITHELP_PKG_PATH.$res['title'].'/index.html')) {
+ $res['webhelp']='y';
+ } else {
+ $res['webhelp']='n';
+ }
+ $ret[] = $res;
+ }
+
+ $retval = array();
+ $retval["data"] = $ret;
+ $retval["cant"] = $cant;
+ return $retval;
+ }
+
+ /**
+ * clean up and prepare a complete structure in the form of arrays about to be stored
+ * @param $pParamHash is a set of arrays generated by the DynamicTree javascript tree builder
+ * @return TRUE on success, FALSE on failure where $this->mErrors will contain the reason why it failed
+ */
+ function verifyStructure( &$pParamHash ) {
+ $storeNodes = array();
+ if( !static::getParameter( $pParamHash, 'root_structure_id' ) ) {
+ $pParamHash['root_structure_id'] = $this->getField( 'root_structure_id' );
+ }
+
+ if( !static::verifyId( $pParamHash['root_structure_id'] ) ) {
+ $this->mErrors['verify_structure'] = tra( "Unknown root structure." );
+ } else {
+ if( !empty( $pParamHash['structure_json'] ) ) {
+//vd( $pParamHash['structure_json'] );
+ // {{{ closure here to recursively parse json
+ $parseStructureJson = function($treeHash, $pRootId, $pParentId, $pLevel, $pPos ) use ( &$parseStructureJson, &$storeNodes ) {
+ if( is_numeric( key( $treeHash ) ) ) {
+ $pos = 1;
+ foreach( array_keys( $treeHash ) as $i ) {
+ // Array of nodes came in. Process each at the same level
+ $parseStructureJson( $treeHash[$i], $pRootId, $pParentId, $pLevel, $pos++ );
+ }
+ } else {
+ // Base node with data
+ $storeNode = array();
+ $storeNode['root_structure_id'] = $pRootId;
+ $storeNode['parent_id'] = $pParentId;
+ $storeNode['structure_id'] = $treeHash['structure_id'];
+ $storeNode['content_id'] = $treeHash['content_id'];
+ $storeNode['pos'] = $pPos++;
+ //$storeNode['page_alias']
+ $storeNode['structure_level'] = $pLevel;
+ $storeNodes[$treeHash['structure_id']] = $storeNode;
+ if( !empty( $treeHash['children'] ) ) {
+ // current node has children, recurse down in
+ $parseStructureJson( $treeHash['children'], $pRootId, $treeHash['structure_id'], $pLevel + 1, 1 );
+ }
+ }
+ };
+ // }}}
+
+ $parseStructureJson( $pParamHash['structure_json'], $this->mStructureId, $pParamHash['root_structure_id'], 1, 1 );
+ if( !empty( $storeNodes ) ) {
+ $pParamHash['structure_store'] = $storeNodes;
+ }
+//vd( $storeNodes );
+ }
+
+
+ // deprecated flat tree store, code is unused AFAIK.-- spiderr
+ if( !empty( $pParamHash['structure'] ) ) {
+ // LibertyStructure::embellishStructureHash( $pParamHash['structure'] );
+ // $structureHash = LibertyStructure::flattenStructureHash( $pParamHash['structure'] );
+
+ // replace the 'tree' in the data array with the root_structure_id
+ foreach( $pParamHash['data'] as $structure_id => $node ) {
+ if( !@BitBase::verifyId( $pParamHash['data'][$structure_id]['parent_id'] ) ) {
+ $pParamHash['data'][$structure_id]['parent_id'] = $pParamHash['root_structure_id'];
+ }
+ }
+
+ foreach( $structureHash as $node ) {
+ if( @BitBase::verifyId( $node['structure_id'] ) ) {
+ $pParamHash['structure_store'][$node['structure_id']] = array_merge( $node, $pParamHash['data'][$node['structure_id']] );
+ $pParamHash['structure_store'][$node['structure_id']]['root_structure_id'] = $pParamHash['root_structure_id'];
+ }
+ }
+ }
+ }
+
+ if( empty( $pParamHash['structure_store'] ) ) {
+ $this->mErrors['verify_structure'] = tra( "There are no changes to save." );
+ }
+
+ // clear up some memory
+ if( !empty( $pParamHash['structure_json'] ) ) { unset( $pParamHash['structure_json'] ); }
+ if( !empty( $pParamHash['structure'] ) ) { unset( $pParamHash['structure'] ); }
+ if( !empty( $pParamHash['data'] ) ) { unset( $pParamHash['data'] ); }
+
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * store a complete structure where ever subarray contains a complete node as it should go into the database
+ * @param $pParamHash is an array with subarrays, each representing a structure node ready to associativley inserted into the database
+ * @return TRUE on success, FALSE on failure where $this->mErrors will contain the reason why it failed
+ */
+ function storeStructure( $pParamHash ) {
+ if( $this->verifyStructure( $pParamHash ) ) {
+ // now that the structure is ready to be stored, we remove the old structure first and then insert the new one.
+ $this->StartTrans();
+ $query = "DELETE FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `root_structure_id`=? AND `structure_id`!=?";
+ $result = $this->mDb->query( $query, array( (int)$pParamHash['root_structure_id'], (int)$pParamHash['root_structure_id'] ) );
+ foreach( $pParamHash['structure_store'] as $node ) {
+ $this->mDb->associateInsert( BIT_DB_PREFIX."liberty_structures", $node );
+ }
+ $this->CompleteTrans();
+ }
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /**
+ * make sure the array only contains one level depth
+ * @param $pParamHash contains a nested set of arrays with structure_id and pos values set
+ * @return flattened array
+ */
+ function flattenStructureHash( $pParamHash, $i = -10000 ) {
+ $ret = array();
+ foreach( $pParamHash as $key => $node ) {
+ if( !empty( $node ) && count( $node ) > 2 ) {
+ $ret = array_merge( $ret, LibertyStructure::flattenStructureHash( $node, $i ) );
+ $i++;
+ } elseif( count( $node ) == 2 ) {
+ $ret[] = $node;
+ $i++;
+ } else {
+ $ret[$i][$key] = $node;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * cleans up and reorganises data in nested array where keys are structure_id
+ * @param $pParamHash contains a nested set of arrays with structure_id as key
+ * @return reorganised array
+ */
+ function embellishStructureHash( &$pParamHash ) {
+ $pos = 1;
+ foreach( $pParamHash as $structure_id => $node ) {
+ if( !empty( $node ) ) {
+ LibertyStructure::embellishStructureHash( $node );
+ }
+ $node['pos'] = $pos++;
+ $node['structure_id'] = $structure_id;
+ $pParamHash[$structure_id] = $node;
+ }
+ }
+
+ /**
+ * prepare a structure node for storage in the database
+ * @param $pParamHash contains various settings for the node to be stored
+ * @return TRUE on success, FALSE on failure where $this->mErrors will contain the reason why it failed
+ */
+ function verifyNode( &$pParamHash ) {
+ if( !@$this->verifyId( $pParamHash['content_id'] ) ) {
+ $this->mErrors['content'] = 'Could not store structure. Invalid content id. '.$pParamHash['content_id'];
+ } else {
+ if( !@$this->verifyId( $pParamHash['parent_id'] ) ) {
+ $pParamHash['parent_id'] = 0;
+ }
+ if( empty( $pParamHash['alias'] ) ) {
+ $pParamHash['alias'] = '';
+ }
+ if( isset( $pParamHash['after_ref_id'] ) ) {
+ $pParamHash['max'] = $this->mDb->getOne("select `pos` from `".BIT_DB_PREFIX."liberty_structures` where `structure_id`=?",array((int)$pParamHash['after_ref_id']));
+ } else {
+ $pParamHash['max'] = $this->mDb->getOne("select max(`pos`) from `".BIT_DB_PREFIX."liberty_structures` where `parent_id`=?",array((int)$pParamHash['parent_id']));
+ }
+ if( $pParamHash['max'] > 0 ) {
+ // For example, if max is 5 then we are inserting after position 5 so we'll insert 5 and move all the others
+ $query = "update `".BIT_DB_PREFIX."liberty_structures` set `pos`=`pos`+1 where `pos`>? and `parent_id`=?";
+ $result = $this->mDb->query($query,array((int)$pParamHash['max'], (int)$pParamHash['parent_id']));
+ }
+ $pParamHash['max']++;
+
+ if( $pParamHash['structure_id'] = $this->mDb->getOne( "SELECT `structure_id` FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `root_structure_id`=? and `content_id`=?", array($pParamHash['root_structure_id'], $pParamHash['content_id'] ) ) ) {
+ $this->mErrors[] = tra( 'Content already exists in structure.' )." ($pParamHash[structure_id])";
+ }
+ }
+ return( count( $this->mErrors ) == 0 );
+ }
+
+ /** Create a structure entry with the given name
+ * @param parent_id The parent entry to add this to. If NULL, create new structure.
+ * @param after_ref_id The entry to add this one after. If NULL, put it in position 0.
+ * @param name The wiki page to reference
+ * @param alias An alias for the wiki page name.
+ * @return the new entries structure_id or null if not created.
+ */
+ function storeNode( &$pParamHash ) {
+ global $gBitSystem;
+ $ret = null;
+ // If the page doesn't exist then create a new wiki page!
+ $now = $gBitSystem->getUTCTime();
+// $created = $this->create_page($name, 0, '', $now, tra('created from structure'), 'system', '0.0.0.0', '');
+ // if were not trying to add a duplicate structure head
+ if ( $this->verifyNode( $pParamHash ) ) {
+ $this->StartTrans();
+
+ //Create a new structure entry
+ $pParamHash['structure_id'] = $this->mDb->GenID( 'liberty_structures_id_seq' );
+ if( !@$this->verifyId( $pParamHash['root_structure_id'] ) ) {
+ $pParamHash['root_structure_id'] = $pParamHash['structure_id'];
+ }
+ $query = "INSERT INTO `".BIT_DB_PREFIX."liberty_structures`( `structure_id`, `parent_id`,`content_id`, `root_structure_id`, `page_alias`, `pos` ) values(?,?,?,?,?,?)";
+ $result = $this->mDb->query( $query, array( $pParamHash['structure_id'], $pParamHash['parent_id'], (int)$pParamHash['content_id'], (int)$pParamHash['root_structure_id'], $pParamHash['alias'], $pParamHash['max'] ) );
+ $this->CompleteTrans();
+ $ret = $pParamHash['structure_id'];
+ } else {
+ //vd( $this->mErrors );
+ }
+ return $ret;
+ }
+
+ /**
+ * moveNodeWest
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function moveNodeWest() {
+ if( $this->isValid() ) {
+ //If there is a parent and the parent isnt the structure root node.
+ $this->StartTrans();
+ if( @$this->verifyId( $this->mInfo["parent_id"] ) ) {
+ $parentNode = $this->getNode( $this->mInfo["parent_id"] );
+ if( @$this->verifyId( $parentNode['parent_id'] ) ) {
+ //Make a space for the node after its parent
+ $query = "update `".BIT_DB_PREFIX."liberty_structures` set `pos`=`pos`+1 where `pos`>? and `parent_id`=?";
+ $this->mDb->query( $query, array( $parentNode['pos'], $parentNode['parent_id'] ) );
+ //Move the node up one level
+ $query = "update `".BIT_DB_PREFIX."liberty_structures` set `parent_id`=?, `pos`=(? + 1) where `structure_id`=?";
+ $this->mDb->query($query, array( $parentNode['parent_id'], $parentNode['pos'], $this->mStructureId ) );
+ }
+ }
+ $this->CompleteTrans();
+ }
+ }
+
+ /**
+ * moveNodeEast
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function moveNodeEast() {
+ if( $this->isValid() ) {
+ $this->StartTrans();
+ $query = "select `structure_id`, `pos` from `".BIT_DB_PREFIX."liberty_structures` where `pos`<? and `parent_id`=? order by `pos` desc";
+ $result = $this->mDb->query($query,array($this->mInfo["pos"], (int)$this->mInfo["parent_id"]));
+ if ($previous = $result->fetchRow()) {
+ //Get last child nodes for previous sibling
+ $query = "select `pos` from `".BIT_DB_PREFIX."liberty_structures` where `parent_id`=? order by `pos` desc";
+ $result = $this->mDb->query($query,array((int)$previous["structure_id"]));
+ if ($res = $result->fetchRow()) {
+ $pos = $res["pos"];
+ } else{
+ $pos = 0;
+ }
+ $query = "update `".BIT_DB_PREFIX."liberty_structures` set `parent_id`=?, `pos`=(? + 1) where `structure_id`=?";
+ $this->mDb->query( $query, array((int)$previous["structure_id"], (int)$pos, (int)$this->mStructureId) );
+ //Move nodes up below that had previous parent and pos
+ $query = "update `".BIT_DB_PREFIX."liberty_structures` set `pos`=`pos`-1 where `pos`>? and `parent_id`=?";
+ $this->mDb->query( $query, array( $this->mInfo['pos'], $this->mInfo['parent_id'] ) );
+ }
+ $this->CompleteTrans();
+ }
+ }
+
+ /**
+ * moveNodeSouth
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function moveNodeSouth() {
+ if( $this->isValid() ) {
+ $this->StartTrans();
+ $query = "select `structure_id`, `pos` from `".BIT_DB_PREFIX."liberty_structures` where `pos`>? and `parent_id`=? order by `pos` asc";
+ $result = $this->mDb->query($query,array((int)$this->mInfo["pos"], (int)$this->mInfo["parent_id"]));
+ $res = $result->fetchRow();
+ if ($res) {
+ //Swap position values
+ $query = "update `".BIT_DB_PREFIX."liberty_structures` set `pos`=? where `structure_id`=?";
+ $this->mDb->query($query,array((int)$this->mInfo["pos"], (int)$res["structure_id"]) );
+ $this->mDb->query($query,array((int)$res["pos"], (int)$this->mInfo["structure_id"]) );
+ }
+ $this->CompleteTrans();
+ }
+ }
+
+ /**
+ * moveNodeNorth
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function moveNodeNorth() {
+ if( $this->isValid() ) {
+ $this->StartTrans();
+ $query = "select `structure_id`, `pos` from `".BIT_DB_PREFIX."liberty_structures` where `pos`<? and `parent_id`=? order by `pos` desc";
+ $result = $this->mDb->query($query,array((int)$this->mInfo["pos"], (int)$this->mInfo["parent_id"]));
+ $res = $result->fetchRow();
+ if ($res) {
+ //Swap position values
+ $query = "update `".BIT_DB_PREFIX."liberty_structures` set `pos`=? where `structure_id`=?";
+ $this->mDb->query($query,array((int)$res["pos"], (int)$this->mInfo["structure_id"]) );
+ $this->mDb->query($query,array((int)$this->mInfo["pos"], (int)$res["structure_id"]) );
+ }
+ $this->CompleteTrans();
+ }
+ }
+
+
+
+
+
+
+
+
+
+ // ============== OLD struct_lib STUFF
+
+
+
+ function removeStructureNode( $structure_id, $delete=FALSE ) {
+ // Now recursively remove
+ if( @$this->verifyId( $structure_id ) ) {
+ $query = "SELECT *
+ FROM `".BIT_DB_PREFIX."liberty_structures`
+ WHERE `parent_id`=?";
+ $result = $this->mDb->query( $query, array( (int)$structure_id ) );
+ // Iterate down through the child nodes
+ while( $res = $result->fetchRow() ) {
+ $this->removeStructureNode( $res["structure_id"], $delete );
+ }
+
+ // Only delete a page if other structures arent referencing it
+ if( $delete ) {
+ $page_info = $this->getNode( $structure_id );
+ $query = "SELECT COUNT(*) FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `content_id`=?";
+ $count = $this->mDb->getOne( $query, array( (int)$page_info["page_id"] ) );
+ if( $count = 1 ) {
+ $this->remove_all_versions( $page_info["page_id"] );
+ }
+ }
+
+ // If we are removing the root node, remove the entry in liberty_content as well
+ $query = "SELECT `content_id`
+ FROM `".BIT_DB_PREFIX."liberty_structures`
+ WHERE `structure_id`=? AND `structure_id`=`root_structure_id`";
+ $content_id = $this->mDb->getOne( $query, array( (int)$structure_id ) );
+
+ // Delete the liberty_content stuff
+ $lc = new LibertyContent($content_id);
+ $lc->expunge();
+
+ // Remove the structure node
+ $query = "DELETE FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `structure_id`=?";
+ $result = $this->mDb->query( $query, array( (int)$structure_id) );
+ return true;
+ }
+ }
+
+ /**
+ * Returns an array of info about the parent
+ *
+ * @param array $structure_id
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getStructureParentInfo($structure_id) {
+ $parent_id = $this->mDb->getOne( "SELECT `parent_id` FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `structure_id`=?", array( (int)$structure_id ) );
+
+ if( !@BitBase::verifyId( $parent_id ) ) {
+ return null;
+ }
+
+ return( $this->getNode( $parent_id ) );
+ }
+
+ /**
+ * getContentIds
+ *
+ * @param array $pStructureId
+ * @param array $pToc
+ * @param float $pLevel
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getContentIds( $pStructureId, &$pToc, $pLevel=0 ) {
+ $ret = array();
+
+ $query = "SELECT * from `".BIT_DB_PREFIX."liberty_structures` where `parent_id`=? ORDER BY pos, page_alias, content_id";
+ $result = $this->mDb->query( $query, array( (int)$pStructureId ) );
+ while ( $row = $result->fetchRow() ) {
+ array_push( $pToc, $row['content_id'] );
+ $this->getContentIds( $row['structure_id'], $pToc, ++$pLevel );
+ }
+ }
+
+ /**
+ * getContentArray
+ *
+ * @param array $pStructureId
+ * @param array $pToc
+ * @param float $pLevel
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getContentArray( $pStructureId, &$pToc, $pLevel=0 ) {
+ $query = "SELECT * from `".BIT_DB_PREFIX."liberty_structures` where `structure_id`=?";
+ $result = $this->mDb->query( $query, array( (int)$pStructureId ) );
+ while ( $row = $result->fetchRow() ) {
+ array_push( $pToc, $row['content_id'] );
+ $this->getContentIds( $pStructureId, $pToc, $pLevel );
+ }
+ }
+
+ /**
+ * exportHtml
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function exportHtml() {
+ $ret = array();
+ $toc = array();
+ $this->getContentArray( $this->mStructureId, $toc );
+ if( count( $toc ) ) {
+ foreach( $toc as $conId ) {
+ if( $viewContent = LibertyBase::getLibertyObject( $conId ) ) {
+ $ret[] = array(
+ 'type' => $viewContent->mContentTypeGuid,
+ 'landscape' => FALSE,
+ 'url' => $viewContent->getDisplayUrl(),
+ 'content_id' => $viewContent->mContentId,
+ );
+ }
+ }
+ }
+ return $ret;
+ }
+
+ function isInStructure( $pContentId ) {
+ $ret = FALSE;
+ if( $this->isValid() ) {
+ $ret = $this->mDb->getOne( "SELECT structure_id FROM `".BIT_DB_PREFIX."liberty_structures` WHERE `root_structure_id`=? AND `content_id`=?", array( $this->mStructureId, $pContentId ) );
+ }
+ return $ret;
+ }
+
+ function loadStructure() {
+ if( $this->isValid() ) {
+ if( empty( $this->mTree ) ) {
+ $this->mTree = $this->buildSubtreeToc();
+ }
+ }
+ return( !empty( $this->mTree ) );
+ }
+
+ /**
+ * buildSubtreeToc
+ *
+ * @param array $id
+ * @param array $slide
+ * @param string $order
+ * @param string $tocPrefix can be used to Prefix a subtree as it would start from a given number (e.g. 2.1.3)
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function buildTreeToc( $id, $order='asc', $tocPrefix='', $pPrefixDepth=1, $pDepth=1 ) {
+ if( $ret[0] = $this->getNode( $id ) ) {
+ $ret[0]['sub'] = $this->buildSubtreeToc( $id, $order, $tocPrefix, $pPrefixDepth, $pDepth );
+ }
+ return $ret;
+ }
+
+
+ /**
+ * buildSubtreeToc
+ *
+ * @param array $id
+ * @param array $slide
+ * @param string $order
+ * @param string $tocPrefix can be used to Prefix a subtree as it would start from a given number (e.g. 2.1.3)
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function buildSubtreeToc( $id, $order='asc', $tocPrefix='', $pPrefixDepth=1, $pDepth=1 ) {
+ global $gLibertySystem, $gBitSystem;
+ $back = array();
+ $cant = $this->mDb->getOne("select count(*) from `".BIT_DB_PREFIX."liberty_structures` where `parent_id`=?",array((int)$id));
+ if ($cant) {
+ $query = "SELECT `structure_id`, `root_structure_id`, `parent_id`, `page_alias`, `pos`, `structure_level`, lc.`user_id`, lc.`title`, lc.`content_type_guid`, uu.`login`, uu.`real_name`, lc.`content_id`, lc.`last_modified`, lct.*
+ FROM `".BIT_DB_PREFIX."liberty_structures` ls
+ INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON ( lc.`content_id`=ls.`content_id` )
+ INNER JOIN `".BIT_DB_PREFIX."liberty_content_types` lct ON ( lc.`content_type_guid`=lct.`content_type_guid` )
+ LEFT JOIN `".BIT_DB_PREFIX."users_users` uu ON ( uu.`user_id` = lc.`user_id` )
+ WHERE `parent_id`=?
+ ORDER BY ".$this->mDb->convertSortmode("pos_".$order);
+ $result = $this->mDb->query($query,array((int)$id));
+ $prefix = 1;
+ $contentTypes = $gLibertySystem->mContentTypes;
+ while ($res = $result->fetchRow()) {
+ $res['prefix']='';
+ if( $pDepth >= $pPrefixDepth ) {
+ $res['prefix']=($tocPrefix=='')?'':"$tocPrefix.";
+ $res['prefix'].=$prefix;
+ }
+ $prefix++;
+ if( !empty( $contentTypes[$res['content_type_guid']] ) ) {
+ // quick alias for code readability
+ $type = &$contentTypes[$res['content_type_guid']];
+ if( empty( $type['content_object'] ) && !empty( $gBitSystem->mPackages[$type['handler_package']] ) ) {
+ // create *one* object for each object *type* to call virtual methods.
+ $handlerFile = $gBitSystem->mPackages[$type['handler_package']]['path'].$type['handler_file'];
+ if( file_exists( $handlerFile ) ) {
+ include_once( $handlerFile );
+ if( class_exists( $type['handler_class'] ) ) {
+ $type['content_object'] = new $type['handler_class']();
+ }
+ }
+ }
+ if( !empty( $type['content_object'] ) && is_object( $type['content_object'] ) ) {
+ $res['title'] = $type['content_object']->getTitleFromHash( $res );
+ }
+ if ($res['structure_id'] != $id) {
+ $sub = $this->buildSubtreeToc( $res['structure_id'],$order,$res['prefix'], $pPrefixDepth, ($pDepth + 1) );
+ if (is_array($sub)) {
+ $res['sub'] = $sub;
+ }
+ }
+ }
+ $pkgPath = strtoupper( $res['handler_package'] ).'_PKG_PATH';
+ if( defined( $pkgPath ) ) {
+ $classFile = constant( strtoupper( $res['handler_package'] ).'_PKG_PATH' ).$res['handler_file'];
+ if( file_exists( $classFile ) ) {
+ require_once( $classFile );
+ if( class_exists( $res['handler_class'] ) ) {
+ $res['display_url'] = $res['handler_class']::getDisplayUrlFromHash( $res );
+ }
+ }
+ }
+ $back[] = $res;
+ }
+ } else {
+ return false;
+ }
+ return $back;
+ }
+
+ /**
+ * getToc
+ *
+ * @param array $pStructureId
+ * @param string $order
+ * @param array $showdesc
+ * @param array $numbering
+ * @param string $numberPrefix
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getToc($pStructureId=NULL,$order='asc',$showdesc=false,$pNumberDepth=true,$numberPrefix='',$pCss='') {
+ if( !@$this->verifyId( $pStructureId ) ) {
+ $pStructureId = $this->mStructureId;
+ }
+ $structureTree = $this->buildSubtreeToc( $pStructureId, $order, $numberPrefix, $pNumberDepth );
+ return '<div class="aciTree" id="structure-'.$this->mStructureId.'">'.$this->fetchToc( $structureTree, $showdesc, $pNumberDepth, $pCss ).'</div>';
+ }
+
+ /**
+ * fetchToc
+ *
+ * @param array $structureTree
+ * @param array $showdesc
+ * @param array $numbering
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function fetchToc($structureTree,$showdesc,$numbering,$pCss='') {
+ global $gBitSmarty;
+ $ret='';
+ if ($structureTree != '') {
+ $gBitSmarty->verifyCompileDir();
+ $gBitSmarty->assignByRef( 'structureId', $this->mStructureId );
+ $ret.=$gBitSmarty->fetch( "bitpackage:liberty/structure_toc_startul.tpl");
+ foreach($structureTree as $leaf) {
+ //echo "<br />";print_r($leaf);echo "<br />";
+ $gBitSmarty->assignByRef('structure_tree',$leaf);
+ $gBitSmarty->assign('showdesc',$showdesc);
+ $gBitSmarty->assign('numbering',$numbering);
+ $ret .= $gBitSmarty->fetch( "bitpackage:liberty/structure_toc_leaf.tpl");
+ if(isset($leaf["sub"]) && is_array($leaf["sub"])) {
+ // recurse down in - li tags are for w3c standard
+ $ret .= $this->fetchToc($leaf["sub"],$showdesc,$numbering);
+ }
+ $ret .= '</li>';
+ }
+ $ret.=$gBitSmarty->fetch( "bitpackage:liberty/structure_toc_endul.tpl");
+ }
+ return $ret;
+ }
+
+ /**
+ * getNextStructureNode
+ *
+ * @param array $structure_id
+ * @param array $deep
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getNextStructureNode($structure_id, $deep = true) {
+ // If we have children then get the first child
+ if ($deep) {
+ $query = "SELECT `structure_id`
+ FROM `".BIT_DB_PREFIX."liberty_structures` ls
+ WHERE `parent_id`=?
+ ORDER BY ".$this->mDb->convertSortmode("pos_asc");
+ $result1 = $this->mDb->query($query,array((int)$structure_id));
+
+ if ($result1->numRows()) {
+ $res = $result1->fetchRow();
+ return $res["structure_id"];
+ }
+ }
+
+ // Try to get the next page with the same parent as this
+ $page_info = $this->getNode($structure_id);
+ $parent_id = $page_info["parent_id"];
+ $page_pos = $page_info["pos"];
+
+ if (!$parent_id)
+ return null;
+
+ $query = "SELECT `structure_id`
+ FROM `".BIT_DB_PREFIX."liberty_structures` ls
+ WHERE `parent_id`=? and `pos`>?
+ ORDER BY ".$this->mDb->convertSortmode("pos_asc");
+ $result2 = $this->mDb->query($query,array((int)$parent_id, (int)$page_pos));
+
+ if ($result2->numRows()) {
+ $res = $result2->fetchRow();
+ return $res["structure_id"];
+ }
+ else {
+ return $this->getNextStructureNode($parent_id, false);
+ }
+ }
+
+ /**
+ * getPrevStructureNode
+ *
+ * @param array $structure_id
+ * @param array $deep
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getPrevStructureNode($structure_id, $deep = false) {
+ //Drill down to last child for this tree node
+ if ($deep) {
+ $query = "select `structure_id` ";
+ $query .= "from `".BIT_DB_PREFIX."liberty_structures` ls ";
+ $query .= "where `parent_id`=? ";
+ $query .= "order by ".$this->mDb->convertSortmode("pos_desc");
+ $result = $this->mDb->query($query,array($structure_id));
+
+ if ($result->numRows()) {
+ //There are more children
+ $res = $result->fetchRow();
+ $structure_id = $this->getPrevStructureNode($res["structure_id"], true);
+ }
+ return $structure_id;
+ }
+ // Try to get the previous page with the same parent as this
+ $page_info = $this->getNode($structure_id);
+ $parent_id = $page_info["parent_id"];
+ $pos = $page_info["pos"];
+
+ //At the top of the tree
+ if (!isset($parent_id))
+ return null;
+
+ $query = "select `structure_id` ";
+ $query .= "from `".BIT_DB_PREFIX."liberty_structures` ls ";
+ $query .= "where `parent_id`=? and `pos`<? ";
+ $query .= "order by ".$this->mDb->convertSortmode("pos_desc");
+ $result = $this->mDb->query($query,array((int)$parent_id, (int)$pos));
+
+ if ($result->numRows()) {
+ //There is a previous sibling
+ $res = $result->fetchRow();
+ $structure_id = $this->getPrevStructureNode($res["structure_id"], true);
+ }
+ else {
+ //No previous siblings, just the parent
+ $structure_id = $parent_id;
+ }
+ return $structure_id;
+ }
+
+ /**
+ * Return an array of subpages
+ *
+ * @param array $pParentId
+ * @access public
+ * @return array of child structure pages
+ */
+ function getStructureNodes( $pParentId ) {
+ $ret = array();
+ $query = "SELECT `pos`, `structure_id`, `parent_id`, ls.`content_id`, lc.`title`, `page_alias`
+ FROM `".BIT_DB_PREFIX."liberty_structures` ls, `".BIT_DB_PREFIX."liberty_content` lc
+ WHERE ls.`content_id` = lc.`content_id` AND `parent_id`=? ";
+ $query .= "order by ".$this->mDb->convertSortmode("pos_asc");
+ $result = $this->mDb->query($query,array((int)$pParentId));
+ while ($res = $result->fetchRow()) {
+ //$ret[] = $this->populate_page_info($res);
+ $ret[] = $res;
+ }
+ return $ret;
+ }
+}
+?>
diff --git a/includes/classes/LibertySystem.php b/includes/classes/LibertySystem.php
new file mode 100644
index 0000000..9165fc2
--- /dev/null
+++ b/includes/classes/LibertySystem.php
@@ -0,0 +1,865 @@
+<?php
+/**
+* System class for handling the liberty package
+*
+* @package liberty
+* @version $Header$
+* @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>
+// +----------------------------------------------------------------------+
+
+/**
+ * Local base defines
+ */
+// Plugin Definitions
+define( 'STORAGE_PLUGIN', 'storage' );
+define( 'FORMAT_PLUGIN', 'format' );
+define( 'DATA_PLUGIN', 'data' );
+define( 'MIME_PLUGIN', 'mime' );
+define( 'FILTER_PLUGIN', 'filter' );
+
+if( !defined( 'LIBERTY_DEFAULT_MIME_HANDLER' )) {
+ define( 'LIBERTY_DEFAULT_MIME_HANDLER', 'mimedefault' );
+}
+
+// Service Definitions
+define( 'LIBERTY_SERVICE_ACCESS_CONTROL', 'access_control' );
+define( 'LIBERTY_SERVICE_CATEGORIZATION', 'categorization' );
+define( 'LIBERTY_SERVICE_COMMERCE', 'commerce' );
+define( 'LIBERTY_SERVICE_CONTENT_TEMPLATES', 'content_templates' );
+define( 'LIBERTY_SERVICE_DOCUMENT_GENERATION', 'document_generation' );
+define( 'LIBERTY_SERVICE_FORUMS', 'forums' );
+define( 'LIBERTY_SERVICE_GROUP', 'groups' );
+define( 'LIBERTY_SERVICE_MAPS', 'map_display' );
+define( 'LIBERTY_SERVICE_METADATA', 'metadata' );
+define( 'LIBERTY_SERVICE_MENU', 'menu' );
+define( 'LIBERTY_SERVICE_RATING', 'rating' );
+define( 'LIBERTY_SERVICE_REBLOG', 'reblogging_rss_feeds' );
+define( 'LIBERTY_SERVICE_SEARCH', 'search' );
+define( 'LIBERTY_SERVICE_THEMES', 'themes' );
+define( 'LIBERTY_SERVICE_TOPICA', 'topica' );
+define( 'LIBERTY_SERVICE_TRANSLATION', 'translation' );
+define( 'LIBERTY_SERVICE_TRANSLITERATION', 'transliteration' );
+define( 'LIBERTY_SERVICE_LIBERTYSECURE', 'security' );
+define( 'LIBERTY_SERVICE_MODCOMMENTS', 'comment_moderation' );
+define( 'LIBERTY_SERVICE_UPLOAD', 'upload' );
+
+define( 'LIBERTY_TEXT_AREA', 'editliberty' );
+define( 'LIBERTY_UPLOAD', 'upload' );
+
+/**
+ * Link to base class
+ */
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyBase.php' );
+
+/**
+ * System class for handling the liberty package
+ *
+ * @package liberty
+ */
+class LibertySystem extends BitSingleton {
+
+
+ // Hash of plugin data
+ public $mPlugins = array();
+
+ // Liberty data tags
+ public $mDataTags = array();
+
+ // Content Status
+ public $mContentStatus;
+
+ // Content types
+ public $mContentTypes;
+
+ // File name of last plug that registered
+ public $mPluginFileName;
+
+ // Packages using LibertySystem
+ // this makes it possible to extend LibertySystem by another package
+ public $mSystem = LIBERTY_PKG_NAME;
+ public $mPluginPath;
+
+
+ /**
+ * Initiate Class
+ **/
+ function __construct( $pExtras = TRUE ) {
+ parent::__construct();
+
+ $this->mPluginPath = LIBERTY_PKG_PATH.'plugins/';
+
+ $this->loadContentTypes();
+ }
+
+
+ public function __sleep() {
+ return array_merge( parent::__sleep(), array( 'mPlugins', 'mDataTags', 'mContentStatus', 'mContentTypes', 'mPluginFileName', 'mSystem', 'mPluginPath' ) );
+ }
+
+ // ****************************** Plugin Functions
+ /**
+ * Load only active plugins from disk
+ *
+ * @return none
+ * @access public
+ **/
+ function loadActivePlugins() {
+ global $gBitSystem;
+ // all active plugins
+ $configs = array_keys( $gBitSystem->getConfigMatch( "/^{$this->mSystem}_plugin_status_/i", 'y' ));
+
+ // first we include the default one - this allows other plugins to make use of default functions
+ if( $this->mSystem == LIBERTY_PKG_NAME ) {
+ if( $key = array_search( 'liberty_plugin_status_'.LIBERTY_DEFAULT_MIME_HANDLER , $configs )) {
+ unset( $configs[$key] );
+ }
+ array_unshift( $configs, 'liberty_plugin_status_'.LIBERTY_DEFAULT_MIME_HANDLER );
+ }
+
+ foreach( $configs as $config ) {
+ $pluginGuid = preg_replace( "/^{$this->mSystem}_plugin_status_/", '', $config, 1 );
+ if( $pluginFile = $gBitSystem->getConfig( "{$this->mSystem}_plugin_path_$pluginGuid" ) ) {
+ if( is_file( BIT_ROOT_PATH.$pluginFile )) {
+ $this->mPluginFilePath = BIT_ROOT_PATH.$pluginFile;
+ include_once( BIT_ROOT_PATH.$pluginFile );
+ }
+ } elseif( $pluginFile = $gBitSystem->getConfig( "{$this->mSystem}_plugin_file_$pluginGuid" ) ) {
+ // TODO: all this is deprecated and doesn't really rock bitweavers boat anymore - we use the _plugin_path_ setting now.
+ // this code here is only relevant if a user has updated bitweaver and scanAllPlugins() hasn't been called yet.
+ // scanAllPlugins() is called during the upgrade in the installer so we really are only keeping this here for CVS users
+ // and people who use nexus since it makes use of this plugin system as well.
+ // - xing - Saturday Jul 05, 2008 20:47:29 CEST
+
+ // check for the plugin in the default location - in case bitweaver root path changed.
+
+ if( file_exists( $pluginFile )) {
+ $this->mPluginFilePath = $pluginFile;
+ include_once( $pluginFile );
+ } else {
+ $defaultFile = $this->mPluginPath.basename( $pluginFile );
+ if( file_exists( $defaultFile )) {
+ $this->mPluginFilePath = $defaultFile;
+ include_once( $defaultFile );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Load all plugins found in specified directory
+ * Use loadActivePlugins to load only the active plugins
+ *
+ * @param string $pPluginsPath Set the path where to scan for plugins
+ * @param string $pPrefixPattern Perl regex for filenames can start with to prevent inclusion of unwanted filenames (e.g. (data\.|storage\.)). Final regex: /^{$pPrefixPattern}.*\.php$/
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function scanAllPlugins( $pPluginsPath = NULL, $pPrefixPattern = NULL ) {
+ global $gBitSystem;
+ if( empty( $pPluginsPath )) {
+ $pPluginsPath = $this->mPluginPath;
+ }
+
+ // check for plugins in plugins/ dir
+ if( $pluginHandle = opendir( $pPluginsPath )) {
+ while( FALSE !== ( $plugin = readdir( $pluginHandle ) ) ) {
+ $pattern = "/^{$pPrefixPattern}.*\.php$/";
+ if( preg_match( $pattern, $plugin ) ) {
+ $this->mPluginFilePath = $pPluginsPath.$plugin;
+ include_once( $pPluginsPath.$plugin );
+ }
+ }
+ }
+
+ // check for liberty plugins in other packages as well
+ if( $this->mSystem == LIBERTY_PKG_NAME && $pkgHandle = opendir( BIT_ROOT_PATH )) {
+ while( FALSE !== ( $dirName = readdir( $pkgHandle ))) {
+ if( preg_match( '/^\w/', $dirName ) && $dirName != 'CVS' && is_dir( $pluginDir = BIT_ROOT_PATH.$dirName.'/liberty_plugins/' ) && ( $pluginHandle = opendir( $pluginDir ))) {
+ while( FALSE !== ( $plugin = readdir( $pluginHandle ))) {
+ if( preg_match( "/^{$pPrefixPattern}.*\.php$/", $plugin )) {
+ $this->mPluginFilePath = $pluginDir.$plugin;
+ include_once( $pluginDir.$plugin );
+ }
+ }
+ }
+ }
+ }
+
+ // keep plugin list in sorted order
+ if( !empty( $this->mPlugins ) and is_array( $this->mPlugins ) ) {
+ asort( $this->mPlugins );
+ }
+
+ // only execute the following if this class hasn't been extended
+ if( $this->mSystem == LIBERTY_PKG_NAME ) {
+ // There must be at least one format plugin active and set as the default format
+ $format_plugin_count = $default_format_found = 0;
+ $current_default_format_guid = $gBitSystem->getConfig( 'default_format' );
+ foreach( $this->mPlugins as $guid => $plugin ) {
+ // load all the requirements that we can display them on the plugin page
+ if( $requirement_func = $this->getPluginFunction( $guid, 'requirement_function', FALSE, TRUE )) {
+ $this->mPlugins[$guid]['requirements'] = $requirement_func();
+ }
+
+ if( $this->isPluginActive( $guid )) {
+ if( $plugin['plugin_type'] == FORMAT_PLUGIN ) {
+ $format_plugin_count++;
+ }
+ if( $current_default_format_guid == $guid ) {
+ $default_format_found++;
+ }
+ }
+ }
+
+ // if no current default format or no format plugins active
+ // activate format.tikiwiki and make it the default format plugin
+ // This happens during installation and therefore requires that we include the plugin file for the constant definitions
+ $plugin_file = $this->mPluginPath.'format.tikiwiki.php';
+ if( $format_plugin_count == 0 || $default_format_found == 0 && is_file( $plugin_file ) ) {
+ require_once( $plugin_file );
+ $this->setActivePlugin( PLUGIN_GUID_TIKIWIKI );
+ $gBitSystem->storeConfig( 'default_format', PLUGIN_GUID_TIKIWIKI, $this->mSystem );
+ //make memory match db
+ $this->loadActivePlugins();
+ }
+ }
+
+ // remove any config settings for plugin files that have been removed
+ $plugins = $gBitSystem->getConfigMatch( "/^{$this->mSystem}_plugin_path_/" );
+ foreach( $plugins as $config => $path ) {
+ if( !is_file( BIT_ROOT_PATH.$path )) {
+ $guid = str_replace( "{$this->mSystem}_plugin_path_", '', $config );
+ $gBitSystem->storeConfigMatch( "/^{$this->mSystem}_plugin_\w+_$guid/i", NULL );
+ }
+ }
+
+ // TODO: we can remove this at some point since it's not really important - it just clears out stuff from the database that we don't use anymore
+ $gBitSystem->storeConfigMatch( "/^{$this->mSystem}_plugin_file_/", NULL );
+ }
+
+ /**
+ * Check to see if a given plugin is activ or not
+ *
+ * @param $pPluginGuid Plugin GUID of the plugin you want to check
+ * @return TRUE if the plugin is active, FALSE if it's not
+ **/
+ function isPluginActive( $pPluginGuid ) {
+ return( !empty( $this->mPlugins[$pPluginGuid]['is_active'] ) && ( $this->mPlugins[$pPluginGuid]['is_active'] == 'y' ));
+ }
+
+ /**
+ * Allow data plugins to register their tag
+ *
+ * @param string $pTag Tag of plugin, e.g.: TOC
+ * @param string $pPluginGuid GUID of plugin, e.g.: PLUGIN_GUID_TOC
+ * @access public
+ * @return void
+ */
+ function registerDataTag( $pTag, $pPluginGuid ) {
+ $this->mDataTags[strtolower( $pTag )] = $pPluginGuid;
+ }
+
+ /**
+ * Allow plugins to register themselves using this function. Data is added directly to the list of existing plugins
+ *
+ * @param $pGuid GUID of plugin
+ * @param $pPluginParams Set of plugin parameters (see treasury/plugins/mime.*.php for example)
+ * @return none
+ * @access public
+ **/
+ function registerPlugin( $pGuid, $pPluginParams ) {
+ global $gBitSystem;
+ // plugins can set their own file_name. this is not mandatory but makes sure we store the path to the correct file
+ // this is useful for files that are included by other plugins
+ if( !empty( $pPluginParams['file_name'] )) {
+ $pluginPath = dirname( $this->mPluginFilePath )."/".$pPluginParams['file_name'];
+ } else {
+ $pluginPath = $this->mPluginFilePath;
+ }
+
+ if( !empty( $pGuid ) && !empty( $pluginPath ) && is_file( $pluginPath ) ) {
+ // store the relative path - we need to store the path to all plugins and not just active ones since we don't have access to this information when we use setActivePlugins()
+ $gBitSystem->storeConfig( "{$this->mSystem}_plugin_path_".$pGuid, str_replace( BIT_ROOT_PATH, "", $pluginPath ), LIBERTY_PKG_NAME );
+ $settings['is_active'] = $gBitSystem->getConfig( "{$this->mSystem}_plugin_status_".$pGuid );
+ if( empty( $settings['is_active'] ) && !empty( $pPluginParams['auto_activate'] )) {
+ $this->setActivePlugin( $pGuid );
+ }
+ $settings['plugin_guid'] = $pGuid;
+ $this->mPlugins[$pGuid] = array_merge( $settings, $pPluginParams );
+ }
+
+ }
+
+ /**
+ * setActivePlugins
+ *
+ * @param array $pPluginGuids an array of all the plugin guids that are active. Any left out are *inactive*!
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function setActivePlugins( $pPluginGuids ) {
+ global $gBitSystem;
+
+ if( is_array( $pPluginGuids ) ) {
+ // zap list of plugins from DB
+ $gBitSystem->storeConfigMatch( "/^{$this->mSystem}_plugin_status/i", NULL, 'n', LIBERTY_PKG_NAME );
+ foreach( array_keys( $this->mPlugins ) as $guid ) {
+ $this->mPlugins[$guid]['is_active'] = 'n';
+ }
+
+ // set active those specified
+ foreach( array_keys( $pPluginGuids ) as $guid ) {
+ if( $pPluginGuids[$guid][0] == 'y' ) {
+ $this->setActivePlugin( $guid );
+ }
+ }
+ // load any plugins made active, but not already loaded
+ $this->loadActivePlugins();
+
+ // finally we need to remove all cache files since the content has been changed
+ LibertyContent::expungeCache();
+ }
+ }
+
+ /**
+ * set a single plugin as active and store the appropriate information in the database
+ *
+ * @param array $pPluginGuid the plugin guid we want to set active
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function setActivePlugin( $pPluginGuid ) {
+ global $gBitSystem;
+ $gBitSystem->storeConfig( "{$this->mSystem}_plugin_status_".$pPluginGuid, 'y', LIBERTY_PKG_NAME );
+ if( isset( $this->mPlugins[$pPluginGuid] )) {
+ $this->mPlugins[$pPluginGuid]['is_active'] = 'y';
+ }
+
+ // the requirement function can return a set of tables, indexes and sequences that need to be created for the plugin to work.
+ if( $requirement_func = $this->getPluginFunction( $pPluginGuid, 'requirement_function' )) {
+ $reqs = $requirement_func( TRUE );
+ if( !empty( $reqs['schema']['tables'] )) {
+ // fetch a list of tables in the database that we know if we need to insert any plugin ones
+ if( strlen( BIT_DB_PREFIX ) > 0 ) {
+ $lastQuote = strrpos( BIT_DB_PREFIX, '`' );
+ if( $lastQuote != FALSE ) {
+ $lastQuote++;
+ }
+ $prefix = substr( BIT_DB_PREFIX, $lastQuote );
+ } else {
+ $prefix = '';
+ }
+
+ global $gBitDbType, $gBitDbHost, $gBitDbUser, $gBitDbPassword, $gBitDbName;
+ $db = &ADONewConnection( $gBitDbType );
+ if( $db->Connect( $gBitDbHost, $gBitDbUser, $gBitDbPassword, $gBitDbName )) {
+ $dict = NewDataDictionary( $db );
+
+ if( !$gBitSystem->mDb->getCaseSensitivity() ) {
+ $dict->connection->nameQuote = '';
+ }
+
+ if( $dbTables = $gBitSystem->mDb->MetaTables( 'TABLES', FALSE, ( $prefix ? $prefix.'%' : NULL ))) {
+ // If we use MySql check which storage engine to use
+ if( isset( $_SESSION['use_innodb'] )) {
+ if( $_SESSION['use_innodb'] == TRUE ) {
+ $build = array( 'NEW', 'MYSQL' => 'ENGINE=INNODB' );
+ } else {
+ $build = array( 'NEW', 'MYSQL' => 'ENGINE=MYISAM' );
+ }
+ } else {
+ $build = 'NEW';
+ }
+
+ // create tables
+ foreach( $reqs['schema']['tables'] as $table => $tableDict ) {
+ $fullTable = $prefix.$table;
+ if( !in_array( $fullTable, $dbTables )) {
+ if( $sql = $dict->CreateTableSQL( $fullTable, $tableDict, $build )) {
+ $ret = $dict->ExecuteSQLArray( $sql );
+ if( $ret === FALSE ) {
+ $errors[] = 'Failed to create table '.$completeTableName;
+ $tablesInstalled = TRUE;
+ }
+ }
+ }
+ }
+
+ // only continue if we installed at least one table
+ if( !empty( $tablesInstalled )) {
+ $schemaQuote = strrpos( BIT_DB_PREFIX, '`' );
+ $sequencePrefix = ( $schemaQuote ? substr( BIT_DB_PREFIX, $schemaQuote + 1 ) : BIT_DB_PREFIX );
+
+ // create indexes
+ if( !empty( $reqs['schema']['indexes'] )) {
+ foreach( $reqs['schema']['indexes'] as $idx => $idxDict ) {
+ $completeTableName = $sequencePrefix.$reqs['schema']['indexes'][$idx]['table'];
+ if( $sql = $dict->CreateIndexSQL( $idx, $completeTableName, $reqs['schema']['indexes'][$idx]['cols'], $reqs['schema']['indexes'][$idx]['opts'] )) {
+ $ret = $dict->ExecuteSQLArray( $sql );
+ if( $ret === FALSE ) {
+ $errors[] = 'Failed to create index '.$completeTableName;
+ }
+ }
+ }
+ }
+
+ // create sequences
+ if( !empty( $reqs['schema']['sequences'] )) {
+ // If we use InnoDB for MySql we need this to get sequence tables created correctly.
+ if( isset( $_SESSION['use_innodb'] ) ) {
+ if( $_SESSION['use_innodb'] == TRUE ) {
+ $gBitInstallDb->_genSeqSQL = "create table %s (id int not null) ENGINE=INNODB";
+ } else {
+ $gBitInstallDb->_genSeqSQL = "create table %s (id int not null) ENGINE=MYISAM";
+ }
+ }
+
+ foreach( array_keys( $reqs['schema']['sequences'] ) as $sequenceIdx ) {
+ if( !$gBitInstallDb->CreateSequence( $sequencePrefix.$sequenceIdx, $reqs['schema']['sequences'][$sequenceIdx]['start'] )) {
+ $errors[] = 'Failed to create sequence '.$sequencePrefix.$sequenceIdx;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return( !empty( $errors ) ? $errors : NULL );
+ }
+
+ /**
+ * getPluginInfo
+ *
+ * @param array $pGuid
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function getPluginInfo( $pGuid ) {
+ $ret = NULL;
+ if( !empty( $pGuid ) && !empty( $this->mPlugins[$pGuid] )) {
+ $ret = $this->mPlugins[$pGuid];
+ }
+ return $ret;
+ }
+
+ /**
+ * getPluginFunction
+ *
+ * @param string $pGuid GUID of plugin used - if empty, we get all available functions of that type in all active plugins
+ * @param string $pFunctionName Function type we want to use
+ * @param string $pGetDefault Get default function for a given plugin type such as 'mime'
+ * @param string $pGetInactive don't worry if plugin is active or not
+ * @access public
+ * @return function name on success, NULL on failure
+ */
+ function getPluginFunction( $pGuid, $pFunctionName, $pGetDefault = FALSE, $pGetInactive = FALSE ) {
+ if(( $this->isPluginActive( $pGuid ) || $pGetInactive ) && !empty( $this->mPlugins[$pGuid][$pFunctionName] ) && function_exists( $this->mPlugins[$pGuid][$pFunctionName] )) {
+ $ret = $this->mPlugins[$pGuid][$pFunctionName];
+ }
+
+ // if we can't get a function on the first round, we fetch the default
+ if( empty( $ret ) && $pGetDefault == 'mime' && $pGuid != LIBERTY_DEFAULT_MIME_HANDLER ) {
+ $ret = $this->getPluginFunction( LIBERTY_DEFAULT_MIME_HANDLER, $pFunctionName );
+ }
+
+ return( !empty( $ret ) ? $ret : NULL );
+ }
+
+ /**
+ * getPluginFunctions Get a list of functions of a given type
+ *
+ * @param string $pFunctionName Function type we want to get
+ * @access public
+ * @return array of functions with the GUID as key
+ */
+ function getPluginFunctions( $pFunctionName ) {
+ foreach( $this->mPlugins as $guid => $plugin ) {
+ if( $this->isPluginActive( $guid ) && !empty( $plugin[$pFunctionName] ) && function_exists( $plugin[$pFunctionName] )) {
+ $ret[$guid] = $plugin[$pFunctionName];
+ }
+ }
+
+ return( !empty( $ret ) ? $ret : array() );
+ }
+
+ /**
+ * getMimeTemplate will fetch an appropriate template to display a given filetype
+ *
+ * @param string $pTemplate Basename of the template
+ * @param string $pGuid GUID of plugin
+ * @access public
+ * @return resource path to template
+ */
+ function getMimeTemplate( $pTemplate, $pGuid = LIBERTY_DEFAULT_MIME_HANDLER ) {
+ $ret = NULL;
+ if( $this->isPluginActive( $pGuid ) && ( $plugin = $this->getPluginInfo( $pGuid )) && !empty( $plugin[$pTemplate.'_tpl'] )) {
+ $ret = $plugin[$pTemplate.'_tpl'];
+ } elseif( $pGuid != LIBERTY_DEFAULT_MIME_HANDLER ) {
+ $ret = $this->getMimeTemplate( $pTemplate );
+ }
+ return $ret;
+ }
+
+ /**
+ * getAllMimeTemplates will fetch templates of a given type from all active plugins
+ *
+ * @param array $pTemplate Name of the template
+ * @access public
+ * @return array of resource paths to templates
+ */
+ function getAllMimeTemplates( $pTemplate ) {
+ $ret = array();
+ foreach( $this->getPluginsOfType( MIME_PLUGIN ) as $guid => $plugin ) {
+ if( $this->isPluginActive( $guid ) && !empty( $plugin[$pTemplate.'_tpl'] )) {
+ $ret[] = $plugin[$pTemplate.'_tpl'];
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * getPluginsOfType will fetch all plugins of a given type
+ *
+ * @param string $pPluginType
+ * @access public
+ * @return an array of plugins of a given type
+ */
+ function getPluginsOfType( $pPluginType ) {
+ $ret = array();
+ if( !empty( $pPluginType )) {
+ foreach( $this->mPlugins as $guid => $plugin ) {
+ if( !empty( $plugin['plugin_type'] ) && $plugin['plugin_type'] == $pPluginType ) {
+ $ret[$guid] = $plugin;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * This function will purge all plugin settings set in kernel_config. useful when the path to plugins changes
+ * or plugins don't seem to be working
+ *
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+ function resetAllPluginSettings() {
+ global $gBitSystem;
+ $gBitSystem->storeConfigMatch( "/^{$this->mSystem}_plugin_/", NULL );
+ if( $this->mSystem == LIBERTY_PKG_NAME ) {
+ // also remove the default format
+ $gBitSystem->storeConfig( 'default_format', NULL, $this->mSystem );
+ }
+ $this->scanAllPlugins();
+ }
+
+ /**
+ * Load all available content types into $this->mContentTypes
+ *
+ * @return none
+ **/
+ function loadContentTypes( $pCacheTime=BIT_QUERY_CACHE_TIME ) {
+ if( $rs = $this->mDb->query( "SELECT * FROM `".BIT_DB_PREFIX."liberty_content_types`", FALSE, BIT_QUERY_DEFAULT, BIT_QUERY_DEFAULT ) ) {
+ while( $row = $rs->fetchRow() ) {
+ // translate name
+ // content_description backward compatibility for now
+ $row['content_description'] = $row['content_name'] = tra( $row['content_name'] );
+ if( !empty( $row['content_name_plural'] ) ){
+ $row['content_name_plural'] = tra( $row['content_name_plural'] );
+ }
+ $this->mContentTypes[$row['content_type_guid']] = $row;
+ }
+ }
+ }
+
+ /**
+ * Register new content type
+ *
+ * @return none
+ * @access public
+ **/
+ function registerContentType( $pGuid, $pTypeParams ) {
+ global $gBitSystem;
+ if ( !$this->mDb->isValid() ) return;
+ if( !isset( $this->mContentTypes ) ) {
+ $this->loadContentTypes();
+ }
+ $pTypeParams['content_type_guid'] = $pGuid;
+ // automagically populate plural name value if none is set using most comment english of appending 's'
+ if( empty( $pTypeParams['content_name_plural'] ) ){
+ $pTypeParams['content_name_plural'] = $pTypeParams['content_name'].'s';
+ }
+ if( empty( $this->mContentTypes[$pGuid] ) && !empty( $pTypeParams ) ) {
+ $this->StartTrans();
+ $result = $this->mDb->associateInsert( BIT_DB_PREFIX."liberty_content_types", $pTypeParams );
+ $this->CompleteTrans();
+ // we just ran some SQL - let's flush the loadContentTypes query cache
+ $this->loadContentTypes( 0 );
+ } else {
+ if( $pTypeParams['handler_package'] != $this->mContentTypes[$pGuid]['handler_package'] ||
+ $pTypeParams['handler_file'] != $this->mContentTypes[$pGuid]['handler_file'] ||
+ $pTypeParams['handler_class'] != $this->mContentTypes[$pGuid]['handler_class'] ||
+ ( empty( $this->mContentTypes[$pGuid]['content_name_plural'] ) && version_compare( $gBitSystem->getVersion( LIBERTY_PKG_NAME ), '2.1.4', '>=' ) ) // temporary update condition during migration of content_description to content_name remove after april 20 2011
+ ) {
+ $this->StartTrans();
+ $result = $this->mDb->associateUpdate( BIT_DB_PREFIX."liberty_content_types", $pTypeParams, array( 'content_type_guid'=>$pGuid ) );
+ $this->CompleteTrans();
+ // we just ran some SQL - let's flush the loadContentTypes query cache
+ $this->loadContentTypes( 0 );
+ }
+ }
+ }
+
+ /**
+ * requireHandlerFile will require_once() the handler file if given the hash found in $gLibertySystem->mContentTypes[content_type_guid]
+ *
+ * @param array $pContentTypeHash the hash found in $gLibertySystem->mContentTypes[content_type_guid]
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+ private function requireHandlerFile( $pContentTypeHash ) {
+ $ret = FALSE;
+ $pkgName = strtoupper( $pContentTypeHash['handler_package'] );
+ foreach( array( '_PKG_CLASS_PATH', '_PKG_INCLUDE_PATH', '_PKG_PATH' ) as $pkgConstPath ) {
+ if( defined( $pkgName.$pkgConstPath ) && ($pkgDef = constant( $pkgName.$pkgConstPath )) ) {
+ $handlerFile = $pkgDef.$pContentTypeHash['handler_file'];
+ if( is_file( $handlerFile ) ) {
+ require_once( $handlerFile );
+ $ret = TRUE;
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function getContentType( $pContentTypeGuid ){
+ $ret = NULL;
+ if( !isset( $this->mContentTypes ) ) {
+ $this->loadContentTypes();
+ }
+ if( !empty( $this->mContentTypes[$pContentTypeGuid] ) ) {
+ $ret = $this->mContentTypes[$pContentTypeGuid];
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ public function getContentClassName( $pContentTypeGuid ) {
+ $ret = NULL;
+ if( !isset( $this->mContentTypes ) ) {
+ $this->loadContentTypes();
+ }
+ if( !empty( $this->mContentTypes[$pContentTypeGuid] ) && $this->requireHandlerFile( $this->mContentTypes[$pContentTypeGuid] ) ) {
+ $ret = $this->mContentTypes[$pContentTypeGuid]['handler_class'];
+ }
+ return $ret;
+ }
+
+ /**
+ * 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
+ */
+ function getContentTypeName( $pContentTypeGuid, $pPlural=FALSE ){
+ $ret = NULL;
+ if( $pPlural && isset( $this->mContentTypes[$pContentTypeGuid]['content_name_plural'] ) ) {
+ $ret = tra( $this->mContentTypes[$pContentTypeGuid]['content_name_plural'] );
+ } elseif( !empty( $this->mContentTypes[$pContentTypeGuid]['content_name'] ) ) {
+ $ret = tra( $this->mContentTypes[$pContentTypeGuid]['content_name'] );
+ }
+ return $ret;
+ }
+
+ /**
+ * Get the description of a given content type
+ *
+ * @param $pContentType Content type GUID you want the description for
+ * @return Content type description
+ * @access public
+ **/
+ function getContentTypeDescription( $pContentType ) {
+ deprecated( 'You are calling the deprecated method getContentTypeDescription, use getContentTypeName( $pPlural )' );
+ return $this->getContentTypeName( $pContentType );
+ }
+
+
+
+
+ // ****************************** Service Functions
+ /**
+ * Get the service details of a given package
+ *
+ * @param $pPackageName Package name of you want the service details for
+ * @return Service details if the package has them - FALSE if the package is not a service
+ * @access public
+ **/
+ function getService( $pPackageName ) {
+ global $gBitSystem;
+ return( !empty( $gBitSystem->mPackages[$pPackageName]['service'] ) ? $gBitSystem->mPackages[$pPackageName]['service'] : NULL );
+ }
+
+ /**
+ * Register package as service - hash added to $this->mServices
+ *
+ * $pServiceHash Service hash details. see existing service hashes found in <package>/bit_setup_inc.php for examples and details
+ * @return none
+ * @access public
+ **/
+ function registerService( $pServiceName, $pPackageName, $pServiceHash, $pOptions = array() ) {
+ $this->mServices[$pServiceName] = array(
+ 'package' => $pPackageName,
+ 'services' => $pServiceHash,
+ 'description' => !empty( $pOptions['description'] ) ? $pOptions['description'] : NULL,
+ 'required' => !empty( $pOptions['required'] ) ? $pOptions['required'] : FALSE,
+ );
+ }
+
+ /**
+ * Check to see if a package has any service capabilities
+ *
+ * @return TRUE on success, FALSE on failure
+ * @access public
+ **/
+ function hasService( $pServiceName ) {
+ return( !empty( $this->mServices[$pServiceName] ) );
+ }
+
+ /**
+ * Get contents of a given service value
+ *
+ * @param $pServiceValue Service value you want to work to get
+ * @return Value of a given service value
+ * @access private
+ **/
+ function getServiceValues( $pServiceValue ) {
+ global $gBitSystem;
+ $ret = NULL;
+ if( !empty( $this->mServices ) ) {
+ foreach( array_keys( $this->mServices ) as $service ) {
+ if( $this->hasService( $service ) ) {
+ // DEPRECATED - this is mostly circular logic - getting the package name from itself to look itself up
+ // Service names are key values - regardless of package
+ // Accessing services directly by name infact allows multiple packages to provide the same kind of service
+ /*
+ if( !($package = $gBitSystem->getConfig( 'liberty_service_'.$service )) ) {
+ $package = key( $this->mServices[$service] );
+ }
+ if( !empty( $this->mServices[$service][$package][$pServiceValue] ) ) {
+ $ret[$service] = $this->mServices[$service][$package][$pServiceValue];
+ }
+ */
+ if( !empty( $this->mServices[$service]['services'][$pServiceValue] ) ) {
+ $ret[$service] = $this->mServices[$service]['services'][$pServiceValue];
+ }
+ }
+ }
+ }
+ return $ret;
+ }
+
+
+
+
+ // ****************************** Miscellaneous Functions
+ /**
+ * Get the URL to the icon for the mime type passed in. This should probably check for files of multiple image types instead of just jpg
+ *
+ * @param string $pMimeType Mime type of the file
+ * @param string $pExt Extension of the file - used to get backup mime icon
+ * @access public
+ * @return Full image HTML tag to mime icon
+ */
+ public static function getMimeThumbnailURL($pMimeType, $pExt=NULL) {
+ $ret = NULL;
+ $parts = explode( '/',$pMimeType );
+ if( count( $parts ) > 1 ) {
+ global $gBitSmarty;
+ $gBitSmarty->loadPlugin( 'smarty_function_biticon' );
+
+ $ext = strtolower( $parts[1] );
+ $biticon = array(
+ 'ipackage' => 'liberty',
+ 'ipath' => 'mime/',
+ 'iname' => $ext,
+ 'iexplain' => $ext,
+ 'url' => 'only',
+ );
+
+ if( !$ret = smarty_function_biticon( $biticon ) ) {
+ $biticon['iname'] = strtolower( $pExt );
+ if( !$ret = smarty_function_biticon( $biticon ) ) {
+ $biticon['iname'] = 'generic';
+ $ret = smarty_function_biticon( $biticon );
+ }
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Will return the plugin that is responsible for the given mime type
+ *
+ * @param string $pFileHash['mimetype'] (required if no tmp_name) Mime type of file that needs to be dealt with
+ * @param string $pFileHash['tmp_name'] (required if no mimetype) Full path to file that needs to be dealt with
+ * @access public
+ * @return handler plugin guid
+ * TODO: Currently this will return the first found handler - might want to have a sort order?
+ **/
+ function lookupMimeHandler( &$pFileHash ) {
+ global $gBitSystem;
+
+ if( empty( $this->mPlugins )) {
+ $this->scanAllPlugins( NULL, "mime\." );
+ }
+
+ // we will do our best to work out what this file is.
+ // both these methods use a different method for fetching the filetype
+ // this can be particularly important when fetching the mime-type of video files.
+ // ! Windows looses the file extension when creating the tmp file
+ // need a better way of handling this
+ if( !is_windows() ) {
+ $pFileHash['type'] = $gBitSystem->verifyMimeType( $pFileHash['tmp_name'] );
+ }
+
+ if( $pFileHash['type'] == 'application/binary' || $pFileHash['type'] == 'application/octet-stream' || $pFileHash['type'] == 'application/octetstream' ) {
+ $pFileHash['type'] = $gBitSystem->lookupMimeType( $pFileHash['name'] );
+ }
+
+ foreach( $this->getPluginsOfType( MIME_PLUGIN ) as $handler => $plugin ) {
+ if( $this->isPluginActive( $handler ) && !empty( $plugin['mimetypes'] ) && is_array( $plugin['mimetypes'] )) {
+ foreach( $plugin['mimetypes'] as $pattern ) {
+ if( preg_match( $pattern, $pFileHash['type'] )) {
+ return $handler;
+ }
+ }
+ }
+ }
+
+ return LIBERTY_DEFAULT_MIME_HANDLER;
+ }
+}
+?>
diff --git a/includes/comments_inc.php b/includes/comments_inc.php
new file mode 100644
index 0000000..b891d80
--- /dev/null
+++ b/includes/comments_inc.php
@@ -0,0 +1,368 @@
+<?php
+/**
+ * comment_inc
+ *
+ * @author spider <spider@steelsun.com>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+// Copyright (c) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
+// 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.
+
+// This file sets up the information needed to display
+// the comments preferences, post-comment box and the
+// list of comments. Finally it displays comments.tpl
+// using this information
+
+// Setup URLS for the Comments next and prev buttons and use variables that
+// cannot be aliased by normal Bit variables.
+// Traverse each _REQUEST data adn put them in an array
+
+// this script may only be included - so its better to die if called directly.
+
+
+/**
+ * Parameters that need to be set when calling this file
+ * @param numeric $commentsParentId The content id of the object where a new comment will be attached (required)
+ * @param array $commentsParentIds The list of content id of object the comments will be displayed - if not defined $commentsParentId (required if $commentsParentId is not set)
+ * @param string $comments_return_url The URL the user should be sent to after posting the comment (required)
+**/
+
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyComment.php' );
+
+global $commentsLib, $gBitSmarty, $gBitSystem, $gBitThemes;
+
+$postComment = array();
+$formfeedback = array( 'error' => array() );
+$gBitSmarty->assignByRef( 'formfeedback', $formfeedback );
+
+// make sure that we don't feed ajax comments if we don't have javascript enabled
+if( !$gBitThemes->isJavascriptEnabled() ) {
+ $gBitSystem->setConfig( 'comments_ajax', 'n' );
+}
+
+if( @BitBase::verifyId( $_REQUEST['delete_comment_id'] )) {
+ $deleteComment = new LibertyComment($_REQUEST['delete_comment_id']);
+ // make sure we're loaded up before we delete
+ $deleteComment->loadComment();
+ if( $deleteComment->isValid() && $gContent->hasUserPermission( 'p_liberty_admin_comments' )) {
+ // delete entire thread
+ $deleteComment->expunge();
+ }
+}
+
+if( @BitBase::verifyId( $_REQUEST['post_comment_id'] ) && $gContent->hasUserPermission( 'p_liberty_post_comments' )) {
+ $post_comment_id = $_REQUEST['post_comment_id'];
+ $editComment = new LibertyComment( $post_comment_id );
+ //if we are passed a comment id but not going to store it then turn off ajax
+ if( !isset( $_REQUEST['post_comment_submit'] ) && !isset( $_REQUEST['post_comment_cancel'] )){
+ //even if ajax is on - we force it off in this case
+ $gBitSmarty->assign( 'comments_ajax', FALSE );
+ }
+
+ if( $editComment->mInfo['content_id'] ) {
+ if( $editComment->userCanUpdate( $gContent )) {
+ $postComment['data'] = $editComment->mInfo['data'];
+ $postComment['title'] = $editComment->mInfo['title'];
+ } else {
+ $formfeedback['error'][] = "You do not have permission to edit this comment.";
+ $editComment = NULL;
+ $post_comment_id = NULL;
+ }
+ } else {
+ $formfeedback['error'][] = "Comment does not exist.";
+ $editComment = NULL;
+ $post_comment_id = NULL;
+ }
+} else {
+ $post_comment_id = NULL;
+ $editComment = NULL;
+}
+$gBitSmarty->assign('post_comment_id', $post_comment_id);
+
+// Store comment posts
+if( !empty( $_REQUEST['post_comment_submit'] ) && $gContent->hasUserPermission( 'p_liberty_post_comments' )) {
+
+ // check for !anon_post before logging in (auto-fill can hork things up)
+ if( empty( $_REQUEST['anon_post'] ) && !empty( $_REQUEST['login_email'] ) && !empty( $_REQUEST['login_password'] ) ) {
+ $gBitUser->login( $_REQUEST['login_email'], $_REQUEST['login_password'] );
+ if( !empty( $gBitUser->mErrors['login'] ) ) {
+ $formfeedback['error'][] = $gBitUser->mErrors['login'];
+ }
+ } else {
+ if( !empty($_REQUEST['comment_name'] )) {
+ $_REQUEST['anon_name'] = $_REQUEST['comment_name'];
+ }
+ }
+
+ // this commentsParentId is some crazy ass business - lets prepare for the day when this can be removed
+ // there are references to it in LibertyComments::verifyComments as well
+ $_REQUEST['comments_parent_id'] = $commentsParentId;
+
+ $storeComment = new LibertyComment( @BitBase::verifyId( $editComment->mCommentId ) ? $editComment->mCommentId : NULL );
+
+ if( empty( $formfeedback['error'] ) && $storeComment->storeComment( $_REQUEST )) {
+ // store successful
+ $storeComment->loadComment();
+ if( empty( $_REQUEST['post_comment_id'] ) && $gBitSystem->isPackageActive( 'switchboard' ) ) {
+ // A new comment, and we have switchboard to send notifications
+ global $gSwitchboardSystem;
+ // Draft the message:
+ $message['subject'] = tra( 'New comment on:' ).' '.$gContent->getTitle().' @ '.$gBitSystem->getConfig( 'site_title' );
+ $message['message'] = tra('A new message was posted to ').' '.$gContent->getTitle()."<br/>\n".$gContent->getDisplayUri()."<br/>\n"
+ .'/----- '.tra('Here is the message')." -----/<br/>\n<br/>\n".'<h2>'.$storeComment->getTitle()."</h2>\n".tra('By').' '.$gBitUser->getDisplayName()."\n<p>".$storeComment->getParsedData().'</p>';
+ $gSwitchboardSystem->sendEvent('My Content', 'new comment', $gContent->mContentId, $message );
+ }
+ $postComment = NULL;
+ } else {
+ // store fails handle errors and preview
+ $formfeedback['error']=array_merge( $formfeedback['error'], $storeComment->mErrors );
+ $postComment['data'] = !empty( $_REQUEST['comment_data'] ) ? $_REQUEST['comment_data'] : '';
+ $postComment['title'] = !empty( $_REQUEST['comment_title'] ) ? $_REQUEST['comment_title'] : '';
+ if( !empty( $_REQUEST['comment_name'] ) ) {
+ $postComment['anon_name'] = $_REQUEST['comment_name'];
+ }
+
+ $_REQUEST['post_comment_request'] = TRUE;
+ //this is critical and triggers other settings if store fails - do not remove without looking at what preview effects
+ $_REQUEST['post_comment_preview'] = TRUE;
+ }
+} elseif(!empty($_REQUEST['post_comment_request']) && !$gContent->hasUserPermission( 'p_liberty_post_comments' )) {
+ $formfeedback['warning']="You don't have permission to post comments.";
+}
+
+// $post_comment_request is a flag indicating whether or not to display the comment input form
+if( empty( $_REQUEST['post_comment_request'] ) && !$gBitSystem->isFeatureActive( 'comments_auto_show_form' ) ) {
+ $post_comment_request = NULL;
+} elseif( $gContent->hasUserPermission( 'p_liberty_post_comments' ) ) {
+ $post_comment_request = TRUE;
+ // force off ajax attachments which does not work for comments attachments
+ if( $gBitSystem->isFeatureActive( 'comments_allow_attachments' ) && $gBitSystem->getConfig( 'liberty_attachment_style') == 'ajax' ){
+ $gBitSystem->setConfig( 'liberty_attachment_style', 'standard' );
+ }
+}
+
+// in anticipation of mainlining LCConfig package - enable comment format configuration
+// hack because comments does not have edit service -wjames5
+if( $gBitSystem->isPackageActive( 'lcconfig' ) ){
+ $spoofHash = array();
+ lcconfig_content_edit( new LibertyComment(), $spoofHash );
+}
+
+if( !empty( $_REQUEST['post_comment_request'] ) && $_REQUEST['post_comment_request'] == 'y' && !$gContent->hasUserPermission( 'p_liberty_post_comments' ) ) {
+ $gBitSystem->fatalPermission( 'p_liberty_post_comments' );
+}
+$gBitSmarty->assignByRef('post_comment_request', $post_comment_request);
+
+if( !empty( $_REQUEST['post_comment_cancel'] ) ) {
+ $postComment = NULL;
+}
+
+// $post_comment_preview is a flag indicating that the user wants to preview their comment prior to saving it
+if( !empty( $_REQUEST['post_comment_preview'] )) {
+ if( isset( $_REQUEST['no_js_preview'] ) && $_REQUEST['no_js_preview']=="y" ) {
+ $no_js_preview = $_REQUEST['no_js_preview'];
+
+ //even if ajax is on - we force it off in this case
+ $gBitSmarty->assign( 'comments_ajax', FALSE );
+ } else {
+ $no_js_preview = "n";
+ }
+
+ $gBitSmarty->assignByRef( 'no_js_preview', $no_js_preview );
+
+ $postComment['user_id'] = $gBitUser->mUserId;
+ $postComment['title'] = $_REQUEST['comment_title'];
+ if( !empty( $_REQUEST['comment_name'] )) {
+ $postComment['anon_name'] = $_REQUEST['comment_name'];
+ }
+ $postComment['data'] = BitBase::getParameter( $_REQUEST, 'comment_data' );
+ $postComment['format_guid'] = empty( $_REQUEST['format_guid'])? $gBitSystem->getConfig( 'default_format' ) : $_REQUEST['format_guid'];
+ $postComment['parsed_data'] = LibertyComment::parseDataHash( $postComment );
+ $postComment['created'] = time();
+ $postComment['last_modified'] = time();
+ $gBitSmarty->assign('post_comment_preview', TRUE);
+}
+
+// $post_comment_reply_id is the content_id which a post is replying to
+if( @BitBase::verifyId( $_REQUEST['post_comment_reply_id'] )) {
+ $post_comment_reply_id = $_REQUEST['post_comment_reply_id'];
+ $tmpComment = new LibertyComment( NULL, $post_comment_reply_id );
+ if( !empty( $_REQUEST['quote'] )) {
+ $postComment['data'] = $tmpComment->getQuoted();
+ }
+ if( preg_match( '/^' . tra( 'Re:' ) . '/', $tmpComment->mInfo['title'] )) {
+ $comment_prefix = '';
+ } else {
+ $comment_prefix = tra( 'Re:' ) . " ";
+ }
+
+ //this always overrides the title with "Re: Parent Title" -- not sure what it really should do so I put in this conditional for previews
+ if( !isset( $_REQUEST['comment_title'] )) {
+ $postComment['title'] = $comment_prefix.$tmpComment->mInfo['title'];
+ }
+
+ $gBitSmarty->assign( 'post_comment_reply_id', $post_comment_reply_id );
+}
+
+if( $gContent->hasUserPermission( 'p_liberty_read_comments' )) {
+
+ if( !empty( $_SESSION['liberty_comments_per_page'] )) {
+ $maxComments = $_SESSION['liberty_comments_per_page'];
+ } else {
+ $maxComments = $gBitSystem->getConfig( 'comments_per_page', 10 );
+ }
+
+ if( !empty( $_REQUEST["comments_maxComments"] )) {
+ $maxComments = $_REQUEST["comments_maxComments"];
+ $comments_at_top_of_page = 'y';
+ $_SESSION['liberty_comments_per_page'] = $maxComments;
+ }
+
+ if( !empty( $_SESSION['liberty_comments_ordering'] )) {
+ $comments_sort_mode = $_SESSION['liberty_comments_ordering'];
+ } else {
+ $comments_sort_mode = $gBitSystem->getConfig( 'comments_default_ordering', 'commentDate_desc' );
+ }
+
+ if( !empty( $_REQUEST["comments_sort_mode"] )) {
+ $comments_sort_mode = $_REQUEST["comments_sort_mode"];
+ $comments_at_top_of_page = 'y';
+ $_SESSION['liberty_comments_ordering'] = $comments_sort_mode;
+ }
+
+ if( !empty( $_SESSION['liberty_comments_display_mode'] )) {
+ $comments_display_style = $_SESSION['liberty_comments_display_mode'];
+ } else {
+ $comments_display_style = $gBitSystem->getConfig( 'comments_default_display_mode', 'threaded' );
+ }
+
+ if( !empty( $_REQUEST["comments_style"] ) ) {
+ $comments_display_style = $_REQUEST["comments_style"];
+ $comments_at_top_of_page = 'y';
+ $_SESSION['liberty_comments_display_mode'] = $comments_display_style;
+ }
+
+ if( !empty( $_REQUEST['comment_page'] ) || !empty( $_REQUEST['post_comment_request'] ) ) {
+ $comments_at_top_of_page = 'y';
+ }
+ $commentOffset = !empty( $_REQUEST['comment_page'] ) ? ($_REQUEST['comment_page'] - 1) * $maxComments : 0;
+
+ if( empty( $gComment )) {
+ $gComment = new LibertyComment();
+ }
+
+ $currentPage = !empty( $_REQUEST['comment_page'] ) ? $_REQUEST['comment_page'] : 1;
+ if( $currentPage < 1 ) {
+ $currentPage = 1;
+ }
+
+ # logic to support displaying a single comment -- used when we need a URL pointing to a comment
+ if( !empty( $_REQUEST['view_comment_id'] )) {
+ $commentOffset = $gComment->getNumComments_upto( $_REQUEST['view_comment_id'] );
+# echo "commentOffset =$commentOffset= maxComments=$maxComments=\n";
+ $comments_sort_mode = 'commentDate_asc';
+ $comments_display_style = 'flat';
+ $comments_at_top_of_page = 'y';
+ $maxComments = 1;
+ $currentPage = ceil( $commentOffset + 1 / $maxComments );
+ } else {
+ $commentOffset = ( $currentPage - 1 ) * $maxComments;
+ }
+
+
+ // $commentsParentId is the content_id which the comment tree is attached to
+ if( !@BitBase::verifyId( $commentsParentId ) ) {
+ $comments = array();
+ $numComments = 0;
+ } else {
+ if( @BitBase::verifyId( $commentsParentIds ) ) {
+ $parents = $commentsParentIds;
+ } else {
+ $parents = $commentsParentId;
+ }
+ // pass in a reference to the root object so that we can do proper permissions checks
+ if ( is_object( $gContent )) {
+ $gComment->mRootObj = $gContent;
+ }
+ $numComments = $gComment->getNumComments( $commentsParentId );
+ if ($commentOffset > $numComments) {
+ $commentOffset = $numComments / $maxComments;
+ $currentPage = ceil( $commentOffset+1 / $maxComments );
+ }
+ $comments = $gComment->getComments( $parents, $maxComments, $commentOffset, $comments_sort_mode, $comments_display_style );
+ }
+
+ if( $comments_display_style == 'flat' ) {
+ $commentsTree = $comments;
+ } else {
+ $commentsTree = array();
+ foreach( $comments as $id => $node ){
+ if( !empty( $comments[ $node['parent_id'] ] )) {
+ $comments[ $node['parent_id'] ]['children'][$id] = &$comments[$id];
+ }
+ if( $node['parent_id'] == $node['root_id'] || empty( $comments[ $node['parent_id'] ] )) {
+ $comments[$id]['level'] = 0;
+ $commentsTree[$id] = &$comments[$id];
+ }
+ }
+ }
+
+ $gBitSmarty->assignByRef( 'comments', $commentsTree );
+ $gBitSmarty->assign( 'maxComments', $maxComments );
+
+ $numCommentPages = ceil( $numComments / $maxComments );
+ $comments_return_url = $comments_return_url.( !strpos( $comments_return_url, '?' ) ? '?' : '' );
+
+ // libertypagination smarty function setup
+ $commentsPgnHash = array(
+ 'numPages' => $numCommentPages,
+ 'pgnName' => 'comment_page',
+ 'page' => $currentPage,
+ 'comment_page' => $currentPage,
+ 'url' => $comments_return_url,
+ 'comments_page' => ( empty( $comments_on_separate_page ) ? FALSE : $comments_on_separate_page ),
+ 'ianchor' => 'editcomments',
+ );
+ $gBitSmarty->assignByRef( 'commentsPgnHash', $commentsPgnHash );
+ $gBitSmarty->assignByRef( 'postComment', $postComment );
+ $gBitSmarty->assignByRef( 'gComment', $gComment );
+
+ $gBitSmarty->assign( 'currentTimestamp', time() );
+ $gBitSmarty->assign( 'comments_return_url', $comments_return_url );
+ $gBitSmarty->assign( 'comments_at_top_of_page', ( isset( $comments_at_top_of_page ) && $gBitSystem->getConfig( 'comments_reorganise_page_layout', 'n' ) == 'y' ) ? $comments_at_top_of_page : NULL );
+ $gBitSmarty->assign( 'comments_style', $comments_display_style );
+ $gBitSmarty->assign( 'comments_sort_mode', $comments_sort_mode );
+ $gBitSmarty->assign( 'textarea_id', 'commentpost' );
+ $gBitSmarty->assign( 'comments_count', $numComments );
+
+ // @TODO get this shit out of here - boards and any other package ridding on comments should make use of services
+ if( $gBitSystem->isPackageActive( 'boards' )) {
+ require_once(BOARDS_PKG_CLASS_PATH.'BitBoardTopic.php');
+ }
+
+ // @TODO get this shit out of here - boards and any other package ridding on comments should make use of services
+ // this clearly can go in an edit service, but need to be careful since comments currently does not call edit service - have to check what doing so might trigger.
+ if( !empty( $_REQUEST['post_comment_request'] )) {
+ if( $gBitSystem->isPackageActive( 'boards' )
+ && (
+ BitBoardTopic::isLockedMsg( @BitBase::verifyId( $storeComment->mInfo['parent_id'] )
+ ? $storeComment->mInfo['parent_id'] : ( !@BitBase::verifyId( $_REQUEST['post_comment_reply_id'] )
+ ? $commentsParentId : $_REQUEST['post_comment_reply_id'] ))
+ )
+ ) {
+ unset( $_REQUEST['post_comment_request'] );
+ unset( $_GET['post_comment_request'] );
+ unset( $_POST['post_comment_request'] );
+ $formfeedback['warning']="The selected Topic is Locked posting is disabled";
+ }
+ }
+
+}
diff --git a/includes/content_history_inc.php b/includes/content_history_inc.php
new file mode 100644
index 0000000..5b1da31
--- /dev/null
+++ b/includes/content_history_inc.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+
+$gBitSmarty->assign( 'source', 0 );
+// If we have to include a preview please show it
+$gBitSmarty->assign( 'preview', FALSE );
+$gBitSmarty->assign( 'compare', 'n' );
+$gBitSmarty->assign( 'diff2', 'n' );
+
+if( isset( $_REQUEST["delete"] ) && isset( $_REQUEST["hist"] )) {
+ foreach( array_keys( $_REQUEST["hist"] ) as $version ) {
+ $gContent->expungeVersion( $version );
+ }
+
+} elseif( isset( $_REQUEST['source'] )) {
+ $gBitSmarty->assign( 'source', $_REQUEST['source'] );
+ if( $_REQUEST['source'] == 'current' ) {
+ $gBitSmarty->assign( 'sourcev', nl2br( htmlentities( $gContent->mInfo["data"] )));
+ } else {
+ $version = $gContent->getHistory( $_REQUEST["source"] );
+ $gBitSmarty->assign( 'sourcev', nl2br( htmlentities( $version["data"][0]["data"] )));
+ }
+
+} elseif( @BitBase::verifyId( $_REQUEST["preview"] )) {
+ if( $version = $gContent->getHistory( $_REQUEST["preview"] )) {
+ $version['data'][0]['no_cache'] = TRUE;
+ $version['data'][0]['parsed_data'] = LibertyContent::parseDataHash( $version["data"][0], $gContent );
+ $gBitSmarty->assignByRef( $smartyContentRef, $version['data'][0] );
+ $gBitSmarty->assignByRef( 'version', $_REQUEST["preview"] );
+ }
+
+} elseif( @BitBase::verifyId( $_REQUEST["diff2"] ) ) {
+ $from_version = $_REQUEST["diff2"];
+ $from_page = $gContent->getHistory( $from_version );
+ $from_lines = explode( "\n",$from_page["data"][0]["data"] );
+ if( isset( $_REQUEST["diff_to"] ) && $_REQUEST["diff_to"] != $gContent->mInfo["version"] ) {
+ $to_version = $_REQUEST["diff_to"];
+ $to_page = $gContent->getHistory( $to_version );
+ $to_lines = explode( "\n",$to_page["data"][0]["data"] );
+ } else {
+ $to_version = $gContent->mInfo["version"];
+ $to_lines = explode( "\n",$gContent->mInfo["data"] );
+ }
+ /**
+ * run 'pear install Text_Diff' to install the library,
+ */
+ if( $gBitSystem->isFeatureActive( 'liberty_inline_diff' ) && @include_once( 'Text/Diff.php' )) {
+ include_once( 'Text/Diff/Renderer/inline.php' );
+ $diff = new Text_Diff( $from_lines, $to_lines );
+ $renderer = new Text_Diff_Renderer_inline();
+ $html = $renderer->render( $diff );
+ } else {
+ include_once( UTIL_PKG_INCLUDE_PATH.'diff.php');
+ $diffx = new WikiDiff( $from_lines,$to_lines );
+ $fmt = new WikiUnifiedDiffFormatter;
+ $html = $fmt->format( $diffx, $from_lines );
+ }
+ $gBitSmarty->assign( 'diffdata', $html );
+ $gBitSmarty->assign( 'diff2', 'y' );
+ $gBitSmarty->assign( 'version_from', $from_version );
+ $gBitSmarty->assign( 'version_to', $to_version );
+
+} elseif( @BitBase::verifyId( $_REQUEST["compare"] )) {
+ $from_version = $_REQUEST["compare"];
+ $from_page = $gContent->getHistory( $from_version );
+ $from_page['data'][0]['no_cache'] = TRUE;
+ $gBitSmarty->assign( 'compare', 'y' );
+ $gBitSmarty->assign( 'diff_from', LibertyContent::parseDataHash( $from_page['data'][0], $gContent ) );
+ $gBitSmarty->assign( 'diff_to', $gContent->getParsedData() );
+ $gBitSmarty->assignByRef( 'version_from', $from_version );
+
+} elseif( @BitBase::verifyId( $_REQUEST["rollback"] )) {
+ $gContent->verifyUserPermission( !empty( $rollbackPerm ) ? $rollbackPerm : $gContent->mUpdateContentPerm );
+ if( !isset( $_REQUEST["rollback_comment"] )) {
+ $_REQUEST["rollback_comment"] = '';
+ }
+ if( $gContent->rollbackVersion( $_REQUEST["rollback"], $_REQUEST["rollback_comment"] )) {
+ bit_redirect( $gContent->getDisplayUrl() );
+ }
+}
+
diff --git a/includes/display_content_inc.php b/includes/display_content_inc.php
new file mode 100644
index 0000000..62c0e9f
--- /dev/null
+++ b/includes/display_content_inc.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * display_content_inc
+ *
+ * @author spider <spider@steelsun.com>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+ global $gBitSmarty, $gBitSystem, $gContent;
+
+ $gBitSmarty->assignByRef( 'pageInfo', $gContent->mInfo );
+
+ $gBitSystem->display( 'bitpackage:liberty/display_content.tpl' , NULL, array( 'display_mode' => 'display' ));
+
+?>
diff --git a/includes/display_structure_inc.php b/includes/display_structure_inc.php
new file mode 100644
index 0000000..4b3844b
--- /dev/null
+++ b/includes/display_structure_inc.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * display_structure_inc
+ *
+ * @author spider <spider@steelsun.com>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+global $gContent;
+include_once( LIBERTY_PKG_INCLUDE_PATH.'lookup_content_inc.php' );
+if( is_object( $gContent ) && $gContent->isValid() ) {
+ $gBitSystem->setBrowserTitle( $gStructure->getRootTitle().' : '.$gContent->getTitle() );
+ $gBitSystem->setCanonicalLink( $gContent->getDisplayUrl() );
+ include $gContent->getRenderFile();
+} else {
+ $gBitSystem->fatalError( tra( 'Page cannot be found' ), NULL, NULL, HttpStatusCodes::HTTP_GONE );
+}
diff --git a/includes/edit_help_inc.php b/includes/edit_help_inc.php
new file mode 100644
index 0000000..1e31a4d
--- /dev/null
+++ b/includes/edit_help_inc.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * $Id$
+ * edit_help_inc
+ *
+ * @author spider <spider@steelsun.com>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+global $gLibertySystem, $gBitSmarty;
+
+$inEditor = TRUE; // Required by PluginHelp to Determin Executed in an Editor
+
+$dataplugins = array_merge( $gLibertySystem->getPluginsOfType( DATA_PLUGIN ), $gLibertySystem->getPluginsOfType( FILTER_PLUGIN ));
+$formatplugins = $gLibertySystem->getPluginsOfType( FORMAT_PLUGIN );
+$mimeplugins = $gLibertySystem->getPluginsOfType( MIME_PLUGIN );
+
+// allow mime plugins to append help to the attachment plugin
+foreach( $mimeplugins as $guid => $plugin ) {
+ if( $func = $gLibertySystem->getPluginFunction( $guid, 'help_function' )) {
+ $plugin['exthelp'] = $func();
+ $mimeplugins[$guid]= $plugin;
+ } else {
+ unset( $mimeplugins[$guid] );
+ }
+}
+
+// refine data plugins and add help where available
+foreach( $dataplugins as $guid => $plugin ) {
+ if( !empty( $plugin['description'] ) && !empty( $plugin['syntax'] )) {
+ $plugin["plugin_guid"] = preg_replace( "/^(data|filter)/", "", $guid );
+ $plugin["exthelp"] = !empty( $plugin['help_function'] ) && $gLibertySystem->getPluginFunction( $guid, 'help_function' ) ? $plugin['help_function']() : '';
+ $dataplugins[$guid] = $plugin;
+ } else {
+ unset( $dataplugins[$guid] );
+ }
+}
+
+foreach( array_keys( $formatplugins ) as $guid ) {
+ // check to see if we have some format syntax help
+ if( is_file( LIBERTY_PKG_PATH."templates/help_format_{$guid}_inc.tpl" )) {
+ $formatplugins[$guid]['format_help'] = "bitpackage:liberty/help_format_{$guid}_inc.tpl";
+ if( is_file( LIBERTY_PKG_INCLUDE_PATH.'help_format_{$guid}_inc.php' )) {
+ include_once( LIBERTY_PKG_INCLUDE_PATH.'help_format_{$guid}_inc.php' );
+ }
+ }
+}
+
+if( !empty( $formatplugins ) ) {
+ usort( $formatplugins, 'usort_by_title' );
+ $gBitSmarty->assignByRef( 'formatplugins', $formatplugins );
+}
+
+if( !empty( $mimeplugins ) ) {
+ usort( $mimeplugins, 'usort_by_title' );
+ $gBitSmarty->assignByRef( 'mimeplugins', $mimeplugins );
+}
+
+if( !empty( $dataplugins ) ) {
+ usort( $dataplugins, 'usort_by_title' );
+ $gBitSmarty->assignByRef( 'dataplugins', $dataplugins );
+}
+?>
diff --git a/includes/edit_storage_inc.php b/includes/edit_storage_inc.php
new file mode 100644
index 0000000..3d40b0f
--- /dev/null
+++ b/includes/edit_storage_inc.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * @version $Header$
+ *
+ * edit_storage_inc
+ * @author spider <spider@steelsun.com>
+ * @package liberty
+ * @subpackage functions
+ */
+global $gBitSmarty, $gContent, $gBitUser, $gBitSystem, $gBitThemes, $gLibertySystem;
+
+// if we have active plugins with an upload function, we call them:
+foreach( $gLibertySystem->getPluginFunctions( 'upload_function' ) as $guid => $func ) {
+ $func( $gContent );
+}
+
+// set up base arguments
+$getArgs = explode( '&', $_SERVER['QUERY_STRING'] );
+$attachmentBaseArgs = '';
+foreach( $getArgs as $arg ) {
+ $parts = explode( '=', $arg );
+ if( $parts[0] != 'deleteAttachment' ) {
+ $attachmentBaseArgs .= $arg."&amp;";
+ }
+}
+$gBitSmarty->assign( 'attachmentBaseArgs', $attachmentBaseArgs );
+
+// delete attachment if requested
+if( !empty( $_REQUEST['deleteAttachment'] )) {
+
+ // $gContent is empty when we're editing our personal webpage
+ // this is a brutish hack but it seems to work without adverse effeects
+ if( empty( $gContent )) {
+ $gContent = $gBitUser;
+ }
+
+ $attachmentId = $_REQUEST['deleteAttachment'];
+ $attachmentInfo = $gContent->getAttachment( $attachmentId );
+
+ // the second part of this check seems odd (never used?) to me, but I'll leave it in for now - spiderr 10/17/2007
+ if( $gContent->hasAdminPermission() || ( $gContent->isOwner( $attachmentInfo ) && $gBitUser->hasPermission( 'p_liberty_delete_attachment' ))) {
+ $gContent->expungeAttachment( $attachmentId );
+ }
+
+ // in case we have deleted attachments
+ // seems like there should be a better way to do this -- maybe original assign should have been by reference?
+ $gBitSmarty->clearAssign( 'gContent' );
+ $gBitSmarty->assign( 'gContent', $gContent );
+}
+
+// make sure js is being loaded
+if( $gBitSystem->getConfig( 'liberty_attachment_style' ) == 'ajax' ) {
+ $gBitThemes->loadJavascript( LIBERTY_PKG_PATH.'scripts/LibertyAttachment.js', TRUE );
+}
+?>
diff --git a/includes/edit_structure_inc.php b/includes/edit_structure_inc.php
new file mode 100644
index 0000000..5fa1b4c
--- /dev/null
+++ b/includes/edit_structure_inc.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * edit_structure_inc
+ *
+ * @author Christian Fowler>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+// Copyright (c) 2004, Christian Fowler, et. al.
+// 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.
+
+/**
+ * required setup
+ */
+require_once( '../kernel/setup_inc.php' );
+include_once( LIBERTY_PKG_CLASS_PATH.'LibertyStructure.php');
+
+if( !@BitBase::verifyId( $_REQUEST["structure_id"] ) ) {
+ $gBitSystem->fatalError( tra( "No structure indicated" ));
+} else {
+ global $gStructure;
+ $gStructure = new LibertyStructure( $_REQUEST["structure_id"] );
+ $gStructure->load();
+
+ // order matters for these conditionals
+ if( empty( $gStructure ) || !$gStructure->isValid() ) {
+ $gBitSystem->fatalError( tra( 'Invalid structure' ));
+ }
+
+ if( $gStructure->mInfo['root_structure_id'] == $gStructure->mInfo['structure_id'] ) {
+ $rootStructure = &$gStructure;
+ } else {
+ $rootStructure = new LibertyStructure( $gStructure->mInfo['root_structure_id'] );
+ $rootStructure->load();
+ $rootStructure->loadNavigation();
+ $rootStructure->loadPath();
+ }
+ if( empty( $gContent ) ) {
+ $gContent = LibertyContent::getLibertyObject( $gStructure->getField( 'content_id' ) );
+ $gContent->verifyUpdatePermission();
+ }
+ $gBitSmarty->assignByRef( 'gStructure', $gStructure );
+ $gBitSmarty->assign( 'editingStructure', TRUE );
+ $gBitSmarty->assign('structureInfo', $gStructure->mInfo);
+
+ // Store the actively stored structure name
+ $gBitUser->storePreference( 'edit_structure_name', $rootStructure->mInfo['title'] );
+ $gBitUser->storePreference( 'edit_structure_id', $rootStructure->mStructureId );
+
+ if( ( isset( $_REQUEST["action"] ) && ( $_REQUEST["action"] == 'remove' ) ) || !empty( $_REQUEST["confirm"] ) ) {
+ $gBitUser->verifyTicket();
+ if( $_REQUEST["action"] == 'remove' && ($gBitThemes->isAjaxRequest() || !empty( $_REQUEST["confirm"] )) ) {
+ $gBitUser->verifyTicket();
+ if( $gStructure->removeStructureNode( $_REQUEST["structure_id"], false ) ) {
+ if( $gBitThemes->isAjaxRequest() ) {
+ $feedback['success'] = tra( "removed from" ).' '.$gContent->getContentTypeName();
+ } else {
+ bit_redirect( $_SERVER['SCRIPT_NAME'].'?structure_id='.$gStructure->mInfo["parent_id"] );
+ }
+ } else {
+ $feedback['error'] = $gStructure->mErrors;
+ }
+ $gBitSmarty->assignByRef('feedback', $feedback);
+ } elseif( $_REQUEST["action"] == 'remove' ) {
+ $gBitSystem->setBrowserTitle( tra('Confirm removal of ').$gContent->getTitle() );
+ $formHash['action'] = 'remove';
+ $formHash['remove'] = TRUE;
+ $formHash['structure_id'] = $_REQUEST['structure_id'];
+ $msgHash = array(
+ 'label' => tra('Remove content from Structure'),
+ 'confirm_item' => $gContent->getTitle().tra('and any subitems'),
+ 'warning' => tra('This will remove the content from the structure but will <strong>not</strong> modify or remove the content itself.'),
+ );
+ $gBitSystem->confirmDialog( $formHash,$msgHash );
+ }
+ } elseif (isset($_REQUEST["move_node"])) {
+ if ($_REQUEST["move_node"] == '1') {
+ $gStructure->moveNodeWest();
+ } elseif ($_REQUEST["move_node"] == '2') {
+ $gStructure->moveNodeNorth();
+ } elseif ($_REQUEST["move_node"] == '3') {
+ $gStructure->moveNodeSouth();
+ } elseif ($_REQUEST["move_node"] == '4') {
+ $gStructure->moveNodeEast();
+ }
+ bit_redirect( $_SERVER['SCRIPT_NAME'].'?structure_id='.$gStructure->mInfo["structure_id"] );
+ } elseif( !empty( $_REQUEST['submit_structure'] ) ) {
+ if( $gStructure->storeStructure( $_REQUEST ) ) {
+ $feedback['success'] = tra( "Your changes were successfully saved." );
+ } else {
+ $feedback['error'] = $gStructure->mErrors;
+ }
+ } elseif (isset($_REQUEST["create"]) || (isset( $_REQUEST["action"] ) && $_REQUEST["action"] == 'add') ) {
+ $structureHash['root_structure_id'] = $rootStructure->mStructureId;
+ $structureHash['parent_id'] = $_REQUEST['structure_id'];
+
+ $after = null;
+ if (isset($_REQUEST['after_ref_id'])) {
+ $structureHash['after_ref_id'] = $_REQUEST['after_ref_id'];
+ }
+ if (!(empty($_REQUEST['name']))) {
+ $gStructure->s_create_page($_REQUEST["structure_id"], $after, $_REQUEST["name"], '');
+ } elseif(!empty($_REQUEST['content'])) {
+ foreach ($_REQUEST['content'] as $conId ) {
+ $structureHash['content_id'] = $conId;
+ if( $new_structure_id = $gStructure->storeNode( $structureHash ) ) {
+ $structureHash['after_ref_id'] = $new_structure_id;
+ $feedback['success'] = tra( "added to" ).' '.$gContent->getContentTypeName();
+ } else {
+ $feedback['failure'] = $gStructure->mErrors;
+ }
+ }
+ }
+ }
+
+ $structureTocId = $rootStructure->mStructureId;
+ $gBitSmarty->assign( 'structureToc', $rootStructure->getToc() );
+ $gBitSmarty->assign( 'structureTocId', $structureTocId );
+ $gBitSmarty->assignByRef('feedback', $feedback);
+}
+ $gBitSmarty->assign( 'editingStructure', FALSE );
+
+?>
diff --git a/includes/get_content_list_inc.php b/includes/get_content_list_inc.php
new file mode 100644
index 0000000..a5bab92
--- /dev/null
+++ b/includes/get_content_list_inc.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * get_content_list
+ *
+ * @author Christian Fowler>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+require_once( LIBERTY_PKG_CLASS_PATH.'LibertyContent.php' );
+global $gContent;
+global $gLibertySystem;
+
+if( empty( $gContent ) || !is_object( $gContent ) ) {
+ $gContent = new LibertyContent();
+}
+
+$contentTypeGuids = array();
+if( !empty( $_REQUEST['content_type_guid'] )) {
+ if( !is_array( $_REQUEST['content_type_guid'] )) {
+ $guids = explode( ",", $_REQUEST['content_type_guid'] );
+ } else {
+ $guids = $_REQUEST['content_type_guid'];
+ }
+ /**
+ * if an empty string was passed in an array (likely since it is used for ALL) then the user has requested all so return all
+ * even if they have requested additional content types too - ALL is ALL
+ * this check is reversed in that if no empty string in the array then we pass the array of content types to be limited on
+ **/
+ if( !in_array( "", $guids ) ){
+ $contentTypeGuids = $guids;
+ }
+}
+
+// get_content_list_inc doesn't use $_REQUEST parameters as it might not be the only list in the page that needs sorting and limiting
+if( empty( $contentListHash ) ) {
+ $contentListHash = array(
+ 'content_type_guid' => $contentSelect = empty( $_REQUEST['content_type_guid'] ) ? NULL : $contentTypeGuids,
+ // pagination offset
+ 'offset' => !empty( $offset_content ) ? $offset_content : NULL,
+ // maximum number of records displayed on a page
+ 'max_records' => !empty( $max_content ) ? $max_content : ( !empty( $_REQUEST['max_records'] ) ? $_REQUEST['max_records'] : 100 ),
+ // sort by this: <table column>_asc (or _desc)
+ 'sort_mode' => !empty( $content_sort_mode ) ? $content_sort_mode : 'title_asc',
+ // limit the result to this set
+ 'find' => !empty( $_REQUEST["find"] ) ? $_REQUEST["find"] : NULL,
+ // display this page number - replaces antiquated offset
+ 'page' => !empty( $_REQUEST["list_page"] ) ? $_REQUEST["list_page"] : NULL,
+ // only display content by this user
+ 'user_id' => @BitBase::verifyId( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : NULL,
+ // only display content modified more recently than this (UTC timestamp)
+ 'from_date' => !empty( $_REQUEST["from_date"] ) ? $_REQUEST["from_date"] : NULL,
+ // only display content modified before this (UTC timestamp)
+ 'until_date' => !empty( $_REQUEST["until_date"] ) ? $_REQUEST["until_date"] : NULL,
+ // get a thumbnail - off by default because it is expensive
+ 'thumbnail_size' => !empty( $_REQUEST["thumbnail_size"] ) ? $_REQUEST["thumbnail_size"] : NULL,
+ );
+
+ if( !empty( $_REQUEST['output'] ) && ( $_REQUEST['output'] == 'json' || $_REQUEST['output'] == 'ajax' ) ) {
+ foreach( $_REQUEST as $key => $value ) {
+ if ( !is_array($value) ){
+ if( strstr( $value, ',' ) ) {
+ $_REQUEST[$key] = explode( ",", $value );
+ }
+ }
+ }
+ }
+
+ $contentListHash = array_merge( $_REQUEST, $contentListHash );
+}
+
+// Finally we're ready to get some content
+$contentList = $gContent->getContentList( $contentListHash );
+
+if( empty( $contentTypes ) ) {
+ $contentTypes = array( '' => tra( 'All Content' ) );
+ foreach( $gLibertySystem->mContentTypes as $cType ) {
+ $contentTypes[$cType['content_type_guid']] = $gLibertySystem->getContentTypeName( $cType['content_type_guid'], TRUE );
+ }
+ asort( $contentTypes );
+}
+global $gBitSystem, $gBitUser;
+if( $gBitSystem->isFeatureActive( 'liberty_display_status' ) && $gBitUser->hasPermission( 'p_liberty_view_all_status' )) {
+ $contentStatuses = $gContent->getAvailableContentStatuses();
+ $contentStatuses[''] = 'All Statuses';
+ $contentStatuses['not_available'] = 'All but Available';
+ $gBitSmarty->assign( 'content_statuses', $contentStatuses );
+}
+?>
diff --git a/includes/help_format_tikiwiki_inc.php b/includes/help_format_tikiwiki_inc.php
new file mode 100644
index 0000000..e6ad141
--- /dev/null
+++ b/includes/help_format_tikiwiki_inc.php
@@ -0,0 +1,239 @@
+<?php
+/**
+ * help_format_tikiwiki_inc
+ *
+ * @author Christian Fowler>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+
+/**
+ * required setup
+ */
+global $gBitSystem, $gBitSmarty;
+require_once( KERNEL_PKG_PATH.'BitCache.php' );
+$cache = new BitCache( 'liberty/help' );
+
+// only regenerate this thing if it's not cached yet
+$cacheFile = 'tikiwiki';
+if( $cache->isCached( $cacheFile, filemtime( __FILE__ ))) {
+ $examples = unserialize( $cache->readCacheFile( $cacheFile ));
+} else {
+ // help for generic options
+ $tikiwiki = array(
+ 'Emphasis' => array(
+ 'Headings' => array(
+ 'data' => "! heading 1\n!! heading 2\n!!! heading 3",
+ 'note' => "Number of ! correponds to heading level.",
+ ),
+ 'Italics' => array(
+ 'data' => "''text''",
+ 'note' => "Two single quotes not one double quote",
+ ),
+ 'Underline' => array(
+ 'data' => "===text===",
+ ),
+ 'Coloured Background' => array(
+ 'data' => "++yellow:text++",
+ ),
+ 'Coloured Text' => array(
+ 'data' => "~~red:text~~",
+ ),
+ 'Bold' => array(
+ 'data' => "__text__",
+ ),
+ 'Centered Text' => array(
+ 'data' => "::text::",
+ ),
+ 'Combined' => array(
+ 'data' => "::__~~red:++yellow:text++~~__::",
+ 'note' => "When you combine options make sure you open and close in the opposite order analogous to: {[(text)]}",
+ ),
+ ),
+ 'Lists' => array(
+ 'Unordered Lists' => array(
+ 'data' => "* First item\n** First subitem\n** Second subitem\n* Second item",
+ ),
+ 'Ordered Lists' => array(
+ 'data' => "# First item\n## First subitem\n## Second subitem\n# Second item",
+ ),
+ 'Definition Lists' => array(
+ 'data' => ";Term: Definition",
+ ),
+ ),
+ 'Links' => array(
+ 'Wiki Links' => array(
+ 'data' => "((Wiki Page))",
+ 'result' => '<a href="#">Wiki Page</a>',
+ ),
+ 'Wiki Links + Description' => array(
+ 'data' => "((Wiki Page|Page Description))",
+ 'result' => '<a href="#">Page Description</a>',
+ ),
+ 'Wiki Links + Anchor + Description' => array(
+ 'data' => "((Wiki Page#Anchor|Page Description))",
+ 'result' => '<a href="#">Page Description</a>',
+ ),
+ 'External Link' => array(
+ 'data' => "[http://www.example.com]",
+ ),
+ 'External Link + Description' => array(
+ 'data' => "[http://www.example.com|Description]",
+ ),
+ 'External Link + Anchor + Description' => array(
+ 'data' => "[http://www.example.com/Page#Anchor|Description]",
+ ),
+ ),
+ 'Miscellaneous' => array(
+ 'Horizontal Rule' => array(
+ 'data' => '---',
+ ),
+ 'Highlighted Bar' => array(
+ 'data' => '-=text=-',
+ ),
+ 'Highlighted Box' => array(
+ 'data' => "^text\nmore text^",
+ ),
+ 'As is Text' => array(
+ 'data' => "~np~~~yellow:yellow~~\nand\n__bold__ text~/np~",
+ 'note' => "This text will not be parsed",
+ ),
+ 'Pre Parsed' => array(
+ 'data' => "~pp~~~yellow:yellow~~\nand\n__bold__ text~/pp~",
+ 'note' => "This text will be treated like code and will not be altered and will be displayed using a monospace font. The same can be achieved by using &lt;pre&gt;text&lt;/pre&gt;.",
+ ),
+ 'Monospaced Text' => array(
+ 'data' => "-+text+-",
+ ),
+ 'Right to Left' => array(
+ 'data' => "{r2l}this text is from\nright to left\n{l2r}and back to\nleft to right.",
+ ),
+ ),
+ 'Simple Tables' => array(
+ 'Simple Table' => array(
+ 'data' => "|| Row1-Col1 | Row1-Col2\nRow2-Col1 | Row2-Col2 ||",
+ ),
+ 'With Headers' => array(
+ 'data' => "||~ Header1 | Header2\nRow1-Col1 | Row1-Col2\nRow2-Col1 | Row2-Col2 ||",
+ ),
+ ),
+ );
+
+ if( $gBitSystem->getConfig( 'wiki_tables' ) == 'old' ) {
+ $tikiwiki['Simple Tables'] = array(
+ 'Tables' => array(
+ 'data' => "|| Row1-Col1 | Row1-Col2 || Row2-Col1 | Row2-Col2 ||",
+ ),
+ );
+ }
+
+ foreach( array_keys( $tikiwiki ) as $section ) {
+ foreach( $tikiwiki[$section] as $title => $example ) {
+ if( empty( $example['result'] )) {
+ $example['format_guid'] = 'tikiwiki';
+ $tikiwiki[$section][$title]['result'] = LibertyContent::parseDataHash( $example );
+ }
+ }
+ }
+
+ // mediawiki type tables
+ $mediawiki = array(
+ 'Example 1' => array(
+ 'data' =>
+'{| class="table"
+|+A Simple Table
+|-
+! Col 1 !! Col 2 !! Col 3
+|-
+| Row1-Col1 || Row1-Col2 || Row1-Col3
+|-
+| Row2-Col1
+| Row2-Col2
+| Row2-Col3
+|-
+| Row3-Col1 || Row3-Col2 || Row3-Col3
+|}'
+ ),
+ 'Example 2' => array(
+ 'data' =>
+'{| class="table table-boardered"
+|+Multiplication table
+|-
+! X !! 1 !! 2 !! 3
+|-
+! 1
+| 1 || 2 || 3
+|-
+! 2
+| 2 || 4 || 6
+|-
+! 3
+| 3 || 6 || 9
+|-
+! 4
+| 4 || 8 || 12
+|-
+! 5
+| 5 || 10 || 15
+|}'
+ ),
+ 'Example 3' => array(
+ 'data' =>
+'{| class="table table-striped"
+|+ Table with alternating rows
+|-
+| one
+| two
+|-
+| three
+| four
+|-
+| five
+| six
+|-
+| seven
+| eight
+|- class="success"
+| colspan="2" | success
+|- class="error"
+| colspan="2" | error
+|- class="warning"
+| colspan="2" | warning
+|- class="info"
+| colspan="2" | info
+|}'
+ ),
+ 'Example 4' => array(
+ 'data' =>
+'{| class="table" style="background:yellow;color:green"
+|+ Table with many colours
+|-
+| abc
+| colspan="2" style="text-align:center;background:lightblue;" | defghi
+|- style="background:red;color:white"
+| jkl
+| mno
+| pqr
+|-
+| style="font-weight:bold" | stu
+| style="background:silver" | vwx
+| yz
+|}'
+ ),
+ );
+
+ // parse tables
+ foreach( $mediawiki as $title => $example ) {
+ if( empty( $example['result'] )) {
+ $example['format_guid'] = 'tikiwiki';
+ $mediawiki[$title]['result'] = LibertyContent::parseDataHash( $example );
+ }
+ }
+
+ $examples['tikiwiki'] = $tikiwiki;
+ $examples['mediawiki'] = $mediawiki;
+ $cache->writeCacheFile( $cacheFile, serialize( $examples ));
+}
+$gBitSmarty->assign( 'examples', $examples );
+?>
diff --git a/includes/liberty_lib.php b/includes/liberty_lib.php
new file mode 100644
index 0000000..4c6f125
--- /dev/null
+++ b/includes/liberty_lib.php
@@ -0,0 +1,939 @@
+<?php
+/**
+ * @package liberty
+ * @subpackage functions
+ */
+
+// ================== Liberty Plugin Parsing ==================
+/**
+ * This crazy function will parse all the data plugin stuff found within any
+ * parsed text section
+ *
+ * @param array $pData Data to be parsed
+ * @access public
+ * @return void
+ */
+function parse_data_plugins( &$pData, &$pReplace, $pCommonObject, $pParseHash ) {
+ global $gLibertySystem, $gBitSystem;
+
+ // note: $curlyTags[0] is the complete match, $curlyTags[1] is plugin name, $curlyTags[2] is plugin arguments
+ preg_match_all( "/\{\/?([A-Za-z0-9]+)([^\}]*)\}/", $pData, $curlyTags, PREG_OFFSET_CAPTURE );
+
+ if( count( $curlyTags[0] ) ) {
+ // if TRUE, replace only CODE plugin, if false, replace all other plugins
+ $code_first = TRUE;
+
+ // Process plugins in reverse order, so that nested plugins are handled from the inside out.
+ $i = count( $curlyTags[0] ) - 1;
+ while( $i >= 0 ) {
+ $plugin_start = $curlyTags[0][$i][0];
+ $plugin = $curlyTags[1][$i][0];
+ // Work out where the plugin starts. This can not be done using the
+ // positional data from $curlyTags since the position might have
+ // changed since the last cycle. We therefore need to determine the
+ // position direclty. - xing - Thursday Nov 01, 2007 22:55:10 CET
+ //$pos = $curlyTags[0][$i][1];
+ $pos = strpos( $pData, $plugin_start );
+ $dataTag = strtolower( $plugin );
+ // hush up the return of this in case someone uses curly braces to enclose text
+ $pluginInfo = $gLibertySystem->getPluginInfo( @$gLibertySystem->mDataTags[$dataTag] ) ;
+
+ // only process a standalone unpaired tag or the start tag for a paired tag
+ if( empty( $paired_close_tag_seen[$dataTag] ) || $paired_close_tag_seen[$dataTag] == 0 ) {
+ $paired_close_tag_seen[$dataTag] = 1;
+ } else {
+ $paired_close_tag_seen[$dataTag] = 0;
+ }
+
+ $is_opening_tag = FALSE;
+ if(( empty( $pluginInfo['requires_pair'] ) && ( strtolower( $plugin_start ) != '{/'. $dataTag . '}' ))
+ || ( strpos( $plugin_start, ' ' ) > 0 )
+ || ( strtolower( $plugin_start ) == '{'.$dataTag.'}' && !$paired_close_tag_seen[$dataTag] )
+ ) {
+ $is_opening_tag = TRUE;
+ }
+
+ if(
+ // when in CODE parsing mode, replace only CODE plugins
+ ( ( $code_first && ( $dataTag == 'code' ) )
+ // when NOT in CODE parsing mode, replace all other plugins
+ || ( !$code_first && ( $dataTag <> 'code' ) )
+ )
+ && !empty( $gLibertySystem->mDataTags[$dataTag] )
+ && !empty( $pluginInfo )
+ && ( $loadFunc = $gLibertySystem->getPluginFunction( $gLibertySystem->mDataTags[$dataTag], 'load_function' ) )
+ && ( $is_opening_tag )
+ ) {
+
+ if( $pluginInfo['requires_pair'] ) {
+ $plugin_end = '{/'.$plugin.'}';
+ $pos_end = strpos( strtolower( $pData ), strtolower( $plugin_end ), $pos ); // where plugin data ends
+ $plugin_end2 = '{'.$plugin.'}';
+ $pos_end2 = strpos( strtolower( $pData ), strtolower( $plugin_end2 ), $pos + 1 ); // where plugin data ends
+
+ if( ( $pos_end2 > 0 && $pos_end2 > 0 && $pos_end2 < $pos_end ) || $pos_end === FALSE ) {
+ $pos_end = $pos_end2;
+ $plugin_end = $plugin_end2;
+ }
+ } else {
+ $pos_end = $pos + strlen( $curlyTags[0][$i][0] );
+ $plugin_end = '';
+ }
+
+ // Extract the plugin data
+ $plugin_data_len = $pos_end - $pos - strlen( $curlyTags[0][$i][0] );
+ $plugin_data = substr( $pData, $pos + strlen( $plugin_start ), $plugin_data_len );
+
+ $arguments = array();
+ // Construct argument list array
+ $paramString = str_replace( '&gt;', '>', trim( $curlyTags[2][$i][0] ) );
+ if( preg_match( '/^\(.*=>.*\)$/', $paramString ) ) {
+ $paramString = preg_replace( '/[\(\)]/', '', $paramString );
+ //we have the old style parms like {CODE (in=>1)}
+ $params = explode( ',', trim( $paramString ) );
+
+ foreach( $params as $param ) {
+ // the following str_replace line is to decode the &gt; char when html is turned off
+ // perhaps the plugin syntax should be changed in 1.8 not to use any html special chars
+ $parts = preg_split( '/=>?/', $param );
+
+ if( isset( $parts[0] ) && isset( $parts[1] ) ) {
+ $name = trim( $parts[0] );
+ $arguments[$name] = trim( $parts[1] );
+ }
+ }
+ } else {
+ $paramString = trim( $curlyTags[2][$i][0], " \t()" );
+ $paramString = str_replace("&quot;", '"', $paramString);
+ $arguments = parse_xml_attributes( $paramString );
+ }
+
+ if( $ret = $loadFunc( $plugin_data, $arguments, $pCommonObject, $pParseHash )) {
+ $key = "parseprotect".md5( mt_rand() );
+ $pReplace[] = array(
+ 'key' => $key,
+ 'data' => $ret,
+ );
+
+ // don't modify data if $pos is FALSE
+ if( $pos !== FALSE ) {
+ $pData = substr_replace( $pData, $key, $pos, $pos_end - $pos + strlen( $plugin_end ));
+ }
+ }
+ }
+ $i--;
+ // if we are in CODE parsing mode and list is done, switch to 'parse other plugins' mode and start all over
+ if( ( $code_first ) && ( $i < 0 ) ) {
+ $i = count( $curlyTags[0] ) - 1;
+ $code_first = FALSE;
+ }
+ } // while
+ }
+}
+
+/**
+ * This function replaces pre- and no-parsed sections with unique keys and
+ * saves the section contents for later reinsertion. It is needed by
+ * parse_data_plugins() to extract sections that don't require parsing
+ *
+ * @param array $pData data that might contain ~np~ or ~pp~ strings
+ * @param array $preparsed array that is updated by refrerence with key and data that needs to be substituted later
+ * @param array $noparsed array that is updated by refrerence with key and data that needs to be substituted later
+ * @access public
+ * @return void
+ */
+function parse_protect( &$pData, &$pReplace ) {
+ // Find all sections delimited by ~pp~ ... ~/pp~
+ preg_match_all( "/\~pp\~(.*?)\~\/pp\~/s", $pData, $preparse );
+ if( count( $preparse[0] )) {
+ foreach( array_unique( $preparse[1] ) as $pp ) {
+ $aux["key"] = md5( mt_rand() );
+ $aux["data"] = "<pre><code>".htmlspecialchars( $pp )."</code></pre>";
+ $pReplace[] = $aux;
+ $pData = str_replace( "~pp~$pp~/pp~", $aux['key'], $pData );
+ }
+ }
+
+ // now remove <pre>...<pre> sections
+ preg_match_all( "!(<pre[^>]*>)(.*?)(</pre[^>]*>)!si", $pData, $preparse );
+ if( count( $preparse[0] )) {
+ foreach( $preparse[2] as $key => $pre ) {
+ $aux["key"] = md5( mt_rand() );
+ $aux["data"] = $preparse[1][$key].htmlspecialchars( $pre ).$preparse[3][$key];
+ $pReplace[] = $aux;
+ $pData = str_replace( $preparse[1][$key].$pre.$preparse[3][$key], $aux['key'], $pData );
+ }
+ }
+
+ // and now ~np~...~/np~ sections
+ preg_match_all( "/\~np\~(.*?)\~\/np\~/s", $pData, $noparse );
+ if( count( $noparse[0] )) {
+ foreach( array_unique( $noparse[1] ) as $np ) {
+ $aux["key"] = md5( mt_rand() );
+ $aux["data"] = htmlspecialchars( $np );
+ $pReplace[] = $aux;
+ $pData = str_replace( "~np~$np~/np~", $aux['key'], $pData );
+ }
+ }
+}
+
+
+// ================== Liberty Plugin Helper ==================
+/**
+ * pass in the plugin parameters and out comes a hash with usable styling information
+ *
+ * @param array $pParamHash
+ * @access public
+ * @return hash full of styling goodies
+ */
+function liberty_plugins_wrapper_style( $pParamHash ) {
+ global $gBitSystem;
+
+ $ret = array();
+ $ret['style'] = $ret['description'] = '';
+
+ if( !empty( $pParamHash ) && is_array( $pParamHash )) {
+ // if align is right and text-align isn't set, we'll align that right as well
+ if( empty( $pParamHash['text-align'] ) && ( !empty( $pParamHash['align'] ) && $pParamHash['align'] == 'right' || !empty( $pParamHash['align'] ) && $pParamHash['align'] == 'right' )) {
+ $pParamHash['text-align'] = 'right';
+ }
+
+ // this defines what the wrapper should be - div or span
+ // if someone sets this value manually, they know what they are doing
+ if( empty( $pParamHash['wrapper'] )) {
+ $pParamHash['wrapper'] = 'div';
+
+ if( $gBitSystem->isFeatureActive( 'liberty_use_span_wrapper' )) {
+ // set to 'span' if desired
+ $pParamHash['wrapper'] = 'span';
+
+ // force display:block to the "div" if not specified otherwise
+ if( empty( $pParamHash['display'] )) {
+ $pParamHash['display'] = "inline-block";
+ }
+ }
+ }
+
+ foreach( $pParamHash as $key => $value ) {
+ if( !empty( $value )) {
+ switch( $key ) {
+ // description
+ case 'desc':
+ $key = 'description';
+ case 'description':
+ $ret[$key] = $value;
+ break;
+ // styling
+ case 'width':
+ case 'height':
+ if( preg_match( "/^\d+(em|px|%|pt)$/", trim( $value ))) {
+ $ret['style'] .= "{$key}:{$value};";
+ } elseif( preg_match( "/^\d+$/", $value )) {
+ $ret['style'] .= "{$key}:{$value}px;";
+ }
+ break;
+ case 'background':
+ case 'background-color':
+ case 'border':
+ case 'color':
+ case 'display':
+ case 'float':
+ case 'font':
+ case 'font-family':
+ case 'font-size':
+ case 'font-weight':
+ case 'margin':
+ case 'overflow':
+ case 'padding':
+ case 'text-align':
+ $ret['style'] .= "{$key}:{$value};";
+ break;
+ // align and float are special
+ case 'align':
+ if( $value == 'center' || $value == 'middle' ) {
+ $ret['style'] .= 'text-align:center;';
+ } else {
+ $ret['style'] .= "float:{$value};";
+ }
+ break;
+ // default just gets re-assigned
+ default:
+ $ret[$key] = $value;
+ break;
+ }
+ }
+ }
+ }
+
+ return $ret;
+}
+
+
+// ================== Liberty Service Functions ==================
+/**
+ * liberty_content_load_sql
+ *
+ * @access public
+ * @return content load sql
+ */
+function liberty_content_load_sql( &$pObject, $pParamHash=NULL ) {
+ global $gBitSystem, $gBitUser;
+ $ret = array();
+
+ $hasPerm = ( is_object( $pObject ) && isset( $pObject->hasUserPermission )) ? $pObject->hasUserPermission( 'p_liberty_edit_all_status' ) : $gBitUser->hasPermission( 'p_liberty_edit_all_status' );
+
+ if( $gBitSystem->isFeatureActive( 'liberty_display_status' ) && !$hasPerm ) {
+ if(( is_object( $pObject ) && !empty( $pObject->mType['content_type_guid'] ) && $pObject->mType['content_type_guid'] == 'bitcomment' )
+ || ( !empty( $pParamHash['include_comments'] ) && $pParamHash['include_comments'] == 'y' )) {
+ // if we are getting a list of comments then lets check the owner of the comment root and the owner of the content
+ $ret['join_sql'] = "
+ INNER JOIN `".BIT_DB_PREFIX."liberty_comments` lcoms ON (lc.`content_id` = lcoms.`content_id`)
+ INNER JOIN `".BIT_DB_PREFIX."liberty_content` rlcs ON( rlcs.`content_id`=lcoms.`root_id` )";
+ $ret['where_sql'] = " AND lc.`content_status_id` < 100 AND ( ( (rlcs.`user_id` = '".$gBitUser->getUserId()."' OR lc.`user_id` = '".$gBitUser->getUserId()."') AND lc.`content_status_id` > -100) OR lc.`content_status_id` > 0 )";
+ } else {
+ // let owner see any of their own content with a status > -100
+ $ret['where_sql'] = " AND lc.`content_status_id` < 100 AND ( (lc.`user_id` = '".$gBitUser->getUserId()."' AND lc.`content_status_id` > -100) OR lc.`content_status_id` > 0 )";
+ }
+ }
+
+ // Make sure owner comes out properly for all content
+ if ($gBitSystem->isFeatureActive('liberty_allow_change_owner') && $gBitUser->hasPermission('p_liberty_edit_content_owner')) {
+ $ret['select_sql'] = " , lc.`user_id` AS owner_id";
+ }
+ return $ret;
+}
+
+/**
+ * liberty_content_list_sql
+ *
+ * @param array $pParamHash
+ * @param array $pParamHash['enforce_status'] will add joins to status_id even if user is admin
+ * @param array $pParamHash['min_status_id'] one less than the minimum status a content can have to be included
+ * @param array $pParamHash['max_status_id'] one more than the maximum status a content can have to be included
+ * @param array $pParamHash['min_owner_status_id'] one less than the mimimum status a content can have to be included that is owned by the requester
+ * @access public
+ * @return content list sql
+ */
+function liberty_content_list_sql( &$pObject, $pParamHash=NULL ) {
+ global $gBitSystem, $gBitUser;
+ $ret = array();
+
+ $hasPerm = FALSE;
+ // enforce_status will require the status limit on everyone including admin and thus we can ignore permission checks
+ if( !isset( $pParamHash['enforce_status'] )) {
+ $hasPerm = ( is_object( $pObject ) && method_exists( $pObject, 'hasUserPermission' )) ? $pObject->hasUserPermission( 'p_liberty_edit_all_status', FALSE ) : $gBitUser->hasPermission( 'p_liberty_edit_all_status' );
+ }
+
+ // default show content with status between 0 and 100;
+ $min_status_id = isset( $pParamHash['min_status_id'] ) && ( @BitBase::verifyId( $pParamHash['min_status_id'] ) || $pParamHash['min_status_id'] === 0 ) ? $pParamHash['min_status_id'] : 0;
+ $max_status_id = isset( $pParamHash['max_status_id'] ) && ( @BitBase::verifyId( $pParamHash['max_status_id'] ) || $pParamHash['max_status_id'] === 0 ) ? $pParamHash['max_status_id'] : 100;
+ // let owner see any of their own content with a status > -100
+ $min_owner_status_id = isset( $pParamHash['min_owner_status_id'] ) && ( @BitBase::verifyId( $pParamHash['min_owner_status_id'] ) || $pParamHash['min_owner_status_id'] === 0 ) ? $pParamHash['min_owner_status_id'] : -100;
+
+ if( $gBitSystem->isFeatureActive('liberty_display_status') && !$hasPerm ) {
+ if(( is_object( $pObject ) && !empty( $pObject->mType['content_type_guid'] ) && $pObject->mType['content_type_guid'] == 'bitcomment' )
+ || ( !empty( $pParamHash['include_comments'] ) && $pParamHash['include_comments'] == 'y' )) {
+ // if we are getting a list of comments then lets check the owner of the comment root and the owner of the content
+ $ret['join_sql'] = "
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_comments` lcoms ON (lc.`content_id` = lcoms.`content_id`)
+ LEFT OUTER JOIN `".BIT_DB_PREFIX."liberty_content` rlcs ON( rlcs.`content_id`=lcoms.`root_id` )";
+ $ret['where_sql'] =
+ " AND lc.`content_status_id` < ".$max_status_id.
+ " AND (
+ ( (rlcs.`user_id` = '".$gBitUser->getUserId()."' OR lc.`user_id` = '".$gBitUser->getUserId()."') AND lc.`content_status_id` > ".$min_owner_status_id.")
+ OR lc.`content_status_id` > ".$min_status_id."
+ )";
+ } else {
+ $ret['where_sql'] =
+ " AND lc.`content_status_id` < ".$max_status_id.
+ " AND (
+ (lc.`user_id` = '".$gBitUser->getUserId()."' AND lc.`content_status_id` > ".$min_owner_status_id.")
+ OR lc.`content_status_id` > ".$min_status_id."
+ )";
+ }
+ }
+
+ return $ret;
+}
+
+/**
+ * liberty_content_preview
+ *
+ * @param array $pObject
+ * @access public
+ * @return void
+ */
+function liberty_content_preview( &$pObject ) {
+ global $gBitSystem, $gBitUser;
+ if( $gBitSystem->isFeatureActive( 'liberty_display_status' )
+ && ( $gBitUser->hasPermission( 'p_liberty_edit_content_status' ) || $gBitUser->hasPermission( 'p_libert_edit_all_status' ))
+ && @BitBase::verifyId( $_REQUEST['content_status_id'] )) {
+ $pObject->mInfo['content_status_id'] = $_REQUEST['content_status_id'];
+ }
+ if( $gBitSystem->isFeatureActive( 'liberty_allow_change_owner' )
+ && $gBitUser->hasPermission( 'p_liberty_edit_content_owner' )
+ && @BitBase::verifyId( $_REQUEST['owner_id'] )) {
+ $pObject->mInfo['owner_id'] = $_REQUEST['owner_id'];
+ }
+ include_once( LIBERTY_PKG_INCLUDE_PATH.'edit_help_inc.php' );
+}
+
+/**
+ * liberty_content_display
+ *
+ * @param array $pObject
+ * @param array $pParamHash
+ * @access public
+ * @return void
+ */
+function liberty_content_display( &$pObject, &$pParamHash ) {
+ if( $pObject->isValid() ) {
+ global $gBitUser, $gBitSystem;
+
+ // make sure user has appropriate permissions to view this content
+ if( !empty( $pParamHash['perm_name'] )) {
+ $pObject->verifyViewPermission();
+ }
+ }
+}
+
+/**
+ * liberty_content_edit
+ *
+ * @param array $pObject
+ * @param array $pParamHash
+ * @access public
+ * @return void
+ */
+function liberty_content_edit( &$pObject ) {
+ include_once( LIBERTY_PKG_INCLUDE_PATH.'edit_help_inc.php' );
+ include_once( LIBERTY_PKG_INCLUDE_PATH.'edit_storage_inc.php' );
+}
+
+
+// ================== Liberty File Processing Functions ==================
+
+/**
+ * Process uploaded files. Will automagically generate thumbnails for images
+ *
+ * @param array $pFileHash Data require to process the files
+ * @param array $pFileHash['name'] (required) Name of the uploaded file
+ * @param array $pFileHash['type'] (required) Mime type of the file uploaded
+ * @param array $pFileHash['dest_branch'] (required) Relative path where you want to store the file (trailing slash required)
+ * @param array $pFileHash['tmp_name'] (required) Absolute path to file including file name
+ * @param boolean $pFileHash['thumbnail'] (optional) Set to FALSE if you don't want to generate thumbnails
+ * @param array $pFileHash['thumbnail_sizes'] (optional) Decide what sizes thumbnails you want to create: icon, avatar, small, medium, large
+ * @param boolean $pMoveFile (optional) specify if you want to move or copy the original file
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function liberty_process_upload( &$pFileHash, $pMoveFile = TRUE ) {
+ global $gBitSystem;
+
+ // Check for evil file extensions that could be execed on the server
+ if( preg_match( EVIL_EXTENSION_PATTERN, $pFileHash['name'] )) {
+ $pFileHash['type'] = 'text/plain';
+ $pFileHash['name'] = $pFileHash['name'].'.txt';
+ }
+
+ if ( !is_windows() ) {
+ list( $pFileHash['name'], $pFileHash['type'] ) = $gBitSystem->verifyFileExtension( $pFileHash['tmp_name'], $pFileHash['name'] );
+ } else {
+ //$pFile['type'] = $gBitSystem->verifyMimeType( $pFile['tmp_name'] );
+ }
+
+ $ext = strrpos( $pFileHash['name'], '.' );
+
+ // clean out crap that can make life difficult in server maintenance
+ $cleanedBaseName = preg_replace( '/[&\%:\/\\\]/', '', substr( $pFileHash['name'], 0, $ext ) );
+ $pFileHash['dest_base_name'] = $cleanedBaseName;
+ $pFileHash['source_file'] = $pFileHash['tmp_name'];
+ // lowercase all file extensions
+
+ $pFileHash['name'] = $cleanedBaseName.strtolower( substr( $pFileHash['name'], $ext ) );
+
+ // Thumbs.db is a windows My Photos/ folder file, and seems to really piss off imagick
+ $canThumbFunc = liberty_get_function( 'can_thumbnail' );
+ if( !empty( $canThumbFunc ) && $canThumbFunc( $pFileHash['type'] ) && $pFileHash['name'] != 'Thumbs.db' ) {
+ $ret = liberty_process_image( $pFileHash, $pMoveFile );
+ } else {
+ $ret = liberty_process_generic( $pFileHash, $pMoveFile );
+ }
+
+ return $ret;
+}
+
+/**
+ * liberty_process_archive
+ *
+ * @param array $pFileHash
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function liberty_process_archive( &$pFileHash ) {
+ // sanity check: make sure tmp_name isn't empty. will scan / if it is
+ if( !is_array( $pFileHash ) || empty( $pFileHash['tmp_name'] ) || empty( $pFileHash['name'] ) ) {
+ return FALSE;
+ }
+
+ $cwd = getcwd();
+ // if the file has been uploaded using a form, we'll process the uploaded
+ // file directly. if it's been ftp uploaded or some other method used,
+ // we'll copy the file. in the case of xuploaded files, the files have been
+ // processed but don't have to be copied
+ if( empty( $pFileHash['preprocessed'] ) && !is_uploaded_file( $pFileHash['tmp_name'] ) && is_file( $pFileHash['tmp_name'] ) ) {
+ $tmpDir = get_temp_dir();
+ $copyFile = tempnam( !empty( $tmpDir ) ? $tmpDir : '/tmp', $pFileHash['name'] );
+ copy( $pFileHash['tmp_name'], $copyFile );
+ $pFileHash['tmp_name'] = $copyFile;
+ }
+
+ $dir = dirname( $pFileHash['tmp_name'] );
+ $upExt = strtolower( substr( $pFileHash['name'], ( strrpos( $pFileHash['name'], '.' ) + 1 ) ) );
+ $baseDir = $dir.'/';
+ if( is_file( $pFileHash['tmp_name'] ) ) {
+ global $gBitUser;
+ $baseDir .= $gBitUser->mUserId;
+ }
+
+ $destDir = $baseDir.'/'.basename( $pFileHash['tmp_name'] );
+ // this if is very important logic back so subdirs get processed properly
+ if( ( is_dir( $baseDir ) || mkdir( $baseDir ) ) && @mkdir( $destDir ) ) {
+ // Some commands don't nicely support extracting to other directories
+ chdir( $destDir );
+ list( $mimeType, $mimeExt ) = explode( '/', strtolower( $pFileHash['type'] ) );
+ switch( $mimeExt ) {
+ case 'x-rar-compressed':
+ case 'x-rar':
+ $shellResult = shell_exec( "unrar x \"{$pFileHash['tmp_name']}\" \"$destDir\"" );
+ break;
+ case 'x-bzip2':
+ case 'bzip2':
+ case 'x-gzip':
+ case 'gzip':
+ case 'x-tgz':
+ case 'x-tar':
+ case 'tar':
+ switch( $upExt ) {
+ case 'gz':
+ case 'tgz': $compressFlag = '-z'; break;
+ case 'bz2': $compressFlag = '-j'; break;
+ default: $compressFlag = ''; break;
+ }
+ $shellResult = shell_exec( "tar -x $compressFlag -f \"{$pFileHash['tmp_name']}\" -C \"$destDir\"" );
+ break;
+ case 'x-zip-compressed':
+ case 'x-zip':
+ case 'zip':
+ $shellResult = shell_exec( "unzip \"{$pFileHash['tmp_name']}\" -d \"$destDir\"" );
+ break;
+ case 'x-stuffit':
+ case 'stuffit':
+ $shellResult = shell_exec( "unstuff -d=\"$destDir\" \"{$pFileHash['tmp_name']}\" " );
+ break;
+ default:
+ if( $upExt == 'zip' ) {
+ $shellResult = shell_exec( "unzip \"{$pFileHash['tmp_name']}\" -d \"$destDir\"" );
+ } elseif( $upExt == 'rar' ) {
+ $shellResult = shell_exec( "unrar x \"{$pFileHash['tmp_name']}\" \"$destDir\"" );
+ } elseif( $upExt == 'sit' || $upExt == 'sitx' ) {
+ print( "unstuff -d=\"$destDir\" \"{$pFileHash['tmp_name']}\" " );
+ $shellResult = shell_exec( "unstuff -d=\"$destDir\" \"{$pFileHash['tmp_name']}\" " );
+ } else {
+ $destDir = NULL;
+ }
+ break;
+ }
+ }
+
+ chdir( $cwd );
+
+ // if we created a copy of the original, we remove it
+ if( !empty( $copyFile ) ) {
+ @unlink( $copyFile );
+ }
+
+ if( preg_match( "!^/+$!", $destDir )) {
+ // obviously something went horribly wrong
+ return FALSE;
+ } else {
+ return $destDir;
+ }
+}
+
+/**
+ * liberty_process_generic
+ *
+ * @param array $pFileHash
+ * @param array $pMoveFile
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function liberty_process_generic( &$pFileHash, $pMoveFile = TRUE ) {
+ global $gBitSystem;
+ $ret = NULL;
+ if( !empty( $pFileHash['dest_file'] ) ) {
+ $destFile = $pFileHash['dest_file'];
+ } else {
+ if( $gBitSystem->isFeatureActive( 'liberty_originalize_file_names' ) ) {
+ $destFile = STORAGE_PKG_PATH.$pFileHash['dest_branch'].liberty_mime_get_default_file_name( $pFileHash['name'], $pFileHash['type'] );
+ } else {
+ $destFile = STORAGE_PKG_PATH.$pFileHash['dest_branch'].$pFileHash['name'];
+ }
+ if ( is_windows() ) {
+ $destFile = str_replace( '//', '\\', str_replace( "\\", '\\', $destFile ) );
+ }
+ }
+
+ mkdir_p( dirname( $destFile ) );
+
+ if( is_file( $pFileHash['source_file']) ) {
+ if( $pFileHash['source_file'] == $destFile ) {
+ // do nothing if source and dest are the same
+ } elseif( $pMoveFile ) {
+ if( is_uploaded_file( $pFileHash['source_file'] ) ) {
+ move_uploaded_file( $pFileHash['source_file'], $destFile );
+ } else {
+ rename( $pFileHash['source_file'], $destFile );
+ }
+ } else {
+ copy( $pFileHash['source_file'], $destFile );
+ }
+ $ret = $destFile;
+ }
+ $pFileHash['size'] = filesize( $destFile );
+
+ return $ret;
+}
+
+
+/**
+ * liberty_process_image
+ *
+ * @param array $pFileHash
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function liberty_process_image( &$pFileHash, $pMoveFile = TRUE ) {
+ global $gBitSystem;
+ $ret = NULL;
+
+ list($type, $ext) = explode( '/', strtolower( $pFileHash['type'] ) );
+ if( $resizePath = liberty_process_generic( $pFileHash, $pMoveFile )) {
+ $pFileHash['source_file'] = $resizePath;
+
+ //set permissions if possible - necessary for some wonky shared hosting environments
+ if(chmod($pFileHash['source_file'], 0644)){
+ //does nothing, but fails elegantly
+ }
+ $nameHold = $pFileHash['name'];
+ $sizeHold = $pFileHash['size'];
+ $ret = $pFileHash['source_file'];
+
+ // do not thumbnail only if intentionally set to FALSE
+ if( !isset( $pFileHash['thumbnail'] ) || $pFileHash['thumbnail']==TRUE ) {
+ liberty_generate_thumbnails( $pFileHash );
+ }
+ $pFileHash['name'] = $nameHold;
+ $pFileHash['size'] = $sizeHold;
+ }
+ return $ret;
+}
+
+/**
+ * liberty_clear_thumbnails will clear all thummbnails found in a given directory
+ *
+ * @param array $pFileHash['dest_branch'] should contain the path to the dir where we should remove thumbnails
+ * @access public
+ * @return TRUE on success, FALSE on failure
+ */
+function liberty_clear_thumbnails( &$pFileHash ) {
+ if( !empty( $pFileHash['dest_branch'] )) {
+ $thumbHash = array(
+ 'source_file' => $pFileHash['dest_branch'],
+ 'mime_image' => FALSE,
+ );
+
+ // get thumbnails we want to remove
+ if( $thumbs = liberty_fetch_thumbnails( $thumbHash )) {
+ foreach( $thumbs as $thumb ) {
+ $thumb = BIT_ROOT_PATH.$thumb;
+ if( is_writable( $thumb )) {
+ unlink( $thumb );
+ }
+ }
+ // if this was the thumbs subdirectory, we'll remove it if it's empty
+ if( basename( dirname( $thumb )) == 'thumbs' ) {
+ @rmdir( dirname( $thumb ));
+ }
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * liberty_get_function
+ *
+ * @param array $pType
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function liberty_get_function( $pType ) {
+ global $gBitSystem;
+ $processor = $gBitSystem->getConfig( 'image_processor', 'gd' );
+ $ret = 'liberty_'.$processor.'_'.$pType.'_image';
+ return( function_exists( $ret ) ? $ret : FALSE );
+}
+
+/**
+ * liberty_generate_thumbnails
+ *
+ * @param array $pFileHash
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function liberty_generate_thumbnails( $pFileHash ) {
+ global $gBitSystem, $gThumbSizes;
+ $ret = FALSE;
+
+ if( $resizeFunc = liberty_get_function( 'resize' ) ) {
+
+ // allow custom selection of thumbnail sizes
+ if( empty( $pFileHash['thumbnail_sizes'] )) {
+ if( !empty( $gThumbSizes ) && is_array( $gThumbSizes )) {
+ $pFileHash['thumbnail_sizes'] = array_keys( $gThumbSizes );
+ } else {
+ $pFileHash['thumbnail_sizes'] = array( 'large', 'medium', 'small', 'avatar', 'icon' );
+ }
+ }
+
+ if( ( !preg_match( '#image/(gif|jpe?g|png)#i', $pFileHash['type'] ) && $gBitSystem->isFeatureActive( 'liberty_jpeg_originals' )) || in_array( 'original', $pFileHash['thumbnail_sizes'] ) ) {
+ // jpeg version of original
+ if( preg_match( '/pdf/i', $pFileHash['type'] ) ) {
+ // has a customer pdf rasterization function been defined?
+ if( function_exists( 'liberty_rasterize_pdf' ) && $rasteredFile = liberty_rasterize_pdf( $pFileHash['source_file'] ) ) {
+ $pFileHash['source_file'] = $rasteredFile;
+ } else {
+ $magickWand = NewMagickWand();
+ if( !$pFileHash['error'] = liberty_magickwand_check_error( MagickReadImage( $magickWand, $pFileHash['source_file'] ), $magickWand )) {
+ MagickSetFormat( $magickWand, 'JPG' );
+ if( MagickGetImageColorspace( $magickWand ) == MW_CMYKColorspace ) {
+ MagickProfileImage( $magickWand,"ICC", UTIL_PKG_PATH.'icc/srgb.icm' );
+ MagickSetImageColorspace( $magickWand, MW_sRGBColorspace );
+ }
+
+ $imgWidth = MagickGetImageWidth( $magickWand );
+ $imgHeight = MagickGetImageHeight( $magickWand );
+
+ MagickSetImageUnits( $magickWand, MW_PixelsPerInchResolution );
+ MagickSetResolution( $magickWand, 300, 300 );
+ $rasteredFile = dirname( $pFileHash['source_file'] ).'/original.jpg';
+ if( !$pFileHash['error'] = liberty_magickwand_check_error( MagickWriteImage( $magickWand, $rasteredFile ), $magickWand )) {
+ $pFileHash['source_file'] = $rasteredFile;
+ }
+ }
+ }
+ } else {
+ $pFileHash['dest_base_name'] = 'original';
+ $pFileHash['name'] = 'original.jpg';
+ $pFileHash['max_width'] = MAX_THUMBNAIL_DIMENSION;
+ $pFileHash['max_height'] = MAX_THUMBNAIL_DIMENSION;
+ if( $convertedFile = $resizeFunc( $pFileHash )) {
+ $pFileHash['source_file'] = $convertedFile;
+ $ret = TRUE;
+ }
+ }
+ $pFileHash['type'] = $gBitSystem->verifyMimeType( $pFileHash['source_file'] );
+ }
+
+ // override $mimeExt if we have a custom setting for it
+ if( $gBitSystem->isFeatureActive( 'liberty_thumbnail_format' )) {
+ $mimeExt = $gBitSystem->getConfig( 'liberty_thumbnail_format' );
+ } else {
+ list( $type, $mimeExt ) = preg_split( '#/#', strtolower( $pFileHash['type'] ));
+ }
+
+ if( preg_match( "!(png|gif)!", $mimeExt )) {
+ $destExt = '.'.$mimeExt;
+ } else {
+ $destExt = '.jpg';
+ }
+
+ $initialDestPath = $pFileHash['dest_branch'];
+ foreach( $pFileHash['thumbnail_sizes'] as $thumbSize ) {
+ if( isset( $gThumbSizes[$thumbSize] )) {
+ $pFileHash['dest_base_name'] = $thumbSize;
+ $pFileHash['name'] = $thumbSize.$destExt;
+ if( !empty( $gThumbSizes[$thumbSize]['width'] )) {
+ $pFileHash['max_width'] = $gThumbSizes[$thumbSize]['width'];
+ } else {
+ // Have to unset since we reuse $pFileHash
+ unset( $pFileHash['max_width'] );
+ }
+
+ // reset dest_branch for created thumbs
+ if( !empty( $pFileHash['thumb_path'] ) ) {
+ $pFileHash['dest_file'] = $pFileHash['thumb_path'].$pFileHash['name'];
+ } else {
+ // create a subdirectory for the thumbs
+ $pFileHash['dest_branch'] = $initialDestPath.'thumbs/';
+ clearstatcache();
+ if( !is_dir( STORAGE_PKG_PATH.$pFileHash['dest_branch'] )) {
+ @mkdir( STORAGE_PKG_PATH.$pFileHash['dest_branch'], 0775, TRUE );
+ clearstatcache();
+ }
+ }
+
+ if( !empty( $gThumbSizes[$thumbSize]['height'] )) {
+ $pFileHash['max_height'] = $gThumbSizes[$thumbSize]['height'];
+ } else {
+ // Have to unset since we reuse $pFileHash
+ unset( $pFileHash['max_height'] );
+ }
+ if( $pFileHash['icon_thumb_path'] = $resizeFunc( $pFileHash )) {
+ $ret = TRUE;
+ // use the previous thumb as the source for the next, decreasingly smaller thumb as this GREATLY increases speed
+ $pFileHash['source_file'] = $pFileHash['icon_thumb_path'];
+ }
+ }
+ }
+
+ // to keep everything in bitweaver working smoothly, we need to remove the thumbs/ subdir again
+ $pFileHash['dest_branch'] = $initialDestPath;
+ }
+
+ return $ret;
+}
+
+/**
+ * fetch all available thumbnails for a given item. if no thumbnails are present, get thumbnailing image or the appropriate mime type icon
+ *
+ * @param array $pParamHash Hash of all settings used to fetch thumbnails, including source_file, default_image, thumbnail_sizes, and mime_image
+ * @access public
+ * @return array of available thumbnails or mime icons
+ * TODO: individual options are only for legacy reasons - remove options and deprecated() soon - xing - Monday Jun 23, 2008 22:36:53 CEST
+ */
+function liberty_fetch_thumbnails( $pParamHash ) {
+ global $gBitSystem, $gThumbSizes;
+ $ret = array();
+
+ if( !empty( $pParamHash['source_file'] )) {
+ if( empty( $pParamHash['thumbnail_sizes'] )) {
+ $pParamHash['thumbnail_sizes'] = array_keys( $gThumbSizes );
+ }
+
+ // liberty file processors automatically pick the best format for us. we can force a format though.
+ // using array_unique will give us the best order in which to look for the thumbnails
+ $exts = array_unique( array( $gBitSystem->getConfig( 'liberty_thumbnail_format', 'jpg' ), 'jpg', 'png', 'gif', 'x-jpeg' ));
+
+ // short hand
+ $path = &$pParamHash['source_file'];
+ // $path might already be the absolute path or it might already contain BIT_ROOT_URL
+ if( !( $path = preg_replace( "!^".preg_quote( STORAGE_PKG_PATH, "!" )."!", "", $path ))) {
+ $path = preg_replace( "!^".preg_quote( STORAGE_PKG_URL, "!" )."!", "", $path );
+ }
+
+ // remove the filename if there is one (we can't just use dirname() becuase we might only have the path to the dir)
+ $dir = substr( $path, 0, strrpos( $path, '/' ) + 1 );
+ // assume thumb sizes are from largest to smallest. reverse so smaller can be used if larger don't exist
+ $lastSize = NULL;
+ foreach( array_reverse( $pParamHash['thumbnail_sizes'] ) as $size ) {
+ foreach( $exts as $ext ) {
+ $image = $size.'.'.$ext;
+ $thumbDir = is_dir( STORAGE_PKG_PATH.$dir.'thumbs/' ) ? $dir.'thumbs/' : $dir;
+ if( is_readable( STORAGE_PKG_PATH.$thumbDir.$image )) {
+ $ret[$size] = (empty( $_REQUEST['uri_mode'] ) ? STORAGE_PKG_URL : STORAGE_PKG_URI).$thumbDir.$image;
+ }
+ }
+ // fetch mime image unless we set this to FALSE
+ if(( !isset( $pParamHash['mime_image'] ) || $pParamHash['mime_image'] === TRUE ) && empty( $ret[$size] )) {
+ if( $lastSize && !empty( $ret[$lastSize] ) ) {
+ $ret[$size] = $ret[$lastSize];
+ }
+ }
+ $lastSize = $size;
+ }
+
+ // default if nothing else is available
+ foreach( array_reverse( $pParamHash['thumbnail_sizes'] ) as $size ) {
+ if( empty( $ret[$size] ) ) {
+ if( !empty( $pParamHash['default_image'] )) {
+ $ret[$size] = $pParamHash['default_image'];
+ } else {
+ // we need to make sure we have an image name that we can look up the mime type
+ $path .= ( strrpos( $dir, '/' ) == strlen( $path ) ? 'dummy.jpg' : basename( $path ));
+ $ret[$size] = LibertySystem::getMimeThumbnailURL( $gBitSystem->lookupMimeType( $path ), substr( $path, strrpos( $path, '.' ) + 1 ));
+ }
+ }
+ }
+ }
+
+ return $ret;
+}
+
+/**
+ * fetch a single available thumbnail for a given item. if no thumbnail is present, return NULL
+ *
+ * @param array $pParamHash Hash of all settings used to fetch thumbnails including: size, source_file, default_image, and mime_image
+ * @access public
+ * @return string url
+ * TODO: individual options are only for legacy reasons - remove options and deprecated() soon - xing - Monday Jun 23, 2008 22:36:53 CEST
+ */
+function liberty_fetch_thumbnail_url( $pParamHash ) {
+ if( !empty( $pParamHash['source_file'] )) {
+ if( empty( $pParamHash['size'] )) {
+ $pParamHash['size'] = 'small';
+ }
+
+ $pParamHash['thumbnail_sizes'] = array( $pParamHash['size'] );
+ $ret = liberty_fetch_thumbnails( $pParamHash );
+
+ return( !empty( $ret[$pParamHash['size']] ) ? $ret[$pParamHash['size']] : NULL );
+ }
+}
+
+/**
+ * get a set of image size options based on $gThumbSizes
+ *
+ * @param string $pEmptyOption string to use as empty option - if set to FALSE no empty string is eincluded - Note that string is tra()'d
+ * @access public
+ * @return array of image size options suitable for use in a form
+ */
+function get_image_size_options( $pEmptyOption = 'Disable this feature' ) {
+ global $gThumbSizes;
+ $ret = array();
+ if( !empty( $pEmptyOption )) {
+ $ret[''] = tra( $pEmptyOption );
+ }
+ foreach( $gThumbSizes as $key => $size ) {
+ $ret[$key] = tra( ucfirst( $key ))." ( ". ( empty( $size['width'] ) ? tra( 'unlimited' ) : $size['width'] ) ." x ". ( empty($size['height'] ) ? tra('unlimited') : $size['height'] ) ." ".tra( 'pixels' )." )";
+ }
+ return $ret;
+}
+
+/**
+ * get_leadtitle will fetch the string before the liberty_subtitle_delimiter
+ *
+ * @param string $pString string that should be checked for the delimiter
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function get_leadtitle( $pString ) {
+ global $gBitSystem;
+ return( substr( $pString, 0, strpos( $pString, $gBitSystem->getConfig( 'liberty_subtitle_delimiter', ':' ))));
+}
+
+/**
+ * get_subtitle will fetch the string after the liberty_subtitle_delimiter
+ *
+ * @param string $pString string that should be checked for the delimiter
+ * @access public
+ * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
+ */
+function get_subtitle( $pString ) {
+ global $gBitSystem;
+ if(( $start = strpos( $pString, $gBitSystem->getConfig( 'liberty_subtitle_delimiter', ':' ))) !== FALSE ) {
+ return( substr( $pString, ( $start + 1 )));
+ }
+}
+?>
diff --git a/includes/lookup_content_inc.php b/includes/lookup_content_inc.php
new file mode 100644
index 0000000..d9ebaa6
--- /dev/null
+++ b/includes/lookup_content_inc.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * lookup_content_inc
+ *
+ * @author spider <spider@steelsun.com>
+ * @version $Revision$
+ * @package liberty
+ * @subpackage functions
+ */
+ global $gContent;
+
+ if( @BitBase::verifyId( $_REQUEST['structure_id'] ) ) {
+ /**
+ * required setup
+ */
+ require_once( LIBERTY_PKG_CLASS_PATH.'LibertyStructure.php');
+ $_REQUEST['structure_id'] = preg_replace( '/[\D]/', '', $_REQUEST['structure_id'] );
+ $gStructure = new LibertyStructure( $_REQUEST['structure_id'] );
+ if( $gStructure->load() ) {
+ $gStructure->loadNavigation();
+ $gStructure->loadPath();
+ $gBitSmarty->assign( 'structureInfo', $gStructure->mInfo );
+ // $_REQUEST['page_id'] = $gStructure->mInfo['page_id'];
+ if( $viewContent = LibertyBase::getLibertyObject( $gStructure->mInfo['content_id'], $gStructure->mInfo['content_type']['content_type_guid'] ) ) {
+ $viewContent->setStructure( $_REQUEST['structure_id'] );
+ $gBitSmarty->assignByRef( 'pageInfo', $viewContent->mInfo );
+ $gContent = &$viewContent;
+ $gBitSmarty->assignByRef( 'gContent', $gContent );
+ }
+ }
+ } elseif( @BitBase::verifyId( $_REQUEST['content_id'] ) ) {
+ $_REQUEST['content_id'] = preg_replace( '/[\D]/', '', $_REQUEST['content_id'] );
+ require_once( LIBERTY_PKG_CLASS_PATH.'LibertyBase.php');
+ if( $gContent = LibertyBase::getLibertyObject( $_REQUEST['content_id'] ) ) {
+ $gBitSmarty->assignByRef( 'gContent', $gContent );
+ $gBitSmarty->assignByRef( 'pageInfo', $gContent->mInfo );
+ }
+ }
+
+?>