Version 4.7.0

This commit is contained in:
Neo committed 2025-12-19 16:00:35 -08:00
1 parent 517a5e1f70
commit 8ba7d43898
1933 files changed
+65613 -11332

No files matched your search

+127 -18
View File
@@ -117,7 +117,8 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
'bw_disable_prefixes' => 2,
'bw_enable_answers' => 4,
'bw_enable_answers_member' => 8,
'bw_enable_answers_moderator' => 16
'bw_enable_answers_moderator' => 16,
'bw_fluid_view' => 32,
)
)
);
@@ -234,7 +235,22 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
{
$this->topics = (int) $val;
}
/**
* [Node] Return the custom badge for each row
*
* @return NULL|array Null for no badge, or an array of badge data (0 => CSS class type, 1 => language string, 2 => optional raw HTML to show instead of language string)
*/
protected function get__badge()
{
if( $this->isCombinedView() )
{
return array( 0 => 'positive ipsPos_right', 1 => 'combined_fluid_view' );
}
return parent::get__badge();
}
/**
* Get number of comments
*
@@ -641,7 +657,11 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
{
if ( $this->skin_id )
{
\IPS\Theme::switchTheme( $this->skin_id );
try
{
\IPS\Theme::switchTheme( $this->skin_id );
}
catch ( \Exception $e ) { }
}
if ( !$this->viglink )
@@ -703,7 +723,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
if ( \in_array( $permissionCheck, array( 'read', 'add' ) ) )
{
if ( static::hasPasswordForums() )
if ( static::customPermissionNodes() )
{
$whereString = 'password=? OR ' . \IPS\Db::i()->findInSet( 'forums_forums.password_override', $member->groups );
$whereParams = array( NULL );
@@ -951,7 +971,14 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
$form->add( new \IPS\Helpers\Form\Upload( 'forum_card_image', $this->card_image ? \IPS\File::get( 'forums_Cards', $this->card_image ) : NULL, FALSE, array( 'image' => array( 'maxWidth' => 800, 'maxHeight' => 800 ), 'storageExtension' => 'forums_Cards', 'allowStockPhotos' => TRUE ), NULL, NULL, NULL, 'forum_card_image' ) );
$form->add( new \IPS\Helpers\Form\Url( 'forum_redirect_url', $this->id ? $this->redirect_url : array(), FALSE, array( 'placeholder' => 'http://www.example.com/' ), NULL, NULL, NULL, 'forum_redirect_url' ) );
$form->add( new \IPS\Helpers\Form\Url( 'forum_redirect_url', $this->id ? $this->redirect_url : array(), FALSE, array( 'placeholder' => 'http://www.example.com/' ),
function( $val )
{
if ( !$val and \IPS\Request::i()->forum_type === 'redirect' )
{
throw new \DomainException('form_required');
}
}, NULL, NULL, 'forum_redirect_url' ) );
$form->add( new \IPS\Helpers\Form\Number( 'forum_redirect_hits', $this->id ? $this->redirect_hits : 0, FALSE, array(), NULL, NULL, NULL, 'forum_redirect_hits' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_password_on', $this->id ? ( $this->password !== NULL ) : FALSE, FALSE, array( 'togglesOn' => array( 'forum_password', 'forum_password_override' ) ), NULL, NULL, NULL, 'forum_password_on' ) );
$form->add( new \IPS\Helpers\Form\Password( 'forum_password', $this->password, FALSE, array(), NULL, NULL, NULL, 'forum_password' ) );
@@ -1022,6 +1049,19 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
),
)
) ) );
/* Show the combined fluid view option if we have children */
$disabledFluidView = FALSE;
if ( ! $this->hasChildren() )
{
$disabledFluidView = TRUE;
$this->forums_bitoptions['bw_fluid_view'] = FALSE;
\IPS\Member::loggedIn()->language()->words['bw_fluid_view_warning'] = \IPS\Member::loggedIn()->language()->addToStack( 'bw_fluid_view__warning' );
}
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_fluid_view', $this->id ? $this->forums_bitoptions['bw_fluid_view'] : FALSE, FALSE, array( 'disabled' => $disabledFluidView ), NULL, NULL, NULL, 'bw_fluid_view' ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'forum_rules_title', NULL, FALSE, array( 'app' => 'forums', 'key' => ( $this->id ? "forums_forum_{$this->id}_rulestitle" : NULL ) ), NULL, NULL, NULL, 'forum_rules_title' ) );
$form->add( new \IPS\Helpers\Form\Translatable( 'forum_rules_text', NULL, FALSE, array( 'app' => 'forums', 'key' => ( $this->id ? "forums_forum_{$this->id}_rules" : NULL ), 'editor' => array( 'app' => 'forums', 'key' => 'Forums', 'autoSaveKey' => ( $this->id ? "forums-rules-{$this->id}" : "forums-new-rules" ), 'attachIds' => $this->id ? array( $this->id, NULL, 'rules' ) : NULL ) ), NULL, NULL, NULL, 'forum_rules_text' ) );
@@ -1148,7 +1188,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
if( !isset( $values['forum_type'] ) AND isset( $values['bw_enable_answers__member'] ) )
{
/* We don't want to copy this to QA forums */
if( $this->forum_type == 'normal' )
if( $this->_forum_type == 'normal' )
{
$values['bw_enable_answers_member'] = $values['bw_enable_answers__member'];
}
@@ -1169,11 +1209,11 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
}
/* Bitwise */
foreach ( array( 'bw_disable_tagging', 'bw_disable_prefixes', 'bw_enable_answers_member', 'bw_enable_answers_moderator' ) as $k )
foreach ( array( 'bw_disable_tagging', 'bw_disable_prefixes', 'bw_enable_answers_member', 'bw_enable_answers_moderator', 'bw_fluid_view' ) as $k )
{
if( isset( $values[ $k ] ) )
{
$values['forums_bitoptions'][ $k ] = ( $k == 'bw_enable_answers_member' or $k == 'bw_enable_answers_moderator' ) ? $values[ $k ] : !$values[ $k ];
$values['forums_bitoptions'][ $k ] = ( $k == 'bw_enable_answers_member' or $k == 'bw_enable_answers_moderator' or $k == 'bw_fluid_view' ) ? $values[ $k ] : !$values[ $k ];
unset( $values[ $k ] );
}
}
@@ -1301,7 +1341,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
*/
public function postSaveForm( $values )
{
unset( \IPS\Data\Store::i()->forumsHavePasswords );
unset( \IPS\Data\Store::i()->forumsCustomNodes );
\IPS\File::claimAttachments( 'forums-new-forum', $this->id, NULL, 'description', TRUE );
\IPS\File::claimAttachments( 'forums-new-permerror', $this->id, NULL, 'permerror', TRUE );
@@ -1357,7 +1397,7 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
}
catch( \Exception $ex ) { }
unset( \IPS\Data\Store::i()->forumsHavePasswords );
unset( \IPS\Data\Store::i()->forumsCustomNodes );
parent::delete();
@@ -1842,6 +1882,11 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
$return['permissions'] = $this->permissions();
}
if ( \IPS\IPS::classUsesTrait( \get_called_class(), 'IPS\Content\ClubContainer' ) AND $this->club() )
{
$return['public'] = ( $this->isPublic() ) ? 1 : 0;
}
return $return;
}
@@ -1853,12 +1898,17 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
* @param \IPS\Helpers\Form $form Form object
* @return void
*/
public function clubForm( \IPS\Helpers\Form $form )
public function clubForm( \IPS\Helpers\Form $form, \IPS\Member\Club $club )
{
$itemClass = static::$contentItemClass;
$form->add( new \IPS\Helpers\Form\Text( 'club_node_name', $this->_id ? $this->_title : \IPS\Member::loggedIn()->language()->addToStack( $itemClass::$title . '_pl' ), TRUE, array( 'maxLength' => 255 ) ) );
$form->add( new \IPS\Helpers\Form\Editor( 'club_node_description', $this->_id ? \IPS\Member::loggedIn()->language()->get( static::$titleLangPrefix . $this->_id . '_desc' ) : NULL, FALSE, array( 'app' => 'forums', 'key' => 'Forums', 'autoSaveKey' => ( $this->id ? "forums-forum-{$this->id}" : "forums-new-forum" ), 'attachIds' => $this->id ? array( $this->id, NULL, 'description' ) : NULL, 'minimize' => 'forum_description_placeholder' ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'forum_can_view_others_club', $this->id ? $this->can_view_others : TRUE ) );
if( $club->type == 'closed' )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'club_node_public', $this->id ? $this->isPublic() : FALSE ) );
}
}
/**
@@ -1894,7 +1944,18 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
\IPS\File::claimAttachments( 'forums-new-forum', $this->id, NULL, 'description' );
}
}
/**
* Is this forum set to show topics from this forum and any children in one
* combined 'simple' view?
*
* @return boolean
*/
public function isCombinedView()
{
return (bool) $this->forums_bitoptions['bw_fluid_view'];
}
/* !Simple view */
/**
@@ -2017,18 +2078,34 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
}
/**
* Return if this forum has a password set or not
* Return if this node has custom permissions
*
* @return boolean
* @return null|array
*/
public static function hasPasswordForums()
public static function customPermissionNodes()
{
if ( ! isset( \IPS\Data\Store::i()->forumsHavePasswords ) )
if ( ! isset( \IPS\Data\Store::i()->forumsCustomNodes ) )
{
\IPS\Data\Store::i()->forumsHavePasswords = (bool) \IPS\Db::i()->select( 'COUNT(*)', 'forums_forums', array( 'password IS NOT NULL' ) )->first();
$data = [ 'count' => 0, 'password' => [], 'cannotViewOthersItems' => [] ];
foreach( \IPS\Db::i()->select( '*', 'forums_forums', array( 'password IS NOT NULL or can_view_others=0' ) ) as $forum )
{
$data['count']++;
if ( $forum['password'] )
{
$data['password'][] = $forum['id'];
}
if ( ! $forum['can_view_others'] )
{
$data['cannotViewOthersItems'][] = $forum['id'];
}
}
\IPS\Data\Store::i()->forumsCustomNodes = $data;
}
return \IPS\Data\Store::i()->forumsHavePasswords;
return ( \IPS\Data\Store::i()->forumsCustomNodes['count'] ) ? \IPS\Data\Store::i()->forumsCustomNodes : NULL;
}
/**
@@ -2045,6 +2122,11 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
return parent::url();
}
/**
* @brief The class of the ACP \IPS\Node\Controller that manages this node type
*/
protected static $acpController = "IPS\\forums\\modules\\admin\\forums\\forums";
/**
* Get URL from index data
@@ -2063,4 +2145,31 @@ class _Forum extends \IPS\Node\Model implements \IPS\Node\Permissions
return parent::urlFromIndexData( $indexData, $itemData, $containerData );
}
/**
* [Node] Get type
*
* @return string
*/
protected function get__forum_type()
{
if ( $this->redirect_url )
{
$type = 'redirect';
}
elseif ( !$this->sub_can_post )
{
$type = 'category';
}
elseif ( $this->forums_bitoptions['bw_enable_answers'] )
{
$type = 'qa';
}
else
{
$type = 'normal';
}
return $type;
}
}
@@ -40,23 +40,16 @@ class _ArchivedPost extends Post
public static function db()
{
/* Connect to the remote DB if needed */
if ( \IPS\CIC2 )
{
return \IPS\Cicloud\getForumArchiveDb();
}
else
{
return ( !\IPS\Settings::i()->archive_remote_sql_host ) ? \IPS\Db::i() : \IPS\Db::i( 'archive', array(
'sql_host' => \IPS\Settings::i()->archive_remote_sql_host,
'sql_user' => \IPS\Settings::i()->archive_remote_sql_user,
'sql_pass' => \IPS\Settings::i()->archive_remote_sql_pass,
'sql_database' => \IPS\Settings::i()->archive_remote_sql_database,
'sql_port' => \IPS\Settings::i()->archive_sql_port,
'sql_socket' => \IPS\Settings::i()->archive_sql_socket,
'sql_tbl_prefix'=> \IPS\Settings::i()->archive_sql_tbl_prefix,
'sql_utf8mb4' => isset( \IPS\Settings::i()->sql_utf8mb4 ) ? \IPS\Settings::i()->sql_utf8mb4 : FALSE
) );
}
return ( !\IPS\Settings::i()->archive_remote_sql_host ) ? \IPS\Db::i() : \IPS\Db::i( 'archive', array(
'sql_host' => \IPS\Settings::i()->archive_remote_sql_host,
'sql_user' => \IPS\Settings::i()->archive_remote_sql_user,
'sql_pass' => \IPS\Settings::i()->archive_remote_sql_pass,
'sql_database' => \IPS\Settings::i()->archive_remote_sql_database,
'sql_port' => \IPS\Settings::i()->archive_sql_port,
'sql_socket' => \IPS\Settings::i()->archive_sql_socket,
'sql_tbl_prefix'=> \IPS\Settings::i()->archive_sql_tbl_prefix,
'sql_utf8mb4' => isset( \IPS\Settings::i()->sql_utf8mb4 ) ? \IPS\Settings::i()->sql_utf8mb4 : FALSE
) );
}
/**
@@ -91,8 +84,8 @@ class _ArchivedPost extends Post
* @brief Bitwise values for post_bwoptions field
*/
public static $bitOptions = array(
'bwoptions' => array(
'bwoptions' => array(
'post_bwoptions' => array(
'post_bwoptions' => array(
'best_answer' => 1
)
)
@@ -102,28 +95,38 @@ class _ArchivedPost extends Post
* @brief Database Column ID
*/
public static $databaseColumnId = 'id';
/**
* Post count for member
*
* @param \IPS\Member $member The memner
* @param \IPS\Member $member The member
* @param bool $includeNonPostCountIncreasing If FALSE, will skip any posts which would not cause the user's post count to increase
* @param bool $includeHiddenAndPendingApproval If FALSE, will skip any hidden posts, or posts pending approval
* @return int
*/
public static function memberPostCount( \IPS\Member $member, bool $includeNonPostCountIncreasing = FALSE, bool $includeHiddenAndPendingApproval = TRUE )
{
$where = [];
$where[] = [ 'archive_author_id=?', $member->member_id ];
if ( !$includeNonPostCountIncreasing )
try
{
$where[] = [ \IPS\Db::i()->in( 'archive_forum_id', iterator_to_array( \IPS\Db::i()->select( 'id', 'forums_forums', 'inc_postcount=0' ) ) ) ];
if ( !$includeNonPostCountIncreasing )
{
$where[] = [ static::db()->in( 'archive_forum_id', iterator_to_array( \IPS\Db::i()->select( 'id', 'forums_forums', 'inc_postcount=1' ) ) ) ];
}
if ( !$includeHiddenAndPendingApproval )
{
$where[] = [ 'archive_queued=0' ];
}
return static::db()->select( 'COUNT(*)', static::$databaseTable, $where )->first();
}
if ( !$includeHiddenAndPendingApproval )
catch ( \IPS\Db\Exception $e )
{
$where[] = [ 'archive_queued=0' ];
return 0;
}
return \IPS\Db::i()->select( 'COUNT(*)', 'forums_posts', $where )->first();
}
/**
@@ -207,4 +210,4 @@ class _ArchivedPost extends Post
{
return FALSE;
}
}
}
+18 -2
View File
@@ -110,6 +110,21 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
*/
public static $joinProfileFields = TRUE;
/**
* @brief Used for the datalayer
*/
public static $commentType = 'reply';
/**
* @brief Value to set for the 'tab' parameter when redirecting to the comment (via _find())
*/
public static $tabParameter = NULL; /* We do not want ?tab=comments appended to links in from findComment links */
/**
* @brief Whether to track the sentiment scores in cloud_sentiment_scores for this content
*/
public $trackSentimentScore = true;
/**
* Create comment
*
@@ -122,11 +137,12 @@ class _Post extends \IPS\Content\Comment implements \IPS\Content\EditHistory, \I
* @param \IPS\DateTime|NULL $time The time
* @param string|NULL $ipAddress The IP address or NULL to detect automatically
* @param int|NULL $hiddenStatus NULL to set automatically or override: 0 = unhidden; 1 = hidden, pending moderator approval; -1 = hidden (as if hidden by a moderator)
* @param int|NULL $anonymous NULL for no value, 0 or 1 for a value (0=no, 1=yes)
* @return static
*/
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL )
public static function create( $item, $comment, $first=FALSE, $guestName=NULL, $incrementPostCount=NULL, $member=NULL, \IPS\DateTime $time=NULL, $ipAddress=NULL, $hiddenStatus=NULL, $anonymous=NULL )
{
$comment = parent::create( $item, $comment, $first, $guestName, $incrementPostCount, $member, $time, $ipAddress, $hiddenStatus );
$comment = parent::create( $item, $comment, $first, $guestName, $incrementPostCount, $member, $time, $ipAddress, $hiddenStatus, $anonymous );
if ( !$comment->hidden() )
{
+34 -7
View File
@@ -174,6 +174,11 @@ class _Topic extends \IPS\Content\Item implements
* @brief Setting name for show_meta
*/
public static $showMetaSettingKey = 'forums_topics_show_meta';
/**
* @brief Whether to track the sentiment scores in cloud_sentiment_scores for this content
*/
public $trackSentimentScore = true;
/**
* Callback from \IPS\Http\Url\Inernal::correctUrlFromVerifyClass()
@@ -337,7 +342,7 @@ class _Topic extends \IPS\Content\Item implements
$member = $member ?: \IPS\Member::loggedIn();
$containerClass = static::$containerNodeClass;
if ( $containerClass::hasPasswordForums() )
if ( $containerClass::customPermissionNodes() )
{
$whereString = 'forums_forums.password=? OR ' . \IPS\Db::i()->findInSet( 'forums_forums.password_override', $member->groups );
$whereParams = array( NULL );
@@ -459,6 +464,23 @@ class _Topic extends \IPS\Content\Item implements
{
return parent::contentCount( $container, FALSE, TRUE, $includeReviews, $depth );
}
/**
* Total item, items only \count(including children)
*
* @param \IPS\Node\Model $container The container
* @param bool $includeItems If TRUE, items will be included (this should usually be true)
* @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
* @note This method includes counts of hidden and unapproved content items as well
*/
public static function contentCountItemsOnly( \IPS\Node\Model $container )
{
return parent::contentCount( $container, TRUE, FALSE, FALSE, 0 );
}
/**
* Get elements for add/edit form
@@ -1059,8 +1081,8 @@ class _Topic extends \IPS\Content\Item implements
\IPS\Db::i()->delete( 'forums_question_ratings', array( 'topic=?', $this->tid ) );
\IPS\Db::i()->delete( 'forums_answer_ratings', array( 'topic=?', $this->tid ) );
/* Delete any moved topic links that point to this topic */
\IPS\Db::i()->delete( 'forums_topics', array( "state=? AND moved_to LIKE CONCAT( ?, '%' )", 'link', $this->tid . '&' ) );
/* Delete any moved topic links that point to this topic - moved_on>? is for query optimisation purposes */
\IPS\Db::i()->delete( 'forums_topics', array( "moved_on>? AND moved_to LIKE CONCAT( ?, '%' ) AND state=?", 0, $this->tid . '&', 'link' ) );
}
/**
@@ -1163,8 +1185,8 @@ class _Topic extends \IPS\Content\Item implements
*/
public function hide( $member, $reason = NULL )
{
/* Hide any moved topic links that point to this topic */
foreach( \IPS\Db::i()->select( '*', 'forums_topics', array( "state=? AND moved_to LIKE CONCAT( ?, '%' )", 'link', $this->tid . '&' ) ) as $link )
/* Hide any moved topic links that point to this topic - moved_on>? is for query optimisation purposes */
foreach( \IPS\Db::i()->select( '*', 'forums_topics', array( "moved_on>? AND moved_to LIKE CONCAT( ?, '%' ) AND state=?", 0, $this->tid . '&', 'link' ) ) as $link )
{
\IPS\forums\Topic::constructFromData( $link )->hide( $member, $reason );
}
@@ -1180,8 +1202,8 @@ class _Topic extends \IPS\Content\Item implements
*/
public function unhide( $member )
{
/* Unhide any moved topic links that point to this topic */
foreach( \IPS\Db::i()->select( '*', 'forums_topics', array( "state=? AND moved_to LIKE CONCAT( ?, '%' )", 'link', $this->tid . '&' ) ) as $link )
/* Unhide any moved topic links that point to this topic - moved_on>? is for query optimisation purposes */
foreach( \IPS\Db::i()->select( '*', 'forums_topics', array( "moved_on>? AND moved_to LIKE CONCAT( ?, '%' ) AND state=?", 0, $this->tid . '&', 'link' ) ) as $link )
{
\IPS\forums\Topic::constructFromData( $link )->unhide( $member );
}
@@ -1541,6 +1563,11 @@ class _Topic extends \IPS\Content\Item implements
*/
public static function _indefiniteArticle( array $containerData = NULL, \IPS\Lang $lang = NULL )
{
if ( !$containerData )
{
return parent::_indefiniteArticle( $containerData, $lang );
}
$bitOptions = ( $containerData['forums_bitoptions'] instanceof \IPS\Patterns\Bitwise ) ? $containerData['forums_bitoptions'] : new \IPS\Patterns\Bitwise( array( 'forums_bitoptions' => $containerData['forums_bitoptions'] ), \IPS\forums\Forum::$bitOptions['forums_bitoptions'] );
if ( $bitOptions['bw_enable_answers'] )