Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+189 -28
View File
@@ -12,7 +12,7 @@
namespace IPS\blog;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -68,16 +68,31 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
* @brief [Node] Description suffix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}_{$descriptionLangSuffix}" as the key
*/
public static $descriptionLangSuffix = '_desc';
/**
* @brief [Node] Parent Node ID Database Column
*/
public static $parentNodeColumnId = 'category_id';
/**
* @brief [Node] Moderator Permission
*/
public static $modPerm = 'blogs';
/**
* @brief [Node] Maximum results to display at a time in any node helper form elements. Useful for user-submitted node types when there may be a lot. NULL for no limit.
*/
public static $maxFormHelperResults = 2000;
/**
* @brief Content Item Class
*/
public static $contentItemClass = 'IPS\blog\Entry';
/**
* @brief Category
*/
protected $category;
/**
* @brief Icon
@@ -152,16 +167,55 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
return FALSE;
}
/**
* Check permissions on any node
*
* For example - can be used to check if the user has
* permission to create content in any node to determine
* if there should be a "Submit" button
*
* @param mixed $permission A key which has a value in static::$permissionMap['view'] matching a column ID in core_permission_index
* @param \IPS\Member|\IPS\Member\Group|NULL $member The member or group to check (NULL for currently logged in member)
* @param array $where Additional WHERE clause
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will return TRUE if "Post Before Registering" feature is enabled
* @return bool
* @throws \OutOfBoundsException If $permission does not exist in static::$permissionMap
*/
public static function canOnAny( $permission, $member=NULL, $where = array(), $considerPostBeforeRegistering = TRUE )
{
$member = $member ?: \IPS\Member::loggedIn();
/* Posts per day - we need to do this independently here, because Blogs do not implement the Permissions interface */
if ( \in_array( $permission, array( 'add', 'reply' ) ) )
{
$checkPostsPerDay = TRUE;
if ( isset( static::$contentItemClass ) )
{
$contentClass = static::$contentItemClass;
$checkPostsPerDay = $contentClass::$checkPostsPerDay;
}
if ( $checkPostsPerDay === TRUE AND $member->checkPostsPerDay() === FALSE )
{
return FALSE;
}
}
/* This will always return true, but we always want to bubble up anyway */
return parent::canOnAny( $permission, $member, $where, $considerPostBeforeRegistering );
}
/**
* Check permissions
*
* @param mixed $permission A key which has a value in static::$permissionMap['view'] matching a column ID in core_permission_index
* @param \IPS\Member|\IPS\Member\Group|NULL $member The member or group to check (NULL for currently logged in member)
* @param mixed $permission A key which has a value in static::$permissionMap['view'] matching a column ID in core_permission_index
* @param \IPS\Member|\IPS\Member\Group|NULL $member The member or group to check (NULL for currently logged in member)
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will return TRUE if "Post Before Registering" feature is enabled
* @return bool
* @throws \OutOfBoundsException If $permission does not exist in static::$permissionMap
*/
public function can( $permission, $member=NULL )
public function can( $permission, $member=NULL, $considerPostBeforeRegistering = TRUE )
{
/* Load member */
if ( $member === NULL )
@@ -169,6 +223,18 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
$member = \IPS\Member::loggedIn();
}
/* If the member has hit their posting limits, then just stop here. */
switch( $permission )
{
case 'add':
case 'reply':
if ( $member->checkPostsPerDay() === FALSE )
{
return FALSE;
}
break;
}
if ( $club = $this->club() )
{
switch ( $permission )
@@ -227,7 +293,7 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
}
}
return parent::can( $permission, $member );
return parent::can( $permission, $member, $considerPostBeforeRegistering );
}
/**
@@ -314,7 +380,7 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
foreach ( \IPS\Db::i()->select( 'data', 'core_queue', array( 'app=? AND `key`=?', 'core', 'DeleteOrMoveContent' ) ) as $row )
{
$data = json_decode( $row, TRUE );
if ( $data['class'] === get_class( $this ) and $data['id'] == $this->_id )
if ( $data['class'] === \get_class( $this ) and $data['id'] == $this->_id )
{
return FALSE;
}
@@ -327,12 +393,23 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
* [Node] Add/Edit Form
*
* @param \IPS\Helpers\Form $form The form
* @param bool $public Whether this is a public form or not
* @return void
*/
public function form( &$form, $public=FALSE )
{
if( $public )
{
$form->addTab( 'blog_settings' );
if ( !$this->id OR \IPS\Member::loggedIn()->modPermission('can_mod_blogs') )
{
$form->add( new \IPS\Helpers\Form\Node( 'blog_category_id', $this->id ? $this->category_id : NULL, TRUE, array(
'class' => '\IPS\blog\Category',
'subnodes' => FALSE,
), NULL, NULL, NULL, 'blog_category_id' ) );
}
$form->add( new \IPS\Helpers\Form\Text( 'blog_name', $this->id ? $this->_title : NULL, TRUE, array(), NULL, NULL, NULL, 'blog_name' ) );
$form->add( new \IPS\Helpers\Form\Editor( 'blog_desc', $this->id ? $this->description : NULL, FALSE, array( 'app' => 'blog', 'key' => 'Blogs', 'autoSaveKey' => ( $this->id ? "blogs-blog-{$this->id}" : "blogs-new-blog" ), 'attachIds' => $this->id ? array( $this->id, NULL, 'description' ) : NULL, 'minimize' => 'blog_desc_placeholder' ), NULL, NULL, NULL, 'blog_desc_wrap' ) );
@@ -345,7 +422,13 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
}
else
{
$form->addHeader( 'blog_settings' );
if( $this->id )
{
$form->add( new \IPS\Helpers\Form\Node( 'blog_category_id', $this->category_id, TRUE, array(
'class' => '\IPS\blog\Category',
'subnodes' => FALSE,
), NULL, NULL, NULL, 'blog_category_id' ) );
}
$groups = array();
foreach ( \IPS\Member\Group::groups() as $k => $v )
@@ -370,14 +453,14 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
{
if ( \IPS\Request::i()->blog_type === 'member' )
{
if( !is_object( $member ) or !$member->member_id )
if( !\is_object( $member ) or !$member->member_id )
{
throw new \InvalidArgumentException( 'no_blog_author_selected' );
}
}
},
NULL, NULL, 'blog_member_id' ) );
$form->add( new \IPS\Helpers\Form\Select( 'blog_groupblog_ids', $this->id ? explode( ',', $this->groupblog_ids ) : array(), FALSE, array( 'options' => $groups, 'multiple' => TRUE ), NULL, NULL, NULL, 'blog_groupblog_ids' ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'blog_groupblog_name', NULL, FALSE, array( 'app' => 'blog', 'key' => ( $this->id ? "blogs_groupblog_name_{$this->id}" : NULL ) ), function( $value ) {
if ( \IPS\Request::i()->blog_type === 'group' )
@@ -458,6 +541,13 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
{
$form->add( new \IPS\Helpers\Form\YesNo( 'blog_enable_rss', $this->id ? $this->settings['allowrss'] : TRUE ) );
}
/* Categories */
if ( $public and $this->id )
{
$form->addTab( 'blog_entry_categories' );
$form->addHtml( \IPS\Theme::i()->getTemplate( 'view', 'blog', 'front' )->manageCategories( $this ) );
}
}
/**
@@ -468,7 +558,13 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
*/
public function formatFormValues( $values )
{
if( isset( $values['blog_type'] ) and $values['blog_type'] == 'member' and isset( $values['blog_member_id'] ) and is_object( $values['blog_member_id'] ) )
if ( isset( $values['blog_category_id'] ) and $values[ 'blog_category_id' ] )
{
$values[ 'blog_category_id' ] = \intval( $values[ 'blog_category_id' ]->id );
}
if( isset( $values['blog_type'] ) and $values['blog_type'] == 'member' and isset( $values['blog_member_id'] ) and \is_object( $values['blog_member_id'] ) )
{
$values['blog_member_id']->create_menu = NULL;
$values['blog_member_id']->save();
@@ -521,6 +617,7 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
if ( !$this->id )
{
$this->category_id = $values[ 'blog_category_id' ];
$this->save();
if( $values['blog_member_id'] )
@@ -544,7 +641,7 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
if ( $fieldKey === 'blog_name' )
{
$values['seo_name'] = \IPS\Http\Url\Friendly::seoTitle( ( is_array( $values[ $fieldKey ] ) ) ? $values[ $fieldKey ][ \IPS\Lang::defaultLanguage() ] : $values[ $fieldKey ] );
$values['seo_name'] = \IPS\Http\Url\Friendly::seoTitle( ( \is_array( $values[ $fieldKey ] ) ) ? $values[ $fieldKey ][ \IPS\Lang::defaultLanguage() ] : $values[ $fieldKey ] );
}
unset( $values[ $fieldKey ] );
@@ -563,7 +660,7 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
{
try
{
$feed = \IPS\blog\Blog\Feed::constructFromData( \IPS\Db::i()->select( '*', 'blog_rss_import', array( 'rss_blog_id=?', $this->id ) )->first() );
$feed = \IPS\core\Rss\Import::constructFromData( \IPS\Db::i()->select( '*', 'core_rss_import', array( 'rss_import_class=? AND rss_import_node_id=?', 'IPS\\blog\\Entry', $this->id ) )->first() );
}
catch ( \UnderflowException $e )
{
@@ -684,7 +781,7 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
public function get_seo_name()
{
/* If we have the seo_name, just return it - don't query the language string */
if( $this->_data['seo_name'] )
if( isset( $this->_data['seo_name'] ) and $this->_data['seo_name'] )
{
return $this->_data['seo_name'];
}
@@ -796,7 +893,7 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
$memberIds[] = $member;
}
if( count( $memberIds ) )
if( \count( $memberIds ) )
{
foreach( \IPS\Db::i()->select( '*', 'core_members', 'member_id IN(' . implode( ',', $memberIds ) . ')' ) as $member )
{
@@ -1000,6 +1097,23 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
return array( \IPS\Theme::i()->getTemplate( 'browse', 'blog' ), 'rows' );
}
/**
* Save Changed Columns
*
* @return void
*/
public function save()
{
$new = $this->_new;
parent::save();
if ( $new )
{
\IPS\Api\Webhook::fire( 'blogBlog_create', $this, $this->webhookFilters() );
}
}
/**
* [ActiveRecord] Delete Record
*
@@ -1008,7 +1122,12 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
public function delete()
{
/* Delete RSS Imports */
\IPS\Db::i()->delete( 'blog_rss_import', array( "rss_blog_id=?", $this->_id ) );
try
{
$import = \IPS\core\Rss\Import::constructFromData( \IPS\Db::i()->select( '*', 'core_rss_import', array( "rss_import_class=? AND rss_import_node_id=?", 'IPS\\blog\\Entry', $this->_id ) )->first() );
$import->delete();
}
catch( \UnderflowException $e ) { }
/* Delete Language Strings */
foreach ( array( 'blog_groupblog_name' => "blogs_groupblog_name_{$this->id}" ) as $fieldKey => $langKey )
@@ -1088,15 +1207,16 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
*
* @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 name Name
* @apiresponse string description Description
* @apiresponse \IPS\Member owner If the blog is owned by a single member, the member
* @apiresponse [\IPS\Member\Group] groups If the blog is owned by groups, an array of the groups
* @apiresponse bool pinned If the blog is pinned
* @apiresponse int entries Number of entries
* @apiresponse int comments Number of comments
* @apiresponse string url URL
* @apiresponse int id ID number
* @apiresponse string name Name
* @apiresponse string description Description
* @apiresponse \IPS\Member owner If the blog is owned by a single member, the member
* @apiresponse [\IPS\Member\Group] groups If the blog is owned by groups, an array of the groups
* @apiresponse bool pinned If the blog is pinned
* @apiresponse int entries Number of entries
* @apiresponse int comments Number of comments
* @apiresponse string url URL
* @apiresponse \IPS\blog\Category|NULL category Category, or NULL for club blogs
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
@@ -1113,17 +1233,26 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
}
}
return array(
$return = array(
'id' => $this->id,
'name' => $this->_title,
'description' => \IPS\Member::loggedIn()->language()->addToStack("blogs_blog_{$this->id}_desc"),
'description' => $this->member_id ? \IPS\Text\Parser::removeLazyLoad( $this->desc ) : \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $this->_id . static::$descriptionLangSuffix , NULL, array( 'removeLazyLoad' => TRUE ) ),
'owner' => $this->member_id ? $this->owner()->apiOutput( $authorizedMember ) : null,
'groups' => $groups,
'pinned' => (bool) $this->pinned,
'entries' => $this->count_entries,
'comments' => $this->count_comments,
'url' => (string) $this->url()
'url' => (string) $this->url(),
'category' => NULL
);
try
{
$return['category'] = $this->category()->apiOutput();
}
catch( \OutOfRangeException $e ){}
return $return;
}
/**
@@ -1234,7 +1363,39 @@ class _Blog extends \IPS\Node\Model implements \IPS\Node\Ratings, \IPS\Content\E
}
$where[] = array( \IPS\Db::i()->in( 'status', $statuses ) );
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'blog_blogs', $where )->join( 'core_clubs_memberships', array( 'club_id=blog_club_id AND member_id=?', $member->member_id ) ), get_called_class() );
return iterator_to_array( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'blog_blogs', $where )->join( 'core_clubs_memberships', array( 'club_id=blog_club_id AND member_id=?', $member->member_id ) ), \get_called_class() ) );
}
}
/**
* Get category
*
* @return \IPS\Node\Model
* @throws \OutOfRangeException|\BadMethodCallException
*/
public function category()
{
if( !$this->category_id )
{
throw new \OutOfRangeException;
}
if ( $this->category === NULL )
{
$this->category = \IPS\blog\Category::load( $this->category_id );
}
return $this->category;
}
/**
* Get last comment time
*
* @note This should return the last comment time for this node only, not for children nodes
* @return \IPS\DateTime|NULL
*/
public function getLastCommentTime()
{
return $this->last_edate ? \IPS\DateTime::ts( $this->last_edate ) : NULL;
}
}
-163
View File
@@ -1,163 +0,0 @@
<?php
/**
* @brief Blog RSS/Atom feed active record
* @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 02 Apr 2014
*/
namespace IPS\blog\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;
}
/**
* Blog RSS/Atom feed active record
*/
class _Feed extends \IPS\Patterns\ActiveRecord
{
/**
* @brief [ActiveRecord] Multiton Store
*/
protected static $multitons;
/**
* @brief [ActiveRecord] Database Table
*/
public static $databaseTable = 'blog_rss_import';
/**
* @brief [ActiveRecord] Database Prefix
*/
public static $databasePrefix = 'rss_';
/**
* Run
*
* @return void
* @throws \IPS\Http\Url\Exception|\RuntimeException
*/
public function run()
{
/* Skip this if the member is restricted from posting */
if( \IPS\Member::load( $this->member )->restrict_post or \IPS\Member::load( $this->member )->members_bitoptions['unacknowledged_warnings'] )
{
return;
}
$previouslyImportedGuids = iterator_to_array( \IPS\Db::i()->select( 'rss_imported_guid', 'blog_rss_imported', array( 'rss_imported_impid=?', $this->id ) ) );
$request = \IPS\Http\Url::external( $this->url )->request();
if ( $this->auth_user or $this->auth_pass )
{
$request = $request->login( $this->auth_user, $this->auth_pass );
}
$request = $request->get();
$container = \IPS\blog\Blog::load( $this->blog_id );
$inserts=array();
$i = 0;
$request = $request->decodeXml();
if( !( $request instanceof \IPS\Xml\Rss1 ) AND !( $request instanceof \IPS\Xml\Rss ) AND !( $request instanceof \IPS\Xml\Atom ) )
{
throw new \RuntimeException( 'rss_import_invalid' );
}
foreach ( $request->articles( $this->id ) as $guid => $article )
{
if ( !in_array( $guid, $previouslyImportedGuids ) )
{
/* Don't post entries in the future */
if( $article['date']->getTimestamp() > time() )
{
$article['date'] = \IPS\DateTime::create();
}
$entry = \IPS\blog\Entry::createItem( \IPS\Member::load( $this->member ), NULL, $article['date'], $container );
$entry->name = $article['title'];
if ( $article['link'] )
{
$rel = array();
if( \IPS\Settings::i()->posts_add_nofollow )
{
$rel['nofollow'] = 'nofollow';
}
if( \IPS\Settings::i()->links_external )
{
$rel['external'] = 'external';
}
$linkRelPart = '';
if ( count ( $rel ) )
{
$linkRelPart = 'rel="' . implode($rel, ' ') . '"';
}
$link = htmlspecialchars( $this->import_show_link, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE );
$article['content'] .= "<br><p><a href='{$article['link']}' {$linkRelPart}>{$link}</a></p>";
}
$entry->content = \IPS\Text\Parser::parseStatic( $article['content'], TRUE, NULL, \IPS\Member::load( $this->member ) );
$entry->status = 'published';
$entry->save();
/* Add to search index */
\IPS\Content\Search\Index::i()->index( $entry );
/* Send notifications */
$entry->sendNotifications();
$entry->setTags( json_decode( $this->tags, TRUE ) );
$inserts[] = array(
'rss_imported_guid' => $guid,
'rss_imported_entry_id' => $entry->id,
'rss_imported_impid'=> $this->id
);
$i++;
if ( $i >= 10 )
{
break;
}
}
}
if( count( $inserts ) )
{
\IPS\Db::i()->insert( 'blog_rss_imported', $inserts );
}
$this->last_import = time();
$this->save();
$container->setLastComment();
$container->save();
}
/**
* [ActiveRecord] Delete Record
*
* @return void
*/
public function delete()
{
\IPS\Db::i()->delete( 'blog_rss_import', array( 'rss_id=?', $this->id ) );
return parent::delete();
}
}
@@ -12,7 +12,7 @@
namespace IPS\blog\Blog;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -26,7 +26,7 @@ class _ProfileTable extends Table
/**
* Constructor
*
* @param \IPS\Http\Url Base URL
* @param \IPS\Http\Url $url Base URL
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL )
+5 -5
View File
@@ -12,7 +12,7 @@
namespace IPS\blog\Blog;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -31,7 +31,7 @@ class _Table extends \IPS\Helpers\Table\Table
/**
* Constructor
*
* @param \IPS\Http\Url Base URL
* @param \IPS\Http\Url $url Base URL
* @return void
*/
public function __construct( \IPS\Http\Url $url=NULL )
@@ -76,17 +76,17 @@ class _Table extends \IPS\Helpers\Table\Table
$subquery = NULL;
/* Check sortBy */
$this->sortBy = in_array( $this->sortBy, $this->sortOptions ) ? $this->sortBy : 'blog_name';
$this->sortBy = \in_array( $this->sortBy, $this->sortOptions ) ? $this->sortBy : 'blog_name';
/* What are we sorting by? */
$sortBy = 'blog_pinned DESC, ' . $this->sortBy . ' ' . ( mb_strtolower( $this->sortDirection ) == 'asc' ? 'asc' : 'desc' );
/* Specify filter in where clause */
$where = isset( $this->where ) ? is_array( $this->where ) ? $this->where : array( $this->where ) : array();
$where = isset( $this->where ) ? \is_array( $this->where ) ? $this->where : array( $this->where ) : array();
$where[] = array( 'blog_disabled=0' );
if ( $this->filter and isset( $this->filters[ $this->filter ] ) )
{
$where[] = is_array( $this->filters[ $this->filter ] ) ? $this->filters[ $this->filter ] : array( $this->filters[ $this->filter ] );
$where[] = \is_array( $this->filters[ $this->filter ] ) ? $this->filters[ $this->filter ] : array( $this->filters[ $this->filter ] );
}
/* Exclude private blogs unless we have permission to view them */
@@ -0,0 +1,255 @@
<?php
/**
* @brief Blog Category Node
* @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 30 Jul 2019
*/
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;
}
/**
* Blog Category
*/
class _Category extends \IPS\Node\Model
{
/**
* @brief [ActiveRecord] Multiton Store
*/
protected static $multitons;
/**
* @brief [ActiveRecord] Database Table
*/
public static $databaseTable = 'blog_categories';
/**
* @brief [ActiveRecord] Database Prefix
*/
public static $databasePrefix = 'category_';
/**
* @brief [Node] Parent ID Database Column
*/
public static $databaseColumnParent = 'parent';
/**
* @brief [Node] Order Database Column
*/
public static $databaseColumnOrder = 'position';
/**
* @brief [Node] Node Title
*/
public static $nodeTitle = 'blog_categories';
/**
* @brief [Node] Subnode class
*/
public static $subnodeClass = 'IPS\blog\Blog';
/**
* @brief Content Item Class
*/
public static $contentItemClass = 'IPS\blog\Entry';
/**
* @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key
*/
public static $titleLangPrefix = 'blog_category_';
/**
* @brief [Node] Description suffix. If specified, will look for a language key with "{$titleLangPrefix}_{$id}_{$descriptionLangSuffix}" as the key
*/
public static $descriptionLangSuffix = '_desc';
/**
* @brief SEO Title Column
*/
public static $seoTitleColumn = 'seo_name';
/**
* @brief [Node] ACP Restrictions
* @code
array(
'app' => 'core', // The application key which holds the restrictrions
'module' => 'foo', // The module key which holds the restrictions
'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix"
'add' => 'foo_add',
'edit' => 'foo_edit',
'permissions' => 'foo_perms',
'delete' => 'foo_delete'
),
'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4)
'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete"
* @endcode
*/
protected static $restrictions = array(
'app' => 'blog',
'module' => 'categories',
'prefix' => 'categories_',
);
/**
* [Node] Add/Edit Form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( &$form )
{
$form->addHeader('category_basic_settings');
$form->add( new \IPS\Helpers\Form\Translatable( 'category_name', NULL, TRUE, array( 'app' => 'blog', 'key' => $this->id ? "blog_category_{$this->id}" : NULL ) ) );
\IPS\Member::loggedIn()->language()->words['category_desc'] = \IPS\Member::loggedIn()->language()->addToStack('blog_category_desc');
$form->add( new \IPS\Helpers\Form\Translatable( 'category_desc', NULL, FALSE, array(
'app' => 'blog',
'key' => ( $this->id ? "blog_category_{$this->id}_desc" : NULL ),
'editor' => array(
'app' => 'blog',
'key' => 'Categories',
'autoSaveKey' => ( $this->id ? "blog-category-{$this->id}" : "blog-new-category" ),
'attachIds' => $this->id ? array( $this->id, NULL, 'bcategory' ) : NULL, 'minimize' => 'category_desc_placeholder'
)
) ) );
$class = \get_called_class();
$form->add( new \IPS\Helpers\Form\Node( 'category_parent', $this->id ? $this->parent : 0, TRUE, array( 'class' => 'IPS\blog\Category', 'subnodes' => FALSE, 'zeroVal' => 'no_parent', 'permissionCheck' => function( $node ) use ( $class )
{
if( isset( $class::$subnodeClass ) AND $class::$subnodeClass AND $node instanceof $class::$subnodeClass )
{
return FALSE;
}
return !isset( \IPS\Request::i()->id ) or ( $node->id != \IPS\Request::i()->id and !$node->isChildOf( $node::load( \IPS\Request::i()->id ) ) );
} ) ) );
}
/**
* [Node] Format form values from add/edit form for save
*
* @param array $values Values from the form
* @return array
*/
public function formatFormValues( $values )
{
if( isset( $values['category_parent'] ) )
{
$values['parent'] = $values['category_parent'] ? $values['category_parent']->id : 0;
}
if ( !$this->id )
{
$this->save();
\IPS\File::claimAttachments( 'blog-new-category', $this->id, NULL, 'bcategory', TRUE );
}
elseif( isset( $values['category_name'] ) OR isset( $values['category_desc'] ) )
{
$this->save();
}
if( isset( $values['category_name'] ) )
{
\IPS\Lang::saveCustom( 'blog', "blog_category_{$this->id}", $values['category_name'] );
$values['seo_name'] = \IPS\Http\Url\Friendly::seoTitle( $values['category_name'][ \IPS\Lang::defaultLanguage() ] );
unset( $values['category_name'] );
}
if( isset( $values['category_desc'] ) )
{
\IPS\Lang::saveCustom( 'blog', "blog_category_{$this->id}_desc", $values['category_desc'] );
unset( $values['category_desc'] );
}
return $values;
}
/**
* @brief Cached URL
*/
protected $_url = NULL;
/**
* Get URL
*
* @return \IPS\Http\Url
*/
public function url()
{
if( $this->_url === NULL )
{
$seoTitleColumn = static::$seoTitleColumn;
$this->_url = \IPS\Http\Url::internal( "app=blog&module=blogs&controller=browse&id={$this->id}", 'front', 'blog_category', $this->$seoTitleColumn );
}
return $this->_url;
}
/**
* Get HTML link
*
* @return string
*/
public function link()
{
return \IPS\Theme::i()->getTemplate( 'global', 'blog' )->blogCategoryLink( $this );
}
/**
* Get URL from index data
*
* @param array $indexData Data from the search index
* @param array $itemData Basic data about the item. Only includes columns returned by item::basicDataColumns()
* @param array|NULL $containerData Basic data about the container. Only includes columns returned by container::basicDataColumns()
* @return \IPS\Http\Url
*/
public static function urlFromIndexData( $indexData, $itemData, $containerData )
{
return \IPS\Http\Url::internal( "app=blog&module=blogs&controller=browse&category={$indexData['index_container_id']}", 'front', 'blog_category', \IPS\Member::loggedIn()->language()->addToStack( 'blog_category_' . $indexData['index_container_id'], FALSE, array( 'seotitle' => TRUE ) ) );
}
/**
* [Node] Get Node Icon
*
* @return string
*/
protected function get__icon()
{
return ( $this->parent > 0 ) ? 'caret-down' : NULL ;
}
/**
* 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 name Name
* @apiresponse string url URL
* @apiresponse string class Node class
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
$return = array(
'id' => $this->id,
'name' => $this->_title,
'url' => (string) $this->url(),
'class' => \get_class( $this ),
);
return $return;
}
}
@@ -0,0 +1,121 @@
<?php
/**
* @brief Blog Entry Category Node
* @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 2 Aug 2019
*/
namespace IPS\blog\Entry;
/* 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;
}
/**
* Blog Category
*/
class _Category extends \IPS\Node\Model
{
/**
* @brief [ActiveRecord] Multiton Store
*/
protected static $multitons;
/**
* @brief [ActiveRecord] Database Table
*/
public static $databaseTable = 'blog_entry_categories';
/**
* @brief [ActiveRecord] Database Prefix
*/
public static $databasePrefix = 'entry_category_';
/**
* @brief [Node] Order Database Column
*/
public static $databaseColumnOrder = 'position';
/**
* @brief [Node] Node Title
*/
public static $nodeTitle = 'blog_entry_categories';
/**
* @brief Cached URL
*/
protected $_url = NULL;
/**
* Get URL
*
* @return \IPS\Http\Url
*/
public function url()
{
$blog = \IPS\blog\Blog::load( $this->blog_id );
if( $this->_url === NULL )
{
$this->_url = \IPS\Http\Url::internal( "app=blog&module=blogs&controller=view&id={$blog->id}&cat={$this->id}", 'front', 'blogs_blog_cat', array( $blog->seo_name, $this->seo_name ) );
}
return $this->_url;
}
/**
* Get HTML link
*
* @return string
*/
public function link()
{
return \IPS\Theme::i()->getTemplate( 'global' )->categoryLink( $this );
}
/**
* 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 name Name
* @apiresponse string url URL
* @apiresponse string class Node class
* @apiresponse int position Node order
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
$return = array(
'id' => $this->id,
'name' => $this->name,
'url' => (string) $this->url(),
'class' => \get_class( $this ),
'position' => $this->position
);
return $return;
}
/**
* [ActiveRecord] Save Changed Columns
*
* @return void
*/
public function save()
{
if( !$this->id )
{
$this->position = \IPS\Db::i()->select( 'MAX(entry_category_position)', 'blog_entry_categories', array( 'entry_category_blog_id=?', $this->blog_id ))->first() + 1;
}
return parent::save();
}
}
+17 -12
View File
@@ -12,7 +12,7 @@
namespace IPS\blog\Entry;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -82,7 +82,7 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
public static $hideLogKey = 'blog-entries';
/**
* Get items with permisison check
* Get items with permission check
*
* @param array $where Where clause
* @param string $order MySQL ORDER BY clause (NULL to order by date)
@@ -94,11 +94,13 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
* @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)
* @return \IPS\Patterns\ActiveRecordIterator|int
* @param bool $countOnly If true will return the count
* @param array|null $joins Additional arbitrary joins for the query
* @return array|NULL|\IPS\Content\Comment If $limit is 1, will return \IPS\Content\Comment or NULL for no results. For any other number, will return an array.
*/
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 )
{
if ( in_array( $permissionKey, array( 'view', 'read' ) ) )
if ( \in_array( $permissionKey, array( 'view', 'read' ) ) )
{
$joinContainer = TRUE;
$member = $member ?: \IPS\Member::loggedIn();
@@ -115,13 +117,16 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
{
$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=? )', \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 );
}
{
if ( !$member->modPermission( 'can_access_all_clubs' ) )
{
$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 );
}
}
}
@@ -139,7 +144,7 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
public static function modPermission( $type, \IPS\Member $member = NULL, \IPS\Node\Model $container = NULL )
{
$member = $member ?: \IPS\Member::loggedIn();
if ( in_array( $type, array( 'edit', 'delete', 'lock' ) ) and $container and $container->member_id === $member->member_id )
if ( \in_array( $type, array( 'edit', 'delete', 'lock' ) ) and $container and $container->member_id === $member->member_id )
{
return $member->group['g_blog_allowownmod'];
}
+222 -81
View File
@@ -12,7 +12,7 @@
namespace IPS\blog;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -92,6 +92,10 @@ class _Entry extends \IPS\Content\Item implements
'future_date' => 'publish_date',
'status' => 'status',
'meta_data' => 'meta_data',
'edit_time' => 'edit_time',
'edit_member_name' => 'edit_name',
'edit_show' => 'append_edit',
'edit_reason' => 'edit_reason'
);
/**
@@ -152,7 +156,7 @@ class _Entry extends \IPS\Content\Item implements
/**
* Set the title
*
* @param string $title Title
* @param string $name Title
* @return void
*/
public function set_name( $name )
@@ -190,12 +194,8 @@ class _Entry extends \IPS\Content\Item implements
{
$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' ) );
}
$gallery = \IPS\Application::load( 'gallery' );
$gallery::outputCss();
return \IPS\Theme::i()->getTemplate( 'browse', 'gallery', 'front' )->miniAlbum( $album );
}
@@ -225,6 +225,11 @@ class _Entry extends \IPS\Content\Item implements
* @brief SEO Title Column
*/
public static $seoTitleColumn = 'name_seo';
/**
* @brief Category
*/
protected $category;
/**
* Can view this entry
@@ -238,13 +243,17 @@ class _Entry extends \IPS\Content\Item implements
$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 ) ) ) )
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;
if ( ( $club = $this->container()->club() AND \in_array( $club->memberStatus( \IPS\Member::loggedIn() ), array( \IPS\Member\Club::STATUS_LEADER, \IPS\Member\Club::STATUS_MODERATOR ) ) ) )
{
$return = TRUE;
}
}
/* 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 ) ) ) )
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;
}
@@ -259,7 +268,7 @@ class _Entry extends \IPS\Content\Item implements
}
/* Private blog */
if( $this->container()->social_group != 0 AND $this->container()->owner() != $member )
if( $this->container()->social_group != 0 AND $this->container()->owner()->member_id != $member->member_id )
{
/* This will throw an exception of the row does not exist */
try
@@ -304,7 +313,7 @@ class _Entry extends \IPS\Content\Item implements
*/
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' ) ) )
if ( \in_array( $permissionKey, array( 'view', 'read' ) ) )
{
$joinContainer = TRUE;
@@ -322,13 +331,16 @@ class _Entry extends \IPS\Content\Item implements
{
$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, );
}
{
if ( !$member->modPermission( 'can_access_all_clubs' ) )
{
$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 );
@@ -381,6 +393,23 @@ class _Entry extends \IPS\Content\Item implements
$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 ) ) ) );
/* Edit Log Fields need to be under the editor */
$editReason = NULL;
if( isset( $return['edit_reason']) )
{
$editReason = $return['edit_reason'];
unset( $return['edit_reason'] );
$return['edit_reason'] = $editReason;
}
$logEdit = NULL;
if( isset( $return['log_edit']) )
{
$logEdit = $return['log_edit'];
unset( $return['log_edit'] );
$return['log_edit'] = $logEdit;
}
/* Gallery album association */
if( \IPS\Application::appIsEnabled( 'gallery' ) )
{
@@ -391,7 +420,34 @@ class _Entry extends \IPS\Content\Item implements
) );
}
$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 ) ) );
$categories = \IPS\blog\Entry\Category::roots( NULL, NULL, array( 'entry_category_blog_id=?', $container->id ) );
$choiceOptions = array( 0 => 'entry_category_choice_new' );
$choiceToggles = array( 0 => array( 'blog_entry_new_category' ) );
if( \count( $categories ) )
{
$choiceOptions[1] = 'entry_category_choice_existing';
$choiceToggles[1] = array( 'entry_category_id' );
}
$return['entry_category_choice'] = new \IPS\Helpers\Form\Radio( 'entry_category_choice', ( ( $item AND $item->category_id ) or \IPS\Request::i()->cat ) ? 1 : 0, FALSE, array(
'options' => $choiceOptions,
'toggles' => $choiceToggles
) );
if( \count( $categories ) )
{
$options = array();
foreach ( $categories as $category )
{
$options[ $category->id ] = $category->name;
}
$return[ 'entry_category_id' ] = new \IPS\Helpers\Form\Select( 'entry_category_id', ( $item AND $item->category_id ) ? $item->category_id : ( \IPS\Request::i()->cat ? \IPS\Request::i()->cat : NULL ), FALSE, array( 'options' => $options ), NULL, NULL, NULL, "entry_category_id" );
}
$return['blog_entry_new_category'] = new \IPS\Helpers\Form\Text( 'blog_entry_new_category', NULL, TRUE, array(), NULL, NULL, NULL, "blog_entry_new_category" );
$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', 'allowStockPhotos' => TRUE, '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' ) ) );
@@ -414,7 +470,7 @@ class _Entry extends \IPS\Content\Item implements
$return['poll'] = $poll;
}
return $return;
}
@@ -436,7 +492,10 @@ class _Entry extends \IPS\Content\Item implements
$oldContent = $this->content;
}
$this->content = $values['blog_entry_content'];
if ( !$new )
$sendFilterNotifications = $this->checkProfanityFilters( FALSE, $new );
if ( !$new AND $sendFilterNotifications === FALSE )
{
$this->sendAfterEditNotifications( $oldContent );
}
@@ -464,6 +523,22 @@ class _Entry extends \IPS\Content\Item implements
{
$this->status = 'draft';
}
if( $values['entry_category_choice'] == 1 and $values['entry_category_id'] )
{
$this->category_id = $values['entry_category_id'];
}
else
{
$newCategory = new \IPS\blog\Entry\Category;
$newCategory->name = $values['blog_entry_new_category'];
$newCategory->seo_name = \IPS\Http\Url\Friendly::seoTitle( $values['blog_entry_new_category'] );
$newCategory->blog_id = $this->blog_id;
$newCategory->save();
$this->category_id = $newCategory->id;
}
/* Ping */
$this->container()->ping();
@@ -481,6 +556,18 @@ class _Entry extends \IPS\Content\Item implements
{
parent::canCreate( $member, $container, $showError );
if ( $member->member_id AND $member->checkPostsPerDay() === FALSE )
{
if ( $showError )
{
\IPS\Output::i()->error( 'posts_per_day_error', '1B203/2', 403, '' );
}
else
{
return FALSE;
}
}
$return = TRUE;
$blogs = \IPS\blog\Blog::loadByOwner( $member );
@@ -492,7 +579,7 @@ class _Entry extends \IPS\Content\Item implements
$return = $club->isModerator( $member );
$error = 'no_module_permission';
}
elseif ( !in_array( $container->id, array_keys( $blogs ) ) )
elseif ( !\in_array( $container->id, array_keys( $blogs ) ) )
{
$return = FALSE;
$error = 'no_module_permission';
@@ -506,7 +593,7 @@ class _Entry extends \IPS\Content\Item implements
}
else
{
if( !count( $blogs ) )
if( !\count( $blogs ) )
{
$return = FALSE;
$error = 'no_module_permission';
@@ -585,13 +672,31 @@ class _Entry extends \IPS\Content\Item implements
/**
* Can comment?
*
* @param \IPS\Member\NULL $member The member (NULL for currently logged in member)
* @param \IPS\Member\NULL $member The member (NULL for currently logged in member)
* @param bool $considerPostBeforeRegistering If TRUE, and $member is a guest, will return TRUE if "Post Before Registering" feature is enabled
* @return bool
*/
public function canComment( $member=NULL )
public function canComment( $member=NULL, $considerPostBeforeRegistering = TRUE )
{
$member = $member ?: \IPS\Member::loggedIn();
return parent::canComment( $member ) and $member->group['g_blog_allowcomment'];
if ( parent::canComment( $member, $considerPostBeforeRegistering ) )
{
if ( $member->checkPostsPerDay() === FALSE )
{
return FALSE;
}
elseif ( $member->group['g_blog_allowcomment'] )
{
return TRUE;
}
elseif ( !$member->member_id and $considerPostBeforeRegistering and \IPS\Settings::i()->post_before_registering and \IPS\Login::registrationType() != 'disabled' )
{
return (bool) \IPS\Member\Group::load( \IPS\Settings::i()->member_group )->g_blog_allowcomment;
}
}
return FALSE;
}
/**
@@ -622,7 +727,7 @@ class _Entry extends \IPS\Content\Item implements
if ( $result !== TRUE )
{
if ( in_array( $type, array( 'edit', 'delete', 'lock', 'unlock' ) ) and $container and $container->member_id === $member->member_id )
if ( \in_array( $type, array( 'edit', 'delete', 'lock', 'unlock' ) ) and $container and $container->member_id === $member->member_id )
{
$result = $member->group['g_blog_allowownmod'];
}
@@ -641,6 +746,16 @@ class _Entry extends \IPS\Content\Item implements
return array( \IPS\Theme::i()->getTemplate( 'global', 'blog', 'front' ), 'rows' );
}
/**
* WHERE clause for getting items for digest (permissions are already accounted for)
*
* @return array
*/
public static function digestWhere(): array
{
return array( array( 'blog_entries.entry_is_future_entry=0 AND blog_entries.entry_status!=?', 'draft' ) );
}
/**
* Search Index Permissions
*
@@ -672,27 +787,28 @@ class _Entry extends \IPS\Content\Item implements
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @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
* @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
* @apiresponse \IPS\blog\Entry\Category category Category
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
{
@@ -703,7 +819,7 @@ class _Entry extends \IPS\Content\Item implements
'author' => $this->author()->apiOutput( $authorizedMember ),
'draft' => $this->status == 'draft',
'date' => \IPS\DateTime::ts( $this->date )->rfc3339(),
'entry' => $this->content(),
'entry' => \IPS\Text\Parser::removeLazyLoad( $this->content() ),
'comments' => $this->num_comments,
'views' => $this->views,
'prefix' => $this->prefix(),
@@ -716,6 +832,7 @@ class _Entry extends \IPS\Content\Item implements
'poll' => $this->poll_state ? \IPS\Poll::load( $this->poll_state )->apiOutput( $authorizedMember ) : null,
'url' => (string) $this->url(),
'rating' => $this->averageRating(),
'category' => $this->category_id ? $this->category()->apiOutput() : NULL,
);
}
@@ -739,34 +856,10 @@ class _Entry extends \IPS\Content\Item implements
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
* @param \IPS\Member|NULL $member The member, or NULL for currently logged in
* @return bool
*/
public function canFeatureComment( \IPS\Member $member = NULL )
@@ -789,7 +882,7 @@ class _Entry extends \IPS\Content\Item implements
/**
* Can Unfeature a Comment
*
* @param \IPS\Member|NULL The member, or NULL for currently logged in
* @param \IPS\Member|NULL $member The member, or NULL for currently logged in
* @return bool
*/
public function canUnfeatureComment( \IPS\Member $member = NULL )
@@ -808,17 +901,16 @@ class _Entry extends \IPS\Content\Item implements
return $return;
}
/**
* Returns the content images
*
* @param int|null $limit Number of attachments to fetch, or NULL for all
*
* @param int|null $limit Number of attachments to fetch, or NULL for all
* @param bool $ignorePermissions If set to TRUE, permission to view the images will not be checked
* @return array|NULL
* @throws \BadMethodCallException
*/
public function contentImages( $limit = NULL )
public function contentImages( $limit = NULL, $ignorePermissions = FALSE )
{
$idColumn = static::$databaseColumnId;
$internal = NULL;
@@ -836,8 +928,41 @@ class _Entry extends \IPS\Content\Item implements
$attachments[] = array( 'core_Attachment' => $row['attach_location'] );
}
}
/* Does the blog entry have a cover photo? */
if( $this->cover_photo )
{
$attachments[] = array( 'blog_Entries' => $this->cover_photo );
}
/* And what about the blog itself? */
if( $this->container()->cover_photo )
{
$attachments[] = array( 'blog_Blogs' => $this->container()->cover_photo );
}
/* IS there a club with a cover photo? */
if ( \IPS\IPS::classUsesTrait( $this->container(), 'IPS\Content\ClubContainer' ) and $club = $this->container()->club() )
{
$attachments[] = array( 'core_Clubs' => $club->cover_photo );
}
return count( $attachments ) ? $attachments : NULL;
return \count( $attachments ) ? $attachments : NULL;
}
/**
* Is this a future entry?
*
* @return bool
*/
public function isFutureDate()
{
if ( $this->date > time() )
{
return TRUE;
}
return FALSE;
}
/**
@@ -851,4 +976,20 @@ class _Entry extends \IPS\Content\Item implements
\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 ) );
}
/**
* Get category
*
* @return \IPS\blog\Entry\Category
* @throws \OutOfRangeException
*/
public function category()
{
if ( $this->category === NULL )
{
$this->category = \IPS\blog\Entry\Category::load( $this->category_id );
}
return $this->category;
}
}