Version 4.1.19

This commit is contained in:
Neo committed 2025-12-19 05:38:56 -08:00
1 parent 28bd025b35
commit 2bd5025018
2674 files changed
+233518 -77766

No files matched your search

+403 -85
View File
@@ -2,9 +2,9 @@
/**
* @brief Content Discovery Stream
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @package IPS Community Suite
* @since 1 Jul 2015
* @version SVN_VERSION_NUMBER
*/
@@ -70,11 +70,21 @@ class _Stream extends \IPS\Node\Model
'all' => 'streams_manage',
);
/**
* @brief The base URL of this stream
*/
public $baseUrl = NULL;
/**
* @brief The default stream either set by member or admin
*/
protected static $defaultStream = NULL;
/**
* @brief The config of the stream when it is first loaded
*/
protected $defaultConfig = NULL;
/**
* Fetch All Root Nodes
*
@@ -96,6 +106,12 @@ class _Stream extends \IPS\Node\Model
*/
public static function defaultStream()
{
/* If we've already loaded it, return it */
if( static::$defaultStream !== NULL )
{
return static::$defaultStream;
}
/* Check the member first */
if ( \IPS\Member::loggedIn()->member_id )
{
@@ -107,24 +123,58 @@ class _Stream extends \IPS\Node\Model
{
if ( $default )
{
return static::load( $default );
static::$defaultStream = static::load( $default );
}
else
{
return static::allActivityStream();
static::$defaultStream = static::allActivityStream();
}
}
catch( \Exception $e )
{
return NULL;
}
return static::$defaultStream;
}
}
/* Still here? Check menu */
try
{
return static::load( \IPS\Db::i()->select( 'id', 'core_streams', array( array( '`default`=?', 1 ) ) )->first() );
if ( !isset( \IPS\Data\Store::i()->defaultStreamData ) )
{
try
{
\IPS\Data\Store::i()->defaultStreamData = \IPS\Db::i()->select( '*', 'core_streams', array( array( '`default`=?', 1 ) ) )->first();
}
catch ( \UnderflowException $e )
{
\IPS\Data\Store::i()->defaultStreamData = 0;
}
}
if ( \IPS\Data\Store::i()->defaultStreamData == 0 )
{
static::$defaultStream = static::allActivityStream();
return static::$defaultStream;
}
$stream = static::constructFromData( \IPS\Data\Store::i()->defaultStreamData );
/* Suitable for guests? */
if ( ! \IPS\Member::loggedIn()->member_id )
{
if ( ! ( ( $stream->ownership == 'all' and $stream->read == 'all' and $stream->follow == 'all' and $stream->date_type != 'last_visit' ) ) )
{
static::$defaultStream = static::allActivityStream();
return static::$defaultStream;
}
}
static::$defaultStream = $stream;
return $stream;
}
catch( \Exception $e )
{
@@ -142,9 +192,130 @@ class _Stream extends \IPS\Node\Model
$stream = new static;
$stream->id = 0;
$stream->include_comments = TRUE;
$stream->date_relative_days = 365;
$stream->date_type = 'relative';
$stream->baseUrl = \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' );
return $stream;
}
/**
* Construct ActiveRecord from database row
*
* @param array $data Row from database table
* @param bool $updateMultitonStoreIfExists Replace current object in multiton store if it already exists there?
* @return static
*/
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
{
$obj = parent::constructFromData( $data, $updateMultitonStoreIfExists );
$obj->initBaseUrl();
$obj->defaultConfig = $obj->config();
return $obj;
}
/**
* The Javascript code handles containers slightly differently
* So we need to convert them ready for use in the URL so javascript can read them as native
* Javascript returns containers[class] = 1,2,3
* The PHP code expects json_encode( containers[class] = array(1,2,3) )
*
* @param string|array $containers Containers to convert
* @return array
*/
static public function containersToUrl( $containers )
{
if ( $containers === NULL )
{
return array();
}
if ( ! is_array( $containers ) )
{
$containers = json_decode( $containers, true );
}
if ( count( $containers ) )
{
foreach( $containers as $class => $ids )
{
$containers[ $class ] = implode( ',', $ids );
}
}
return $containers;
}
/**
* The Javascript code handles containers slightly differently
* This converts javascript formatted containers into PHP native
* Javascript returns containers[class] = 1,2,3
* The PHP code expects json_encode( containers[class] = array(1,2,3) )
*
* @param array $containers Containers to convert
* @return string
*/
static public function containersFromUrl( $containers )
{
if ( is_array( $containers) and count( $containers ) )
{
foreach( $containers as $class => $ids )
{
$containers[ $class ] = explode( ',', $ids );
}
}
return json_encode( $containers );
}
/**
* Initilize the base url
*
* @return void
*/
protected function initBaseUrl()
{
$this->baseUrl = $this->getBaseUrl();
}
/**
* Fetch the base url
*
* @return void
*/
public function getBaseUrl()
{
if ( $this->id )
{
switch ( $this->id )
{
case 1:
$furlKey = 'discover_unread';
break;
case 2:
$furlKey = 'discover_istarted';
break;
case 3:
$furlKey = 'discover_followed';
break;
case 4:
$furlKey = 'discover_following';
break;
case 5:
$furlKey = 'discover_posted';
break;
default:
$furlKey = 'discover_stream';
break;
}
return \IPS\Http\Url::internal( "app=core&module=discover&controller=streams&id={$this->id}", 'front', $furlKey );
}
else
{
return \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' );
}
}
/**
* [Node] Get Title
*
@@ -193,28 +364,18 @@ class _Stream extends \IPS\Node\Model
/* Work out all the different classes */
$classes = array();
$classContainers = array();
$classToggles = array();
foreach ( \IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
{
$classes[ $class ] = $class::$title . '_pl';
if ( isset( $class::$containerNodeClass ) )
{
$classContainers[] = $class;
$classToggles[ $class ][] = 'stream_containers_' . $class::$title;
}
}
/* Add the fields for them */
$currentContainers = $this->containers ? json_decode( $this->containers, TRUE ) : array();
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'stream_classes', $this->classes ? explode( ',', $this->classes ) : array(), NULL, array( 'options' => $classes, 'toggles' => $classToggles ), NULL, NULL, NULL, 'stream_classes' ) );
foreach ( $classContainers as $class )
{
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, isset( $currentContainers[ $class ] ) ? $currentContainers[ $class ] : array(), NULL, array( 'class' => $class::$containerNodeClass, 'multiple' => TRUE, 'permissionCheck' => 'view' ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
$containerClass = $class::$containerNodeClass;
$field->label = \IPS\Member::loggedIn()->language()->addToStack( $containerClass::$nodeTitle );
$form->add( $field );
}
/* Tags */
if ( \IPS\Settings::i()->tags_enabled )
@@ -228,7 +389,7 @@ class _Stream extends \IPS\Node\Model
'custom' => array( 'stream_tags' )
)
) ) );
$form->add( new \IPS\Helpers\Form\Text( 'stream_tags', $this->tags ? explode( ',', $this->tags ) : NULL, NULL, array( 'autocomplete' => array() ), NULL, NULL, NULL, 'stream_tags' ) );
$form->add( new \IPS\Helpers\Form\Text( 'stream_tags', $this->tags ? explode( ',', $this->tags ) : NULL, NULL, array( 'autocomplete' => true ), NULL, NULL, NULL, 'stream_tags' ) );
}
/* Ownership */
@@ -251,6 +412,12 @@ class _Stream extends \IPS\Node\Model
'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(
'options' => array(
@@ -261,7 +428,7 @@ class _Stream extends \IPS\Node\Model
'followed' => array( 'stream_followed_types' )
)
) ) );
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'stream_followed_types', $this->followed_type ? explode( ',', $this->followed_types ) : array( 'items' ), NULL, array( 'options' => array(
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'stream_followed_types', $this->followed_types ? explode( ',', $this->followed_types ) : array( 'items' ), NULL, array( 'options' => array(
'containers' => 'stream_followed_types_areas',
'items' => 'stream_followed_types_items',
'members' => 'stream_followed_types_members',
@@ -294,6 +461,12 @@ class _Stream extends \IPS\Node\Model
throw new \DomainException('form_required');
}
}, NULL, NULL, 'stream_date_range' ) );
/* Default View */
$form->add( new \IPS\Helpers\Form\Radio( 'stream_default_view', $this->default_view, TRUE, array( 'options' => array(
'condensed' => 'stream_default_view_condensed',
'expanded' => 'stream_default_view_expanded',
) ) ) );
/* Sort */
$form->add( new \IPS\Helpers\Form\Radio( 'stream_sort', $this->sort, TRUE, array( 'options' => array(
@@ -316,7 +489,7 @@ class _Stream extends \IPS\Node\Model
public function formatFormValues( $values )
{
/* Title */
if ( !$this->id )
if ( ! $this->id and $this->id !== 0 )
{
$this->save();
}
@@ -326,9 +499,15 @@ class _Stream extends \IPS\Node\Model
$values['stream_title'] = NULL;
}
unset( $values['__custom_stream'] );
unset( $values['__stream_owner'] );
/* Sort out stream_classes_type */
if ( $values['stream_classes_type'] )
if ( ( isset( $values['stream_classes_type'] ) && $values['stream_classes_type'] == 0 ) )
{
$values['stream_classes'] = NULL;
$values['stream_containers'] = NULL;
}
elseif ( ( isset( $values['stream_classes'] ) && is_array( $values['stream_classes'] ) ) )
{
$classes = array();
$containers = NULL;
@@ -342,7 +521,7 @@ class _Stream extends \IPS\Node\Model
}
$values['stream_classes'] = implode( ',', $classes );
$values['stream_containers'] = $containers ? json_encode( $containers ) : NULL;
$values['stream_containers'] = $containers ? json_encode( $containers ) : NULL;
}
else
{
@@ -352,40 +531,69 @@ class _Stream extends \IPS\Node\Model
unset( $values['stream_classes_type'] );
/* And tags */
if ( $values['stream_tags_type'] == 'all' or !$values['stream_tags'] )
if ( isset( $values['stream_tags'] ) )
{
$values['stream_tags'] = NULL;
if ( !$values['stream_tags'] )
{
$values['stream_tags'] = NULL;
}
else
{
$values['stream_tags'] = ( is_array( $values['stream_tags'] ) ) ? implode( ',', $values['stream_tags'] ) : $values['stream_tags'];
}
}
else
if ( isset( $values['stream_tags_type'] ) )
{
$values['stream_tags'] = implode( ',', $values['stream_tags'] );
unset( $values['stream_tags_type'] );
}
unset( $values['stream_tags_type'] );
/* And follows */
$values['stream_followed_types'] = ( $values['stream_follow'] == 'followed' ? implode( ',', $values['stream_followed_types'] ) : NULL );
if ( isset( $values['stream_follow'] ) )
{
$values['stream_followed_types'] = ( isset( $values['stream_followed_types'] ) && $values['stream_follow'] == 'followed' ? implode( ',', $values['stream_followed_types'] ) : NULL );
}
/* And members */
if ( $values['stream_ownership'] == 'custom' )
if ( isset( $values['stream_ownership'] ) )
{
$members = array();
foreach ( $values['stream_custom_members'] as $member )
if ( $values['stream_ownership'] == 'custom' )
{
$members[] = $member->member_id;
if( !empty( $values['stream_custom_members'] ) )
{
$members = array();
foreach ( $values['stream_custom_members'] as $member )
{
$members[] = $member->member_id;
}
$values['stream_custom_members'] = implode( ',', $members );
}
else
{
$values['stream_custom_members'] = NULL;
$values['stream_ownership'] = 'all';
}
}
else
{
$values['stream_custom_members'] = NULL;
}
$values['stream_custom_members'] = implode( ',', $members );
}
else
/* And dates */
if ( isset( $values['stream_date_type'] ) && $values['stream_date_type'] )
{
$values['stream_custom_members'] = NULL;
if ( ! isset( $values['stream_date_start'] ) )
{
$values['stream_date_start'] = isset( $values['stream_date_range'] ) && $values['stream_date_range']['start'] ? ( $values['stream_date_range']['start'] instanceof \IPS\DateTime ? $values['stream_date_range']['start']->getTimestamp() : $values['stream_date_range']['start'] ) : NULL;
}
if ( ! isset( $values['stream_date_end'] ) )
{
$values['stream_date_end'] = isset( $values['stream_date_range'] ) && $values['stream_date_range']['end'] ? ( $values['stream_date_range']['end'] instanceof \IPS\DateTime ? $values['stream_date_range']['end']->getTimestamp() : $values['stream_date_range']['end'] ) : NULL;
}
}
/* And dates */
if ( $values['stream_date_type'] )
{
$values['stream_date_start'] = $values['stream_date_range']['start'] ? $values['stream_date_range']['start']->getTimestamp() : NULL;
$values['stream_date_end'] = $values['stream_date_range']['end'] ? $values['stream_date_range']['end']->getTimestamp() : NULL;
}
unset( $values['stream_date_range'] );
if ( \IPS\Dispatcher::i()->controllerLocation === 'admin' and ! empty( $values['stream_default'] ) )
@@ -453,7 +661,7 @@ class _Stream extends \IPS\Node\Model
$_types = array();
foreach ( $subClasses as $class )
{
$_types[] = \IPS\Member::loggedIn()->language()->addToStack( $class::$title . '_pl', FALSE, array( 'strtolower' => TRUE ) );
$_types[] = \IPS\Member::loggedIn()->language()->addToStack( $class::$title . '_pl_lc' );
}
$_types = \IPS\Member::loggedIn()->language()->formatList( $_types );
@@ -490,9 +698,9 @@ class _Stream extends \IPS\Node\Model
if ( $this->tags )
{
$terms[] = \IPS\Member::loggedIn()->language()->addToStack( 'stream_includes_tags', FALSE, array( 'sprintf' => \IPS\Member::loggedIn()->language()->formatList( array_map( function( $val ){
$terms[] = \IPS\Member::loggedIn()->language()->addToStack( 'stream_includes_tags', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( array_map( function( $val ){
return '\'' . $val . '\'';
}, explode( ',', $this->tags ) ), \IPS\Member::loggedIn()->language()->get('or_list_format') ) ) );
}, explode( ',', $this->tags ) ) ), \IPS\Member::loggedIn()->language()->get('or_list_format') ) ) );
}
switch ( $this->ownership )
@@ -507,15 +715,26 @@ class _Stream extends \IPS\Node\Model
case 'custom':
$memberNames = array();
foreach ( explode( ',', $this->custom_members ) as $memberId )
$members = ( ! is_array( $this->custom_members ) ? explode( ',', $this->custom_members ) : $this->custom_members );
foreach ( $members as $member )
{
$_member = \IPS\Member::load( $memberId );
if ( $_member->member_id )
if ( ! ( $member instanceof \IPS\Member ) )
{
$memberNames[] = $_member->name;
$_member = \IPS\Member::load( $member );
if ( $_member->member_id )
{
$memberNames[] = $_member->name;
}
}
else
{
$memberNames[] = $member->name;
}
}
$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') ) ) ) );
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') ) ) ) );
}
break;
}
if ( $this->read == 'unread' )
@@ -576,6 +795,126 @@ class _Stream extends \IPS\Node\Model
return \IPS\Member::loggedIn()->language()->addToStack( 'steam_blurb_no_terms', FALSE, array( 'sprintf' => array( $type ) ) );
}
}
/**
* Gets an array representing the config of this stream
*
* @return array
*/
public function config()
{
/* The simple values */
$config = array(
'id' => $this->id,
'url' => (string) $this->getBaseUrl(),
'owner' => $this->member,
'stream_include_comments' => $this->include_comments,
'stream_read' => $this->read,
'stream_follow' => $this->follow,
'stream_default_view' => $this->default_view,
'stream_sort' => $this->sort,
'stream_ownership' => $this->ownership,
'stream_date_type' => $this->date_type,
'stream_classes' => NULL,
'stream_unread_links' => $this->unread_links,
'containers' => static::containersToUrl( $this->containers )
);
/* Follows */
$followedTypes = explode( ',', $this->followed_types );
$config['stream_followed_types'] = array();
foreach( $followedTypes as $type ){
if ( $type )
{
$config['stream_followed_types'][ $type ] = 1;
}
}
/* Classes */
if ( $this->classes )
{
$classes = array();
foreach ( explode( ',', $this->classes ) as $class )
{
if ( class_exists( $class ) )
{
if ( in_array( 'IPS\Content\Review', class_parents( $class ) ) )
{
$classes[ $class::$itemClass ]['reviews'] = $class;
}
elseif ( in_array( 'IPS\Content\Comment', class_parents( $class ) ) )
{
$classes[ $class::$itemClass ]['comments'] = $class;
}
elseif ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
$classes[ $class ]['items'] = $class;
}
}
}
$types = array();
foreach ( $classes as $itemClass => $subClasses )
{
foreach ( $subClasses as $class )
{
$types[ $class ] = \IPS\Member::loggedIn()->language()->addToStack( $class::$title . '_pl', FALSE );
}
}
$config['stream_classes'] = $types;
}
$config['stream_classes_type'] = (int) ( $config['stream_classes'] !== NULL );
/* Ownership */
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
{
$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 ) { }
}
/* Tags */
if( $this->tags )
{
$config['stream_tags'] = $this->tags;
}
/* Dates */
if( $this->date_type == 'relative' )
{
$config['stream_date_relative_days'] = $this->date_relative_days;
}
elseif( $this->date_type == 'custom' )
{
$config['stream_date_range'] = array(
'start' => intval( $this->date_start ),
'end' => intval( $this->date_end )
);
}
if ( $this->defaultConfig !== NULL )
{
/* Record what has been altered outside the class */
$changed = array();
foreach( $config as $k => $v )
{
if ( array_key_exists( $k, $this->defaultConfig ) and $v != $this->defaultConfig[ $k ] )
{
$changed[ $k ] = $this->defaultConfig[ $k ];
}
}
$config['changed'] = $changed;
}
return $config;
}
/**
* Get results
@@ -615,8 +954,9 @@ class _Stream extends \IPS\Node\Model
foreach ( $classes as $class => $options )
{
/* Init */
if ( 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, isset( $options['reviews'] ) )->onlyLastComment();
}
else
@@ -642,18 +982,12 @@ class _Stream extends \IPS\Node\Model
{
if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
if ( $class::$firstCommentRequired )
{
$filters[] = \IPS\Content\Search\ContentFilter::init( $class, TRUE, TRUE, FALSE )->onlyLastComment();
}
else
{
$filters[] = \IPS\Content\Search\ContentFilter::init( $class, TRUE, FALSE, FALSE );
}
$filters[] = \IPS\Content\Search\ContentFilter::init( $class, TRUE, TRUE, FALSE )->onlyLastComment();
}
}
}
}
if ( count( $filters ) )
{
$query->filterByContent( $filters );
@@ -736,35 +1070,7 @@ class _Stream extends \IPS\Node\Model
*/
public function url()
{
if ( $this->id )
{
switch ( $this->id )
{
case 1:
$furlKey = 'discover_unread';
break;
case 2:
$furlKey = 'discover_istarted';
break;
case 3:
$furlKey = 'discover_followed';
break;
case 4:
$furlKey = 'discover_following';
break;
case 5:
$furlKey = 'discover_posted';
break;
default:
$furlKey = 'discover_stream';
break;
}
return \IPS\Http\Url::internal( "app=core&module=discover&controller=streams&id={$this->id}", 'front', $furlKey );
}
else
{
return \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' );
}
return $this->baseUrl;
}
/**
@@ -776,10 +1082,17 @@ class _Stream extends \IPS\Node\Model
{
parent::save();
$this->initBaseUrl();
if ( !$this->member )
{
unset( \IPS\Data\Store::i()->globalStreamIds );
}
if ( $this->default )
{
unset( \IPS\Data\Store::i()->defaultStreamData );
}
}
/**
@@ -797,6 +1110,11 @@ class _Stream extends \IPS\Node\Model
{
parent::delete();
unset( \IPS\Data\Store::i()->globalStreamIds );
if ( $this->default )
{
unset( \IPS\Data\Store::i()->defaultStreamData );
}
}
}
}