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

+98 -48
View File
@@ -11,7 +11,7 @@
namespace IPS\core;
/* 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;
@@ -61,7 +61,7 @@ class _Stream extends \IPS\Node\Model
),
'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"
* @encode
* @endcode
*/
protected static $restrictions = array(
'app' => 'core',
@@ -90,12 +90,13 @@ class _Stream extends \IPS\Node\Model
* @param string|NULL $permissionCheck The permission key to check for or NULl to not check permissions
* @param \IPS\Member|NULL $member The member to check permissions for or NULL for the currently logged in member
* @param mixed $where Additional WHERE clause
* @param array|NULL $limit Limit/offset to use, or NULL for no limit (default)
* @return array
*/
public static function roots( $permissionCheck='view', $member=NULL, $where=array() )
public static function roots( $permissionCheck='view', $member=NULL, $where=array(), $limit=NULL )
{
$where[] = array( 'member IS NULL' );
return parent::roots( $permissionCheck, $member, $where );
$where[] = array( '`member` IS NULL' );
return parent::roots( $permissionCheck, $member, $where, $limit );
}
/**
@@ -230,12 +231,12 @@ class _Stream extends \IPS\Node\Model
return array();
}
if ( ! is_array( $containers ) )
if ( ! \is_array( $containers ) )
{
$containers = json_decode( $containers, true );
}
if ( count( $containers ) )
if ( \count( $containers ) )
{
foreach( $containers as $class => $ids )
{
@@ -257,7 +258,7 @@ class _Stream extends \IPS\Node\Model
*/
static public function containersFromUrl( $containers )
{
if ( is_array( $containers) and count( $containers ) )
if ( \is_array( $containers) and \count( $containers ) )
{
foreach( $containers as $class => $ids )
{
@@ -368,7 +369,7 @@ class _Stream extends \IPS\Node\Model
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
{
if ( isset( $class::$databaseColumnMap['date'] ) )
if ( is_subclass_of( $class, 'IPS\Content\Searchable' ) AND isset( $class::$databaseColumnMap['date'] ) )
{
$classes[ $class ] = $class::$title . '_pl';
if ( isset( $class::$containerNodeClass ) )
@@ -393,7 +394,7 @@ class _Stream extends \IPS\Node\Model
$nodeClass = $class::$containerNodeClass;
$value = isset( $containers->$class ) ? $containers->$class : 0;
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, $value, FALSE, array( 'class' => $nodeClass, 'multiple' => TRUE, 'zeroVal' => 'all', 'permissionCheck' => $nodeClass::searchableNodesPermission(), 'forceOwner' => FALSE, 'subnodes' => FALSE ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, $value, FALSE, array( 'class' => $nodeClass, 'clubs' => TRUE, 'multiple' => TRUE, 'zeroVal' => 'all', 'permissionCheck' => $nodeClass::searchableNodesPermission(), 'forceOwner' => FALSE, 'subnodes' => FALSE ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
$field->label = \IPS\Member::loggedIn()->language()->addToStack( $nodeClass::$nodeTitle );
$form->add( $field );
}
@@ -403,8 +404,9 @@ class _Stream extends \IPS\Node\Model
/* Clubs */
if( \IPS\Settings::i()->clubs )
{
$clubs = \IPS\Member\Club::clubs( NULL, NULL, 'name', FALSE, array(), \IPS\Settings::i()->clubs_require_approval ? array( 'approved=1' ) : NULL );
if ( is_object( $clubs ) AND $clubs->count( TRUE ) )
$clubs = $this->_getOurClubs();
if ( \is_array( $clubs ) AND \count( $clubs ) )
{
$clubOptions = array();
foreach ( $clubs as $club )
@@ -473,12 +475,7 @@ class _Stream extends \IPS\Node\Model
'all' => 'stream_read_all',
'unread' => 'stream_read_unread',
) ) ) );
/* Link to first unread */
$form->add( new \IPS\Helpers\Form\Radio( 'stream_unread_links', $this->unread_links, TRUE, array( 'options' => array(
'0' => 'stream_unread_links_normal',
'1' => 'stream_unread_links_unread',
) ) ) );
/* Follow */
$form->add( new \IPS\Helpers\Form\Radio( 'stream_follow', $this->follow, TRUE, array(
@@ -509,13 +506,13 @@ class _Stream extends \IPS\Node\Model
'custom' => array( 'stream_date_range' )
)
) ) );
$form->add( new \IPS\Helpers\Form\Number( 'stream_date_relative_days', $this->date_relative_days ?: 7, NULL, array(), function( $val )
$form->add( new \IPS\Helpers\Form\Interval( 'stream_date_relative_days', $this->date_relative_days ?: 7, NULL, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS ), function( $val )
{
if ( \IPS\Request::i()->stream_date_type == 'relative' and !$val )
{
throw new \DomainException('form_required');
}
}, \IPS\Member::loggedIn()->language()->addToStack('stream_date_relative_days_prefix'), \IPS\Member::loggedIn()->language()->addToStack('days'), 'stream_date_relative_days' ) );
}, \IPS\Member::loggedIn()->language()->addToStack('stream_date_relative_days_prefix'), NULL, 'stream_date_relative_days' ) );
$form->add( new \IPS\Helpers\Form\DateRange( 'stream_date_range', array( 'start' => $this->date_start, 'end' => $this->date_end ), NULL, array(), function( $val )
{
if ( \IPS\Request::i()->stream_date_type == 'custom' and !$val['start'] and !$val['end'] )
@@ -541,6 +538,28 @@ class _Stream extends \IPS\Node\Model
$form->add( new \IPS\Helpers\Form\YesNo( 'stream_default', $this->default, FALSE ) );
}
}
/**
* @brief Cached clubs
*/
protected static $ourClubs = NULL;
/**
* Fetch our clubs and cache
*
* @return array
*/
protected function _getOurClubs()
{
if( static::$ourClubs === NULL )
{
static::$ourClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), NULL, 'name', TRUE, array(), \IPS\Settings::i()->clubs_require_approval ? array( 'approved=1' ) : NULL );
static::$ourClubs = static::$ourClubs ? iterator_to_array( static::$ourClubs ) : array();
}
return static::$ourClubs;
}
/**
* [Node] Format form values from add/edit form for save
@@ -555,7 +574,7 @@ class _Stream extends \IPS\Node\Model
{
$this->save();
}
if ( isset( $values['stream_title'] ) and is_array( $values['stream_title'] ) )
if ( isset( $values['stream_title'] ) and \is_array( $values['stream_title'] ) )
{
\IPS\Lang::saveCustom( 'core', "stream_title_{$this->id}", $values['stream_title'] );
$values['stream_title'] = NULL;
@@ -569,7 +588,7 @@ class _Stream extends \IPS\Node\Model
$values['stream_classes'] = NULL;
$values['stream_containers'] = NULL;
}
elseif ( ( isset( $values['stream_classes'] ) && is_array( $values['stream_classes'] ) ) )
elseif ( ( isset( $values['stream_classes'] ) && \is_array( $values['stream_classes'] ) ) )
{
$classes = array();
$containers = NULL;
@@ -608,7 +627,7 @@ class _Stream extends \IPS\Node\Model
case 'select':
if ( $values['stream_club_filter'] )
{
$values['stream_clubs'] = is_array( $values['stream_club_filter'] ) ? implode( ',', $values['stream_club_filter'] ) : $values['stream_club_filter'];
$values['stream_clubs'] = \is_array( $values['stream_club_filter'] ) ? implode( ',', $values['stream_club_filter'] ) : $values['stream_club_filter'];
}
else
{
@@ -634,7 +653,7 @@ class _Stream extends \IPS\Node\Model
}
else
{
$values['stream_tags'] = ( is_array( $values['stream_tags'] ) ) ? implode( ',', $values['stream_tags'] ) : $values['stream_tags'];
$values['stream_tags'] = ( \is_array( $values['stream_tags'] ) ) ? implode( ',', $values['stream_tags'] ) : $values['stream_tags'];
}
}
@@ -734,15 +753,15 @@ class _Stream extends \IPS\Node\Model
{
if ( class_exists( $class ) )
{
if ( in_array( 'IPS\Content\Review', class_parents( $class ) ) )
if ( \in_array( 'IPS\Content\Review', class_parents( $class ) ) )
{
$classes[ $class::$itemClass ]['reviews'] = $class;
}
elseif ( in_array( 'IPS\Content\Comment', class_parents( $class ) ) )
elseif ( \in_array( 'IPS\Content\Comment', class_parents( $class ) ) )
{
$classes[ $class::$itemClass ]['comments'] = $class;
}
elseif ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
elseif ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$classes[ $class ]['items'] = $class;
}
@@ -760,7 +779,7 @@ class _Stream extends \IPS\Node\Model
}
$_types = \IPS\Member::loggedIn()->language()->formatList( $_types );
if ( isset( $allowedContainers[ $itemClass ] ) )
if ( isset( $allowedContainers[ $itemClass ] ) and isset( $itemClass::$containerNodeClass ) )
{
$containers = array();
$containerClass = $itemClass::$containerNodeClass;
@@ -810,7 +829,7 @@ class _Stream extends \IPS\Node\Model
}
catch ( \Exception $e ) { }
}
if ( count( $clubs ) )
if ( \count( $clubs ) )
{
$terms[] = \IPS\Member::loggedIn()->language()->addToStack( 'stream_blurb_in_clubs', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $clubs ) ) ) );
}
@@ -839,7 +858,7 @@ class _Stream extends \IPS\Node\Model
case 'custom':
$memberNames = array();
$members = ( ! is_array( $this->custom_members ) ? explode( ',', $this->custom_members ) : $this->custom_members );
$members = ( ! \is_array( $this->custom_members ) ? explode( ',', $this->custom_members ) : $this->custom_members );
foreach ( $members as $member )
{
if ( ! ( $member instanceof \IPS\Member ) )
@@ -855,7 +874,7 @@ class _Stream extends \IPS\Node\Model
$memberNames[] = $member->name;
}
}
if ( count( $memberNames ) )
if ( \count( $memberNames ) )
{
$terms[] = \IPS\Member::loggedIn()->language()->addToStack( 'stream_blurb_by_members', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $memberNames, \IPS\Member::loggedIn()->language()->get('or_list_format') ) ) ) );
}
@@ -892,7 +911,16 @@ class _Stream extends \IPS\Node\Model
$terms[] = \IPS\Member::loggedIn()->language()->addToStack('stream_blurb_since_last_visit');
break;
case 'relative':
$terms[] = \IPS\Member::loggedIn()->language()->addToStack( 'stream_blurb_relative', FALSE, array( 'sprintf' => array( $this->date_relative_days ) ) );
if( \IPS\Request::i()->stream_date_relative_days )
{
$period = ( \IPS\Request::i()->stream_date_relative_days['unit'] == 'd' ) ? \IPS\Member::loggedIn()->language()->addToStack( 'f_days', FALSE, array( 'pluralize' => array( \IPS\Request::i()->stream_date_relative_days['val'] ) ) ) : \IPS\Member::loggedIn()->language()->addToStack( 'f_weeks', FALSE, array( 'pluralize' => array( \IPS\Request::i()->stream_date_relative_days['val'] ) ) );
}
else
{
$period = \IPS\Member::loggedIn()->language()->addToStack( 'f_days', FALSE, array( 'pluralize' => array( $this->date_relative_days ) ) );
}
$terms[] = \IPS\Member::loggedIn()->language()->addToStack( 'stream_blurb_relative', FALSE, array( 'sprintf' => array( $period ) ) );
break;
case 'custom':
if ( $this->date_start and $this->date_end )
@@ -910,7 +938,7 @@ class _Stream extends \IPS\Node\Model
break;
}
if ( count( $terms ) )
if ( \count( $terms ) )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'steam_blurb_with_terms', FALSE, array( 'sprintf' => array( $type, \IPS\Member::loggedIn()->language()->formatList( $terms ) ) ) );
}
@@ -940,7 +968,6 @@ class _Stream extends \IPS\Node\Model
'stream_ownership' => $this->ownership,
'stream_date_type' => $this->date_type,
'stream_classes' => NULL,
'stream_unread_links' => $this->unread_links,
'containers' => static::containersToUrl( $this->containers ),
);
@@ -965,7 +992,8 @@ class _Stream extends \IPS\Node\Model
$followedTypes = explode( ',', $this->followed_types );
$config['stream_followed_types'] = array();
foreach( $followedTypes as $type ){
foreach( $followedTypes as $type )
{
if ( $type )
{
$config['stream_followed_types'][ $type ] = 1;
@@ -980,15 +1008,15 @@ class _Stream extends \IPS\Node\Model
{
if ( class_exists( $class ) )
{
if ( in_array( 'IPS\Content\Review', class_parents( $class ) ) )
if ( \in_array( 'IPS\Content\Review', class_parents( $class ) ) )
{
$classes[ $class::$itemClass ]['reviews'] = $class;
}
elseif ( in_array( 'IPS\Content\Comment', class_parents( $class ) ) )
elseif ( \in_array( 'IPS\Content\Comment', class_parents( $class ) ) )
{
$classes[ $class::$itemClass ]['comments'] = $class;
}
elseif ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
elseif ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$classes[ $class ]['items'] = $class;
}
@@ -1013,11 +1041,18 @@ class _Stream extends \IPS\Node\Model
if( $this->ownership == 'custom' and $this->custom_members )
{
/* Values are store as int in the db, but the json needs names so it matches the form data */
try
if( mb_strpos( $this->custom_members, ',' ) === FALSE AND $this->custom_members == \IPS\Member::loggedIn()->member_id )
{
$config['stream_custom_members'] = iterator_to_array( \IPS\Db::i()->select( 'name', 'core_members', array( \IPS\Db::i()->in( 'member_id', explode( ',', $this->custom_members ) ) ) )->setValueField('name') );
$config['stream_custom_members'] = \IPS\Member::loggedIn()->name;
}
else
{
try
{
$config['stream_custom_members'] = iterator_to_array( \IPS\Db::i()->select( 'name', 'core_members', array( \IPS\Db::i()->in( 'member_id', explode( ',', $this->custom_members ) ) ) )->setValueField('name') );
}
catch( \Exception $e ) { }
}
catch( \Exception $e ) { }
}
/* Tags */
@@ -1034,8 +1069,8 @@ class _Stream extends \IPS\Node\Model
elseif( $this->date_type == 'custom' )
{
$config['stream_date_range'] = array(
'start' => intval( $this->date_start ),
'end' => intval( $this->date_end )
'start' => \intval( $this->date_start ),
'end' => \intval( $this->date_end )
);
}
@@ -1097,7 +1132,7 @@ class _Stream extends \IPS\Node\Model
foreach ( $classes as $class => $options )
{
/* Init */
if ( ! $this->include_comments or ( isset( $options['items'] ) and !isset( $options['comments'] ) and in_array( 'IPS\Content\Item', class_parents( $class ) ) and $class::$firstCommentRequired ) )
if ( ! $this->include_comments or ( isset( $options['items'] ) and !isset( $options['comments'] ) and \in_array( 'IPS\Content\Item', class_parents( $class ) ) and $class::$firstCommentRequired ) )
{
/* items with no comments are flagged as being the last comment */
$filter = \IPS\Content\Search\ContentFilter::init( $class, TRUE, TRUE, TRUE )->onlyLastComment();
@@ -1124,7 +1159,7 @@ class _Stream extends \IPS\Node\Model
{
foreach ( $object->classes as $class )
{
if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
if ( \in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
if( !$this->include_comments )
{
@@ -1143,7 +1178,7 @@ class _Stream extends \IPS\Node\Model
$itemClass::searchEngineFiltering( $filters, $query );
}
if ( count( $filters ) )
if ( \count( $filters ) )
{
$query->filterByContent( $filters );
}
@@ -1184,7 +1219,7 @@ class _Stream extends \IPS\Node\Model
if ( $this->follow == 'followed' )
{
$followTypes = explode( ',', $this->followed_types );
$query->filterByFollowed( in_array( 'containers', $followTypes ), in_array( 'items', $followTypes ), in_array( 'members', $followTypes ) );
$query->filterByFollowed( \in_array( 'containers', $followTypes ), \in_array( 'items', $followTypes ), \in_array( 'members', $followTypes ) );
}
/* If we are showing all items (not grouping on last comment, then we need to filter by creation date */
@@ -1197,7 +1232,7 @@ class _Stream extends \IPS\Node\Model
$query->$filterDateMethod( \IPS\DateTime::ts( \IPS\Member::loggedIn()->last_visit ) );
break;
case 'relative':
$query->$filterDateMethod( \IPS\DateTime::create()->sub( new \DateInterval( 'P' . intval( $this->date_relative_days ) . 'D' ) ) );
$query->$filterDateMethod( \IPS\DateTime::create()->sub( new \DateInterval( 'P' . \intval( $this->date_relative_days ) . 'D' ) ) );
case 'custom':
$query->$filterDateMethod( $this->date_start ? \IPS\DateTime::ts( $this->date_start ) : NULL, $this->date_end ? \IPS\DateTime::ts( $this->date_end ) : NULL );
break;
@@ -1276,6 +1311,21 @@ class _Stream extends \IPS\Node\Model
}
else
{
/* If it's in the menu, remove it */
foreach( \IPS\Db::i()->select( '*', 'core_menu', array( "extension=?", 'YourActivityStreamsItem' ) ) AS $row )
{
$config = json_decode( $row['config'], true );
if ( isset( $config['menu_stream_id'] ) )
{
if ( $config['menu_stream_id'] == $this->_id )
{
\IPS\Db::i()->delete( 'core_menu', array( 'id=?', $row['id'] ) );
\IPS\Lang::deleteCustom( 'core', "menu_item_{$row['id']}" );
}
}
}
unset( \IPS\Data\Store::i()->frontNavigation );
parent::delete();
unset( \IPS\Data\Store::i()->globalStreamIds );