Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
+121
-70
@@ -11,36 +11,61 @@
|
||||
namespace IPS\Member;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use ArrayIterator;
|
||||
use DateInterval;
|
||||
use InvalidArgumentException;
|
||||
use IPS\Application;
|
||||
use IPS\Application\Module;
|
||||
use IPS\Content\Search\Index;
|
||||
use IPS\core\StaffDirectory\User;
|
||||
use IPS\Data\Store;
|
||||
use IPS\DateTime;
|
||||
use IPS\Db;
|
||||
use IPS\Dispatcher;
|
||||
use IPS\Lang;
|
||||
use IPS\Member;
|
||||
use IPS\Patterns\ActiveRecord;
|
||||
use IPS\Patterns\ActiveRecordIterator;
|
||||
use IPS\Settings;
|
||||
use IPS\Widget;
|
||||
use OutOfRangeException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function str_replace;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group Model
|
||||
*/
|
||||
class _Group extends \IPS\Patterns\ActiveRecord
|
||||
class Group extends ActiveRecord
|
||||
{
|
||||
/**
|
||||
* @brief [ActiveRecord] Multiton Store
|
||||
*/
|
||||
protected static $multitons;
|
||||
protected static array $multitons;
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] Database Table
|
||||
*/
|
||||
public static $databaseTable = 'core_groups';
|
||||
public static ?string $databaseTable = 'core_groups';
|
||||
|
||||
/**
|
||||
* @brief [ActiveRecord] ID Database Column
|
||||
*/
|
||||
public static $databaseColumnId = 'g_id';
|
||||
public static string $databaseColumnId = 'g_id';
|
||||
|
||||
/**
|
||||
* @brief Bitwise keys
|
||||
*/
|
||||
protected static $bitOptions = array(
|
||||
protected static array $bitOptions = array(
|
||||
'g_bitoptions' => array(
|
||||
'g_bitoptions' => array(
|
||||
'gbw_mod_post_unit_type' => 1, // Lift moderation after x. 1 is days, 0 is posts. Corresponds to g_mod_post_unit
|
||||
@@ -48,7 +73,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
'gbw_displayname_unit_type' => 4, // Username change restrictions. 1 is days, 0 is posts. Corresponds to g_displayname_unit
|
||||
'gbw_sig_unit_type' => 8, // Signature edit restrictions. 1 is days, 0 is posts. Corresponds to g_sig_unit
|
||||
'gbw_promote_unit_type' => 16, // 16 is deprecated (previously gbw_promote_unit_type). Remove in 4.1.19.
|
||||
'gbw_no_status_update' => 32, // Can NOT post status updates
|
||||
// 32 is deprecated (previously gbw_no_status_update)
|
||||
// 64 is deprecated (previously gbw_soft_delete)
|
||||
// 128 is deprecated (previously gbw_soft_delete_own)
|
||||
// 256 is deprecated (previously gbw_soft_delete_own_topic)
|
||||
@@ -79,7 +104,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
'gbw_post_highlight' => 1, // 1 means on, 0 means off
|
||||
'gbw_hide_group' => 2, // Hide this group on the front end in search and similar areas: 1 is on, 0 is off
|
||||
// 4 is deprecated (previously gbw_lock_unlock_own)
|
||||
'gbw_promote' => 8, // Allow users in this group to promote to twitter
|
||||
// 8 is deprecated (gbw_promote)
|
||||
'gbw_immune_auto_mod' => 16, // Users in this group can not be auto moderated
|
||||
'gbw_paid_clubs' => 32, // Users in this group can create paid clubs
|
||||
'gbw_can_post_anonymously' => 64, // Users in this group can post anonymously (where allowed)
|
||||
@@ -87,6 +112,8 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
'gbw_posted_in' => 256, // Items (topics, articles) will be marked as 'posted in by this group'
|
||||
'gbw_posted_in_secondary' => 512, // Items (topics, articles) will be marked as 'posted in by this group' when the group is set as a secondary group
|
||||
'gbw_club_manage_indexing' => 1024, // Users in this group can manage indexing of clubs
|
||||
'gbw_view_helpful' => 2048, // Users in this group can view who found content helpful
|
||||
'gbw_change_layouts' => 4096, // Users in this group can change the layouts of key areas set by themes
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -95,51 +122,51 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
* @brief [ActiveRecord] Attempt to load from cache
|
||||
* @note If this is set to TRUE you MUST define a getStore() method to return the objects from cache
|
||||
*/
|
||||
protected static $loadFromCache = TRUE;
|
||||
protected static bool $loadFromCache = TRUE;
|
||||
|
||||
/**
|
||||
* Group datastore
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getStore()
|
||||
public static function getStore(): array
|
||||
{
|
||||
if ( !isset( \IPS\Data\Store::i()->groups ) )
|
||||
if ( !isset( Store::i()->groups ) )
|
||||
{
|
||||
\IPS\Data\Store::i()->groups = iterator_to_array( \IPS\Db::i()->select( '*', 'core_groups' )->setKeyField( 'g_id' ) );
|
||||
Store::i()->groups = iterator_to_array( Db::i()->select( '*', 'core_groups' )->setKeyField( 'g_id' ) );
|
||||
}
|
||||
|
||||
return \IPS\Data\Store::i()->groups;
|
||||
return Store::i()->groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stored Groups
|
||||
*/
|
||||
protected static $allGroups;
|
||||
|
||||
protected static ?array $allGroups = NULL;
|
||||
|
||||
/**
|
||||
* Get groups
|
||||
*
|
||||
* @param bool $showAdminGroups Show admin groups. Used to restrict admin groups from being available when you cannot add/edit members in them.
|
||||
* @param bool $showGuestGroups Show guest groups. Used to remove the guest group from the available groups returned.
|
||||
* @param bool $hideForFiltering If the group is set not to not be available to filter by, should we hide them?
|
||||
* @return array
|
||||
* @param bool $showAdminGroups Show admin groups. Used to restrict admin groups from being available when you cannot add/edit members in them.
|
||||
* @param bool $showGuestGroups Show guest groups. Used to remove the guest group from the available groups returned.
|
||||
* @param bool $hideForFiltering If the group is set not to not be available to filter by, should we hide them?
|
||||
* @return array|null
|
||||
*/
|
||||
public static function groups( $showAdminGroups=TRUE, $showGuestGroups=TRUE, $hideForFiltering=FALSE )
|
||||
public static function groups( bool $showAdminGroups=TRUE, bool $showGuestGroups=TRUE, bool $hideForFiltering=FALSE ): ?array
|
||||
{
|
||||
if ( !static::$allGroups )
|
||||
{
|
||||
static::$allGroups = iterator_to_array( new \IPS\Patterns\ActiveRecordIterator( new \ArrayIterator( static::getStore() ), 'IPS\Member\Group' ) );
|
||||
static::$allGroups = iterator_to_array( new ActiveRecordIterator( new ArrayIterator( static::getStore() ), 'IPS\Member\Group' ) );
|
||||
|
||||
/* If we have a dispatcher instance, sort the groups alphabetically */
|
||||
if( \IPS\Dispatcher::hasInstance() )
|
||||
if( Dispatcher::hasInstance() )
|
||||
{
|
||||
/* Get all of the group names in one query */
|
||||
$sortedGroupIds = array();
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'word_key LIKE ? AND lang_id=?', 'core_group_%', \IPS\Member::loggedIn()->language()->id ), 'word_custom ASC' ) as $lang )
|
||||
foreach( Db::i()->select( '*', 'core_sys_lang_words', array( 'word_key LIKE ? AND lang_id=?', 'core_group_%', Member::loggedIn()->language()->id ), 'word_custom ASC' ) as $lang )
|
||||
{
|
||||
$sortedGroupIds[] = \str_replace( 'core_group_', '', $lang['word_key'] );
|
||||
$sortedGroupIds[] = str_replace( 'core_group_', '', $lang['word_key'] );
|
||||
}
|
||||
|
||||
$finalGroups = array();
|
||||
@@ -160,12 +187,12 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
if ( !$showGuestGroups )
|
||||
{
|
||||
unset( $groups[ \IPS\Settings::i()->guest_group ] );
|
||||
unset( $groups[ Settings::i()->guest_group ] );
|
||||
}
|
||||
|
||||
if( !$showAdminGroups )
|
||||
{
|
||||
$administrators = \IPS\Member::administrators();
|
||||
$administrators = Member::administrators();
|
||||
foreach( $groups as $k => $_group )
|
||||
{
|
||||
if ( isset( $administrators['g'][ $_group->g_id ] ) )
|
||||
@@ -205,25 +232,25 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
$where = array();
|
||||
foreach( $perms as $key )
|
||||
{
|
||||
$where[] = \IPS\Db::i()->findInSet( $key, array($oldId) );
|
||||
$where[] = Db::i()->findInSet( $key, array($oldId) );
|
||||
}
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_permission_index', implode( ' OR ', $where ) ) as $index )
|
||||
foreach( Db::i()->select( '*', 'core_permission_index', implode( ' OR ', $where ) ) as $index )
|
||||
{
|
||||
foreach( $perms as $key )
|
||||
{
|
||||
$groups = explode( ",", $index[$key] );
|
||||
if( \in_array( $oldId, $groups ) and !\in_array( $this->g_id, $groups ) )
|
||||
if( in_array( $oldId, $groups ) and !in_array( $this->g_id, $groups ) )
|
||||
{
|
||||
$groups[] = $this->g_id;
|
||||
}
|
||||
|
||||
$index[$key] = implode( ",", $groups );
|
||||
}
|
||||
\IPS\Db::i()->update( 'core_permission_index', $index, array( 'perm_id = ?', $index['perm_id'] ) );
|
||||
Db::i()->update( 'core_permission_index', $index, array( 'perm_id = ?', $index['perm_id'] ) );
|
||||
}
|
||||
|
||||
$extensions = \IPS\Application::allExtensions( 'core', 'GroupForm', FALSE, 'core', 'GroupSettings', TRUE );
|
||||
$extensions = Application::allExtensions( 'core', 'GroupForm', FALSE, 'core', 'GroupSettings', TRUE );
|
||||
/* Process each extension */
|
||||
foreach ( $extensions as $class )
|
||||
{
|
||||
@@ -233,15 +260,15 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Lang::saveCustom( 'core', "core_group_{$this->g_id}", iterator_to_array( \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'word_key=?', "core_group_{$oldId}" ) )->setKeyField('lang_id')->setValueField('word_custom') ) );
|
||||
Lang::saveCustom( 'core', "core_group_{$this->g_id}", iterator_to_array( Db::i()->select( '*', 'core_sys_lang_words', array( 'word_key=?', "core_group_{$oldId}" ) )->setKeyField('lang_id')->setValueField('word_custom') ) );
|
||||
|
||||
/* Clear application and module store so that permissions are correctly rebuilt */
|
||||
unset( \IPS\Data\Store::i()->applications );
|
||||
unset( \IPS\Data\Store::i()->modules );
|
||||
unset( \IPS\Data\Store::i()->groups );
|
||||
unset( Store::i()->applications );
|
||||
unset( Store::i()->modules );
|
||||
unset( Store::i()->groups );
|
||||
|
||||
/* Rebuild search index */
|
||||
\IPS\Content\Search\Index::i()->rebuild();
|
||||
Index::i()->rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +276,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data()
|
||||
public function data(): array
|
||||
{
|
||||
return $this->_data;
|
||||
}
|
||||
@@ -270,9 +297,9 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name()
|
||||
public function get_name(): string
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( "core_group_{$this->g_id}" );
|
||||
return Member::loggedIn()->language()->addToStack( "core_group_{$this->g_id}" );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,7 +307,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_formattedName()
|
||||
public function get_formattedName(): string
|
||||
{
|
||||
return $this->formatName( $this->name );
|
||||
}
|
||||
@@ -292,33 +319,33 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
* @note The count is cached for 10 minutes as the query can be resource intensive
|
||||
* @return int
|
||||
*/
|
||||
public function getCount()
|
||||
public function getCount(): int
|
||||
{
|
||||
$key = 'groupMembersCount_' . $this->g_id;
|
||||
|
||||
try
|
||||
{
|
||||
$count = \IPS\Data\Store::i()->$key;
|
||||
$count = Store::i()->$key;
|
||||
|
||||
if( $values = json_decode( $count, true ) )
|
||||
{
|
||||
if( $values['expire'] < time() )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
|
||||
return $values['count'];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', array( 'member_group_id=? OR FIND_IN_SET( ?, mgroup_others )', $this->g_id, $this->g_id ) )->first();
|
||||
$count = Db::i()->select( 'COUNT(*)', 'core_members', array( 'member_group_id=? OR FIND_IN_SET( ?, mgroup_others )', $this->g_id, $this->g_id ) )->first();
|
||||
|
||||
\IPS\Data\Store::i()->$key = json_encode( array( 'count' => $count, 'expire' => \IPS\DateTime::create()->add( new \DateInterval( 'PT10M' ) )->getTimestamp() ) );
|
||||
Store::i()->$key = json_encode( array( 'count' => $count, 'expire' => DateTime::create()->add( new DateInterval( 'PT10M' ) )->getTimestamp() ) );
|
||||
}
|
||||
|
||||
return (int) $count;
|
||||
@@ -330,7 +357,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
* @param string $name The name to format
|
||||
* @return string
|
||||
*/
|
||||
public function formatName( $name )
|
||||
public function formatName( string $name ): string
|
||||
{
|
||||
return ( $this->prefix . htmlspecialchars( $name, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) . $this->suffix );
|
||||
}
|
||||
@@ -338,10 +365,10 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
/**
|
||||
* Can use module
|
||||
*
|
||||
* @param \IPS\Application\Module $module The module to test
|
||||
* @param Module $module The module to test
|
||||
* @return bool
|
||||
*/
|
||||
public function canAccessModule( $module )
|
||||
public function canAccessModule( Module $module ): bool
|
||||
{
|
||||
return $module->can( 'view', $this );
|
||||
}
|
||||
@@ -350,28 +377,28 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
* @brief [ActiveRecord] Caches
|
||||
* @note Defined cache keys will be cleared automatically as needed
|
||||
*/
|
||||
protected $caches = array( 'groups', 'moderators', 'administrators' );
|
||||
protected array $caches = array( 'groups', 'moderators', 'administrators' );
|
||||
|
||||
/**
|
||||
* [ActiveRecord] Delete Record
|
||||
*
|
||||
* @return void
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
public function delete(): void
|
||||
{
|
||||
if ( \in_array( $this->g_id, array( \IPS\Settings::i()->guest_group, \IPS\Settings::i()->member_group, \IPS\Settings::i()->admin_group ) ) )
|
||||
if ( in_array( $this->g_id, array( Settings::i()->guest_group, Settings::i()->member_group, Settings::i()->admin_group ) ) )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
throw new InvalidArgumentException;
|
||||
}
|
||||
|
||||
/* remove group from mod & staff section */
|
||||
|
||||
\IPS\Db::i()->delete( 'core_leaders', array( 'leader_type=? AND leader_type_id = ?', 'g', $this->g_id ) );
|
||||
\IPS\Db::i()->delete( 'core_moderators', array( 'type=? AND id = ?', 'g', $this->g_id ) );
|
||||
\IPS\Db::i()->delete( 'core_admin_permission_rows', array( 'row_id=? and row_id_type=?', $this->g_id, 'group' ) );
|
||||
Db::i()->delete( 'core_leaders', array( 'leader_type=? AND leader_type_id = ?', 'g', $this->g_id ) );
|
||||
Db::i()->delete( 'core_moderators', array( 'type=? AND id = ?', 'g', $this->g_id ) );
|
||||
Db::i()->delete( 'core_admin_permission_rows', array( 'row_id=? and row_id_type=?', $this->g_id, 'group' ) );
|
||||
|
||||
/* Make sure no other groups have this group ID set to promote to */
|
||||
foreach( static::groups() as $group )
|
||||
foreach(static::groups() as $group )
|
||||
{
|
||||
$promote = explode( '&', $group->g_promotion );
|
||||
|
||||
@@ -382,7 +409,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
$extensions = \IPS\Application::allExtensions( 'core', 'GroupForm', FALSE, 'core', 'GroupSettings', TRUE );
|
||||
$extensions = Application::allExtensions( 'core', 'GroupForm', FALSE, 'core', 'GroupSettings', TRUE );
|
||||
/* Process each extension */
|
||||
foreach ( $extensions as $class )
|
||||
{
|
||||
@@ -393,13 +420,13 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
|
||||
parent::delete();
|
||||
\IPS\Lang::deleteCustom( 'core', 'core_group_' . $this->g_id );
|
||||
Lang::deleteCustom( 'core', 'core_group_' . $this->g_id );
|
||||
|
||||
/* Update Core Leaders */
|
||||
\IPS\core\StaffDirectory\User::updateEmptySetting();
|
||||
User::updateEmptySetting();
|
||||
|
||||
/* Deleted group, expire widget caches (online stats may try to load a group) */
|
||||
\IPS\Widget::deleteCaches();
|
||||
Widget::deleteCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,7 +436,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public function canDelete() : bool
|
||||
{
|
||||
$extensions = \IPS\Application::allExtensions( 'core', 'GroupForm', FALSE, 'core', 'GroupSettings', TRUE );
|
||||
$extensions = Application::allExtensions( 'core', 'GroupForm', FALSE, 'core', 'GroupSettings', TRUE );
|
||||
|
||||
foreach ( $extensions as $class )
|
||||
{
|
||||
@@ -429,7 +456,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
* @param array $secondary Array of Secondary Group IDs
|
||||
* @return array|FALSE
|
||||
*/
|
||||
public static function postedIn( array $primary, array $secondary )
|
||||
public static function postedIn( array $primary, array $secondary ): array|bool
|
||||
{
|
||||
if ( !static::$allGroups )
|
||||
{
|
||||
@@ -439,7 +466,7 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
foreach( array( 'primary', 'secondary' ) as $type )
|
||||
{
|
||||
if ( \is_array( $$type ) and \count( $$type ) )
|
||||
if ( is_array( $$type ) and count( $$type ) )
|
||||
{
|
||||
foreach ( $$type as $id )
|
||||
{
|
||||
@@ -451,19 +478,19 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
}
|
||||
}
|
||||
|
||||
return ( \count( $return ) ? $return : FALSE );
|
||||
return ( count( $return ) ? $return : FALSE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get output for API
|
||||
*
|
||||
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
|
||||
* @param Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
|
||||
* @return array
|
||||
* @apiresponse int id ID number
|
||||
* @apiresponse string name Name
|
||||
* @apiresponse string formattedName Name with formatting
|
||||
*/
|
||||
public function apiOutput( \IPS\Member $authorizedMember = NULL )
|
||||
public function apiOutput( Member $authorizedMember = NULL ): array
|
||||
{
|
||||
return array(
|
||||
'id' => $this->g_id,
|
||||
@@ -471,4 +498,28 @@ class _Group extends \IPS\Patterns\ActiveRecord
|
||||
'formattedName' => $this->formattedName,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the restriction level this group has. This will be an integer of 0, 1 or 2, which correspond to minimal, standard and advanced
|
||||
*
|
||||
* @param bool $comments
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getEditorRestrictionLevel( bool $comments=false ) : int
|
||||
{
|
||||
$field = $comments ? "g_editor_comments_restriction" : "g_editor_restriction";
|
||||
if ( is_int( $this->$field ) )
|
||||
{
|
||||
return $this->$field;
|
||||
}
|
||||
|
||||
$administrators = Member::administrators();
|
||||
if ( isset( $administrators['g'][$this->g_id] ) )
|
||||
{
|
||||
return 2; // the advanced editor
|
||||
}
|
||||
|
||||
return $comments ? 0 : 1; // minimal for comments, standard otherwise
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user