Version 4.7.23

This commit is contained in:
Neo committed 2025-12-19 16:21:27 -08:00
1 parent 7124a02564
commit 25ddeb65d6
1791 files changed
+76990 -44452

No files matched your search

+1 -1
View File
@@ -665,7 +665,7 @@ EOF;
@eval( $val );
ob_get_clean();
}
catch ( \Exception $e )
catch ( \ParseError|\Exception $e )
{
throw new \DomainException( $e->getMessage() );
}
@@ -12,6 +12,9 @@
namespace IPS\cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Content\Comment;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
@@ -109,6 +112,11 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
* @breif Used by the dataLayer, overwritten by \IPS\cms\Application
*/
public static $containerType = 'pages_database_category';
/**
* @brief SEO Title Column
*/
public static string $seoTitleColumn = 'furl_name';
/**
* @brief [Node] ACP Restrictions
@@ -331,7 +339,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
$path = trim( $path , '/');
$bits = explode( '/', $path );
$root = $bits[0];
/* _ is here due to IP.Board 3.x using that to denote the articles database (eg. articles.html/_/category/record-r1/), which we need to redirect from for records and articles. */
if ( \in_array( $root, array( 'submit', '_' ) ) )
{
@@ -612,6 +620,34 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
), NULL, NULL, NULL, 'category_fields' ) );
}
}
/**
* See if a furl name is taken within a database
*
* @param string $furl The default FURL (of a category)
* @param int $databaseId The database
* @param int|null $excludeId Optional: an id to exclude from the query
*
* @return bool
*/
public static function furlNameIsTaken( string $furl, int $databaseId, ?int $excludeId=null ) : bool
{
$where = array( ['category_database_id=?', $databaseId ] );
if ( $excludeId )
{
$where[] = [ 'category_id <> ?', $excludeId ];
}
foreach ( (new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'cms_database_categories', $where ), self::class )) as $cat )
{
if ( ( !empty( $cat->furl_name ) and $cat->furl_name == $furl ) or ( empty( $cat->furl_name ) AND !empty( $cat->name ) AND \IPS\Http\Url\Friendly::seoTitle( $cat->name ) == $furl ) )
{
return true;
}
}
return false;
}
/**
* [Node] Format form values from add/edit form for save
@@ -667,44 +703,14 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
if ( isset( $name ) AND empty( $values['category_furl_name'] ) )
{
$ok = FALSE;
try
{
$cat = \IPS\cms\Categories::load( \IPS\Http\Url\Friendly::seoTitle( $name ), 'category_furl_name' );
/* We have a cat, is it in a different database? */
if ( $cat->database_id != \IPS\Request::i()->database_id )
{
$ok = TRUE;
}
if ( isset( \IPS\Request::i()->id ) )
{
if ( $cat->id == \IPS\Request::i()->id )
{
$ok = TRUE;
}
}
}
catch ( \OutOfRangeException $e )
{
$ok = TRUE;
}
$ok = static::furlNameIsTaken( \IPS\Http\Url\Friendly::seoTitle( $name ), $this->database_id, $this->id ?: null );
if ( \IPS\Request::i()->category_parent_id == 0 and \IPS\cms\Categories::isFurlCollision( \IPS\Http\Url\Friendly::seoTitle( $name ) ) )
{
$ok = FALSE;
}
if ( $ok === TRUE )
{
$values['furl_name'] = \IPS\Http\Url\Friendly::seoTitle( $name );
}
else
{
$values['furl_name'] = $this->id . '_' . \IPS\Http\Url\Friendly::seoTitle( $name );
}
$values['furl_name'] = $ok ? \IPS\Http\Url\Friendly::seoTitle( $name ) : $this->id . '_' . \IPS\Http\Url\Friendly::seoTitle( $name );
}
else if( isset( $values['category_furl_name'] ) )
{
@@ -962,8 +968,8 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function permissions()
{
/* Let the ACP/Setup use normal permissions or it'll get messy */
if ( !\IPS\Dispatcher::hasInstance() OR \IPS\Dispatcher::i()->controllerLocation !== 'front' )
/* Let the ACP/Setup use normal permissions or it'll get messy. Also use normal permissions for club categories */
if ( !\IPS\Dispatcher::hasInstance() OR \IPS\Dispatcher::i()->controllerLocation !== 'front' OR $this->isClubCategory() )
{
$this->_permsMashed = true;
return parent::permissions();
@@ -1147,7 +1153,7 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
if ( $recordClass::database()->use_categories )
{
return \IPS\Member::loggedIn()->language()->addToStack( static::$titleLangPrefix . $indexData['index_container_id'], NULL, $escape ? array( 'escape' => $escape ) : array() );
return parent::titleFromIndexData( $indexData, $itemData, $containerData, $escape );
}
else
{
@@ -1170,6 +1176,20 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
return $this->database()->pageTitle();
}
/**
* Get the club of this category, or null if there is no club
*
* @return \IPS\Member\Club|null
*/
public function get__club() : \IPS\Member\Club|null
{
if ( $this->isClubCategory() )
{
return $this->club();
}
return null;
}
/**
* Get title of category
*
@@ -1178,8 +1198,8 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
protected function get__title()
{
/* If the DB is in a page, and we're not using categories, then return the page title, not the category title for continuity */
$database = \IPS\cms\Databases::load( $this->database_id );
if ( ! $database->use_categories )
$database = static::database();
if ( ! $database->use_categories and !$this->_club )
{
if ( ! $this->_catTitle )
{
@@ -1454,14 +1474,15 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
return static::database()->template_display;
}
/**
* Set last comment
*
* @param \IPS\Content\Comment $comment The latest comment or NULL to work it out
* @return void
* @param Comment|null $comment The latest comment or NULL to work it out
* @param Item|null $updatedItem We sometimes run setLastComment() when an item has been edited, if so, that item will be here
* @return void
*/
public function setLastComment( \IPS\Content\Comment $comment=NULL )
public function _setLastComment( \IPS\Content\Comment $comment=NULL, \IPS\Content\Item $updatedItem=NULL )
{
$database = \IPS\cms\Databases::load( $this->database_id );
@@ -1715,6 +1736,16 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function canManagePermissions()
{
/* For club categories, we don't want the permissions to be managed via the ACP */
if ( $this->isClubCategory() and !( \IPS\Dispatcher::i() instanceof \IPS\Dispatcher\Front ) )
{
return false;
}
else if ( $this->isClubCategory() )
{
return $this->can( 'add' );
}
$can = parent::canManagePermissions();
return ( $can === FALSE ) ? FALSE : (boolean) $this->has_perms;
@@ -1864,6 +1895,90 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$_member = $member ?: \IPS\Member::loggedIn();
if ( $club = $this->_club )
{
if ( \in_array( $permission, [ 'add', 'edit', 'delete', 'reply', 'review' ] ) and $_member instanceof \IPS\Member )
{
if( \in_array( $club->memberStatus( $_member ), [
\IPS\Member\Club::STATUS_MEMBER,
\IPS\Member\Club::STATUS_LEADER,
\IPS\Member\Club::STATUS_MODERATOR
] ) or $_member->modPermission('can_access_all_clubs' ) )
{
return parent::can( $permission, $_member, $considerPostBeforeRegistering );
}
return false;
}
if ( \in_array( $permission, ['view', 'read' ] ) )
{
/* Check the database permission first */
if( !static::database()->can( $permission, $_member ) )
{
return false;
}
/* Site moderators can see everything */
if ( $_member instanceof \IPS\Member and $_member->modPermission('can_access_all_clubs') )
{
return TRUE;
}
/* If it's not approved, only moderators and the person who created it can see it */
if ( \IPS\Settings::i()->clubs_require_approval and !$club->approved and $_member instanceof \IPS\Member )
{
return ( $_member->modPermission('can_access_all_clubs') or ( $club->owner AND $_member->member_id == $club->owner->member_id ) );
}
/* Owner or leader? */
if ( $_member instanceof \IPS\Member and ( $_member->member_id === $club->owner->member_id OR $club->memberStatus( $_member ) === \IPS\Member\Club::STATUS_LEADER ) )
{
return TRUE;
}
$viewGroups = explode( ',', $this->club_view_permission );
/* Can literally anyone, guest included, view this and the club? */
if ( $this->club_category_meta_index and $club->type !== \IPS\Member\Club::TYPE_PRIVATE and \in_array( 'nonmember', $viewGroups ) )
{
return true;
}
/* Nonmembers */
if ( \in_array( 'nonmember', $viewGroups ) and $_member instanceof \IPS\Member and $_member->member_id )
{
return true;
}
/* Moderators? */
if ( \in_array( 'moderator', $viewGroups ) AND $club->memberStatus( $_member ) === \IPS\Member\Club::STATUS_MODERATOR )
{
return TRUE;
}
/* Members */
if (
( \mb_strpos( '*', $this->club_view_permission ) !== false or \in_array( 'member', $viewGroups ) ) and
\in_array(
$club->memberStatus( $_member ),
[
\IPS\Member\Club::STATUS_MEMBER,
\IPS\Member\Club::STATUS_INVITED,
\IPS\Member\Club::STATUS_INVITED_BYPASSING_PAYMENT,
\IPS\Member\Club::STATUS_EXPIRED_MODERATOR
]
)
)
{
return TRUE;
}
return false;
}
}
if ( !$_member->member_id and !$this->can_view_others )
{
return FALSE;
@@ -1917,4 +2032,24 @@ class _Categories extends \IPS\Node\Model implements \IPS\Node\Permissions
return $this->_dataLayerProperties;
}
/**
* See if this category belongs to a club
* @return bool
*/
public function isClubCategory() : bool
{
try
{
$database = \IPS\cms\Databases::load( $this->database_id );
$class = '\IPS\cms\Categories' . $this->database_id;
if ( $database->allow_club_categories and \IPS\IPS::classUsesTrait( $class, 'IPS\Content\ClubContainer' ) )
{
return true;
}
}
catch ( \OutOfBoundsException|\OutOfRangeException ) { }
return false;
}
}
@@ -762,6 +762,19 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$this->_data['featured_settings'] = ( \is_array( $value ) ? json_encode( $value ) : $value );
}
/**
* @return false|mixed
*/
public function get_allow_club_categories()
{
if( \IPS\Settings::i()->clubs )
{
return $this->_data['allow_club_categories'];
}
return false;
}
/**
* Get the title of the page when using a database
@@ -959,8 +972,7 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$fieldsClass = '\IPS\cms\Fields' . $this->id;
$class = '\IPS\cms\Categories' . $this->id;
foreach( $class::roots( NULL ) as $id => $cat )
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'cms_database_categories', array( 'category_database_id=?', $this->id ) ), '\IPS\cms\Categories' . $this->id ) as $cat )
{
$cat->delete();
}
@@ -1163,24 +1175,34 @@ class _Databases extends \IPS\Node\Model implements \IPS\Node\Permissions
}
}
/**
* @brief Number of categories
*/
protected $_numberOfCategories = NULL;
/**
* Get the number of categories in this database
*
* @return int
*/
public function numberOfCategories()
public function numberOfCategories() : int
{
if ( $this->_numberOfCategories === NULL )
static $count = null;
if ( $count === NULL )
{
$this->_numberOfCategories = \IPS\Db::i()->select( 'count(*)', 'cms_database_categories', array( 'category_database_id=?', $this->_id ) )->first();
$count = \IPS\Db::i()->select( 'count(*)', 'cms_database_categories', array( 'category_database_id=?', $this->_id ) )->first();
}
return $this->_numberOfCategories;
return $count;
}
/**
* Get the number of categories in this database
*
* @return int
*/
public function numberOfClubCategories() : int
{
static $count = null;
if ( $count === NULL )
{
$count = \IPS\Db::i()->select( 'count(*)', 'cms_database_categories', array( 'category_database_id=? AND category_club_id IS NOT NULL', $this->_id ) )->first();
}
return $count;
}
/**
+14 -9
View File
@@ -330,9 +330,10 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
* @param \IPS\Node\Model|NULL $container Parent container
* @param int $flags Bit flags
* @param \IPS\cms\Records|NULL $record The record itself
* @param bool $includeDefaultValue Include the default value for the field
* @return array
*/
public static function fields( $values, $permissionCheck='view', \IPS\Node\Model $container=NULL, $flags=0, \IPS\cms\Records $record = NULL )
public static function fields( $values, $permissionCheck='view', \IPS\Node\Model $container=NULL, $flags=0, \IPS\cms\Records $record = NULL, bool $includeDefaultValue = TRUE )
{
$fields = array();
$database = \IPS\cms\Databases::load( static::$customDatabaseId );
@@ -430,10 +431,9 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
}
else
{
$fields[ $row->id ] = $row->buildHelper( $row->default_value, $customValidationCode, $record, $flags );
$fields[ $row->id ] = $row->buildHelper( $includeDefaultValue ? $row->default_value : NULL , $customValidationCode, $record, $flags );
}
}
return $fields;
}
@@ -1061,11 +1061,12 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
$where = array( array( 'field_' . $field->id . '=?', $val ) );
if ( $record !== NULL )
if ( $record !== NULL and $record->_id )
{
$where[] = array( 'primary_id_field != ?', $record->_id );
}
if ( \IPS\Db::i()->select( 'COUNT(*)', 'cms_custom_database_' . $database->id, $where )->first() )
{
throw new \LogicException( \IPS\Member::loggedIn()->language()->addToStack( "field_unique_entry_not_unique", FALSE, array( 'sprintf' => array( $database->recordWord( 1 ) ) ) ) );
@@ -2121,7 +2122,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
if ( ! $isTitleOrContent )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'field_display_listing', $this->id ? $this->display_listing : 1, FALSE, array(
'togglesOn' => array('field_truncate', 'field_display_listing_json_badge', 'field_show_map_listing', 'field_show_map_listing_dims' )
'togglesOn' => array('field_display_listing_json_badge' )
), NULL, NULL, NULL, 'field_display_listing' ) );
}
@@ -2132,7 +2133,9 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
$form->add( new \IPS\Helpers\Form\YesNo( 'field_display_listing_json_badge_right', ( isset( $displayJson['listing']['right'] ) ? $displayJson['listing']['right'] : 0 ), FALSE, array(), NULL, NULL, NULL, 'field_display_listing_json_badge_right' ) );
}
$form->add( new \IPS\Helpers\Form\YesNo( 'field_show_map_listing', $mapListing, FALSE, array(), NULL, NULL, NULL, 'field_show_map_listing' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'field_show_map_listing', $mapListing, FALSE, array(
'togglesOn' => [ 'field_show_map_listing_dims' ]
), NULL, NULL, NULL, 'field_show_map_listing' ) );
$form->add( new \IPS\Helpers\Form\WidthHeight( 'field_show_map_listing_dims', $mapListingDims, FALSE, array( 'resizableDiv' => FALSE ), NULL, NULL, NULL, 'field_show_map_listing_dims' ) );
$form->add( new \IPS\Helpers\Form\Codemirror( 'field_display_listing_json_custom', ( isset( $displayJson['listing']['html'] ) ? $displayJson['listing']['html'] : NULL ), FALSE, array( 'placeholder' => '{label}: {value}' ), function( $val )
@@ -2163,13 +2166,15 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
if ( ! $isTitleOrContent )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'field_display_display', $this->id ? $this->display_display : 1, FALSE, array(
'togglesOn' => array( 'field_display_display_json_badge', 'field_show_map_display', 'field_show_map_display_dims', 'field_display_display_json_where' )
'togglesOn' => array( 'field_display_display_json_badge', 'field_display_display_json_where' )
), NULL, NULL, NULL, 'field_display_display' ) );
}
$form->add( new \IPS\Helpers\Form\Radio( 'field_display_display_json_badge', $displayDefault, FALSE, array( 'options' => $displayOptions, 'toggles' => $displayToggles, 'parse' => 'raw' ), NULL, NULL, NULL, 'field_display_display_json_badge' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'field_show_map_display', $mapDisplay, FALSE, array(), NULL, NULL, NULL, 'field_show_map_display' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'field_show_map_display', $mapDisplay, FALSE, array(
'togglesOn' => [ 'field_show_map_display_dims' ]
), NULL, NULL, NULL, 'field_show_map_display' ) );
$form->add( new \IPS\Helpers\Form\WidthHeight( 'field_show_map_display_dims', $mapDisplayDims, FALSE, array( 'resizableDiv' => FALSE ), NULL, NULL, NULL, 'field_show_map_display_dims' ) );
$form->add( new \IPS\Helpers\Form\Codemirror( 'field_display_display_json_custom', ( isset( $displayJson['display']['html'] ) ? $displayJson['display']['html'] : NULL ), FALSE, array( 'placeholder' => '{label}: {value}' ), function( $val )
+17 -8
View File
@@ -23,8 +23,6 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
{
use \IPS\Content\ViewUpdates;
/**
* @brief [ActiveRecord] Multiton Store
*/
@@ -448,7 +446,8 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
/* Got a page called index? */
if ( ! isset( static::$defaultPage[ $folderId ] ) )
{
foreach( static::getChildren( $folderId ) as $id => $obj )
$children = static::getChildren( $folderId );
foreach( $children as $id => $obj )
{
if ( \mb_substr( $obj->seo_name, 0, 5 ) === 'index' )
{
@@ -1445,7 +1444,7 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
$return['edit_content'] = $buttons['edit_content'];
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'cms', 'pages', 'page_edit' ) )
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'cms', 'pages', 'page_edit' ) and !$this->default )
{
$return['default'] = array(
'icon' => $this->default ? 'star' : 'star-o',
@@ -2360,10 +2359,20 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
}
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'pages/page.css', 'cms', 'front' ) );
if ( ! ( \IPS\Application::load('cms')->default AND ! $this->folder_id AND $this->default ) )
try
{
\IPS\Output::i()->breadcrumb['module'] = array( $this->url(), $this->_title );
$database = \IPS\cms\Databases\Dispatcher::i()->databaseId ? \IPS\cms\Databases::load( \IPS\cms\Databases\Dispatcher::i()->databaseId ) : null;
if ( !$database or !$database->allow_club_categories )
{
throw new \OutOfRangeException;
}
}
catch ( \OutOfRangeException|\OutOfBoundsException )
{
if ( !( \IPS\Application::load( 'cms' )->default and !$this->folder_id and $this->default ) )
{
\IPS\Output::i()->breadcrumb['module'] = array( $this->url(), $this->_title );
}
}
\IPS\Output::i()->pageName = $this->full_path;
@@ -2660,7 +2669,7 @@ class _Page extends \IPS\Node\Model implements \IPS\Node\Permissions
/**
* Get item
*
* @return \IPS\nexus\Package\Item
* @return \IPS\cms\Pages\PageItem
*/
public function item()
{
+47 -1
View File
@@ -23,6 +23,8 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _PageItem extends \IPS\Content\Item implements \IPS\Content\Searchable
{
use \IPS\Content\ViewUpdates;
/**
* @brief Application
*/
@@ -52,7 +54,7 @@ class _PageItem extends \IPS\Content\Item implements \IPS\Content\Searchable
* @brief Database Column Map
*/
public static $databaseColumnMap = array(
'views' => 'views'
);
/**
@@ -234,4 +236,48 @@ class _PageItem extends \IPS\Content\Item implements \IPS\Content\Searchable
{
return FALSE;
}
/**
* Title for Analytics
*
* @return string
*/
public function titleForAnalytics(): string
{
try
{
return \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( \IPS\cms\Pages\Page::$titleLangPrefix . $this->id );
}
catch( \OutOfRangeException | \UnderflowException )
{
return \IPS\cms\Pages\Page::$titleLangPrefix . $this->id;
}
}
/**
* Get node
*
* @return \IPS\cms\Pages\Page
*/
public function node()
{
$data = array();
foreach ( $this->_data as $k => $v )
{
$data[ 'page_' . $k ] = $v;
}
return \IPS\cms\Pages\Page::constructFromData( $data );
}
/**
* URL
*
* @param string|NULL $action Action
* @return \IPS\Http\Url
*/
public function url( $action = null )
{
return $this->node()->url();
}
}
@@ -320,6 +320,16 @@ class _Comment extends \IPS\Content\Comment implements \IPS\Content\EditHistory,
{
return array( 'comment_database_id=?', static::$customDatabaseId );
}
/**
* WHERE clause for getting items for ACP overview statistics
*
* @return array
*/
public static function overviewStatisticsWhere()
{
return [ static::commentWhere() ];
}
/**
* Reaction Type
@@ -165,17 +165,18 @@ class _CommentTopicSync extends \IPS\cms\Records\Comment
return parent::html();
}
}
/**
* Report
*
* @param string $reportContent Report content message from member
* @param int $reportType Report type (see constants in \IPS\core\Reports\Report
* @param \IPS\Member|NULL $member Member making the report (or NULL for loggedIn())
* @return \\PS\core\Reports\Report
* @param string $reportContent Report content message from member
* @param int $reportType Report type (see constants in \IPS\core\Reports\Report)
* @param \IPS\Member|NULL $member Member making the report (or NULL for loggedIn())
* @param array $guestDetails Details for a guest report
* @return \IPS\core\Reports\Report
* @throws \UnexpectedValueException If there is a permission error - you should only call this method after checking canReport
*/
public function report( $reportContent, $reportType=1, $member=NULL )
public function report( $reportContent, $reportType=1, $member=NULL, $guestDetails=array() )
{
$comment = \IPS\forums\Topic\Post::load( $this->pid );
return $comment->report( $reportContent, $reportType, $member );
+121 -53
View File
@@ -12,6 +12,9 @@
namespace IPS\cms;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Content\Comment;
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
@@ -243,6 +246,11 @@ class _Records extends \IPS\Content\Item implements
*/
public static function getCommentsPerPage()
{
if ( static::database()->comments_perpage )
{
return static::database()->comments_perpage;
}
if ( ! empty( \IPS\cms\Databases\Dispatcher::i()->recordId ) )
{
$class = 'IPS\cms\Records' . static::$customDatabaseId;
@@ -373,7 +381,7 @@ class _Records extends \IPS\Content\Item implements
catch( \OutOfRangeException $ex ) { }
}
$where = array( array( '? LIKE CONCAT( record_dynamic_furl, \'%\') OR LOWER(record_static_furl)=?', $slug, mb_strtolower( $slug ) ) );
$where = array( array( '(? LIKE CONCAT( record_dynamic_furl, \'%\') OR LOWER(record_static_furl)=?)', $slug, mb_strtolower( $slug ) ) );
if ( $categoryId )
{
$where[] = array( 'category_id=?', $categoryId );
@@ -755,18 +763,11 @@ class _Records extends \IPS\Content\Item implements
* @param bool $includeComments If TRUE, comments will be included
* @param bool $includeReviews If TRUE, reviews will be included
* @param int $depth Used to keep track of current depth to avoid going too deep
* @return int|NULL|string When depth exceeds 10, will return "NULL" and initial call will return something like "100+"
* @note This method may return something like "100+" if it has lots of children to avoid exahusting memory. It is intended only for display use
* @return int
* @note This method includes counts of hidden and unapproved content items as well
*/
public static function contentCount( \IPS\Node\Model $container, $includeItems=TRUE, $includeComments=FALSE, $includeReviews=FALSE, $depth=0 )
{
/* Are we in too deep? */
if ( $depth > 10 )
{
return '+';
}
$count = $container->_items;
if ( static::canViewHiddenItems( NULL, $container ) )
@@ -785,19 +786,10 @@ class _Records extends \IPS\Content\Item implements
}
/* Add Children */
$childDepth = $depth++;
$childDepth = ++$depth;
foreach ( $container->children() as $child )
{
$toAdd = static::contentCount( $child, $includeItems, $includeComments, $includeReviews, $childDepth );
if ( \is_string( $toAdd ) )
{
return $count . '+';
}
else
{
$count += $toAdd;
}
$count += static::contentCount( $child, $includeItems, $includeComments, $includeReviews, $childDepth );
}
return $count;
}
@@ -2016,13 +2008,15 @@ class _Records extends \IPS\Content\Item implements
$category->save();
}
}
/**
* Resync last comment
*
* @param Comment|null $comment The comment
*
* @return void
*/
public function resyncLastComment()
public function resyncLastComment( \IPS\Content\Comment $comment = NULL )
{
if ( $this->useForumComments() )
{
@@ -2056,7 +2050,7 @@ class _Records extends \IPS\Content\Item implements
if ( $setCategory and $this->category_id )
{
$class = '\IPS\cms\Categories' . static::$customDatabaseId;
$class::load( $this->category_id )->setLastComment( NULL );
$class::load( $this->category_id )->setLastComment( $comment );
$class::load( $this->category_id )->save();
}
}
@@ -2594,8 +2588,9 @@ class _Records extends \IPS\Content\Item implements
catch( \Exception $ex ) { }
/* Try again */
/** @var \IPS\forums\Topic $topic */
$topic = $this->topic( FALSE );
if ( ! $topic )
if ( ! $topic or $topic->isArchived() )
{
return parent::processCommentForm( $values );
}
@@ -2923,6 +2918,11 @@ class _Records extends \IPS\Content\Item implements
$topic->save();
$firstPost = $topic->comments( 1 );
if( !$firstPost )
{
return;
}
$content = \IPS\Theme::i()->getTemplate( 'submit', 'cms', 'front' )->topic( $this );
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $content );
@@ -3060,37 +3060,46 @@ class _Records extends \IPS\Content\Item implements
{
if ( $this->recordCommentPageCount === NULL or $recache === TRUE )
{
if ( $this->useForumComments() )
{
try
{
$topic = $this->topic();
if( $topic !== NULL )
{
/* Store the real count so it is not accidentally written as the actual value */
$realCount = $topic->posts;
/* Compensate for the first post (which is actually the record) */
$topic->posts = ( $topic->posts - 1 ) > 0 ? $topic->posts - 1 : 0;
/* Get our page count considering all of that */
$this->recordCommentPageCount = $topic->commentPageCount();
/* Reset the count back to the real count */
$topic->posts = $realCount;
}
else
{
$this->recordCommentPageCount = 1;
}
}
catch( \Exception $e ) { }
}
else
/* If the database has a custom per page set, use that. */
if ( static::database()->comments_perpage )
{
$this->recordCommentPageCount = parent::commentPageCount( $recache );
}
/* Otherwise do the normal stuff */
else
{
if ( $this->useForumComments() )
{
try
{
$topic = $this->topic();
if( $topic !== NULL )
{
/* Store the real count so it is not accidentally written as the actual value */
$realCount = $topic->posts;
/* Compensate for the first post (which is actually the record) */
$topic->posts = ( $topic->posts - 1 ) > 0 ? $topic->posts - 1 : 0;
/* Get our page count considering all of that */
$this->recordCommentPageCount = $topic->commentPageCount();
/* Reset the count back to the real count */
$topic->posts = $realCount;
}
else
{
$this->recordCommentPageCount = 1;
}
}
catch( \Exception $e ) { }
}
else
{
$this->recordCommentPageCount = parent::commentPageCount( $recache );
}
}
}
return $this->recordCommentPageCount;
@@ -3232,6 +3241,25 @@ class _Records extends \IPS\Content\Item implements
return TRUE;
}
/**
* Get the club of this record's container if there is one
*
* @return \IPS\Member\Club|null
*/
public function get_club() : \IPS\Member\Club|null
{
static $club = false;
if ( $club !== null and !( $club instanceof \IPS\Member\Club ) )
{
$club = null;
if ( $container = $this->container() and $container instanceof \IPS\cms\Categories )
{
$club = $container->_club;
}
}
return $club;
}
/**
* Could edit an item?
* Useful to see if one can edit something even if the cut off has expired
@@ -3378,6 +3406,46 @@ class _Records extends \IPS\Content\Item implements
return $member->canAccessModule( \IPS\Application\Module::get( static::$application, 'database', 'front' ) );
}
/**
* 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 )
{
$return = parent::canCreate( $member, $container, $showError );
if( $return )
{
/* Check for title and content field permissions */
try
{
$class = '\IPS\cms\Fields' . static::database()->id;
$title = $class::load( static::database()->field_title );
$content = $class::load( static::database()->field_content );
$return = $title->can( 'add', $member ) and $content->can( 'add', $member );
}
catch( \Exception $e )
{
$return = FALSE;
}
}
/* Return */
if ( $showError and !$return )
{
$error = 'cms_no_title_content_permission';
\IPS\Output::i()->error( $error, '2C137/3', 403 );
}
return (bool) $return;
}
/**
* Already reviewed?
*
@@ -3813,7 +3881,7 @@ class _Records extends \IPS\Content\Item implements
* @apiresponse string description Event description
* @apiresponse int comments Number of comments
* @apiresponse int reviews Number of reviews
* @apiresponse int views Number of posts
* @apiresponse int views Number of views
* @apiresponse string prefix The prefix tag, if there is one
* @apiresponse [string] tags The tags
* @apiresponse bool locked Event is locked
@@ -153,6 +153,16 @@ class _Review extends \IPS\Content\Review implements \IPS\Content\EditHistory, \
return $review;
}
/**
* WHERE clause for getting items for ACP overview statistics
*
* @return array
*/
public static function overviewStatisticsWhere()
{
return [ static::commentWhere() ];
}
/**
* Return custom where for SQL delete
*