Seditio Source
Root |
./othercms/ips_4.3.4/applications/blog/sources/Entry/Entry.php
<?php
/**
 * @brief        Entry Model
 * @author        <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
 * @copyright    (c) Invision Power Services, Inc.
 * @license        https://www.invisioncommunity.com/legal/standards/
 * @package        Invision Community
 * @subpackage    Blog
 * @since        3 Mar 2014
 */

namespace IPS\blog;

/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
   
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
    exit;
}

/**
 * Entry Model
 */
class _Entry extends \IPS\Content\Item implements
    \
IPS\Content\Pinnable, \IPS\Content\Lockable, \IPS\Content\Hideable, \IPS\Content\Featurable,
    \
IPS\Content\Tags,
    \
IPS\Content\Followable,
    \
IPS\Content\Shareable,
    \
IPS\Content\ReadMarkers,
    \
IPS\Content\Views,
    \
IPS\Content\Polls,
    \
IPS\Content\Ratings,
    \
IPS\Content\EditHistory,
    \
IPS\Content\Searchable,
    \
IPS\Content\Embeddable,
    \
IPS\Content\FuturePublishing,
    \
IPS\Content\MetaData
{
    use \
IPS\Content\Reactable, \IPS\Content\Reportable;
   
   
/**
     * @brief    Multiton Store
     */
   
protected static $multitons;
       
   
/**
     * @brief    Application
     */
   
public static $application = 'blog';
   
   
/**
     * @brief    Module
     */
   
public static $module = 'blogs';
   
   
/**
     * @brief    Database Table
     */
   
public static $databaseTable = 'blog_entries';
   
   
/**
     * @brief    Database Prefix
     */
   
public static $databasePrefix = 'entry_';
       
   
/**
     * @brief    Database Column Map
     */
   
public static $databaseColumnMap = array(
       
'author'                => 'author_id',
       
'author_name'            => 'author_name',
       
'content'                => 'content',
       
'container'                => 'blog_id',
       
'date'                    => 'date',
       
'updated'                => 'last_update',
       
'title'                    => 'name',
       
'num_comments'            => 'num_comments',
       
'unapproved_comments'    => 'queued_comments',
       
'hidden_comments'        => 'hidden_comments',
       
'last_comment_by'        => 'last_comment_mid',
       
'last_comment'            => 'last_update',    // Same as updated above
       
'views'                    => 'views',
       
'approved'                => 'hidden',
       
'pinned'                => 'pinned',
       
'poll'                    => 'poll_state',
       
'featured'                => 'featured',
       
'ip_address'            => 'ip_address',
       
'locked'                => 'locked',
       
'cover_photo'            => 'cover_photo',
       
'cover_photo_offset'    => 'cover_offset',
       
'is_future_entry'        => 'is_future_entry',
       
'future_date'           => 'publish_date',
       
'status'                => 'status',
       
'meta_data'                => 'meta_data',
    );
   
   
/**
     * @brief    Title
    */
   
public static $title = 'blog_entry';
   
   
/**
     * @brief    Node Class
     */
   
public static $containerNodeClass = 'IPS\blog\Blog';
   
   
/**
     * @brief    [Content\Item]    Comment Class
     */
   
public static $commentClass = 'IPS\blog\Entry\Comment';
   
   
/**
     * @brief    [Content\Item]    First "comment" is part of the item?
     */
   
public static $firstCommentRequired = FALSE;
   
   
/**
     * @brief    [Content\Comment]    Language prefix for forms
     */
   
public static $formLangPrefix = 'blog_entry_';
   
   
/**
     * @brief    Icon
     */
   
public static $icon = 'file-text';
   
   
/**
     * @brief    The map of permission columns
     */
   
public static $permissionMap = array(
           
'view'                 => 'view',
           
'read'                => 2,
           
'add'                => 3,
           
'reply'                => 4,
    );
   
   
/**
     * @brief    [Content]    Key for hide reasons
     */
   
public static $hideLogKey = 'blog-entry';
   
   
/**
     * @brief    [CoverPhoto]    Storage extension
     */
   
public static $coverPhotoStorageExtension = 'blog_Entries';
   
   
/**
     * @brief    Use a default cover photo
     */
   
public static $coverPhotoDefault = true;
   
   
/**
     * Set the title
     *
     * @param    string    $title    Title
     * @return    void
     */
   
public function set_name( $name )
    {
       
$this->_data['name'] = $name;
       
$this->_data['name_seo'] = \IPS\Http\Url\Friendly::seoTitle( $name );
    }

   
/**
     * Get SEO name
     *
     * @return    string
     */
   
public function get_name_seo()
    {
        if( !
$this->_data['name_seo'] )
        {
           
$this->name_seo    = \IPS\Http\Url\Friendly::seoTitle( $this->name );
           
$this->save();
        }

        return
$this->_data['name_seo'] ?: \IPS\Http\Url\Friendly::seoTitle( $this->name );
    }

   
/**
     * Get the album HTML, if there is one associated
     *
     * @return    string
     */
   
public function get__album()
    {
        if( \
IPS\Application::appIsEnabled( 'gallery' ) AND $this->gallery_album )
        {
            try
            {
               
$album = \IPS\gallery\Album::loadAndCheckPerms( $this->gallery_album );
   
                \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'gallery.css', 'gallery', 'front' ) );
   
                if ( \
IPS\Theme::i()->settings['responsive'] )
                {
                    \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'gallery_responsive.css', 'gallery', 'front' ) );
                }
   
                return \
IPS\Theme::i()->getTemplate( 'browse', 'gallery', 'front' )->miniAlbum( $album );
            }
            catch( \
OutOfRangeException $e ){}
            catch( \
UnderflowException $e ){}
        }
   
        return
'';
    }
   
   
/**
     * @brief    Cached URLs
     */
   
protected $_url    = array();
   
   
/**
     * @brief    URL Base
     */
   
public static $urlBase = 'app=blog&module=blogs&controller=entry&id=';
   
   
/**
     * @brief    URL Base
     */
   
public static $urlTemplate = 'blog_entry';
   
   
/**
     * @brief    SEO Title Column
     */
   
public static $seoTitleColumn = 'name_seo';
   
   
/**
     * Can view this entry
     *
     * @param    \IPS\Member|NULL    $member        The member or NULL for currently logged in member.
     * @return    bool
     */
   
public function canView( $member = NULL )
    {
       
$member = $member ?: \IPS\Member::loggedIn();
       
       
$return = parent::canView( $member );

        if (
$this->status == 'draft' AND !static::canViewHiddenItems( $member, $this->container() ) AND !in_array( $this->container()->id, array_keys( \IPS\blog\Blog::loadByOwner( $member ) ) ) )
        {
           
$return = FALSE;
        }
       
       
/* Is this a future publish entry and we are the owner of the blog? */
       
if ( $this->status == 'draft' AND $this->is_future_entry == 1 AND in_array( $this->container()->id, array_keys( \IPS\blog\Blog::loadByOwner( $member ) ) ) )
        {
           
$return = TRUE;
        }
       
       
/* Club blog */
       
if ( $club = $this->container()->club() )
        {
            if ( !
$club->canRead( $member ) )
            {
                return
FALSE;
            }
        }

       
/* Private blog */
       
if( $this->container()->social_group != 0 AND $this->container()->owner() != $member )
        {
           
/* This will throw an exception of the row does not exist */
           
try
            {
                if( !
$member->member_id )
                {
                    return
FALSE;
                }

               
$member    = \IPS\Db::i()->select( '*', 'core_sys_social_group_members', array( 'group_id=? AND member_id=?', $this->container()->social_group, $member->member_id ) )->first();
            }
            catch( \
UnderflowException $e )
            {
                return
FALSE;
            }
        }
       
        return
$return;
    }
   
   
/**
     * Get items with permisison check
     *
     * @param    array        $where                Where clause
     * @param    string        $order                MySQL ORDER BY clause (NULL to order by date)
     * @param    int|array    $limit                Limit clause
     * @param    string|NULL    $permissionKey        A key which has a value in the permission map (either of the container or of this class) matching a column ID in core_permission_index or NULL to ignore permissions
     * @param    mixed        $includeHiddenItems    Include hidden items? NULL to detect if currently logged in member has permission, -1 to return public content only, TRUE to return unapproved content and FALSE to only return unapproved content the viewing member submitted
     * @param    int            $queryFlags            Select bitwise flags
     * @param    \IPS\Member    $member                The member (NULL to use currently logged in member)
     * @param    bool        $joinContainer        If true, will join container data (set to TRUE if your $where clause depends on this data)
     * @param    bool        $joinComments        If true, will join comment data (set to TRUE if your $where clause depends on this data)
     * @param    bool        $joinReviews        If true, will join review data (set to TRUE if your $where clause depends on this data)
     * @param    bool        $countOnly            If true will return the count
     * @param    array|null    $joins                Additional arbitrary joins for the query
     * @param    mixed        $skipPermission        If you are getting records from a specific container, pass the container to reduce the number of permission checks necessary or pass TRUE to skip conatiner-based permission. You must still specify this in the $where clause
     * @param    bool        $joinTags            If true, will join the tags table
     * @param    bool        $joinAuthor            If true, will join the members table for the author
     * @param    bool        $joinLastCommenter    If true, will join the members table for the last commenter
     * @param    bool        $showMovedLinks        If true, moved item links are included in the results
     * @return    \IPS\Patterns\ActiveRecordIterator|int
     */
   
public static function getItemsWithPermission( $where=array(), $order=NULL, $limit=10, $permissionKey='read', $includeHiddenItems=\IPS\Content\Hideable::FILTER_AUTOMATIC, $queryFlags=0, \IPS\Member $member=NULL, $joinContainer=FALSE, $joinComments=FALSE, $joinReviews=FALSE, $countOnly=FALSE, $joins=NULL, $skipPermission=FALSE, $joinTags=TRUE, $joinAuthor=TRUE, $joinLastCommenter=TRUE, $showMovedLinks=FALSE )
    {
        if (
in_array( $permissionKey, array( 'view', 'read' ) ) )
        {
           
$joinContainer = TRUE;
                       
           
$member = $member ?: \IPS\Member::loggedIn();
            if (
$member->member_id )
            {
               
$where[] = array( '( blog_blogs.blog_member_id=' . $member->member_id . ' OR ( ' . \IPS\Content::socialGroupGetItemsWithPermissionWhere( 'blog_blogs.blog_social_group', $member ) . ' ) OR blog_blogs.blog_social_group IS NULL )' );
            }
            else
            {
               
$where[] = array( "(" . \IPS\Content::socialGroupGetItemsWithPermissionWhere( 'blog_blogs.blog_social_group', $member ) . " OR blog_blogs.blog_social_group IS NULL )" );
            }
           
            if ( \
IPS\Settings::i()->clubs )
            {
               
$joins[] = array( 'from' => 'core_clubs', 'where' => 'core_clubs.id=blog_blogs.blog_club_id' );
                if (
$member->member_id )
                {
                   
$where[] = array( '( blog_blogs.blog_club_id IS NULL OR ' . \IPS\Db::i()->in( 'blog_blogs.blog_club_id', $member->clubs() ) . ' OR core_clubs.type=? OR core_clubs.type=?  OR core_clubs.type=?)', \IPS\Member\Club::TYPE_PUBLIC, \IPS\Member\Club::TYPE_READONLY, \IPS\Member\Club::TYPE_OPEN, );
                }
                else
                {
                   
$where[] = array( '( blog_blogs.blog_club_id IS NULL OR core_clubs.type=? OR core_clubs.type=? OR core_clubs.type=? )', \IPS\Member\Club::TYPE_PUBLIC, \IPS\Member\Club::TYPE_READONLY, \IPS\Member\Club::TYPE_OPEN, );
                }
            }
        }
        return
parent::getItemsWithPermission( $where, $order, $limit, $permissionKey, $includeHiddenItems, $queryFlags, $member, $joinContainer, $joinComments, $joinReviews, $countOnly, $joins, $skipPermission, $joinTags, $joinAuthor, $joinLastCommenter, $showMovedLinks );
    }
   
   
/**
     * Additional WHERE clauses for Follow view
     *
     * @param    bool        $joinContainer        If true, will join container data (set to TRUE if your $where clause depends on this data)
     * @param    array        $joins                Other joins
     * @return    array
     */
   
public static function followWhere( &$joinContainer, &$joins )
    {
       
$joinContainer = TRUE;
        if ( \
IPS\Member::loggedIn()->member_id )
        {
           
$where = array( array( '( blog_blogs.blog_social_group IS NULL OR blog_blogs.blog_member_id=' . \IPS\Member::loggedIn()->member_id . ' OR ( ' . \IPS\Content::socialGroupGetItemsWithPermissionWhere( 'blog_blogs.blog_social_group', \IPS\Member::loggedIn() ) . ' ) )' ) );
        }
        else
        {
           
$where = array( \IPS\Content::socialGroupGetItemsWithPermissionWhere( 'blog_blogs.blog_social_group', \IPS\Member::loggedIn() ) );
        }
       
        if ( \
IPS\Settings::i()->clubs )
        {
           
$joins[] = array( 'from' => 'core_clubs', 'where' => 'core_clubs.id=blog_blogs.blog_club_id' );
            if ( \
IPS\Member::loggedIn()->member_id )
            {
               
$where[] = array( '( blog_blogs.blog_club_id IS NULL OR ' . \IPS\Db::i()->in( 'blog_blogs.blog_club_id', \IPS\Member::loggedIn()->clubs() ) . ' OR core_clubs.type=? OR core_clubs.type=? )', \IPS\Member\Club::TYPE_PUBLIC, \IPS\Member\Club::TYPE_READONLY );
            }
            else
            {
               
$where[] = array( '( blog_blogs.blog_club_id IS NULL OR core_clubs.type=? OR core_clubs.type=? )', \IPS\Member\Club::TYPE_PUBLIC, \IPS\Member\Club::TYPE_READONLY );
            }
        }

        return
array_merge( parent::followWhere( $joinContainer, $joins ), $where );
    }
   
   
/**
     * Get elements for add/edit form
     *
     * @param    \IPS\Content\Item|NULL    $item        The current item if editing or NULL if creating
     * @param    int                        $container    Container (e.g. forum) ID, if appropriate
     * @return    array
     */
   
public static function formElements( $item=NULL, \IPS\Node\Model $container=NULL )
    {
       
$return = parent::formElements( $item, $container );
       
$return['entry'] = new \IPS\Helpers\Form\Editor( 'blog_entry_content', $item ? $item->content : NULL, TRUE, array( 'app' => 'blog', 'key' => 'Entries', 'autoSaveKey' => ( $item === NULL ) ? 'blog-entry-' . $container->id : 'blog-edit-' . $item->id, 'attachIds' => ( $item === NULL ? NULL : array( $item->id ) ) ) );

       
/* Gallery album association */
       
if( \IPS\Application::appIsEnabled( 'gallery' ) )
        {
           
$return['album']    = new \IPS\Helpers\Form\Node( 'entry_gallery_album', ( $item AND $item->gallery_album ) ? $item->gallery_album : NULL, FALSE, array(
                   
'url'                    => \IPS\Http\Url::internal( 'app=blog&module=blogs&controller=submit', 'front', 'blog_submit' ),
                   
'class'                    => 'IPS\gallery\Album',
                   
'permissionCheck'        => 'add',
            ) );
        }

       
$return['image'] = new \IPS\Helpers\Form\Upload( 'blog_entry_cover_photo', ( ( $item AND $item->cover_photo ) ? \IPS\File::get( 'blog_Entries', $item->cover_photo ) : NULL ), FALSE, array( 'storageExtension' => 'blog_Entries', 'image' => array( 'maxWidth' => 4800, 'maxHeight' => 4800 ) ) );
       
       
$return['publish'] = new \IPS\Helpers\Form\YesNo( 'blog_entry_publish', $item ? $item->status : TRUE, FALSE, array( 'togglesOn' => array( 'blog_entry_date' ) ) );
       
       
/* Publish date needs to go near the bottom */
       
$date = NULL;
        if ( isset(
$return['date'] ) )
        {
           
$date = $return['date'];
            unset(
$return['date'] );
           
           
$return['date'] = $date;
        }
       
       
/* Poll always needs to go on the end */
       
$poll = NULL;
        if ( isset(
$return['poll'] ) )
        {
           
$poll = $return['poll'];
            unset(
$return['poll'] );
           
           
$return['poll'] = $poll;
        }
       
       
        return
$return;
    }
   
   
/**
     * Process create/edit form
     *
     * @param    array                $values    Values from form
     * @return    void
     */
   
public function processForm( $values )
    {
       
parent::processForm( $values );
       
        if ( !
$this->_new )
        {
           
$oldContent = $this->content;
        }
       
$this->content    = $values['blog_entry_content'];
        if ( !
$this->_new )
        {
           
$this->sendAfterEditNotifications( $oldContent );
        }
       
       
$this->status = $values['blog_entry_publish'] ? 'published' : 'draft';
       
        if ( isset(
$values['blog_entry_date'] ) )
        {
           
$this->date = ( $values['blog_entry_date'] AND $values['blog_entry_publish'] ) ? $values['blog_entry_date']->getTimestamp() : time();
        }

       
$this->cover_photo = (string) $values['blog_entry_cover_photo'];
       
       
/* Gallery album association */
       
if( \IPS\Application::appIsEnabled( 'gallery' ) AND $values['entry_gallery_album'] instanceof \IPS\gallery\Album )
        {
           
$this->gallery_album = $values['entry_gallery_album']->_id;
        }
        else
        {
           
$this->gallery_album = NULL;
        }
       
        if (
$this->date > time() )
        {
           
$this->status = 'draft';
        }
       
       
/* Ping */
       
$this->container()->ping();
    }
   
   
/**
     * Can a given member create this type of content?
     *
     * @param    \IPS\Member    $member        The member
     * @param    \IPS\Node\Model|NULL    $container    Container (e.g. forum), if appropriate
     * @param    bool        $showError    If TRUE, rather than returning a boolean value, will display an error
     * @return    bool
     */
   
public static function canCreate( \IPS\Member $member, \IPS\Node\Model $container=NULL, $showError=FALSE )
    {
       
parent::canCreate( $member, $container, $showError );
       
       
$return = TRUE;

       
$blogs = \IPS\blog\Blog::loadByOwner( $member );

        if (
$container )
        {
            if (
$club = $container->club() )
            {
               
$return = $club->isModerator( $member );
               
$error = 'no_module_permission';
            }
            elseif ( !
in_array( $container->id, array_keys( $blogs ) ) )
            {
               
$return = FALSE;
               
$error = 'no_module_permission';
            }
           
            if (
$container->disabled )
            {
               
$return = FALSE;
               
$error = 'no_module_permission';
            }
        }
        else
        {
            if( !
count( $blogs ) )
            {
               
$return = FALSE;
               
$error = 'no_module_permission';
            }
        }
               
       
/* Return */
       
if ( $showError and !$return )
        {
            \
IPS\Output::i()->error( $error, '1B203/1', 403, '' );
        }
       
        return
$return;
    }
   
   
/**
     * Process created object AFTER the object has been created
     *
     * @param    \IPS\Content\Comment|NULL    $comment    The first comment
     * @param    array                        $values        Values from form
     * @return    void
     */
   
protected function processAfterCreate( $comment, $values )
    {
       
parent::processAfterCreate( $comment, $values );

        \
IPS\File::claimAttachments( 'blog-entry-' . $this->container()->id, $this->id );

        if (
$this->status == 'published' )
        {
           
$blog                        = $this->container();
           
$lastUpdateColumn            = $blog::$databaseColumnMap['date'];
           
$blog->$lastUpdateColumn    = time();
           
$blog->save();
        }
    }

   
/**
     * Syncing to run when publishing something previously pending publishing
     *
     * @param    \IPS\Member|NULL|FALSE    $member    The member doing the action (NULL for currently logged in member, FALSE for no member)
     * @return    void
     */
   
public function onPublish( $member )
    {
       
$this->status = 'published';
       
$this->save();
       
       
parent::onPublish( $member );
       
       
/* The blog system is slightly different from the \Content future entry stuff. Future entries are treated as drafts,
            so do count towards entries but parent::onPublish will try and increment item count again after publish */
       
$this->container()->resetCommentCounts();
       
$this->container()->save();
    }
   
   
/**
     * Syncing to run when unpublishing an item (making it a future dated entry when it was already published)
     *
     * @param    \IPS\Member|NULL|FALSE    $member    The member doing the action (NULL for currently logged in member, FALSE for no member)
     * @return    void
     */
   
public function onUnpublish( $member )
    {
       
$this->status = 'draft';
       
$this->save();
       
       
parent::onUnpublish( $member );
       
       
/* The blog system is slightly different from the \Content future entry stuff. Future entries are treated as drafts,
            so do count towards entries but parent::onUnpublish will try and decrement item count after unpublish */
       
$this->container()->resetCommentCounts();
       
$this->container()->save();
    }
   
   
/**
     * Can comment?
     *
     * @param    \IPS\Member\NULL    $member    The member (NULL for currently logged in member)
     * @return    bool
     */
   
public function canComment( $member=NULL )
    {
       
$member = $member ?: \IPS\Member::loggedIn();
        return
parent::canComment( $member ) and $member->group['g_blog_allowcomment'];
    }
   
   
/**
     * Can set items to be published in the future?
     *
     * @param    \IPS\Member|NULL        $member            The member to check for (NULL for currently logged in member)
     * @param   \IPS\Node\Model|null    $container      Container
     * @return    bool
     */
   
public static function canFuturePublish( $member=NULL, \IPS\Node\Model $container = NULL )
    {
       
$member = $member ?: \IPS\Member::loggedIn();
        return (boolean)
$member->member_id > 0;
    }
   
   
/**
     * Check Moderator Permission
     *
     * @param    string                        $type        'edit', 'hide', 'unhide', 'delete', etc.
     * @param    \IPS\Member|NULL            $member        The member to check for or NULL for the currently logged in member
     * @param    \IPS\Node\Model|NULL        $container    The container
     * @return    bool
     */
   
public static function modPermission( $type, \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
    {
       
$member = $member ?: \IPS\Member::loggedIn();
       
$result = parent::modPermission( $type, $member, $container );
       
        if (
$result !== TRUE )
        {
            if (
in_array( $type, array( 'edit', 'delete', 'lock', 'unlock' ) ) and $container and $container->member_id === $member->member_id )
            {
               
$result = $member->group['g_blog_allowownmod'];
            }
        }
       
        return
$result;
    }

   
/**
     * Get template for content tables
     *
     * @return    callable
     */
   
public static function contentTableTemplate()
    {
        return array( \
IPS\Theme::i()->getTemplate( 'global', 'blog', 'front' ), 'rows' );
    }

   
/**
     * Search Index Permissions
     *
     * @return    string    Comma-delimited values or '*'
     *     @li            Number indicates a group
     *    @li            Number prepended by "m" indicates a member
     *    @li            Number prepended by "s" indicates a social group
     */
   
public function searchIndexPermissions()
    {
        if (
$this->status == 'draft' )
        {
            return
'0';
        }
       
        return
parent::searchIndexPermissions();
    }

   
/**
     * WHERE clause for getting items for sitemap (permissions are already accounted for)
     *
     * @return    array
     */
   
public static function sitemapWhere()
    {
        return array( array(
'blog_entries.entry_is_future_entry=0 AND blog_entries.entry_status!=?', 'draft' ) );
    }
   
   
/**
     * Get output for API
     *
     * @param    \IPS\Member|NULL    $authorizedMember    The member making the API request or NULL for API Key / client_credentials
     * @return    array
     * @apiresponse    int                        id                ID number
     * @apiresponse    string                    title            Title
     * @apiresponse    \IPS\blog\Blog            blog            Blog
     * @apiresponse    \IPS\Member                author            The member that created the entry
     * @apiresponse    bool                    draft            If this entry is a draft
     * @apiresponse    datetime                date            Date
     * @apiresponse    string                    entry            Entry content
     * @apiresponse    int                        comments        Number of comments
     * @apiresponse    int                        views            Number of posts
     * @apiresponse    string                    prefix            The prefix tag, if there is one
     * @apiresponse    [string]                tags            The tags
     * @apiresponse    bool                    locked            Entry is locked
     * @apiresponse    bool                    hidden            Entry is hidden
     * @apiresponse    bool                    future            Will be published at a future date?
     * @apiresponse    bool                    pinned            Entry is pinned
     * @apiresponse    bool                    featured        Entry is featured
     * @apiresponse    \IPS\Poll                poll            Poll data, if there is one
     * @apiresponse    string                    url                URL
     * @apiresponse    float                    rating            Average Rating
     */
   
public function apiOutput( \IPS\Member $authorizedMember = NULL )
    {
        return array(
           
'id'            => $this->id,
           
'title'            => $this->name,
           
'blog'            => $this->container()->apiOutput( $authorizedMember ),
           
'author'        => $this->author()->apiOutput( $authorizedMember ),
           
'draft'            => $this->status == 'draft',
           
'date'            => \IPS\DateTime::ts( $this->date )->rfc3339(),
           
'entry'            => $this->content(),
           
'comments'        => $this->num_comments,
           
'views'            => $this->views,
           
'prefix'        => $this->prefix(),
           
'tags'            => $this->tags(),
           
'locked'        => (bool) $this->locked(),
           
'hidden'        => (bool) $this->hidden(),
           
'future'        => $this->isFutureDate(),
           
'pinned'        => (bool) $this->mapped('pinned'),
           
'featured'        => (bool) $this->mapped('featured'),
           
'poll'            => $this->poll_state ? \IPS\Poll::load( $this->poll_state )->apiOutput( $authorizedMember ) : null,
           
'url'            => (string) $this->url(),
           
'rating'        => $this->averageRating(),
        );
    }
   
   
/**
     * Reaction Type
     *
     * @return    string
     */
   
public static function reactionType()
    {
        return
'entry_id';
    }
   
   
/**
     * Supported Meta Data Types
     *
     * @return    array
     */
   
public static function supportedMetaDataTypes()
    {
        return array(
'core_FeaturedComments', 'core_ContentMessages' );
    }
   
   
/**
     * Can perform an action on a message
     *
     * @param    string                The action
     * @param    \IPS\Member|NULL    The member, or NULL for currently logged in
     * @return    bool
     */
   
public function canOnMessage( $action, \IPS\Member $member = NULL )
    {
       
$return = parent::canOnMessage( $action, $member );
       
        if (
$return === FALSE )
        {
           
$member = $member ?: \IPS\Member::loggedIn();
           
            if (
$member->member_id AND $member->member_id === $this->author()->member_id AND $member->group['g_blog_allowownmod'] )
            {
               
$return = TRUE;
            }
        }
       
        return
$return;
    }
   
   
/**
     * Can Feature a Comment
     *
     * @param    \IPS\Member|NULL    The member, or NULL for currently logged in
     * @return    bool
     */
   
public function canFeatureComment( \IPS\Member $member = NULL )
    {
       
$return = parent::canFeatureComment( $member );
       
        if (
$return === FALSE )
        {
           
$member = $member ?: \IPS\Member::loggedIn();
           
            if (
$member->member_id AND $member->member_id === $this->author()->member_id AND $member->group['g_blog_allowownmod'] )
            {
               
$return = TRUE;
            }
        }
       
        return
$return;
    }
   
   
/**
     * Can Unfeature a Comment
     *
     * @param    \IPS\Member|NULL    The member, or NULL for currently logged in
     * @return    bool
     */
   
public function canUnfeatureComment( \IPS\Member $member = NULL )
    {
       
$return = parent::canUnfeatureComment( $member );
       
        if (
$return === FALSE )
        {
           
$member = $member ?: \IPS\Member::loggedIn();
           
            if (
$member->member_id AND $member->member_id === $this->author()->member_id AND $member->group['g_blog_allowownmod'] )
            {
               
$return = TRUE;
            }
        }
       
        return
$return;
    }
   
       
   
/**
     * Returns the content images
     *
     * @param    int|null    $limit        Number of attachments to fetch, or NULL for all
     *
     * @return    array|NULL
     * @throws    \BadMethodCallException
     */
   
public function contentImages( $limit = NULL )
    {
       
$idColumn = static::$databaseColumnId;
       
$internal = NULL;
       
$attachments = array();
       
        if ( isset( static::
$databaseColumnMap['content'] ) )
        {
           
$internal = \IPS\Db::i()->select( 'attachment_id', 'core_attachments_map', array( 'location_key=? and id1=?', 'blog_Entries', $this->$idColumn ) );
        }

        if (
$internal )
        {
            foreach( \
IPS\Db::i()->select( '*', 'core_attachments', array( array( 'attach_id IN(?)', $internal ), array( 'attach_is_image=1' ) ), 'attach_id ASC', $limit ) as $row )
            {
               
$attachments[] = array( 'core_Attachment' => $row['attach_location'] );
            }
        }
       
        return
count( $attachments ) ? $attachments : NULL;
    }

   
/**
     * Get content for embed
     *
     * @param    array    $params    Additional parameters to add to URL
     * @return    string
     */
   
public function embedContent( $params )
    {
        \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'embed.css', 'blog', 'front' ) );
        return \
IPS\Theme::i()->getTemplate( 'global', 'blog' )->embedEntry( $this, $this->container(), $this->url()->setQueryString( $params ) );
    }
}