Source for file BitArticle.php
Documentation is available at BitArticle.php 
 * Article class is used when accessing BitArticles. It is based on TikiSample  
 * and builds on core bitweaver functionality, such as the Liberty CMS engine.  
 * @copyright 2004-15 bitweaver.org  
 *  Copyright( c )2003 tikwiki.org  
 *  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  
 * @author wolffy <wolff_borg@yahoo.com.au>  
require_once( ARTICLES_PKG_PATH. 'BitArticleTopic.php' );  
require_once( ARTICLES_PKG_PATH. 'BitArticleType.php' );  
require_once( LIBERTY_PKG_PATH. 'LibertyMime.php' );  
require_once( LIBERTY_PKG_PATH. 'LibertyComment.php' );  
define( 'BITARTICLE_CONTENT_TYPE_GUID', 'bitarticle' );  
    * Primary key for articles  
    * Initiate the articles class  
    * @param $pArticleId article id of the article we want to view  
    * @param $pContentId content id of the article we want to view  
    public function BitArticle($pArticleId= NULL, $pContentId= NULL)  
                'content_name' =>  'Article',  
                'handler_class' =>  'BitArticle',  
                'handler_package' =>  'articles',  
                'handler_file' =>  'BitArticle.php',  
                'maintainer_url' =>  'http://www.bitweaver.org'  
        $offset =  $this->mDate->get_display_offset();  
    * Load the data from the database  
    public function load($pContentId =  NULL, $pPluginParams =  NULL)  
            // LibertyContent::load()assumes you have joined already, and will not execute any sql!  
            // This is a significant performance optimization  
            $this->getServicesSql( 'content_load_sql_function', $selectSql, $joinSql, $whereSql, $bindVars );  
            $query =  "SELECT a.*, lc.*, atype.*, atopic.*, lch.hits,  
                uue.`login` AS `modifier_user`, uue.`real_name` AS `modifier_real_name`,  
                uuc.`login` AS `creator_user`, uuc.`real_name` AS `creator_real_name` ,  
                la.`attachment_id` AS `primary_attachment_id`, lf.`file_name` AS `image_attachment_path` $selectSql  
                    LEFT OUTER JOIN `". BIT_DB_PREFIX. "article_types` atype ON( atype.`article_type_id` = a.`article_type_id` )  
                    LEFT OUTER JOIN `". BIT_DB_PREFIX. "article_topics` atopic ON( atopic.`topic_id` = a.`topic_id` )  
                    INNER JOIN `". BIT_DB_PREFIX. "liberty_content` lc ON( lc.`content_id` = a.`content_id` )  
                    LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_hits` lch ON( lch.`content_id` = lc.`content_id` )  
                    LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_users` uue ON( uue.`user_id` = lc.`modifier_user_id` )  
                    LEFT OUTER JOIN `". BIT_DB_PREFIX. "users_users` uuc ON( uuc.`user_id` = lc.`user_id` )  
                    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` lf ON( lf.`file_id` = la.`foreign_id` )  
                WHERE a.`$lookupColumn`=? $whereSql";  
            $result =  $this->mDb->query( $query, $bindVars );  
            if ( $result &&  $result->numRows() ) {  
                $this->mInfo =  $result->fetchRow();  
                // if a custom image for the article exists, use that, then use an attachment, then use the topic image  
                $this->mInfo['thumbnail_url'] =  static::getImageThumbnails( $this->mInfo );  
                $this->mInfo['creator']       =  ( !empty( $this->mInfo['creator_real_name'] ) ?  $this->mInfo['creator_real_name'] :  $this->mInfo['creator_user'] );  
                $this->mInfo['editor']        =  ( !empty( $this->mInfo['modifier_real_name'] ) ?  $this->mInfo['modifier_real_name'] :  $this->mInfo['modifier_user'] );  
                // we need the raw data to display in the textarea  
                // here we have the displayed data without the ...split... stuff  
                $this->mInfo['num_comments'] =  $comment->getNumComments( $this->mInfo['content_id'] );  
                if ( !empty( $this->mInfo['primary_attachment_id'] ) &&  !empty( $this->mStorage[$this->mInfo['primary_attachment_id']] )) {  
                    $this->mInfo['primary_attachment'] =  &$this->mStorage[$this->mInfo['primary_attachment_id']];  
    * Store article data after submission  
    * @param array pParamHash of values that will be used to store the page  
    * @return bool TRUE on success, FALSE if store could not occur. If FALSE, $this->mErrors will have reason why  
    public function store(&$pParamHash)  
        $this->mDb->StartTrans();  
                $result =  $this->mDb->associateUpdate( $table, $pParamHash['article_store'], array( "article_id" =>  $this->mArticleId ));  
                $pParamHash['article_store']['content_id'] =  $pParamHash['content_id'];  
                if ( isset ( $pParamHash['article_id'] )&&  is_numeric( $pParamHash['article_id'] ) ) {  
                    // if pParamHash['article_id'] is set, someone is requesting a particular article_id. Use with caution!  
                    $pParamHash['article_store']['article_id'] =  $pParamHash['article_id'];  
                    $pParamHash['article_store']['article_id'] =  $this->mDb->GenID( 'articles_article_id_seq' );  
                $this->mArticleId =  $pParamHash['article_store']['article_id'];  
                $result =  $this->mDb->associateInsert( $table, $pParamHash['article_store'] );  
            $this->mDb->CompleteTrans();  
    * Make sure the data is safe to store  
    * @param pParamHash be sure to pass by reference in case we need to make modifcations to the hash  
    * @param array pParams reference to hash of values that will be used to store the page, they will be modified where necessary  
    * @return bool TRUE on success, FALSE if verify failed. If FALSE, $this->mErrors will have reason why  
    public function verify(&$pParamHash)  
        global $gBitUser, $gBitSystem;  
        // make sure we're all loaded up of we have a mArticleId  
            $pParamHash['content_id'] =  $this->mInfo['content_id'];  
        // It is possible a derived class set this to something different  
        if ( empty( $pParamHash['content_type_guid'] )&&  !empty( $this->mContentTypeGuid ) ) {  
        if ( @$this->verifyId( $pParamHash['content_id'] ) ) {  
            $pParamHash['article_store']['content_id'] =  $pParamHash['content_id'];  
        if ( !empty( $pParamHash['author_name'] ) ) {  
            $pParamHash['article_store']['author_name'] =  $pParamHash['author_name'];  
        if ( @$this->verifyId( $pParamHash['topic_id'] ) ) {  
            $pParamHash['article_store']['topic_id'] =(int)  $pParamHash['topic_id'];  
        if ( @$this->verifyId( $pParamHash['article_type_id'] ) ) {  
            $pParamHash['article_store']['article_type_id'] =(int)  $pParamHash['article_type_id'];  
        if ( !empty( $pParamHash['format_guid'] ) ) {  
            $pParamHash['content_store']['format_guid'] =  $pParamHash['format_guid'];  
        // we do the substr on load. otherwise we need to store the same data twice.  
        if ( !empty( $pParamHash['edit'] ) ) {  
            $pParamHash['content_store']['data'] =  $pParamHash['edit'];  
        if ( !empty( $pParamHash['rating'] ) ) {  
            $pParamHash['article_store']['rating'] =(int)  ( $pParamHash['rating'] );  
        // check for name issues, first truncate length if too long  
        if ( !empty( $pParamHash['title'] ) ) {  
                if ( empty( $pParamHash['title'] ) ) {  
                    $this->mErrors['title'] =  'You must specify a title.';  
        } elseif ( empty( $pParamHash['title'] ) ) {  
            $this->mErrors['title'] =  'You must specify a title';  
        if ( !empty( $pParamHash['publish_Month'] ) ) {  
            $dateString =  $this->mDate->gmmktime(  
                $pParamHash['publish_Hour'],  
                $pParamHash['publish_Minute'],  
                isset ($pParamHash['publish_Second']) ?  $pParamHash['publish_Second'] :  0, 
                $pParamHash['publish_Month'],  
                $pParamHash['publish_Day'],  
                $pParamHash['publish_Year']  
            $timestamp =  $this->mDate->getUTCFromDisplayDate( $dateString );  
                $pParamHash['publish_date'] =  $timestamp;  
        if ( !empty( $pParamHash['publish_date'] ) ) {  
            $pParamHash['article_store']['publish_date'] =  $pParamHash['publish_date'];  
        if ( !empty( $pParamHash['expire_Month'] ) ) {  
            $dateString =  $this->mDate->gmmktime(  
                $pParamHash['expire_Hour'],  
                $pParamHash['expire_Minute'],  
                isset ($pParamHash['expire_Second']) ?  $pParamHash['expire_Second'] :  0, 
                $pParamHash['expire_Month'],  
                $pParamHash['expire_Day'],  
                $pParamHash['expire_Year']  
            $timestamp =  $this->mDate->getUTCFromDisplayDate( $dateString );  
                $pParamHash['expire_date'] =  $timestamp;  
        if ( !empty( $pParamHash['expire_date'] ) ) {  
            $pParamHash['article_store']['expire_date'] =  $pParamHash['expire_date'];  
        if ( @$this->verifyId( $pParamHash['status_id'] ) ) {  
            if ($pParamHash['status_id'] >  ARTICLE_STATUS_PENDING) {  
                if ( $gBitUser->hasPermission( 'p_articles_approve_submission' )) {  
                    $pParamHash['article_store']['status_id'] =(int)  ( $pParamHash['status_id'] );  
                    $pParamHash['article_store']['status_id'] =  ARTICLE_STATUS_PENDING;  
                $pParamHash['article_store']['status_id'] =(int)  ( $pParamHash['status_id'] );  
            $pParamHash['article_store']['status_id'] =  $this->mInfo['status_id'];  
            if ( $gBitUser->hasPermission( 'p_articles_approve_submission' ) ||  $gBitUser->hasPermission( 'p_articles_auto_approve' ) ) {  
                $pParamHash['article_store']['status_id'] =  ARTICLE_STATUS_APPROVED;  
                $pParamHash['article_store']['status_id'] =  ARTICLE_STATUS_PENDING;        // Default status  
        if ( $gBitUser->hasPermission( 'p_liberty_enter_html' ) ) {  
            $prefs[] =  'content_enter_html';  
        foreach ($prefs as $pref) {  
            if ( !empty( $pParamHash['preferences'][$pref] ) ) {  
                $pParamHash['preferences_store'][$pref] =  $pParamHash['preferences'][$pref];  
                $pParamHash['preferences_store'][$pref] =  NULL;  
        if ( array_search( $pParamHash['article_store']['status_id'], array( ARTICLE_STATUS_DENIED, ARTICLE_STATUS_DRAFT, ARTICLE_STATUS_PENDING ) ) ) {  
                $this->mInfo["no_index"] =  true ;  
        // if we have an error we get them all by checking parent classes for additional errors  
            parent::verify( $pParamHash );  
    * Deal with images and text, modify them apprpriately that they can be returned to the form.  
    * @param $previewData data submitted by form - generally $_REQUEST  
    * @return array of data compatible with article form  
        global $gBitSystem, $gBitUser;  
        $data =  array_merge( $pParamHash, $data['content_store'], $data['article_store'] );  
        $data['raw'] =  $data['edit'];  
        if ( empty( $data['user_id'] ) ) {  
            $data['user_id'] =  $gBitUser->mUserId;  
        if ( empty( $data['hits'] ) ) {  
        if ( empty( $data['publish_date'] ) ) {  
            $data['publish_date'] =  $gBitSystem->getUTCTime();  
        if ( empty( $data['article_type_id'] ) ) {  
            $data['article_type_id'] =  1;  
        if ( empty( $data['topic_id'] ) ) {  
        if ( empty( $data['parsed'] ) ) {  
            $data['no_cache']    =  TRUE;  
            // replace the split syntax with a horizontal rule  
        $data =  array_merge( $data, $articleType->mInfo, $articleTopic->mInfo );  
    * Get the URL for any given article image  
    * @param $pParamHash pass in full set of data returned from article query  
        global $gBitSystem, $gThumbSizes;  
        $thumbHash['mime_image'] =  FALSE;  
        if ( !empty( $pParamHash['image_attachment_path'] )) {  
            $thumbHash['source_file'] =  $pParamHash['image_attachment_path'];  
        } elseif ( !empty( $pParamHash['has_topic_image'] ) &&  $pParamHash['has_topic_image'] ==  'y' ) {  
    * Removes currently loaded article  
    * @return bool TRUE on success, FALSE on failure  
            $this->mDb->StartTrans();  
            $query =  "DELETE FROM `". BIT_DB_PREFIX. "articles` WHERE `content_id` = ?";  
            $result =  $this->mDb->query( $query, array( $this->mContentId ) );  
                $this->mDb->CompleteTrans();  
                $this->mDb->RollbackTrans();  
    * Check if there is an article loaded  
    * @return bool TRUE on success, FALSE on failure  
    * This function generates a list of records from the liberty_content database for use in a list page  
    * @param $pParamHash contains an array of conditions to sort by  
    * @return array of articles  
    public function getList(&$pParamHash)  
        global $gBitSystem, $gBitUser, $gLibertySystem;  
        if ( empty( $pParamHash['sort_mode'] ) ) {  
            // no idea what this is supposed to do  
            //$pParamHash['sort_mode'] = $gBitSystem->isFeatureActive('articles_auto_approve') ? 'order_key_desc' : 'publish_date_desc';  
            $pParamHash['sort_mode'] =  'publish_date_desc';  
        $this->getServicesSql( 'content_list_sql_function', $selectSql, $joinSql, $whereSql, $bindVars, NULL, $pParamHash );  
        $find =  $pParamHash['find'];  
            // you can use an array of articles  
            $whereSql .=  " AND UPPER( lc.`title` ) LIKE ? ";  
        } elseif ( @$this->verifyId( $pParamHash['user_id'] ) ) {  
            $whereSql .=  " AND lc.`user_id` = ? ";  
            $bindVars[] = (int)  $pParamHash['user_id'];  
        if ( @$this->verifyId( $pParamHash['status_id'] ) ) {  
            $whereSql .=  " AND a.`status_id` = ? ";  
            $bindVars[] =  $pParamHash['status_id'];  
        if ( @$this->verifyId( $pParamHash['type_id'] ) ) {  
            $whereSql .=  " AND a.`article_type_id` = ? ";  
            $bindVars[] = (int)  $pParamHash['type_id'];  
        // TODO: we need to check if the article wants to be viewed before / after respective dates  
        // someone better at SQL please get this working without an additional db call - xing  
        $now =  $gBitSystem->getUTCTime();  
        if ( !empty( $pParamHash['show_future'] ) &&  !empty( $pParamHash['show_expired'] ) &&  $gBitUser->hasPermission( 'p_articles_admin' )) {  
            // this will show all articles at once - future, current and expired  
        } elseif ( !empty( $pParamHash['show_future'] ) &&  $gBitUser->hasPermission( 'p_articles_admin' )) {  
            $whereSql .=  " AND ( a.`expire_date` > ? OR atype.`show_post_expire` = ? ) ";  
            $bindVars[] = (int)  $now;  
        } elseif ( !empty( $pParamHash['show_expired'] ) &&  $gBitUser->hasPermission( 'p_articles_admin' )) {  
            $whereSql .=  " AND ( a.`publish_date` < ? OR atype.`show_pre_publ` = ? ) ";  
            $bindVars[] = (int)  $now;  
        } elseif ( !empty( $pParamHash['get_future'] )) {  
            // if we're trying to view these articles, we better have the perms to do so  
            if ( !$gBitUser->hasPermission( 'p_articles_admin' )) {  
            $whereSql .=  " AND a.`publish_date` > ?";  
            $bindVars[] = (int)  $now;  
        } elseif ( !empty( $pParamHash['get_expired'] )) {  
            // show only expired articles  
            // if we're trying to view these articles, we better have the perms to do so  
            if ( !$gBitUser->hasPermission( 'p_articles_admin' )) {  
            $whereSql .=  " AND a.`expire_date` < ? ";  
            $bindVars[] = (int)  $now;  
            // hide future and expired articles - this is the default behaviour  
            // we need all these AND and ORs to ensure that other conditions such as status_id are respected as well  
            $whereSql .=  " AND (( a.`publish_date` > a.`expire_date` ) OR (( a.`publish_date` < ? OR atype.`show_pre_publ` = ? ) AND ( a.`expire_date` > ? OR atype.`show_post_expire` = ? ))) ";  
            $bindVars[] = (int)  $now;  
            $bindVars[] = (int)  $now;  
        if ( @$this->verifyId( $pParamHash['topic_id'] ) ) {  
            $whereSql .=  " AND a.`topic_id` = ? ";  
            $bindVars[] = (int)  $pParamHash['topic_id'];  
        } elseif ( !empty( $pParamHash['topic'] ) ) {  
            $whereSql .=  " AND UPPER( atopic.`topic_name` ) = ? ";  
            $whereSql .=  " AND ( atopic.`active_topic` != 'n' OR atopic.`active_topic` IS NULL ) ";  
            //$whereSql .= " AND atopic.`active_topic` != 'n' ";  
        // Oracle is very particular about naming multiple columns, so need to explicity name them ORA-00918: column ambiguously defined  
                a.`article_id`, a.`description`, a.`author_name`, a.`publish_date`, a.`expire_date`, a.`rating`,  
                atopic.`topic_id`, atopic.`topic_name`, atopic.`has_topic_image`, atopic.`active_topic`,  
                astatus.`status_id`, astatus.`status_name`,  
                atype.*, lc.*, la.`attachment_id` AS `primary_attachment_id`, lf.`file_name` AS `image_attachment_path` $selectSql  
                INNER JOIN      `". BIT_DB_PREFIX. "liberty_content`       lc ON( lc.`content_id`         = a.`content_id` )  
                INNER JOIN      `". BIT_DB_PREFIX. "article_status`   astatus ON( astatus.`status_id`     = a.`status_id` )  
                LEFT OUTER JOIN `". BIT_DB_PREFIX. "liberty_content_hits` lch ON( lc.`content_id`         = lch.`content_id` )  
                LEFT OUTER JOIN `". BIT_DB_PREFIX. "article_topics`    atopic ON( atopic.`topic_id`       = a.`topic_id` )  
                LEFT OUTER JOIN `". BIT_DB_PREFIX. "article_types`      atype ON( atype.`article_type_id` = a.`article_type_id` )  
                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`         lf ON( lf.`file_id`            = la.`foreign_id` )  
            WHERE lc.`content_type_guid` = ? $whereSql  
            ORDER BY ". $this->mDb->convertSortmode( $pParamHash['sort_mode'] );  
        $query_cant =  "SELECT COUNT( * )FROM `". BIT_DB_PREFIX. "articles` a  
            INNER JOIN      `". BIT_DB_PREFIX. "liberty_content`    lc ON lc.`content_id`   = a.`content_id`  
            LEFT OUTER JOIN `". BIT_DB_PREFIX. "article_topics` atopic ON atopic.`topic_id` = a.`topic_id` $joinSql  
            LEFT OUTER JOIN `". BIT_DB_PREFIX. "article_types`   atype ON atype.`article_type_id` = a.`article_type_id`  
            WHERE lc.`content_type_guid` = ? $whereSql";  
        $result =  $this->mDb->query( $query, $bindVars, $pParamHash['max_records'], $pParamHash['offset'] );  
        while ( $res =  $result->fetchRow() ) {  
            $res =  array_merge( $this->parseSplit( $res, $gBitSystem->getConfig( 'articles_description_length', 500 )), $res );  
            $res['thumbnail_url'] =  static::getImageThumbnails( $res );  
            $res['num_comments']  =  $comment->getNumComments( $res['content_id'] );  
            $res['display_url']   =  self::getDisplayUrlFromHash( $res );  
            // fetch the primary attachment that we can display the file on the front page if needed  
            $res['primary_attachment'] =  LibertyMime::loadAttachment( $res['primary_attachment_id'] );  
        $pParamHash["cant"] =  $this->mDb->getOne( $query_cant, $bindVars );  
    * Returns include file that will setup vars for display  
    * @return the fully specified path to file to be included  
        return ARTICLES_PKG_PATH. "display_article_inc.php";  
     * Get a list of articles that are to be published in the future  
     * @param array $pParamHash contains listing options - same as getList()  
     * @return array of articles  
        $pParamHash['get_future'] =  TRUE;  
        return( $this->getList( $pParamHash ));  
     * Get list of articles that have expired and are not displayed on the site anymore  
     * @param array $pParamHash contains listing options - same as getList()  
     * @return array of articles  
        $pParamHash['get_expired'] =  TRUE;  
        return( $this->getList( $pParamHash ));  
    * Generates the URL to the article  
    * @return the link to the full article  
        if ( @BitBase::verifyId( $pParamHash['article_id'] ) ) {  
            if ( $gBitSystem->isFeatureActive( 'pretty_urls_extended' ) ) {  
                // Not needed since it's a number:  $ret = ARTICLES_PKG_URL."view/".$this->mArticleId;  
                $ret =  ARTICLES_PKG_URL. $pParamHash['article_id'];  
            } elseif ( $gBitSystem->isFeatureActive( 'pretty_urls' ) ) {  
                $ret =  ARTICLES_PKG_URL. $pParamHash['article_id'];  
                $ret =  ARTICLES_PKG_URL. "read.php?article_id=". $pParamHash['article_id'];  
    * Function that returns link to display an image  
    * @return the url to display the gallery.  
        $info =  array( 'article_id' =>  $this->mArticleId );  
        return self::getDisplayUrlFromHash( $info );  
    * get a list of all available statuses  
    * @return an array of available statuses  
        $result =  $gBitSystem->mDb->query( $query );  
        return $result->getRows();  
    * set the status of an article  
    * @param $pStatusId new status id of the article  
    * @param $pArticleId of the article that is being changed - if not set, it will attemtp to change the currently loaded article  
    * @return new status of article on success - else returns NULL  
    public function setStatus($pStatusId, $pArticleId =  NULL, $pContentId =  NULL)  
        $validStatuses =  array( ARTICLE_STATUS_DENIED, ARTICLE_STATUS_DRAFT, ARTICLE_STATUS_PENDING, ARTICLE_STATUS_APPROVED, ARTICLE_STATUS_RETIRED );  
        if ( !in_array( $pStatusId, $validStatuses ) ) {  
            $this->mErrors[] =  "Invalid article status";  
        if ( empty( $pArticleId ) &&  $this->isValid() ) {  
        if ( @$this->verifyId( $pArticleId ) ) {  
            $sql =  "UPDATE `". BIT_DB_PREFIX. "articles` SET `status_id` = ? WHERE `article_id` = ?";  
            $rs =  $this->mDb->query( $sql, array( $pStatusId, $pArticleId ));  
            // Calling the index function for approved articles ...  
            if ( $gBitSystem->isPackageActive( 'search' ) ) {  
                include_once( SEARCH_PKG_PATH. 'refresh_functions.php' );  
                if ($pStatusId ==  ARTICLE_STATUS_APPROVED) {  
                } elseif (!$pStatusId ==  ARTICLE_STATUS_RETIRED) {  
                    delete_index($pContentId); // delete it from the search index unless retired ...  
 
 
        
       |