Version 5.0.13
This commit is contained in:
1 parent
b89858b2ff
commit
5efe744c5f
1407 files changed
+69690
-30177
No files matched your search
+226
-15
@@ -33,8 +33,11 @@ use IPS\Db\Exception;
|
||||
use IPS\Db\Select;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Events\Event;
|
||||
use IPS\Extensions\ClubAbstract;
|
||||
use IPS\File;
|
||||
use IPS\GeoLocation;
|
||||
use IPS\Helpers\Badge;
|
||||
use IPS\Helpers\Badge\Icon;
|
||||
use IPS\Helpers\CoverPhoto;
|
||||
use IPS\Helpers\Form;
|
||||
use IPS\Helpers\Form\Address;
|
||||
@@ -56,6 +59,7 @@ use IPS\Math\Number;
|
||||
use IPS\Member;
|
||||
use IPS\Member\Club\CustomField;
|
||||
use IPS\Member\Club\Page as ClubPage;
|
||||
use IPS\Member\Club\Template;
|
||||
use IPS\nexus\Customer;
|
||||
use IPS\nexus\Invoice;
|
||||
use IPS\nexus\Money;
|
||||
@@ -69,6 +73,7 @@ use IPS\Patterns\ActiveRecord;
|
||||
use IPS\Patterns\ActiveRecordIterator;
|
||||
use IPS\Request;
|
||||
use IPS\Settings;
|
||||
use IPS\Task;
|
||||
use IPS\Theme;
|
||||
use OutOfRangeException;
|
||||
use OverflowException;
|
||||
@@ -80,6 +85,7 @@ use function defined;
|
||||
use function get_class;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
@@ -92,7 +98,11 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class Club extends ActiveRecord implements Embeddable
|
||||
{
|
||||
use Followable;
|
||||
use Followable
|
||||
{
|
||||
Followable::unfollow as public _unfollow;
|
||||
Followable::follow as public _follow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Club: public
|
||||
@@ -178,6 +188,13 @@ class Club extends ActiveRecord implements Embeddable
|
||||
* @brief Not actually a status, but used for member history
|
||||
*/
|
||||
const STATUS_LEFT = 'left';
|
||||
|
||||
/**
|
||||
* @breif Constants for node permissions values
|
||||
*/
|
||||
const NODE_PRIVATE = 0;
|
||||
const NODE_PUBLIC = 1;
|
||||
const NODE_MODERATORS = 2;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
@@ -227,9 +244,10 @@ class Club extends ActiveRecord implements Embeddable
|
||||
* @param array $filters Custom field filters
|
||||
* @param mixed|null $extraWhere Additional WHERE clause
|
||||
* @param bool $countOnly Only return a count, instead of an iterator
|
||||
* @param array|null $location Array with lat and lon
|
||||
* @return ActiveRecordIterator|array|int
|
||||
*/
|
||||
public static function clubs( ?Member $member, int|array|null $limit, string $sortOption, bool|array|Member $mineOnly=FALSE, array $filters=array(), mixed $extraWhere=NULL, bool $countOnly=FALSE ): ActiveRecordIterator|int|array
|
||||
public static function clubs( ?Member $member, int|array|null $limit, string $sortOption, bool|array|Member $mineOnly=FALSE, array $filters=array(), mixed $extraWhere=NULL, bool $countOnly=FALSE, ?array $location=null ): ActiveRecordIterator|int|array
|
||||
{
|
||||
$where = array();
|
||||
$joins = array();
|
||||
@@ -300,6 +318,19 @@ class Club extends ActiveRecord implements Embeddable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$selectClause = '*';
|
||||
$having = NULL;
|
||||
|
||||
/* Location? */
|
||||
if ( isset( $location['lat'] ) and isset( $location['lon'] ) )
|
||||
{
|
||||
/* Make sure co-ordinates are in a valid format regardless of locale */
|
||||
$location['lat'] = number_format( $location['lat'], 6, '.', '' );
|
||||
$location['lon'] = number_format( $location['lon'], 6, '.', '' );
|
||||
|
||||
$selectClause .= ', ( 3959 * acos( cos( radians(' . $location['lat'] . ') ) * cos( radians( location_lat ) ) * cos( radians( location_long ) - radians( ' . $location['lon'] . ' ) ) + sin( radians( ' . $location['lat'] . ' ) ) * sin( radians( location_lat ) ) ) ) AS distance';
|
||||
}
|
||||
|
||||
/* Additional where clause */
|
||||
if ( $extraWhere )
|
||||
@@ -313,15 +344,27 @@ class Club extends ActiveRecord implements Embeddable
|
||||
$where[] = array( $extraWhere );
|
||||
}
|
||||
}
|
||||
|
||||
/* Extensions */
|
||||
foreach( Application::allExtensions( 'core', 'Club' ) AS $ext )
|
||||
{
|
||||
/* @var ClubAbstract $ext */
|
||||
foreach( $ext->clubsWhere( $member ) as $clause )
|
||||
{
|
||||
$where[] = $clause;
|
||||
}
|
||||
}
|
||||
|
||||
/* Query */
|
||||
if( $countOnly )
|
||||
{
|
||||
$select = Db::i()->select( 'COUNT(*)', 'core_clubs', $where );
|
||||
$select = Db::i()->select( 'COUNT(*)', 'core_clubs', $where, null, null, null, $having );
|
||||
}
|
||||
else
|
||||
{
|
||||
$select = Db::i()->select( '*', 'core_clubs', $where, ( $sortOption === 'name' ? "{$sortOption} ASC" : "{$sortOption} DESC" ), $limit );
|
||||
/* If we are sorting by distance, we want null values last */
|
||||
$sortOption = ( $sortOption === 'distance' ) ? '-distance' : $sortOption;
|
||||
$select = Db::i()->select( $selectClause, 'core_clubs', $where, "{$sortOption} " . ( $sortOption === 'name' ? 'ASC' : 'DESC' ), $limit, null, $having );
|
||||
}
|
||||
|
||||
foreach ( $joins as $join )
|
||||
@@ -619,6 +662,28 @@ class Club extends ActiveRecord implements Embeddable
|
||||
$form->add( new YesNo( 'club_rules_reacknowledge', FALSE, FALSE, [], NULL, NULL, NULL, 'club_rules_reacknowledge' ) );
|
||||
}
|
||||
|
||||
$form->add( new YesNo( 'club_auto_follow', $this->auto_follow, false ) );
|
||||
if( !$new )
|
||||
{
|
||||
Member::loggedIn()->language()->words['club_auto_follow_desc'] = Member::loggedIn()->language()->get( 'club_auto_follow_desc_edit' );
|
||||
}
|
||||
|
||||
foreach( Application::allExtensions( 'core', 'Club' ) AS $ext )
|
||||
{
|
||||
/* @var ClubAbstract $ext */
|
||||
foreach( $ext->formElements( $this ) as $element )
|
||||
{
|
||||
if( is_object( $element ) )
|
||||
{
|
||||
$form->add( $element );
|
||||
}
|
||||
else
|
||||
{
|
||||
$form->addHtml( $element );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
@@ -737,6 +802,9 @@ class Club extends ActiveRecord implements Embeddable
|
||||
$this->show_membertab = "nonmember";
|
||||
}
|
||||
|
||||
$this->auto_follow = $values['club_auto_follow'];
|
||||
$autoFollow = ( !$new and isset( $this->changed['auto_follow'] ) and $this->changed['auto_follow'] );
|
||||
|
||||
$this->save();
|
||||
|
||||
if ( $values['show_rules'] )
|
||||
@@ -766,6 +834,12 @@ class Club extends ActiveRecord implements Embeddable
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are auto-following and this is not a new club, trigger a background task */
|
||||
if( $autoFollow )
|
||||
{
|
||||
Task::queue( 'core', 'AutoFollowClubs', [ 'club' => $this->id, 'node' => null ], 3, [ 'club', 'node' ] );
|
||||
}
|
||||
|
||||
if ( $new )
|
||||
{
|
||||
$this->addMember( Member::loggedIn(), Club::STATUS_LEADER );
|
||||
@@ -815,6 +889,23 @@ class Club extends ActiveRecord implements Embeddable
|
||||
}
|
||||
}
|
||||
|
||||
/* Extensions */
|
||||
foreach( Application::allExtensions( 'core', 'Club' ) AS $ext )
|
||||
{
|
||||
/* @var ClubAbstract $ext */
|
||||
$ext->saveForm( $this, $values, $new );
|
||||
}
|
||||
|
||||
/* Default nodes */
|
||||
if( $new )
|
||||
{
|
||||
foreach( Template::roots() as $template )
|
||||
{
|
||||
$nodeClass = new ( $template->node_class );
|
||||
$nodeClass->saveClubForm( $this, $template->node_data, $template );
|
||||
}
|
||||
}
|
||||
|
||||
if( $new and Settings::i()->clubs_require_approval and !$this->approved )
|
||||
{
|
||||
/* Add it to the approval queue */
|
||||
@@ -1293,6 +1384,9 @@ class Club extends ActiveRecord implements Embeddable
|
||||
*/
|
||||
public function addMember( Member $member, string $status = 'member', bool $update=FALSE, Member $addedBy = NULL, Member $invitedBy = NULL, bool $updateJoinedDate=FALSE ) : void
|
||||
{
|
||||
/* Get the current status so that we can figure out if this is a new membership */
|
||||
$currentStatus = $this->memberStatus( $member );
|
||||
|
||||
try
|
||||
{
|
||||
Db::i()->insert( 'core_clubs_memberships', array(
|
||||
@@ -1332,6 +1426,7 @@ class Club extends ActiveRecord implements Embeddable
|
||||
Db::i()->update( 'core_clubs_memberships', $save, array( 'club_id=? AND member_id=?', $this->id, $member->member_id ) );
|
||||
|
||||
$member->rebuildPermissionArray();
|
||||
$this->memberStatuses[ $member->member_id ] = $status;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1344,6 +1439,12 @@ class Club extends ActiveRecord implements Embeddable
|
||||
}
|
||||
}
|
||||
|
||||
/* Is this club an auto-follow? */
|
||||
if( $this->auto_follow and in_array( $status, [ static::STATUS_MEMBER, static::STATUS_LEADER, static::STATUS_MODERATOR ] ) and !in_array( $currentStatus, [ static::STATUS_MEMBER, static::STATUS_LEADER, static::STATUS_MODERATOR ] ) )
|
||||
{
|
||||
$this->follow( 'immediate', true, $member );
|
||||
}
|
||||
|
||||
/* Log to Member History */
|
||||
$memberStatus = $this->memberStatus( $member, 2 );
|
||||
if( in_array( $memberStatus['status'], [ Club::STATUS_INVITED, Club::STATUS_INVITED_BYPASSING_PAYMENT, Club::STATUS_BANNED, Club::STATUS_MEMBER, Club::STATUS_DECLINED, Club::STATUS_REQUESTED ] ) )
|
||||
@@ -1404,16 +1505,22 @@ class Club extends ActiveRecord implements Embeddable
|
||||
*/
|
||||
public function removeMember( Member $member ) : void
|
||||
{
|
||||
/* Get the member's current status */
|
||||
$memberStatus = $this->memberStatus( $member );
|
||||
|
||||
Db::i()->delete( 'core_clubs_memberships', array( 'club_id=? AND member_id=?', $this->id, $member->member_id ) );
|
||||
|
||||
$member->rebuildPermissionArray();
|
||||
|
||||
/* Clear out the member from any local caches */
|
||||
if( isset( $this->memberStatuses[ $member->member_id ] ) )
|
||||
{
|
||||
unset( $this->memberStatuses[ $member->member_id ] );
|
||||
}
|
||||
|
||||
/* Unfollow the club */
|
||||
$this->unfollow( $member );
|
||||
|
||||
Webhook::fire( 'club_member_removed', ['club' => $this, 'member' => $member] );
|
||||
|
||||
Event::fire( 'onLeaveClub', $member, array( $this ) );
|
||||
|
||||
$member->rebuildPermissionArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2021,10 +2128,10 @@ class Club extends ActiveRecord implements Embeddable
|
||||
/**
|
||||
* Get the club navbar tabs
|
||||
*
|
||||
* @param Model|NULL $container Container
|
||||
* @param Model|ClubPage|null $container Container
|
||||
* @return array
|
||||
*/
|
||||
public function tabs( Model $container = NULL ) : array
|
||||
public function tabs( Model|ClubPage|null $container = NULL ) : array
|
||||
{
|
||||
if ( !static::$tabs )
|
||||
{
|
||||
@@ -2178,7 +2285,7 @@ class Club extends ActiveRecord implements Embeddable
|
||||
$menu->add( new Link( $this->url()->setQueryString( 'do', 'invite' ), 'club_invite_members', dataAttributes: [ 'data-ipsDialog-size' => 'narrow' ], opensDialog: true ) );
|
||||
}
|
||||
|
||||
if( $this->canManageNavigation() and $container instanceof Model and count( $this->tabs( $container ) ) > 1 )
|
||||
if( $this->canManageNavigation() and count( $this->tabs( $container ) ) > 1 )
|
||||
{
|
||||
$menu->add( new Link( '#', 'reorder_club_menu', dataAttributes: [ 'data-action' => 'reorderClubmenu' ] ) );
|
||||
}
|
||||
@@ -2202,7 +2309,7 @@ class Club extends ActiveRecord implements Embeddable
|
||||
}
|
||||
}
|
||||
|
||||
if( $container instanceof Model and $this->isLeader() )
|
||||
if( $container instanceof Model and $this->isLeader() and $container->canLeaderManage() )
|
||||
{
|
||||
$menu->addSeparator();
|
||||
$menu->add( new Link( $this->url()->setQueryString( [ 'do' => 'nodeForm', 'type' => get_class( $container ), 'node' => $container->_id ] ), Member::loggedIn()->language()->addToStack( 'clubs_edit_this_container', true, [ 'sprintf' => Member::loggedIn()->language()->addToStack( $container::$nodeTitle . '_sg' ) ] ), dataAttributes: [ 'data-ipsDialog-title' => $container->_title ], opensDialog: true ) );
|
||||
@@ -2311,6 +2418,58 @@ class Club extends ActiveRecord implements Embeddable
|
||||
return $buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return badges that should be displayed
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function badges() : array
|
||||
{
|
||||
$badges = [];
|
||||
|
||||
if( Settings::i()->clubs_require_approval and !$this->approved )
|
||||
{
|
||||
$badges[] = new Icon( Badge::BADGE_WARNING, 'fa-eye-slash', Member::loggedIn()->language()->addToStack( 'club_unapproved' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( $this->featured )
|
||||
{
|
||||
$badges[] = new Icon( Badge::BADGE_POSITIVE, 'fa-star', Member::loggedIn()->language()->addToStack( 'featured' ) );
|
||||
}
|
||||
|
||||
$memberStatus = $this->memberStatus( Member::loggedIn() );
|
||||
if( in_array( $memberStatus, [ static::STATUS_MEMBER, static::STATUS_MODERATOR, static::STATUS_LEADER ] ) )
|
||||
{
|
||||
$badges[] = new Icon( Badge::BADGE_POSITIVE, 'fa-check', Member::loggedIn()->language()->addToStack( 'club_member' ) );
|
||||
}
|
||||
elseif( in_array( $memberStatus, [ static::STATUS_EXPIRED, static::STATUS_EXPIRED_MODERATOR ] ) )
|
||||
{
|
||||
$badges[] = new Icon( Badge::BADGE_INTERMEDIARY, 'fa-triangle-exclamation', Member::loggedIn()->language()->addToStack( 'club_expired' ) );
|
||||
}
|
||||
elseif( in_array( $memberStatus, [ static::STATUS_INVITED, static::STATUS_INVITED_BYPASSING_PAYMENT ] ) )
|
||||
{
|
||||
$badges[] = new Icon( Badge::BADGE_NEUTRAL, 'fa-envelope', Member::loggedIn()->language()->addToStack( 'club_invited' ) );
|
||||
}
|
||||
elseif( $memberStatus == static::STATUS_WAITING_PAYMENT )
|
||||
{
|
||||
$badges[] = new Icon( Badge::BADGE_NEUTRAL, 'fa-check', Member::loggedIn()->language()->addToStack( 'club_awaiting_payment_title' ) );
|
||||
}
|
||||
elseif( $memberStatus == static::STATUS_REQUESTED )
|
||||
{
|
||||
$badges[] = new Icon( Badge::BADGE_INTERMEDIARY, 'fa-clock', Member::loggedIn()->language()->addToStack( 'club_requested_desc_short' ) );
|
||||
}
|
||||
}
|
||||
|
||||
foreach( Application::allExtensions( 'core', 'Club' ) AS $ext )
|
||||
{
|
||||
/* @var ClubAbstract $ext */
|
||||
$badges = array_merge( $badges, $ext->badges( $this ) );
|
||||
}
|
||||
|
||||
return $badges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can a member view the members page
|
||||
*
|
||||
@@ -2362,10 +2521,10 @@ class Club extends ActiveRecord implements Embeddable
|
||||
* Can be used by 3rd parties to add own club navigation tabs before they get sorted
|
||||
*
|
||||
* @param array $tabs Tabs
|
||||
* @param Model|null $container Container
|
||||
* @param Model|ClubPage|null $container Container
|
||||
* @return array
|
||||
*/
|
||||
protected function _tabs( array $tabs, Model $container = NULL ) : array
|
||||
protected function _tabs( array $tabs, Model|ClubPage|null $container = NULL ) : array
|
||||
{
|
||||
foreach( Application::allExtensions( 'core', 'Club' ) AS $ext )
|
||||
{
|
||||
@@ -2635,4 +2794,56 @@ class Club extends ActiveRecord implements Embeddable
|
||||
$this->rebuilt = time();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Follow this object
|
||||
*
|
||||
* @param string $frequency ( 'none', 'immediate', 'daily', 'weekly' )
|
||||
* @param bool $public
|
||||
* @param Member|null $member
|
||||
* @return void
|
||||
*/
|
||||
public function follow( string $frequency, bool $public=true, ?Member $member=null ) : void
|
||||
{
|
||||
$member = $member ?: Member::loggedIn();
|
||||
$this->_follow( $frequency, $public, $member );
|
||||
|
||||
/* Follow all nodes */
|
||||
foreach( $this->nodes() as $node )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* @var Model $class */
|
||||
$class = $node['node_class'];
|
||||
$class::load( $node['node_id'] )->follow( $frequency, $public, $member );
|
||||
}
|
||||
catch( OutOfRangeException ){}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfollow this object
|
||||
*
|
||||
* @param Member|null $member
|
||||
* @param string|null $followId
|
||||
* @return void
|
||||
*/
|
||||
public function unfollow( ?Member $member=null, ?string $followId=null ) : void
|
||||
{
|
||||
/* First run the main method so that we unfollow the club itself */
|
||||
$member = $member ?: Member::loggedIn();
|
||||
$this->_unfollow( $member, $followId );
|
||||
|
||||
/* Now unfollow any nodes in the club */
|
||||
foreach( $this->nodes() as $node )
|
||||
{
|
||||
try
|
||||
{
|
||||
/* @var Model $class */
|
||||
$class = $node['node_class'];
|
||||
$class::load( $node['node_id'] )->unfollow( $member );
|
||||
}
|
||||
catch( OutOfRangeException ){}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user