Seditio Source
Root |
./othercms/ips_4.3.4/applications/core/modules/front/discover/streams.php
<?php
/**
 * @brief        streams
 * @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
 * @since        02 Jul 2015
 */

namespace IPS\core\modules\front\discover;

/* 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;
}

/**
 * streams
 */
class _streams extends \IPS\Dispatcher\Controller
{
   
/**
     * Execute
     *
     * @return    void
     */
   
public function execute()
    {
        if ( isset( \
IPS\Request::i()->_nodeSelectName ) )
        {
            return
$this->getContainerNodeElement();
        }
       
       
/* Initiate the breadcrumb */
       
\IPS\Output::i()->breadcrumb = array( array( \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' ), \IPS\Member::loggedIn()->language()->addToStack('activity') ) );

       
/* Necessary CSS/JS */
       
\IPS\Output::i()->jsFiles    = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js('front_streams.js', 'core' ) );
        \
IPS\Output::i()->jsFiles    = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_statuses.js', 'core' ) );
        \
IPS\Output::i()->cssFiles    = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/streams.css' ) );
       
        if ( \
IPS\Theme::i()->settings['responsive'] )
        {
            \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'styles/streams_responsive.css', 'core', 'front' ) );
        }
       
       
/* Execute */
       
return parent::execute();
    }
   
   
/**
     * View Stream
     *
     * @return    void
     */
   
protected function manage()
    {
       
/* If this request is from an auto-poll, kill it and exit */
       
if ( !\IPS\Settings::i()->auto_polling_enabled && \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->after ) )
        {
            \
IPS\Output::i()->json( array( 'error' => 'auto_polling_disabled' ) );
            return;
        }
       
       
/* RSS validate? */
       
$member = NULL;
       
        if ( isset( \
IPS\Request::i()->rss ) )
        {
           
$member = \IPS\Member::load( \IPS\Request::i()->member );
            if ( !\
IPS\Login::compareHashes( md5( ( $member->members_pass_hash ?: $member->email ) . $member->members_pass_salt ), (string) \IPS\Request::i()->key ) )
            {
               
$member = NULL;
            }
        }
       
       
$form = NULL;
       
/* Viewing a particular stream? */
       
if ( isset( \IPS\Request::i()->id ) )
        {
           
/* Get it */
           
try
            {
               
$stream = \IPS\core\Stream::load( \IPS\Request::i()->id );
            }
            catch ( \
OutOfRangeException $e )
            {
                \
IPS\Output::i()->error( 'node_error', '2C280/1', 404, '' );
            }
           
           
/* Suitable for guests? */
           
if ( !\IPS\Member::loggedIn()->member_id and !$member and !( ( $stream->ownership == 'all' or $stream->ownership == 'custom' ) and $stream->read == 'all' and $stream->follow == 'all' and $stream->date_type != 'last_visit' ) )
            {
                \
IPS\Output::i()->error( 'stream_no_permission', '2C280/3', 403, '' );
            }
           
            if ( \
IPS\Member::loggedIn()->member_id and isset( \IPS\Request::i()->default ) )
            {
                \
IPS\Session::i()->csrfCheck();
               
                if ( \
IPS\Request::i()->default )
                {
                    \
IPS\Member::loggedIn()->defaultStream = $stream->_id;
                }
                else
                {
                    \
IPS\Member::loggedIn()->defaultStream = NULL;
                }
               
                if ( \
IPS\Request::i()->isAjax() )
                {
                   
$defaultStream = \IPS\core\Stream::defaultStream();
                   
                    if ( !
$defaultStream )
                    {
                        \
IPS\Output::i()->json( array( 'title' => NULL ) );
                    }
                    else
                    {
                        \
IPS\Output::i()->json( array(
                           
'url'       => $defaultStream->url(),
                           
'title'     => htmlspecialchars( $defaultStream->_title, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE ),
                           
'tooltip'    => $defaultStream->_title, // We need to pass this individually for the tooltip that shows, but the JS itself will escape any entities
                           
'id'        => $defaultStream->_id
                         
) );
                    }
                }
               
                \
IPS\Output::i()->redirect( $stream->url() );
            }
           
           
$form = $this->_buildForm( $stream );
           
           
/* Set title and breadcrumb */
           
\IPS\Output::i()->breadcrumb[] = array( $stream->url(), $stream->_title );
            \
IPS\Output::i()->title = $stream->_title;
        }
       
       
/* Or just everything? */
       
else
        {
            if ( \
IPS\Member::loggedIn()->member_id and isset( \IPS\Request::i()->default ) )
            {
                \
IPS\Session::i()->csrfCheck();

                if ( \
IPS\Request::i()->default )
                {
                    \
IPS\Member::loggedIn()->defaultStream = 0;
                }
                else
                {
                    \
IPS\Member::loggedIn()->defaultStream = NULL;
                }
               
                if ( \
IPS\Request::i()->isAjax() )
                {
                   
$defaultStream = \IPS\core\Stream::defaultStream();
                   
                    if ( !
$defaultStream )
                    {
                        \
IPS\Output::i()->json( array( 'title' => NULL ) );
                    }
                    else
                    {
                        \
IPS\Output::i()->json( array(
                           
'url'   => \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' ),
                           
'title' => htmlspecialchars( $defaultStream->_title, ENT_QUOTES | ENT_DISALLOWED, 'UTF-8', FALSE ),
                           
'id'    => $defaultStream->_id
                         
) );
                    }
                }
               
                \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' ) );
            }

           
/* Start with a blank stream */
           
$stream = \IPS\core\Stream::allActivityStream();
           
$stream->default_view = "expanded";

           
/* Set the title to "All Activity" */
           
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('all_activity');
        }
       
       
/* Look for url params that can come from view switch or load more button */    
        /* but only if we haven't submitted the form on this request */
       
if( !\IPS\Request::i()->stream_submitted )
        {
           
$streamFields = array( 'include_comments', 'classes', 'ownership', 'custom_members', 'read', 'follow', 'followed_types', 'date_type', 'date_start', 'date_end', 'date_relative_days', 'sort', 'tags', 'unread_links' );

           
/* Clubs enabled? */
           
if( \IPS\Settings::i()->clubs )
            {
               
$streamFields[]    = 'clubs';
            }
           
           
/* Build and format field values */
           
$_values = array();
            foreach ( \
IPS\Request::i() as $requestKey => $requestField )
            {
               
$field = str_replace( 'stream_', '', $requestKey );
               
                if (
$field == 'custom_members' and isset( \IPS\Request::i()->stream_custom_members ) )
                {
                   
$members = NULL;
                    foreach(
explode( ',', \IPS\Request::i()->stream_custom_members ) as $name )
                    {
                        try
                        {
                           
$members[] = \IPS\Member::load( $name, 'name' );
                        }
                        catch( \
OutOfRangeException $e ) { }
                    }
                   
                   
$_values['stream_custom_members'] = $members;
                }
                else if (
in_array( $field, $streamFields ) && ( $field == 'classes' || $field == 'followed_types' ) && is_array( \IPS\Request::i()->{ 'stream_' . $field } ) )
                {
                   
/* Some array values will come in as key=1 params, so we only need the keys here */
                   
$_values[ $requestKey ] = array_keys( $requestField );
                }
                else
                {
                   
$_values[ $requestKey ] = $requestField;
                }                
            }
           
            if ( isset(
$_values['stream_club_filter'] ) and !isset( $_values['stream_club_select'] ) )
            {
               
$_values['stream_club_select'] = 'select';
            }
           
           
$formattedValues = $stream->formatFormValues( $_values );
           
           
/* Overwrite stream config if present in the request */
           
foreach ( $streamFields as $k )
            {    
               
$requestKey = 'stream_' . $k;
               
                if ( isset( \
IPS\Request::i()->$requestKey ) or ( $k === 'clubs' and ( isset( \IPS\Request::i()->stream_club_select ) or isset( \IPS\Request::i()->stream_club_filter ) ) ) )
                {
                    if (
$stream->$k != $formattedValues[ $k ] )
                    {
                       
$stream->$k = $formattedValues[ $k ];
                       
$stream->baseUrl = $stream->baseUrl->setQueryString( 'stream_' . $k, \IPS\Request::i()->$requestKey );
                    }
                }            
            }
           
           
/* Containers are special */
           
if ( isset( \IPS\Request::i()->stream_containers ) and is_array( \IPS\Request::i()->stream_containers ) )
            {
               
/* Remove null/'' values as we no longer want to restrict by container for this class if that occurs */
               
$cleanedContainers = array();
                foreach( \
IPS\Request::i()->stream_containers as $class => $containers )
                {
                    if (
$containers )
                    {
                       
$cleanedContainers[ $class ] = $containers;
                    }
                }
               
                if (
count( array_diff_assoc( $cleanedContainers, $stream::containersToUrl( $stream->containers ) ) ) )
                {
                   
$stream->containers = $stream::containersFromUrl( $cleanedContainers );
                   
$stream->baseUrl = $stream->baseUrl->setQueryString( 'stream_containers', $cleanedContainers );
                }
            }
           
            if ( isset( \
IPS\Request::i()->id ) )
            {
               
/* reset the form to account for all the modifications to $stream class variables */
               
$form = $this->_buildForm( $stream );
            }
        }        

       
/* In order to make most streams more efficient, if we are looking at newest items first, and we haven't set a custom date, then just look for the last 12 months max */
       
if ( $stream->date_type == 'all' and $stream->sort == 'newest' )
        {
           
$stream->date_relative_days = 365;
           
$stream->date_type = 'relative';
        }
       
       
/* Build the query */
       
$query = $stream->query( $member );
       
       
/* Set page or the before/after date */
       
$currentPage = 1;
        if ( isset( \
IPS\Request::i()->page ) AND intval( \IPS\Request::i()->page ) > 0 )
        {
           
$currentPage = \IPS\Request::i()->page;
           
$query->setPage( $currentPage );
        }
       
       
$before = isset( \IPS\Request::i()->before ) ? \IPS\Request::i()->before : null;
       
$after  = isset( \IPS\Request::i()->after ) ? \IPS\Request::i()->after : null;
       
       
/* If we sort by oldest, then we need to switch these values */
       
if ( $stream->sort == 'oldest' )
        {
           
$tmp = $after;
           
$after  = $before;
           
$before = $tmp;
            unset(
$tmp );
        }
       
        if ( isset( \
IPS\Request::i()->latest ) )
        {
            if (
$stream->id and !$stream->include_comments )
            {
               
$query->filterByLastUpdatedDate( \IPS\DateTime::ts( \IPS\Request::i()->latest )  );
            }
            else
            {
               
$query->filterByCreateDate( \IPS\DateTime::ts( \IPS\Request::i()->latest ) );
            }
           
           
$query->setLimit(350);
        }
        else if (
$before )
        {
            if (
$stream->id and !$stream->include_comments )
            {
               
$query->filterByLastUpdatedDate( NULL, \IPS\DateTime::ts( $before ) );
            }
            else
            {
               
$query->filterByCreateDate( NULL, \IPS\DateTime::ts( $before ) );
            }
        }
        if (
$after )
        {
            if (
$stream->id and !$stream->include_comments )
            {
               
$query->filterByLastUpdatedDate( \IPS\DateTime::ts( $after ) );
            }
            else
            {
               
$query->filterByCreateDate( \IPS\DateTime::ts( $after ) );
            }
        }

       
/* Get the results */
       
$results = $query->search( NULL, $stream->tags ? explode( ',', $stream->tags ) : NULL, ( $stream->include_comments ? \IPS\Content\Search\Query::TAGS_MATCH_ITEMS_ONLY + \IPS\Content\Search\Query::TERM_OR_TAGS : \IPS\Content\Search\Query::TERM_OR_TAGS ) );
       
       
/* Load data we need like the authors, etc */
       
$results->init();
       
       
/* Add in extra stuff? */
       
if ( !isset( \IPS\Request::i()->id ) )
        {
           
/* Is anything turned on? */
           
$extra = array();
            foreach ( array(
'register', 'follow_member', 'follow_content', 'photo', 'like', 'react', 'clubs' ) as $k )
            {
               
$key = "all_activity_{$k}";
                if ( \
IPS\Settings::i()->$key )
                {
                   
$extra[] = $k;
                }
            }
            if ( !empty(
$extra ) )
            {
               
$results = $results->addExtraItems( $extra, NULL, ( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->after ) ) ? \IPS\DateTime::ts( \IPS\Request::i()->after ) : NULL, ( \IPS\Request::i()->isAjax() and isset( \IPS\Request::i()->before ) ) ? \IPS\DateTime::ts( \IPS\Request::i()->before ) : NULL );
            }
        }
       
       
/* Condensed or expanded? */
       
$view = 'expanded';
       
$streamID = ( \IPS\Request::i()->id ) ? \IPS\Request::i()->id : 'all';

        if ( isset( \
IPS\Request::i()->cookie['stream_view_' . $streamID] ) )
        {
           
$view =  \IPS\Request::i()->cookie['stream_view_' . $streamID];
        }
        else
        {
           
$view = $stream->default_view;
        }

       
/* If this is an AJAX request, just show the results */
       
if ( \IPS\Request::i()->isAjax() )
        {
           
$output = \IPS\Theme::i()->getTemplate('streams')->streamItems( $results, TRUE, ( $stream->id and !$stream->include_comments ) ? 'last_comment' : 'date', $view );

           
$return = array(
               
'title' => htmlspecialchars( $stream->_title, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ),
               
'blurb' => $stream->blurb(),
               
'config' => json_encode( $stream->config() ),
               
'count' => count( $results ),
               
'results' => $output,
               
'id' => ( $stream->id ) ? $stream->id : '',
               
'url' => $stream->url()
            );

            \
IPS\Output::i()->json( $return );
            return;
        }
       
       
/* Display - RSS */
       
if ( \IPS\Settings::i()->activity_stream_rss and isset( \IPS\Request::i()->rss ) )
        {
           
$document = \IPS\Xml\Rss::newDocument( $stream->baseUrl, $stream->_title, sprintf( \IPS\Member::loggedIn()->language()->get( 'stream_rss_title' ), \IPS\Settings::i()->board_name, $stream->_title ) );
           
            foreach (
$results as $result )
            {
                if (
$result instanceof \IPS\Content\Search\Result\Content )
                {
                   
$result->addToRssFeed( $document );
                }
            }
           
            \
IPS\Output::i()->sendOutput( $document->asXML(), 200, 'text/xml' );
            return;
        }
       
       
/* Display - HTML */
       
else
        {            
           
/* What's the RSS Link? */
           
$rssLink = NULL;
            if ( \
IPS\Settings::i()->activity_stream_rss )
            {
                if ( isset( \
IPS\Request::i()->id ) )
                {
                   
$rssLink = \IPS\Http\Url::internal( "app=core&module=discover&controller=streams&id={$stream->id}", 'front', 'discover_rss' );
                    if ( \
IPS\Member::loggedIn()->member_id )
                    {
                       
$rssLink = $rssLink->setQueryString( 'member', \IPS\Member::loggedIn()->member_id )->setQueryString( 'key', md5( ( \IPS\Member::loggedIn()->members_pass_hash ?: \IPS\Member::loggedIn()->email ) . \IPS\Member::loggedIn()->members_pass_salt ) );
                    }
                }
                else
                {
                   
/* It's all activity! */
                   
$rssLink = \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_rss_all_activity' );
                }
            }
           
           
/* Display */
           
$output = \IPS\Theme::i()->getTemplate('streams')->stream( $stream, $results, $stream->id ? FALSE : TRUE, TRUE, ( $stream->id and !$stream->include_comments ) ? 'last_comment' : 'date', $view );
           
            \
IPS\Output::i()->jsVars['stream_config'] = $stream->config();
            \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('streams')->streamWrapper( $stream, $output, $form, $rssLink, isset( \IPS\Request::i()->id ) and $stream->member and \IPS\Member::loggedIn()->member_id and $stream->member != \IPS\Member::loggedIn()->member_id );
        }
    }
   
   
/**
     * Create a new stream
     *
     * @return    void
     */
   
public function create()
    {
        if ( !\
IPS\Member::loggedIn()->member_id )
        {
            \
IPS\Output::i()->error( 'no_module_permission_guest', '2C280/A', 403, '' );
        }
       
       
$stream = new \IPS\core\Stream;
       
$stream->member = \IPS\Member::loggedIn()->member_id;
       
$stream->default_view = 'expanded';
       
        \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'create_new_stream' );
        \
IPS\Output::i()->output = $this->_buildForm( $stream );
    }
   
   
/**
     * Edit a stream's title
     *
     * @return void
     */
   
public function edit()
    {
        \
IPS\Session::i()->csrfCheck();
       
        if ( !\
IPS\Member::loggedIn()->member_id )
        {
            \
IPS\Output::i()->error( 'no_module_permission_guest', '2C280/6', 403, '' );
        }
       
        try
        {
           
$stream = \IPS\core\Stream::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2C280/7', 404, '' );
        }
       
        if (
$stream->member != \IPS\Member::loggedIn()->member_id )
        {
            \
IPS\Output::i()->error( 'no_module_permission', '2C280/8', 403, '' );
        }
       
       
$form = new \IPS\Helpers\Form;
       
$form->class = 'ipsForm_vertical';
       
$form->add( new \IPS\Helpers\Form\Text( 'stream_title', $stream->title ) );
       
        if (
$values = $form->values() )
        {
            if ( isset(
$values['stream_title'] ) and $values['stream_title'] )
            {
               
$stream->title = $values['stream_title'];
               
$stream->save();
               
$this->_rebuildStreams();
                \
IPS\Output::i()->redirect( $stream->url() );
            }    
        }
       
       
/* Output */
       
if ( \IPS\Request::i()->isAjax() )
        {
            \
IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->genericBlock( $form, NULL, 'ipsPad' ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
        }

        \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'stream_edit_title' );
        \
IPS\Output::i()->output = $form;
    }
   
   
/**
     * Copy a stream
     *
     * @return    void
     */
   
public function copy()
    {
        if ( !\
IPS\Member::loggedIn()->member_id )
        {
            \
IPS\Output::i()->error( 'no_module_permission_guest', '2C280/4', 403, '' );
        }
       
        \
IPS\Session::i()->csrfCheck();
       
        try
        {
           
$stream = clone \IPS\core\Stream::load( \IPS\Request::i()->id );
           
$stream->member = \IPS\Member::loggedIn()->member_id;
           
$stream->save();
           
$this->_rebuildStreams();
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2C280/5', 404, '' );
        }

        \
IPS\Output::i()->redirect( $stream->url() );
    }

   
/**
     * Deletes a new stream
     *
     * @return    void
     */
   
public function delete()
    {
        \
IPS\Session::i()->csrfCheck();

       
/* Make sure the user confirmed the deletion */
       
\IPS\Request::i()->confirmedDelete();
       
        try
        {
           
$stream = \IPS\core\Stream::load( \IPS\Request::i()->id );
            if ( !
$stream->member or $stream->member != \IPS\Member::loggedIn()->member_id )
            {
                throw new \
OutOfRangeException;
            }
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2C280/2', 404, '' );
        }
       
       
$stream->delete();
       
$this->_rebuildStreams();

        \
IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' ) );
    }
   
   
/**
     * Get the container Node form element HTML
     *
     * @return string
     */
   
protected function getContainerNodeElement()
    {
       
$currentContainers = array();
       
$stream = NULL;
        if ( isset( \
IPS\Request::i()->id ) )
        {
           
$stream = \IPS\core\Stream::load( \IPS\Request::i()->id );
           
$currentContainers = $stream->containers ? json_decode( $stream->containers, TRUE ) : array();
           
            if ( isset( \
IPS\Request::i()->stream_containers ) )
            {
               
$currentContainers = json_decode( $stream::containersFromUrl( \IPS\Request::i()->stream_containers ), TRUE );
            }
        }
 
        foreach ( \
IPS\Content::routedClasses( TRUE, FALSE, TRUE ) as $class )
        {
           
$classes[ $class ] = $class::$title . '_pl';
            if ( isset(
$class::$containerNodeClass ) and $class == \IPS\Request::i()->className )
            {
               
$url = $stream ? $stream->baseUrl->setQueryString( 'className', $class ) : \IPS\Request::i()->url()->setQueryString( 'className', $class );
               
$containerClass = $class::$containerNodeClass;
               
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, isset( $currentContainers[ $class ] ) ? $currentContainers[ $class ] : array(), NULL, array( 'url' => $url, 'class' => $class::$containerNodeClass, 'multiple' => TRUE, 'permissionCheck' => $containerClass::searchableNodesPermission(), 'clubs' => ( \IPS\Settings::i()->clubs AND \IPS\IPS::classUsesTrait( $containerClass, 'IPS\Content\ClubContainer' ) ) ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
               
$field->label = \IPS\Member::loggedIn()->language()->addToStack( 'stream_narrow_by_container_label', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $containerClass::$nodeTitle ) ) ) );
               
                return \
IPS\Output::i()->json( array( 'node' => \IPS\Theme::i()->getTemplate('streams')->filterFormContentTypeContent( $field, $class, \IPS\Request::i()->key ) ) );
            }
        }

        return
NULL;
    }
   
   
/**
     * Get the club selector form element HTML
     *
     * @return string
     */
   
protected function getClubElement()
    {
        if( !\
IPS\Settings::i()->clubs )
        {
            return \
IPS\Output::i()->json( array( 'field' => '' ) );
        }

       
$clubOptions = array();
        foreach ( \
IPS\Member\Club::clubs( \IPS\Member::loggedIn(), NULL, 'name', TRUE ) as $club )
        {
           
$clubOptions[ "c{$club->id}" ] = $club->name;
        }
       
        if ( isset( \
IPS\Request::i()->stream_club_select ) )
        {
            switch ( \
IPS\Request::i()->stream_club_select )
            {
                case
'all':
                   
$value = 0;
                    break;
               
                case
'none':
                   
$value = array();
                    break;
                   
                default:
                   
$value = explode( ',', \IPS\Request::i()->stream_club_filter );
                    break;
            }
        }
        else
        {
           
$value = 0;
        }
                                       
       
$field = new \IPS\Helpers\Form\Select( 'stream_club_filter_dummy', $value, FALSE, array( 'options' => $clubOptions, 'parse' => 'normal', 'multiple' => TRUE, 'noDefault' => TRUE, 'unlimited' => 0, 'unlimitedLang' => 'stream_club_select_all' ), NULL, NULL, NULL, 'stream_club_filter' );
       
$field->label = \IPS\Member::loggedIn()->language()->addToStack('stream_club_filter');
       
        return \
IPS\Output::i()->json( array( 'field' => \IPS\Theme::i()->getTemplate('streams')->filterFormClubs( $field ) ) );
    }
   
   
/**
     * Build form
     *
     * @param    \IPS\core\Stream    $stream    The stream
     * @return    string
     */
   
protected function _buildForm( \IPS\core\Stream &$stream )
    {
       
/* Build form */
       
$form = new \IPS\Helpers\Form( 'stream', 'continue', ( $stream->id ? $stream->url() : NULL ) );
       
$form->class = 'ipsForm_vertical ipsForm_noLabels';
               
       
$stream->form( $form, 'Text', !$stream->id );
       
$redirectAfterSave = FALSE;        
       
       
/* Note if it's custom */
       
if ( $stream->member && \IPS\Member::loggedIn()->member_id )
        {
           
$form->hiddenValues['__custom_stream'] = TRUE;
        }
       
        if (
$stream->member )
        {
           
$form->hiddenValues['__stream_owner'] = $stream->member;
        }

       
$form->hiddenValues['stream_default_view'] = $stream->default_view;
       
       
/* Handle submissions */
       
if ( $values = $form->values() )
        {
           
/* As the node container form elements are not in the actual form, we need to work some magic.
                And by magic, I do mean a sort of hacky thing. First, if we are editing a stream, we need to get our existing stream filters
                and put these into an array. Then we loop over the input and overwrite the existing filters with what was submitted. If the user
                never loaded the container filters we'll retain what we had (expected) but if they did and adjusted or removed container filters
                then we'll update with what they submitted (expected).
            */
           
if( $stream->id )
            {
               
$currentContainers = $stream->containers ? json_decode( $stream->containers, TRUE ) : array();

                foreach(
$currentContainers as $class => $containers )
                {
                   
$values['stream_containers_' . $class::$title ] = array_combine( $containers, $containers );
                }
            }

            foreach ( \
IPS\Request::i() as $k => $v )
            {
                if (
mb_substr( $k, 0, 18 ) == 'stream_containers_' )
                {
                    if(
$v )
                    {
                       
$vals = explode( ',', $v );
                       
$values[ $k ] = array_combine( $vals, $vals );
                    }
                    elseif( isset(
$values[ $k ] ) )
                    {
                       
$values[ $k ] = NULL;
                    }
                }
            }

           
/* Update only */
           
if ( isset( \IPS\Request::i()->updateOnly ) )
            {
               
$formattedValues = $stream->formatFormValues( $values );

                foreach ( array(
'include_comments', 'classes', 'containers', 'clubs', 'ownership', 'custom_members', 'read', 'follow', 'followed_types', 'date_type', 'date_start', 'date_end', 'date_relative_days', 'sort', 'tags', 'unread_links' ) as $k )
                {
                   
$requestKey = 'stream_' . $k;

                    if (
array_key_exists( $k, $formattedValues ) AND $stream->$k != $formattedValues[ $k ] )
                    {
                       
$stream->$k = $formattedValues[ $k ];
                       
$stream->baseUrl = $stream->baseUrl->setQueryString( 'stream_' . $k, \IPS\Request::i()->$requestKey );
                    }
                }
            }            
           
/* Update & Save */
           
else
            {
                if ( !
$stream->id )
                {
                   
$stream->position = \IPS\Db::i()->select( 'MAX(position)', 'core_streams', array( 'member=?', \IPS\Member::loggedIn()->member_id )  )->first() + 1;
                   
$redirectAfterSave = TRUE;
                }
                else
                {
                    if ( !
$stream->member or $stream->member != \IPS\Member::loggedIn()->member_id )
                    {
                        \
IPS\Output::i()->error( 'no_module_permission', '2C280/9', 403, '' );
                    }
                }
           
               
$stream->saveForm( $stream->formatFormValues( $values ) );
               
               
$this->_rebuildStreams();
               
                if(
$redirectAfterSave )
                {
                    \
IPS\Output::i()->redirect( $stream->url() );
                }
            }
        }
       
       
/* Display */
       
return $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'streams', 'core' ) ), $stream->id ? 'filterInlineForm' : 'filterCreateForm' ) );
    }
   
   
/**
     * Rebuild logged in member's streams
     *
     * @return    void
     */
   
protected function _rebuildStreams()
    {
       
$default = \IPS\Member::loggedIn()->defaultStream;
        \
IPS\Member::loggedIn()->member_streams = json_encode( array( 'default' => $default, 'streams' => iterator_to_array( \IPS\Db::i()->select( 'id, title', 'core_streams', array( 'member=?', \IPS\Member::loggedIn()->member_id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->setKeyField('id')->setValueField('title') ) ) );
        \
IPS\Member::loggedIn()->save();
    }
}