Version 4.7.0
This commit is contained in:
1 parent
517a5e1f70
commit
8ba7d43898
1933 files changed
+65613
-11332
No files matched your search
@@ -42,7 +42,7 @@ class _Comment extends \IPS\core\Achievements\Actions\AbstractContentAchievement
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
if ( isset( $filters['milestone'] ) )
|
||||
{
|
||||
$count = 0;
|
||||
@@ -50,14 +50,32 @@ class _Comment extends \IPS\core\Achievements\Actions\AbstractContentAchievement
|
||||
$itemClassesToDeduct = [];
|
||||
if ( isset( $filters['type'] ) )
|
||||
{
|
||||
if ( isset( $filters[ 'nodes_' . str_replace( '\\', '-', $filters['type'] ) ] ) )
|
||||
$item = $extra->item();
|
||||
|
||||
if ( isset( $filters[ 'nodes_' . str_replace( '\\', '-', \get_class( $item ) ) ] ) )
|
||||
{
|
||||
$class = $filters['type'];
|
||||
$where = [];
|
||||
$where[] = [ $class::$databasePrefix . $class::$databaseColumnMap['author'] . '=?', $subject->member_id ];
|
||||
$where[] = [ \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnMap['container'] ), $filters[ 'nodes_' . str_replace( '\\', '-', $filters['type'] ) ] ];
|
||||
$where = [ [ $class::$databasePrefix . $class::$databaseColumnMap['author'] . '=?', $subject->member_id ] ];
|
||||
|
||||
if ( isset( $extra::$databaseColumnMap['first'] ) )
|
||||
{
|
||||
$where[] = [ $class::$databasePrefix . $class::$databaseColumnMap['first'] . '=0' ];
|
||||
}
|
||||
|
||||
if ( isset( $class::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$where[] = [ $class::$databasePrefix . $class::$databaseColumnMap['approved'] . '=?', 1 ];
|
||||
}
|
||||
elseif ( isset( $class::$databaseColumnMap['hidden'] ) )
|
||||
{
|
||||
$where[] = [ $class::$databasePrefix . $class::$databaseColumnMap['hidden'] . '=?', 0 ];
|
||||
}
|
||||
|
||||
$where[] = [ \IPS\Db::i()->in( $item::$databaseTable . '.' . $item::$databasePrefix . $item::$databaseColumnMap['container'] , $filters[ 'nodes_' . str_replace( '\\', '-', \get_class( $item ) ) ] ) ];
|
||||
|
||||
$count += \IPS\Db::i()->select( 'COUNT(*)', $class::$databaseTable, $where )->first();
|
||||
$count += \IPS\Db::i()->select( 'COUNT(*)', $class::$databaseTable, $where )
|
||||
->join( $item::$databaseTable, $item::$databasePrefix . $item::$databaseColumnId . '=' . $class::$databasePrefix . $class::$databaseColumnMap['item'] )
|
||||
->first();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -88,27 +106,30 @@ class _Comment extends \IPS\core\Achievements\Actions\AbstractContentAchievement
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the labels for the people this action might give awards to
|
||||
*
|
||||
* @param array|NULL $filters Current filter values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function awardOptions(): array
|
||||
public function awardOptions( ?array $filters ): array
|
||||
{
|
||||
return [
|
||||
'subject' => 'achievement_filter_Comment_author',
|
||||
'other' => 'achievement_filter_Comment_item_author'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the "other" people we need to award =stuff to
|
||||
*
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param array|NULL $filters Current filter values
|
||||
* @return array
|
||||
*/
|
||||
public function awardOther( $extra = NULL ): array
|
||||
public function awardOther( $extra = NULL, ?array $filters = NULL ): array
|
||||
{
|
||||
return [ $extra->item()->author() ];
|
||||
}
|
||||
@@ -127,6 +148,11 @@ class _Comment extends \IPS\core\Achievements\Actions\AbstractContentAchievement
|
||||
$sprintf = [];
|
||||
try
|
||||
{
|
||||
if( !\class_exists( $exploded[0] ) )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
$comment = $exploded[0]::load( $exploded[1] );
|
||||
$item = $comment->item();
|
||||
$sprintf = [ 'htmlsprintf' => [
|
||||
|
||||
@@ -91,7 +91,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
|
||||
|
||||
if ( isset( $filters['milestone'] ) )
|
||||
{
|
||||
if ( ( $this->getQuery( 'COUNT(*)', $filters, $filters['promotype'], $subject )->first() + 1 ) < $filters['milestone'] )
|
||||
if ( ( $this->getQuery( 'COUNT(*)', $filters, $extra['promotype'], $subject )->first() + 1 ) < $filters['milestone'] )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@@ -248,7 +248,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
|
||||
break;
|
||||
case 'core_social_promote':
|
||||
$promote = \IPS\core\Promote::load( $row['promote_id'] );
|
||||
$promote->author()->achievementAction( 'core', 'ContentPromotion', [
|
||||
$promote->object()->author()->achievementAction( 'core', 'ContentPromotion', [
|
||||
'content' => $promote->object(),
|
||||
'promotype' => 'promote'
|
||||
] );
|
||||
@@ -267,20 +267,24 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
|
||||
* @param null|boolean $order
|
||||
* @param null $limit
|
||||
* @return \IPS\Db\Select
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function getQuery( $select, $filters, $type, $subject=NULL, $lastId=NULL, $time=NULL, $order=NULL, $limit=NULL )
|
||||
{
|
||||
$itemClass = $filters['type'];
|
||||
|
||||
if ( isset( $itemClass::$databaseColumnMap['hidden'] ) )
|
||||
if( isset( $filters['type'] ) )
|
||||
{
|
||||
$subWhere = [ [ $itemClass::$databaseColumnMap['hidden'] . '=0' ] ];
|
||||
}
|
||||
elseif ( isset( $itemClass::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$subWhere = [ [ $itemClass::$databaseColumnMap['approved'] . '=1' ] ];
|
||||
}
|
||||
$itemClass = $filters['type'];
|
||||
|
||||
if ( isset( $itemClass::$databaseColumnMap['hidden'] ) )
|
||||
{
|
||||
$subWhere = [ [ $itemClass::$databaseColumnMap['hidden'] . '=0' ] ];
|
||||
}
|
||||
elseif ( isset( $itemClass::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$subWhere = [ [ $itemClass::$databaseColumnMap['approved'] . '=1' ] ];
|
||||
}
|
||||
}
|
||||
|
||||
/* Recommended (but really called featured in the code just to confuse me) */
|
||||
if ( $type === 'recommend' )
|
||||
{
|
||||
@@ -324,7 +328,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
|
||||
$where[] = [ 'meta_item_id IN(?)', \IPS\Db::i()->select( $itemClass::$databaseColumnId, $itemClass::$databasePrefix . $itemClass::$databaseTable, $subWhere ) ];
|
||||
}
|
||||
|
||||
$query = \IPS\Db::i()->select( $select, 'core_content_meta', $where );
|
||||
return \IPS\Db::i()->select( $select, 'core_content_meta', $where );
|
||||
}
|
||||
else if ( $type === 'feature' )
|
||||
{
|
||||
@@ -362,7 +366,7 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
|
||||
$where[] = [ 'feature_content_id IN(?)', \IPS\Db::i()->select( $itemClass::$databaseColumnId, $itemClass::$databasePrefix . $itemClass::$databaseTable, $subWhere ) ];
|
||||
}
|
||||
|
||||
$query = \IPS\Db::i()->select( $select, 'core_content_featured', $where );
|
||||
return \IPS\Db::i()->select( $select, 'core_content_featured', $where );
|
||||
}
|
||||
else if ( $type === 'promote' )
|
||||
{
|
||||
@@ -400,9 +404,9 @@ class _ContentPromotion extends \IPS\core\Achievements\Actions\AbstractContentAc
|
||||
$where[] = [ 'promote_class_id IN(?)', \IPS\Db::i()->select( $itemClass::$databaseColumnId, $itemClass::$databasePrefix . $itemClass::$databaseTable, $subWhere ) ];
|
||||
}
|
||||
|
||||
$query = \IPS\Db::i()->select( $select, 'core_social_promote', $where, $order, $limit );
|
||||
return \IPS\Db::i()->select( $select, 'core_social_promote', $where, $order, $limit );
|
||||
}
|
||||
|
||||
return $query;
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
}
|
||||
@@ -80,9 +80,11 @@ class _FollowContentItem extends \IPS\core\Achievements\Actions\AbstractContentA
|
||||
/**
|
||||
* Get the labels for the people this action might give awards to
|
||||
*
|
||||
* @param array|NULL $filters Current filter values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function awardOptions(): array
|
||||
public function awardOptions( ?array $filters ): array
|
||||
{
|
||||
return [
|
||||
'subject' => 'achievement_filter_FollowContentItem_follower',
|
||||
@@ -93,10 +95,11 @@ class _FollowContentItem extends \IPS\core\Achievements\Actions\AbstractContentA
|
||||
/**
|
||||
* Get the "other" people we need to award =stuff to
|
||||
*
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param array|NULL $filters Current filter values
|
||||
* @return array
|
||||
*/
|
||||
public function awardOther( $extra = NULL ): array
|
||||
public function awardOther( $extra = NULL, ?array $filters = NULL ): array
|
||||
{
|
||||
return [ $extra['author'] ];
|
||||
}
|
||||
@@ -199,7 +202,7 @@ class _FollowContentItem extends \IPS\core\Achievements\Actions\AbstractContentA
|
||||
{
|
||||
$className = 'IPS\\' . $row['follow_app'] . '\\' . mb_ucfirst( $row['follow_area'] );
|
||||
|
||||
if ( \in_array( 'IPS\Content\Item', class_parents( $className ) ) )
|
||||
if ( class_exists( $className ) AND \in_array( 'IPS\Content\Item', class_parents( $className ) ) )
|
||||
{
|
||||
$item = $className::load( $row['follow_rel_id'] );
|
||||
\IPS\Member::load( $row['follow_member_id'] )->achievementAction( 'core', 'FollowContentItem', [
|
||||
|
||||
@@ -74,7 +74,7 @@ class _FollowMember extends \IPS\core\Achievements\Actions\AbstractAchievementAc
|
||||
if ( isset( $filters['milestone'] ) )
|
||||
{
|
||||
$where = [];
|
||||
$where[] = [ 'follow_app=? and follow_area=? and follow_member_id=?', 'core', 'member', $subject->member_id ];
|
||||
$where[] = [ 'follow_app=? and follow_area=? and follow_rel_id=?', 'core', 'member', $subject->member_id ];
|
||||
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_follow', $where )->first();
|
||||
|
||||
@@ -90,9 +90,11 @@ class _FollowMember extends \IPS\core\Achievements\Actions\AbstractAchievementAc
|
||||
/**
|
||||
* Get the labels for the people this action might give awards to
|
||||
*
|
||||
* @param array|NULL $filters Current filter values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function awardOptions(): array
|
||||
public function awardOptions( ?array $filters ): array
|
||||
{
|
||||
return [
|
||||
'subject' => 'achievement_filter_FollowMember_receiver',
|
||||
@@ -103,10 +105,11 @@ class _FollowMember extends \IPS\core\Achievements\Actions\AbstractAchievementAc
|
||||
/**
|
||||
* Get the "other" people we need to award =stuff to
|
||||
*
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param array|NULL $filters Current filter values
|
||||
* @return array
|
||||
*/
|
||||
public function awardOther( $extra = NULL ): array
|
||||
public function awardOther( $extra = NULL, ?array $filters = NULL ): array
|
||||
{
|
||||
return [ $extra['giver'] ];
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ class _FollowNode extends \IPS\core\Achievements\Actions\AbstractNodeAchievement
|
||||
public static function rebuildRow( $row, $data )
|
||||
{
|
||||
$class = 'IPS\\' . $row['follow_app'] . '\\' . mb_ucfirst( $row['follow_area'] );
|
||||
if ( \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
|
||||
if ( class_exists( $class ) AND \in_array( 'IPS\Node\Model', class_parents( $class ) ) )
|
||||
{
|
||||
\IPS\Member::load( $row['follow_member_id'] )->achievementAction( 'core', 'FollowNode', $class::load( $row['follow_rel_id'] ) );
|
||||
}
|
||||
|
||||
@@ -96,27 +96,30 @@ class _JoinClub extends \IPS\core\Achievements\Actions\AbstractAchievementAction
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the labels for the people this action might give awards to
|
||||
*
|
||||
* @param array|NULL $filters Current filter values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function awardOptions(): array
|
||||
public function awardOptions( ?array $filters ): array
|
||||
{
|
||||
return [
|
||||
'subject' => 'achievement_filter_JoinClub_joiner',
|
||||
'other' => 'achievement_filter_JoinClub_owner'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the "other" people we need to award =stuff to
|
||||
*
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param array|NULL $filters Current filter values
|
||||
* @return array
|
||||
*/
|
||||
public function awardOther( $extra = NULL ): array
|
||||
public function awardOther( $extra = NULL, ?array $filters = NULL ): array
|
||||
{
|
||||
return [ $extra->owner ];
|
||||
}
|
||||
|
||||
@@ -98,6 +98,11 @@ class _NewContentItem extends \IPS\core\Achievements\Actions\AbstractContentAchi
|
||||
$sprintf = [];
|
||||
try
|
||||
{
|
||||
if( !\class_exists( $exploded[0] ) )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
$item = $exploded[0]::load( $exploded[1] );
|
||||
$sprintf = [ 'htmlsprintf' => [
|
||||
\IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $item->url(), TRUE, $item->mapped('title') ?: $item->indefiniteArticle(), FALSE )
|
||||
|
||||
@@ -40,6 +40,16 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
], NULL, \IPS\Member::loggedIn()->language()->addToStack( 'achievement_subfilter_Reaction_reaction_prefix' ) );
|
||||
$return['reactions'] = $reactionFilter;
|
||||
|
||||
$return['milestone'] = new \IPS\Helpers\Form\Custom( 'achievement_filter_Reaction_nth', ( $filters and isset( $filters['milestone'] ) and $filters['milestone'] ) ? $filters['milestone'] : [], FALSE, array( 'getHtml' => function( $element )
|
||||
{
|
||||
/* 4.6.0 - 4.6.3 */
|
||||
if ( \is_integer( $element->value ) )
|
||||
{
|
||||
$element->value = ['receiver', $element->value];
|
||||
}
|
||||
return \IPS\Theme::i()->getTemplate( 'achievements' )->milestoneWithSubjectSwitch( $element->name, $element->value );
|
||||
} ), NULL, NULL, NULL, 'achievement_filter_Reaction_nth' );
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -56,6 +66,7 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
{
|
||||
$return['reactions'] = array_keys( $values['achievement_filter_Reaction_reaction'] );
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -77,7 +88,7 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
if ( isset( $filters['reactions'] ) )
|
||||
{
|
||||
if ( !\in_array( $extra['reaction']->id, $filters['reactions'] ) )
|
||||
@@ -85,12 +96,26 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( isset( $filters['milestone'] ) )
|
||||
{
|
||||
$query = $this->getQuery( 'COUNT(*)', [ [ 'member_id=?', $subject->member_id ] ], NULL, $filters );
|
||||
|
||||
if ( ( $query->first() + 1 ) < $filters['milestone'] )
|
||||
if ( isset( $filters['milestone'] ) and \is_array( $filters['milestone'] ) )
|
||||
{
|
||||
$field = ( $filters['milestone'][0] == 'giver' ) ? 'member_id' : 'member_received';
|
||||
$member = ( $filters['milestone'][0] == 'giver' ) ? $extra['giver'] : $subject;
|
||||
$milestone = $filters['milestone'][1];
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 4.6.0 - 4.6.3 just had a milestone for member_received */
|
||||
$field = 'member_received';
|
||||
$milestone = $filters['milestone'];
|
||||
$member = $subject;
|
||||
}
|
||||
|
||||
$query = $this->getQuery( 'COUNT(*)', [ [ $field . '=?', $member->member_id ] ], NULL, $filters );
|
||||
|
||||
if ( ( $query->first() + 1 ) < $milestone )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
@@ -111,27 +136,30 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
{
|
||||
return \get_class( $extra['content'] ) . ':' . $extra['content']->{$extra['content']::$databaseColumnId} . ':' . $extra['giver']->member_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the labels for the people this action might give awards to
|
||||
*
|
||||
* @param array|NULL $filters Current filter values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function awardOptions(): array
|
||||
public function awardOptions( ?array $filters ): array
|
||||
{
|
||||
return [
|
||||
'subject' => 'achievement_filter_Reaction_receiver',
|
||||
'other' => 'achievement_filter_Reaction_giver'
|
||||
'subject' => 'achievement_filter_Reaction_receiver',
|
||||
'other' => 'achievement_filter_Reaction_giver'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the "other" people we need to award =stuff to
|
||||
*
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param array|NULL $filters Current filter values
|
||||
* @return array
|
||||
*/
|
||||
public function awardOther( $extra = NULL ): array
|
||||
public function awardOther( $extra = NULL, ?array $filters = NULL ): array
|
||||
{
|
||||
return [ $extra['giver'] ];
|
||||
}
|
||||
@@ -181,9 +209,19 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
$conditions = [];
|
||||
if ( isset( $rule->filters['milestone'] ) )
|
||||
{
|
||||
if ( \is_integer( $rule->filters['milestone'] ) )
|
||||
{
|
||||
/* 4.6.0 - 4.6.3 */
|
||||
$milestone = $rule->filters['milestone'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$milestone = $rule->filters['milestone'][1];
|
||||
}
|
||||
|
||||
$conditions[] = \IPS\Member::loggedIn()->language()->addToStack( 'achievements_title_filter_milestone', FALSE, [
|
||||
'htmlsprintf' => [
|
||||
\IPS\Theme::i()->getTemplate( 'achievements' )->ruleDescriptionBadge( 'milestone', \IPS\Member::loggedIn()->language()->addToStack( 'achievements_title_filter_milestone_nth', FALSE, [ 'pluralize' => [ $rule->filters['milestone'] ] ] ) )
|
||||
\IPS\Theme::i()->getTemplate( 'achievements' )->ruleDescriptionBadge( 'milestone', \IPS\Member::loggedIn()->language()->addToStack( 'achievements_title_filter_milestone_nth', FALSE, [ 'pluralize' => [ $milestone ] ] ) )
|
||||
],
|
||||
'sprintf' => \IPS\Member::loggedIn()->language()->addToStack('AchievementAction__Reaction_title_generic')
|
||||
] );
|
||||
@@ -219,7 +257,9 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
}
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'achievements' )->ruleDescription(
|
||||
\IPS\Member::loggedIn()->language()->addToStack( 'AchievementAction__Reaction_title' ),
|
||||
( ( isset( $rule->filters['milestone'] ) and ( ( \is_array( $rule->filters['milestone'] ) and $rule->filters['milestone'][0] == 'giver' ) or \is_numeric( $rule->filters['milestone'] ) ) ) or ! isset( $rule->filters['milestone']) )
|
||||
? \IPS\Member::loggedIn()->language()->addToStack( 'AchievementAction__Reaction_title' )
|
||||
: \IPS\Member::loggedIn()->language()->addToStack( 'AchievementAction__Reaction_title_received' ),
|
||||
$conditions
|
||||
);
|
||||
}
|
||||
@@ -254,9 +294,27 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
return;
|
||||
}
|
||||
|
||||
$object = $row['rep_class']::load( $row['item_id'] );
|
||||
$object = $row['rep_class']::load( $row['type_id'] );
|
||||
$reaction = \IPS\Content\Reaction::load( $row['reaction'] );
|
||||
|
||||
/* Give points */
|
||||
static::getAuthor( $object )->achievementAction( 'core', 'Reaction', [
|
||||
'giver' => \IPS\Member::load( $row['member_id'] ),
|
||||
'content' => $object,
|
||||
'reaction' => $reaction
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the author of the content / node
|
||||
*
|
||||
* @param $object
|
||||
* @return \IPS\Member|null
|
||||
*/
|
||||
public static function getAuthor( $object ): ?\IPS\Member
|
||||
{
|
||||
$owner = NULL;
|
||||
|
||||
/* Figure out the owner of this - if it is content, it will be the author. If it is a node, then it will be the person who created it */
|
||||
if ( $object instanceof \IPS\Content )
|
||||
{
|
||||
@@ -267,12 +325,7 @@ class _Reaction extends \IPS\core\Achievements\Actions\AbstractContentAchievemen
|
||||
$owner = $object->owner();
|
||||
}
|
||||
|
||||
/* Give points */
|
||||
$owner->achievementAction( 'core', 'Reaction', [
|
||||
'giver' => \IPS\Member::load( $row['member_id'] ),
|
||||
'content' => $object,
|
||||
'reaction' => $reaction
|
||||
] );
|
||||
return $owner;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,27 +91,30 @@ class _Review extends \IPS\core\Achievements\Actions\AbstractContentAchievementA
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the labels for the people this action might give awards to
|
||||
*
|
||||
* @param array|NULL $filters Current filter values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function awardOptions(): array
|
||||
public function awardOptions( ?array $filters ): array
|
||||
{
|
||||
return [
|
||||
'subject' => 'achievement_filter_Review_author',
|
||||
'other' => 'achievement_filter_Review_item_author'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the "other" people we need to award =stuff to
|
||||
*
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param array|NULL $filters Current filter values
|
||||
* @return array
|
||||
*/
|
||||
public function awardOther( $extra = NULL ): array
|
||||
public function awardOther( $extra = NULL, ?array $filters = NULL ): array
|
||||
{
|
||||
return [ $extra->item()->author() ];
|
||||
}
|
||||
|
||||
@@ -126,7 +126,8 @@ class _SessionStartDaily extends \IPS\core\Achievements\Actions\AbstractAchievem
|
||||
*/
|
||||
public function logRow( string $identifier, array $actor ): string
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'AchievementAction__SessionStartDaily_log', FALSE, array( 'sprintf' => ( new \IPS\DateTime( $identifier ) )->dayAndMonth() ) );
|
||||
list( $id, $time ) = explode( '.', $identifier );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'AchievementAction__SessionStartDaily_log', FALSE, array( 'sprintf' => ( new \IPS\DateTime( $time ) )->dayAndMonth() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,28 +86,30 @@ class _VotePoll extends \IPS\core\Achievements\Actions\AbstractAchievementAction
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the labels for the people this action might give awards to
|
||||
*
|
||||
* @param array|NULL $filters Current filter values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function awardOptions(): array
|
||||
public function awardOptions( ?array $filters ): array
|
||||
{
|
||||
return [
|
||||
'subject' => 'achievement_filter_VotePoll_voter',
|
||||
'other' => 'achievement_filter_VotePoll_author'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the "other" people we need to award =stuff to
|
||||
*
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param mixed $extra Any additional information about what is happening (e.g. if a post is being made: the post object)
|
||||
* @param array|NULL $filters Current filter values
|
||||
* @return array
|
||||
*/
|
||||
|
||||
public function awardOther( $extra = NULL ): array
|
||||
public function awardOther( $extra = NULL, ?array $filters = NULL ): array
|
||||
{
|
||||
return [ $extra->author() ];
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ class _Bulletin extends \IPS\core\AdminNotification
|
||||
{
|
||||
try
|
||||
{
|
||||
$bulletin = \IPS\Http\Url::ips("bulletin/{$data['id']}")->request()->get()->decodeJson();
|
||||
$response = \IPS\Http\Url::ips("bulletin/{$data['id']}")->request()->get();
|
||||
$bulletin = $response->decodeJson();
|
||||
if ( isset( $bulletin['title'] ) )
|
||||
{
|
||||
$data = array(
|
||||
@@ -128,6 +129,12 @@ class _Bulletin extends \IPS\core\AdminNotification
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (int) $response->httpResponseCode === 410 )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'core_ips_bulletins', [ 'id=?', $this->extra ] );
|
||||
$this->delete();
|
||||
}
|
||||
|
||||
throw new \DomainException;
|
||||
}
|
||||
}
|
||||
@@ -238,11 +245,7 @@ class _Bulletin extends \IPS\core\AdminNotification
|
||||
|
||||
return !@eval( $this->data()['conditions'] );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
catch ( \Throwable $e )
|
||||
catch ( \Throwable | \Exception $e )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -548,6 +548,10 @@ class _ConfigurationError extends \IPS\core\AdminNotification
|
||||
}
|
||||
elseif ( $this->extra === 'cacheBroken' )
|
||||
{
|
||||
if( \IPS\CIC )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('dashboard_invalid_cachesetup_desc_cic');
|
||||
}
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('dashboard_invalid_cachesetup_desc');
|
||||
}
|
||||
elseif ( $this->extra === 'cicEmailQuota' )
|
||||
|
||||
@@ -60,6 +60,11 @@ class _NewVersion extends \IPS\core\AdminNotification
|
||||
*/
|
||||
public static function permissionCheck( \IPS\Member $member )
|
||||
{
|
||||
if ( \IPS\CIC AND \IPS\IPS::isManaged() )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $member->hasAcpRestriction( 'core', 'overview', 'upgrade_manage' );
|
||||
}
|
||||
|
||||
@@ -163,4 +168,15 @@ class _NewVersion extends \IPS\core\AdminNotification
|
||||
{
|
||||
return \IPS\Http\Url::internal( 'app=core&module=system&controller=upgrade&_new=1', 'admin' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Should this notification dismiss itself?
|
||||
*
|
||||
* @note This is checked every time the notification shows. Should be lightweight.
|
||||
* @return bool
|
||||
*/
|
||||
public function selfDismiss()
|
||||
{
|
||||
return empty( $this->_details['version'] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Community Enhancements
|
||||
* @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 13 Sep 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\CommunityEnhancements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Community Enhancement
|
||||
*/
|
||||
class _GoogleAnalytics
|
||||
{
|
||||
/**
|
||||
* @brief Enhancement is enabled?
|
||||
*/
|
||||
public $enabled = FALSE;
|
||||
|
||||
/**
|
||||
* @brief IPS-provided enhancement?
|
||||
*/
|
||||
public $ips = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Enhancement has configuration options?
|
||||
*/
|
||||
public $hasOptions = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Icon data
|
||||
*/
|
||||
public $icon = "google_analytics.png";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->enabled = ( \IPS\Settings::i()->ga_enabled and \IPS\Settings::i()->ga_code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
|
||||
$validation = function( $val ) {
|
||||
if ( $val and !\IPS\Request::i()->ga_code )
|
||||
{
|
||||
throw new \DomainException('ga_code_required');
|
||||
}
|
||||
};
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'ga_enabled', \IPS\Settings::i()->ga_enabled, FALSE, array(), $validation ) );
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'ga_code', \IPS\Settings::i()->ga_code, FALSE, array( 'height' => 150, 'mode' => 'javascript' ), NULL, NULL, NULL, 'ga_code' ) );
|
||||
|
||||
if ( $form->values() )
|
||||
{
|
||||
try
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
|
||||
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
|
||||
}
|
||||
catch ( \LogicException $e )
|
||||
{
|
||||
$form->error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'help' => array(
|
||||
'title' => 'learn_more',
|
||||
'icon' => 'question-circle',
|
||||
'link' => \IPS\Http\Url::ips( 'docs/googleanalytics' ), //@todo
|
||||
'target' => '_blank'
|
||||
),
|
||||
);
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_GoogleAnalytics', $form );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable
|
||||
*
|
||||
* @param $enabled bool Enable/Disable
|
||||
* @return void
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function toggle( $enabled )
|
||||
{
|
||||
if ( $enabled )
|
||||
{
|
||||
if ( \IPS\Settings::i()->ga_code )
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'ga_enabled' => 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \DomainException;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'ga_enabled' => 0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Community Enhancements
|
||||
* @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 13 Sep 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\CommunityEnhancements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Community Enhancement
|
||||
*/
|
||||
class _GoogleTagManager
|
||||
{
|
||||
/**
|
||||
* @brief Enhancement is enabled?
|
||||
*/
|
||||
public $enabled = FALSE;
|
||||
|
||||
/**
|
||||
* @brief IPS-provided enhancement?
|
||||
*/
|
||||
public $ips = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Enhancement has configuration options?
|
||||
*/
|
||||
public $hasOptions = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Icon data
|
||||
*/
|
||||
public $icon = "google_tag_manager.png";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->enabled = ( \IPS\Settings::i()->googletag_enabled and \IPS\Settings::i()->googletag_head_code and \IPS\Settings::i()->googletag_noscript_code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
|
||||
$validation = function( $val ) {
|
||||
if ( $val and ( !\IPS\Request::i()->googletag_head_code or !\IPS\Request::i()->googletag_noscript_code ) )
|
||||
{
|
||||
throw new \DomainException('googletag_code_required');
|
||||
}
|
||||
};
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'googletag_enabled', \IPS\Settings::i()->googletag_enabled, FALSE, array(), $validation ) );
|
||||
if ( \IPS\Settings::i()->core_datalayer_enabled )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'core_datalayer_use_gtm', \IPS\Settings::i()->core_datalayer_use_gtm, FALSE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'core_datalayer_gtmkey', \IPS\Settings::i()->core_datalayer_gtmkey, FALSE ) );
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'googletag_head_code', \IPS\Settings::i()->googletag_head_code, FALSE, array( 'height' => 150, 'mode' => 'javascript' ), NULL, NULL, NULL, 'googletag_head_code' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'googletag_noscript_code', \IPS\Settings::i()->googletag_noscript_code, FALSE, array( 'height' => 150, 'mode' => 'javascript' ), NULL, NULL, NULL, 'googletag_noscript_code' ) );
|
||||
|
||||
if ( $form->values() )
|
||||
{
|
||||
try
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
|
||||
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
|
||||
}
|
||||
catch ( \LogicException $e )
|
||||
{
|
||||
$form->error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'help' => array(
|
||||
'title' => 'learn_more',
|
||||
'icon' => 'question-circle',
|
||||
'link' => \IPS\Http\Url::ips( 'docs/googletagmanager' ),
|
||||
'target' => '_blank'
|
||||
),
|
||||
);
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_GoogleTagManager', $form );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable
|
||||
*
|
||||
* @param $enabled bool Enable/Disable
|
||||
* @return void
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function toggle( $enabled )
|
||||
{
|
||||
if ( $enabled )
|
||||
{
|
||||
if ( \IPS\Settings::i()->googletag_head_code and \IPS\Settings::i()->googletag_noscript_code )
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'googletag_enabled' => 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \DomainException;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'googletag_enabled' => 0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Community Enhancements
|
||||
* @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 11 Jan 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\CommunityEnhancements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Community Enhancement
|
||||
*/
|
||||
class _IndexNow
|
||||
{
|
||||
/**
|
||||
* @brief Enhancement is enabled?
|
||||
*/
|
||||
public $enabled = FALSE;
|
||||
|
||||
/**
|
||||
* @brief IPS-provided enhancement?
|
||||
*/
|
||||
public $ips = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Enhancement has configuration options?
|
||||
*/
|
||||
public $hasOptions = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Icon data
|
||||
*/
|
||||
public $icon = "";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->enabled = \IPS\Settings::i()->indexnow_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable
|
||||
*
|
||||
* @param $enabled bool Enable/Disable
|
||||
* @return void
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function toggle( $enabled )
|
||||
{
|
||||
/* If we're disabling, just disable */
|
||||
if( !$enabled )
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'indexnow_enabled' => 0 ) );
|
||||
\IPS\Settings::i()->changeValues( array( 'indexnow_key' => '' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
$key = \IPS\Login::generateRandomString();
|
||||
|
||||
\IPS\Settings::i()->changeValues( array( 'indexnow_enabled' => 1 ) );
|
||||
\IPS\Settings::i()->changeValues( array( 'indexnow_key' => $key ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Community Enhancements
|
||||
* @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 13 Sep 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\CommunityEnhancements;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Community Enhancement
|
||||
*/
|
||||
class _Matomo
|
||||
{
|
||||
/**
|
||||
* @brief Enhancement is enabled?
|
||||
*/
|
||||
public $enabled = FALSE;
|
||||
|
||||
/**
|
||||
* @brief IPS-provided enhancement?
|
||||
*/
|
||||
public $ips = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Enhancement has configuration options?
|
||||
*/
|
||||
public $hasOptions = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Icon data
|
||||
*/
|
||||
public $icon = "matomo.png";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->enabled = ( \IPS\Settings::i()->matomo_enabled and \IPS\Settings::i()->matomo_code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
|
||||
$validation = function( $val ) {
|
||||
if ( $val and !\IPS\Request::i()->matomo_code )
|
||||
{
|
||||
throw new \DomainException('matomo_code_required');
|
||||
}
|
||||
};
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'matomo_enabled', \IPS\Settings::i()->matomo_enabled, FALSE, array(), $validation ) );
|
||||
$form->add( new \IPS\Helpers\Form\Codemirror( 'matomo_code', \IPS\Settings::i()->matomo_code, FALSE, array( 'height' => 150, 'mode' => 'javascript' ), NULL, NULL, NULL, 'matomo_code' ) );
|
||||
|
||||
if ( $form->values() )
|
||||
{
|
||||
try
|
||||
{
|
||||
$form->saveAsSettings();
|
||||
|
||||
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
|
||||
}
|
||||
catch ( \LogicException $e )
|
||||
{
|
||||
$form->error = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sidebar['actions'] = array(
|
||||
'help' => array(
|
||||
'title' => 'learn_more',
|
||||
'icon' => 'question-circle',
|
||||
'link' => \IPS\Http\Url::ips( 'docs/matomo' ),
|
||||
'target' => '_blank'
|
||||
),
|
||||
);
|
||||
|
||||
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_Matomo', $form );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable
|
||||
*
|
||||
* @param $enabled bool Enable/Disable
|
||||
* @return void
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function toggle( $enabled )
|
||||
{
|
||||
if ( $enabled )
|
||||
{
|
||||
if ( \IPS\Settings::i()->matomo_code )
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'matomo_enabled' => 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \DomainException;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'matomo_enabled' => 0 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,20 +106,7 @@ class _Viglink
|
||||
}
|
||||
else
|
||||
{
|
||||
$json = $this->_makeApiCall();
|
||||
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->attributes['data-controller'] = 'core.admin.core.viglink';
|
||||
$form->attributes['data-viglinkUrl'] = $json['URL'];
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'viglink_account_type', 'create', FALSE, array(
|
||||
'options' => array(
|
||||
'create' => 'viglink_account_type_create',
|
||||
'existing' => 'viglink_account_existing'
|
||||
),
|
||||
'toggles' => array(
|
||||
'existing' => array( 'viglink_api_key' ),
|
||||
),
|
||||
) ) );
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'viglink_api_key', NULL, FALSE, array(), function( $val ){
|
||||
if( !$val && \IPS\Request::i()->viglink_account_type == 'existing' )
|
||||
{
|
||||
@@ -129,16 +116,7 @@ class _Viglink
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
if ( $values['viglink_account_type'] == 'create' )
|
||||
{
|
||||
$values['viglink_api_key'] = $json['API_KEY'];
|
||||
$values['viglink_subid'] = $json['SUBID'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$values['viglink_subid'] = '';
|
||||
}
|
||||
|
||||
$values['viglink_subid'] = '';
|
||||
$values['viglink_enabled'] = TRUE;
|
||||
unset( $values['viglink_account_type'] );
|
||||
$form->saveAsSettings( $values );
|
||||
@@ -150,45 +128,6 @@ class _Viglink
|
||||
\IPS\Output::i()->output = $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make call to IPS to get API keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _makeApiCall()
|
||||
{
|
||||
$subId = NULL;
|
||||
if ( \IPS\Settings::i()->ipb_reg_number )
|
||||
{
|
||||
$exploded = explode( '-', \IPS\Settings::i()->ipb_reg_number );
|
||||
if ( isset( $exploded[3] ) )
|
||||
{
|
||||
$subId = $exploded[3];
|
||||
}
|
||||
}
|
||||
if ( $subId === NULL )
|
||||
{
|
||||
$subId = \IPS\Settings::i()->board_url;
|
||||
if ( \strlen( $subId ) > 32 )
|
||||
{
|
||||
$subId = str_replace( array( 'http://', 'https://' ), '', $subId );
|
||||
}
|
||||
if ( \strlen( $subId ) > 32 )
|
||||
{
|
||||
$subId = md5( $subId );
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return \IPS\Http\Url::ips( "viglink/{$subId}" )->request()->get()->decodeJson();
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'viglink_error', '5S144/2', 500, '' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable
|
||||
*
|
||||
|
||||
@@ -28,14 +28,17 @@ class _Zapier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function apiKeyPermissions()
|
||||
public static function apiKeyPermissions()
|
||||
{
|
||||
$return = array(
|
||||
'core/clubs/GETindex' => array( 'access' => TRUE ),
|
||||
'core/clubs/GETitem' => array( 'access' => TRUE ),
|
||||
'core/groups/GETindex' => array( 'access' => TRUE ),
|
||||
'core/hello/GETindex' => array( 'access' => TRUE ),
|
||||
'core/members/GETindex' => array( 'access' => TRUE ),
|
||||
'core/members/GETitem' => array( 'access' => TRUE ),
|
||||
'core/members/POSTindex' => array( 'access' => TRUE ),
|
||||
'core/members/POSTitem' => array( 'access' => TRUE ),
|
||||
'core/webhooks/POSTindex' => array( 'access' => TRUE ),
|
||||
'core/webhooks/DELETEitem' => array( 'access' => TRUE ),
|
||||
);
|
||||
@@ -163,7 +166,7 @@ class _Zapier
|
||||
*/
|
||||
public static function isAvailable()
|
||||
{
|
||||
return \IPS\CIC;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,19 +278,22 @@ class _Zapier
|
||||
*/
|
||||
protected function testSettings()
|
||||
{
|
||||
if ( \IPS\Request::i()->isCgi() )
|
||||
{
|
||||
throw new \DomainException( 'zapier_error_cgi' );
|
||||
}
|
||||
if ( !\IPS\Settings::i()->use_friendly_urls or !\IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
{
|
||||
throw new \DomainException( 'zapier_error_friendly_urls' );
|
||||
}
|
||||
|
||||
|
||||
$url = \IPS\Http\Url::external( rtrim( \IPS\Settings::i()->base_url, '/' ) . '/api/core/hello' );
|
||||
try
|
||||
{
|
||||
$response = $url->request()->login( 'test', '' )->get()->decodeJson();
|
||||
if ( \IPS\Request::i()->isCgi() )
|
||||
{
|
||||
$response = $url->setQueryString( 'key', 'test' )->request()->get()->decodeJson();
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $url->request()->login( 'test', '' )->get()->decodeJson();
|
||||
}
|
||||
if ( isset( $response['errorMessage'] ) AND $response['errorMessage'] == 'IP_ADDRESS_BANNED' )
|
||||
{
|
||||
throw new \Exception;
|
||||
|
||||
@@ -36,7 +36,7 @@ class _Status
|
||||
*/
|
||||
public function __construct( $member = NULL )
|
||||
{
|
||||
if ( \IPS\Settings::i()->profile_comments and ( $member === NULL or $member->canAccessModule( \IPS\Application\Module::get( 'core', 'status', 'front' ) ) ) )
|
||||
if ( \IPS\Settings::i()->profile_comments and ( $member === NULL or ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'members', 'front' ) ) AND $member->canAccessModule( \IPS\Application\Module::get( 'core', 'status', 'front' ) ) ) ) )
|
||||
{
|
||||
$this->classes[] = 'IPS\core\Statuses\Status';
|
||||
}
|
||||
|
||||
@@ -116,6 +116,10 @@ class _Admin
|
||||
case 'forumsSavedAction':
|
||||
return \IPS\Http\Url::internal( 'app=forums&module=forums&controller=savedActions&do=form&id=' . $id1, 'admin' );
|
||||
break;
|
||||
|
||||
case 'editor_stored_replies':
|
||||
return \IPS\Http\Url::internal( 'app=core&module=editor&controller=storedreplies&do=form&id=' . $id1, 'admin' );
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Editor Extension: Alerts
|
||||
* @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 12 May 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\EditorLocations;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Editor Extension: Alerts
|
||||
*/
|
||||
class _Alert
|
||||
{
|
||||
/**
|
||||
* Can we use HTML in this editor?
|
||||
*
|
||||
* @param \IPS\Member $member The member
|
||||
* @param \IPS\Helpers\Form\Editor $field The editor field
|
||||
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
|
||||
*/
|
||||
public function canUseHtml( $member, $field )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we use attachments in this editor?
|
||||
*
|
||||
* @param \IPS\Member $member The member
|
||||
* @param \IPS\Helpers\Form\Editor $field The editor field
|
||||
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
|
||||
*/
|
||||
public function canAttach( $member, $field )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission check for attachments
|
||||
*
|
||||
* @param \IPS\Member $member The member
|
||||
* @param int|null $id1 Primary ID
|
||||
* @param int|null $id2 Secondary ID
|
||||
* @param string|null $id3 Arbitrary data
|
||||
* @param array $attachment The attachment data
|
||||
* @param bool $viewOnly If true, just check if the user can see the attachment rather than download it
|
||||
* @return bool
|
||||
*/
|
||||
public function attachmentPermissionCheck( $member, $id1, $id2, $id3, $attachment, $viewOnly=FALSE )
|
||||
{
|
||||
return \IPS\core\Alerts\Alert::load( $id1 )->canView( $member );
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachment lookup
|
||||
*
|
||||
* @param int|null $id1 Primary ID
|
||||
* @param int|null $id2 Secondary ID
|
||||
* @param string|null $id3 Arbitrary data
|
||||
* @return \IPS\Http\Url|\IPS\Content|\IPS\Node\Model
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function attachmentLookup( $id1, $id2, $id3 )
|
||||
{
|
||||
return \IPS\core\Alerts\Alert::load( $id1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild attachment images in non-content item areas
|
||||
*
|
||||
* @param int|null $offset Offset to start from
|
||||
* @param int|null $max Maximum to parse
|
||||
* @return int Number completed
|
||||
* @note This method is optional and will only be called if it exists
|
||||
*/
|
||||
public function rebuildAttachmentImages( $offset, $max )
|
||||
{
|
||||
return $this->performRebuild( $offset, $max, array( 'IPS\Text\Parser', 'rebuildAttachmentUrls' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild content post-upgrade
|
||||
*
|
||||
* @param int|null $offset Offset to start from
|
||||
* @param int|null $max Maximum to parse
|
||||
* @return int Number completed
|
||||
* @note This method is optional and will only be called if it exists
|
||||
*/
|
||||
public function rebuildContent( $offset, $max )
|
||||
{
|
||||
return $this->performRebuild( $offset, $max, array( 'IPS\Text\LegacyParser', 'parseStatic' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Use the cached image URL instead of the original URL
|
||||
*/
|
||||
protected $proxyUrl = FALSE;
|
||||
|
||||
/**
|
||||
* Rebuild content to add or remove image proxy
|
||||
*
|
||||
* @param int|null $offset Offset to start from
|
||||
* @param int|null $max Maximum to parse
|
||||
* @param bool $proxyUrl Use the cached image URL instead of the original URL
|
||||
* @return int Number completed
|
||||
* @note This method is optional and will only be called if it exists
|
||||
*/
|
||||
public function rebuildImageProxy( $offset, $max, $proxyUrl = FALSE )
|
||||
{
|
||||
$this->proxyUrl = $proxyUrl;
|
||||
return $this->performRebuild( $offset, $max, 'parseImageProxy' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store lazy loading status ( true = enabled )
|
||||
*/
|
||||
protected $_lazyLoadStatus = null;
|
||||
|
||||
/**
|
||||
* Rebuild content to add or remove lazy loading
|
||||
*
|
||||
* @param int|null $offset Offset to start from
|
||||
* @param int|null $max Maximum to parse
|
||||
* @param bool $status Enable/Disable lazy loading
|
||||
* @return int Number completed
|
||||
* @note This method is optional and will only be called if it exists
|
||||
*/
|
||||
public function rebuildLazyLoad( $offset, $max, $status=TRUE )
|
||||
{
|
||||
$this->_lazyLoadStatus = $status;
|
||||
|
||||
return $this->performRebuild( $offset, $max, 'parseLazyLoad' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform rebuild - abstracted as the call for rebuildContent() and rebuildAttachmentImages() is nearly identical
|
||||
*
|
||||
* @param int|null $offset Offset to start from
|
||||
* @param int|null $max Maximum to parse
|
||||
* @param callable $callback Method to call to rebuild content
|
||||
* @return int Number completed
|
||||
*/
|
||||
protected function performRebuild( $offset, $max, $callback )
|
||||
{
|
||||
$did = 0;
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_alerts', "alert_content IS NOT NULL", 'alert_id ASC', array( $offset, $max ) ) as $alert )
|
||||
{
|
||||
$did++;
|
||||
|
||||
/* Update */
|
||||
try
|
||||
{
|
||||
if( $callback == 'parseImageProxy' )
|
||||
{
|
||||
$rebuilt = \IPS\Text\Parser::removeImageProxy( $alert['alert_content'], $this->proxyUrl );
|
||||
}
|
||||
elseif( $callback == 'parseLazyLoad' )
|
||||
{
|
||||
$rebuilt = \IPS\Text\Parser::parseLazyLoad( $alert['alert_content'], $this->_lazyLoadStatus );
|
||||
}
|
||||
else
|
||||
{
|
||||
$rebuilt = $callback( $alert['alert_content'], \IPS\Member::load( $alert['alert_member_id'] ), FALSE, 'core_Alert' );
|
||||
}
|
||||
}
|
||||
catch( \InvalidArgumentException $e )
|
||||
{
|
||||
if( $callback[1] == 'parseStatic' AND $e->getcode() == 103014 )
|
||||
{
|
||||
$rebuilt = preg_replace( "#\[/?([^\]]+?)\]#", '', $alert['alert_content'] );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if( $rebuilt !== FALSE )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_alerts', array( 'alert_content' => $rebuilt ), array( 'alert_id=?', $alert['alert_id'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $did;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total content count to be used in progress indicator
|
||||
*
|
||||
* @return int Total Count
|
||||
*/
|
||||
public function contentCount()
|
||||
{
|
||||
return \IPS\Db::i()->select( 'COUNT(*) as count', 'core_alerts', "alert_content IS NOT NULL" )->first();
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,37 @@ class _Members
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can whatever is posted in this editor be moderated?
|
||||
* If this returns TRUE, we must ensure the content is ran through word, link and image filters
|
||||
*
|
||||
* @param \IPS\Member $member The member
|
||||
* @param \IPS\Helpers\Form\Editor $field The editor field
|
||||
* @return bool
|
||||
*/
|
||||
public function canBeModerated( $member, $field )
|
||||
{
|
||||
/* Creating/editing an status update */
|
||||
if ( preg_match( '/^status(\d+)?(?:_\d+)?$/', $field->options['autoSaveKey'] ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
/* Creating/editing a reply to a status update */
|
||||
elseif ( preg_match( '/^(?:reply|editComment)\-core\/members-(\d+)$/', $field->options['autoSaveKey'] ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
/* Unknown */
|
||||
else
|
||||
{
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
throw new \RuntimeException( 'Unknown canBeModerated: ' . $field->options['autoSaveKey'] );
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission check for attachments
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief File Storage Extension: Ranks
|
||||
* @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 25 Mar 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\FileStorage;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* File Storage Extension: Ranks
|
||||
*/
|
||||
class _Ranks
|
||||
{
|
||||
/**
|
||||
* Count stored files
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return \IPS\Db::i()->select( 'COUNT(*)', 'core_member_ranks', 'icon IS NOT NULL' )->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move stored files
|
||||
*
|
||||
* @param int $offset This will be sent starting with 0, increasing to get all files stored by this extension
|
||||
* @param int $storageConfiguration New storage configuration ID
|
||||
* @param int|NULL $oldConfiguration Old storage configuration ID
|
||||
* @throws \UnderflowException When file record doesn't exist. Indicating there are no more files to move
|
||||
* @return void|int An offset integer to use on the next cycle, or nothing
|
||||
*/
|
||||
public function move( $offset, $storageConfiguration, $oldConfiguration=NULL )
|
||||
{
|
||||
$rankData = \IPS\Db::i()->select( '*', 'core_member_ranks', 'icon IS NOT NULL', 'id', array( $offset, 1 ) )->first();
|
||||
|
||||
try
|
||||
{
|
||||
$file = \IPS\File::get( $oldConfiguration ?: 'core_Ranks', $rankData['icon'] )->move( $storageConfiguration );
|
||||
|
||||
if ( (string) $file != $rankData['icon'] )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_member_ranks', array( 'icon' => (string) $file ), array( 'id=?', $rankData['id'] ) );
|
||||
}
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
/* Any issues are logged */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix all URLs
|
||||
*
|
||||
* @param int $offset This will be sent starting with 0, increasing to get all files stored by this extension
|
||||
* @return void
|
||||
*/
|
||||
public function fixUrls( $offset )
|
||||
{
|
||||
$rankData = \IPS\Db::i()->select( '*', 'core_member_ranks', 'icon IS NOT NULL', 'id', array( $offset, 1 ) )->first();
|
||||
|
||||
try
|
||||
{
|
||||
if ( $new = \IPS\File::repairUrl( $rankData['icon'] ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_member_ranks', [ 'icon' => $new ], array( 'id=?', $rankData['id'] ) );
|
||||
}
|
||||
}
|
||||
catch( \Exception $e ) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a file is valid
|
||||
*
|
||||
* @param string $file The file path to check
|
||||
* @return bool
|
||||
*/
|
||||
public function isValidFile( $file )
|
||||
{
|
||||
try
|
||||
{
|
||||
$rankData = \IPS\Db::i()->select( '*', 'core_member_ranks', array( 'icon=?', (string) $file ) )->first();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all stored files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_member_ranks', 'icon IS NOT NULL' ) as $rankData )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\File::get( 'core_member_ranks', $rankData['icon'] )->delete();
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class _Reaction
|
||||
/* Any issues are logged */
|
||||
}
|
||||
|
||||
if ( $url != $reaction['reaction_icon'] )
|
||||
if ( !empty( $url ) AND $url != $reaction['reaction_icon'] )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_reactions', array( 'reaction_icon' => $url ), array( 'reaction_id=?', $reaction['reaction_id'] ) );
|
||||
unset( \IPS\Data\Store::i()->reactions );
|
||||
|
||||
@@ -180,6 +180,10 @@ class _Content
|
||||
return \IPS\Theme::i()->getTemplate( 'members' )->moderationLimits( $element->name, $element->value );
|
||||
} ), NULL, NULL, NULL, 'g_mod_post_unit' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'g_bypass_badwords', $group->g_bypass_badwords ) );
|
||||
if ( \IPS\Settings::i()->ips_imagescanner_enable )
|
||||
{
|
||||
\IPS\Member::loggedIn()->language()->words['g_bypass_badwords'] = \IPS\Member::loggedIn()->language()->addToStack('g_bypass_badwords_images');
|
||||
}
|
||||
|
||||
/* Promotion */
|
||||
if( $group->g_id != \IPS\Settings::i()->guest_group )
|
||||
|
||||
@@ -45,7 +45,7 @@ class _GroupSettings
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'g_icon', $icon, FALSE, array( 'storageExtension' => 'core_Theme', 'allowedFileTypes' => array( 'gif', 'png', 'jpeg', 'jpg' ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Upload( 'g_icon', $icon, FALSE, array( 'storageExtension' => 'core_Theme', 'allowedFileTypes' => \IPS\Image::supportedExtensions() ) ) );
|
||||
|
||||
/* Prefix/Suffix */
|
||||
$form->add( new \IPS\Helpers\Form\Custom( 'g_prefixsuffix', array( 'prefix' => $group->prefix, 'suffix' => $group->suffix ), FALSE, array(
|
||||
@@ -171,7 +171,7 @@ class _GroupSettings
|
||||
/* Search */
|
||||
if ( $group->canAccessModule( \IPS\Application\Module::get( 'core', 'search', 'front' ) ) )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'g_search_flood', $group->g_search_flood, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::SECONDS, 'unlimited' => 0 ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Interval( 'g_search_flood', $group->g_id ? $group->g_search_flood : 0, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::SECONDS, 'unlimited' => 0 ) ) );
|
||||
}
|
||||
|
||||
/* Privacy */
|
||||
@@ -380,4 +380,19 @@ class _GroupSettings
|
||||
$newGroup->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this group be deleted?
|
||||
*
|
||||
* @param \IPS\Member\Group $group The group
|
||||
* @return void
|
||||
*/
|
||||
public function canDelete( $group ) : bool
|
||||
{
|
||||
if ( \in_array( $group->g_id, array( \IPS\Settings::i()->guest_group, \IPS\Settings::i()->member_group, \IPS\Settings::i()->admin_group ) ) )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -59,25 +59,44 @@ class _Content extends \IPS\Content\ExtensionGenerator
|
||||
public function findByIp( $ip, \IPS\Http\Url $baseUrl = NULL )
|
||||
{
|
||||
$class = $this->class;
|
||||
|
||||
|
||||
if( !isset( $class::$databaseColumnMap['ip_address'] ) OR !$class::$databaseColumnMap['ip_address'] )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
if ( ! \IPS\Application::appIsEnabled( $class::$application ) )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
$where = array( "{$class::$databasePrefix}{$class::$databaseColumnMap['ip_address']} LIKE ?" , $ip );
|
||||
|
||||
/* Don't need Posts Before Registration */
|
||||
if ( isset( $class::$databaseColumnMap['hidden'] ) )
|
||||
{
|
||||
$where[0] .= " AND {$class::$databasePrefix}{$class::$databaseColumnMap['hidden']} <> -3";
|
||||
}
|
||||
|
||||
if ( isset( $class::$databaseColumnMap['approved'] ) )
|
||||
{
|
||||
$where[0] .= " AND {$class::$databasePrefix}{$class::$databaseColumnMap['approved']} <> -3";
|
||||
}
|
||||
|
||||
/* Does the class have any filters? */
|
||||
if( method_exists( $class,'findByIPWhere') )
|
||||
{
|
||||
$where[0] .= $class::findByIPWhere();
|
||||
}
|
||||
|
||||
/* Return count */
|
||||
if ( $baseUrl === NULL )
|
||||
{
|
||||
return \IPS\Db::i()->select( 'COUNT(*)', $class::$databaseTable, array( "{$class::$databasePrefix}{$class::$databaseColumnMap['ip_address']} LIKE ?", $ip ) )->first();
|
||||
return \IPS\Db::i()->select( 'COUNT(*)', $class::$databaseTable, $where )->first();
|
||||
}
|
||||
|
||||
/* Init Table */
|
||||
$table = new \IPS\Helpers\Table\Db( $class::$databaseTable, $baseUrl, array( "{$class::$databasePrefix}{$class::$databaseColumnMap['ip_address']} LIKE ?", $ip ) );
|
||||
$table = new \IPS\Helpers\Table\Db( $class::$databaseTable, $baseUrl, $where );
|
||||
|
||||
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'tables', 'core', 'admin' ), 'table' );
|
||||
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'tables', 'core', 'admin' ), 'rows' );
|
||||
|
||||
@@ -103,6 +103,22 @@ class _BasicInformation extends \IPS\core\MemberACPProfile\Block
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('member_export_pi_title') )
|
||||
);
|
||||
}
|
||||
|
||||
/* Data Layer PII Option */
|
||||
if (
|
||||
\IPS\Settings::i()->core_datalayer_enabled AND
|
||||
\IPS\Settings::i()->core_datalayer_include_pii AND
|
||||
\IPS\Settings::i()->core_datalayer_member_pii_choice AND
|
||||
( ( $this->member->isAdmin() AND \IPS\Member::loggedIn()->hasAcpRestriction('core', 'members', 'member_edit_admin' ) ) OR \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit' ) )
|
||||
)
|
||||
{
|
||||
$enabled = $this->member->datalayer_collect_pii;
|
||||
$accountActions[] = array(
|
||||
'title' => $enabled ? 'datalayer_omit_member_pii' : 'datalayer_collect_member_pii',
|
||||
'icon' => 'id-card',
|
||||
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=toggleDataLayerPii&id={$this->member->member_id}" ),
|
||||
);
|
||||
}
|
||||
|
||||
$activeSubscription = FALSE;
|
||||
if ( \IPS\Application::appIsEnabled('nexus') and \IPS\Settings::i()->nexus_subs_enabled ) // I know... this should really be a hook... I won't tell if you won't
|
||||
|
||||
@@ -41,14 +41,6 @@ class _OAuth extends \IPS\core\MemberACPProfile\Block
|
||||
$client = \IPS\Api\OAuthClient::load( $token['client_id'] );
|
||||
|
||||
$title = $client->_title;
|
||||
if ( $count === 1 and $token['issue_user_agent'] )
|
||||
{
|
||||
$agent = \IPS\Http\UserAgent::parse( $token['issue_user_agent'] );
|
||||
if ( $agent->ipsApp )
|
||||
{
|
||||
$title = \IPS\Member::loggedIn()->language()->addToStack( 'mobile_app_user_agent', FALSE, array( 'sprintf' => array( $agent->browserVersion, $agent->platform ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
$tokens[] = array(
|
||||
'title' => $title,
|
||||
|
||||
@@ -46,11 +46,11 @@ class _Points extends \IPS\core\MemberACPProfile\Block
|
||||
'icon' => 'pencil',
|
||||
'link' => $this->member->acpUrl()->setQueryString('do', 'points')->csrf(),
|
||||
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('acp_profile_points_manage_title' ) ),
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack('acp_profile_edit_points', NULL, [ 'sprintf' => $this->member->name ] ),
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'acp_profile_edit_points', FALSE, [ 'sprintf' => $this->member->name ] ),
|
||||
);
|
||||
}
|
||||
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_points_log', $this->member->acpUrl()->setQueryString( array( 'do' => 'editBlock', 'block' => \get_class( $this ) ) ), [ [ 'member=?', $this->member->member_id ] ] );
|
||||
$table = new \IPS\Helpers\Table\Db( 'core_points_log', $this->member->acpUrl()->setQueryString( array( 'do' => 'editBlock', 'block' => \get_class( $this ) ) ), [ [ '`member`=?', $this->member->member_id ] ] );
|
||||
$table->joins[] = [
|
||||
'select' => 'action,identifier',
|
||||
'from' => 'core_achievements_log',
|
||||
@@ -75,8 +75,14 @@ class _Points extends \IPS\core\MemberACPProfile\Block
|
||||
|
||||
if ( isset( $exploded[1] ) )
|
||||
{
|
||||
$extension = \IPS\Application::load( $exploded[0] )->extensions( 'core', 'AchievementAction' )[$exploded[1]];
|
||||
return $extension->logRow( $row['identifier'], explode( ',', $row['actor'] ) );
|
||||
try{
|
||||
$extension = \IPS\Application::load( $exploded[0] )->extensions( 'core', 'AchievementAction' )[$exploded[1]];
|
||||
return $extension->logRow( $row['identifier'], explode( ',', $row['actor'] ) );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'acp_points_rule_deleted');
|
||||
}
|
||||
}
|
||||
else if ( isset( $row['rules'] ) and empty( $row['rules'] ) )
|
||||
{
|
||||
|
||||
@@ -30,6 +30,11 @@ class _Referrals extends \IPS\core\MemberACPProfile\Block
|
||||
*/
|
||||
public function output( $edit = FALSE )
|
||||
{
|
||||
if( !\IPS\Settings::i()->ref_on )
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
$url = $this->member->acpUrl()->setQueryString( array( 'do' => 'editBlock', 'block' => \get_class( $this ) ) );
|
||||
$referCount = \IPS\Db::i()->select( 'COUNT(*)', 'core_referrals', array( 'referred_by=?', $this->member->member_id ) )->first();
|
||||
$referrals = new \IPS\Helpers\Table\Db( 'core_referrals', $url, array( 'referred_by=?', $this->member->member_id ) );
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Member filter extension: Achievements
|
||||
* @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 14 Sept 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\MemberFilter;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Member filter: Achievements
|
||||
*/
|
||||
class _Achievements
|
||||
{
|
||||
/**
|
||||
* Determine if the filter is available in a given area
|
||||
*
|
||||
* @param string $area Area to check
|
||||
* @return bool
|
||||
*/
|
||||
public function availableIn( $area )
|
||||
{
|
||||
return \in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Setting Field
|
||||
*
|
||||
* @param mixed $criteria Value returned from the save() method
|
||||
* @return array Array of form elements
|
||||
*/
|
||||
public function getSettingField( $criteria )
|
||||
{
|
||||
/* Get our badges */
|
||||
$badges['options'] = [];
|
||||
|
||||
foreach( \IPS\core\Achievements\Badge::roots() as $badge )
|
||||
{
|
||||
$badges['options'][ $badge->id ] = $badge->_title;
|
||||
}
|
||||
|
||||
/* If all *available* options are selected, we want to choose 'all' for consistency on create vs edit */
|
||||
$_options = array_keys( $badges['options'] );
|
||||
|
||||
if( isset( $criteria['achievement_badges'] ) )
|
||||
{
|
||||
if( ! \count( array_diff( $_options, explode( ',', $criteria['achievement_badges'] ) ) ) )
|
||||
{
|
||||
$criteria['achievement_badges'] = 'all';
|
||||
}
|
||||
}
|
||||
|
||||
/* Get our ranks */
|
||||
$ranks['options'] = [];
|
||||
|
||||
foreach( \IPS\core\Achievements\Rank::roots() as $rank )
|
||||
{
|
||||
$ranks['options'][ $rank->id ] = $rank->_title;
|
||||
}
|
||||
|
||||
/* If all *available* options are selected, we want to choose 'all' for consistency on create vs edit */
|
||||
$_options = array_keys( $ranks['options'] );
|
||||
|
||||
return array(
|
||||
new \IPS\Helpers\Form\Custom( 'mf_points', array( 0 => isset( $criteria['achievement_points_operator'] ) ? $criteria['achievement_points_operator'] : NULL, 1 => isset( $criteria['achievement_points_value'] ) ? $criteria['achievement_points_value'] : NULL ), FALSE, array(
|
||||
'getHtml' => function( $element )
|
||||
{
|
||||
return \IPS\Theme::i()->getTemplate( 'forms', 'core' )->select( "{$element->name}[0]", $element->value[0], $element->required, array(
|
||||
'any' => \IPS\Member::loggedIn()->language()->addToStack('any'),
|
||||
'gt' => \IPS\Member::loggedIn()->language()->addToStack('gt'),
|
||||
'lt' => \IPS\Member::loggedIn()->language()->addToStack('lt'),
|
||||
'eq' => \IPS\Member::loggedIn()->language()->addToStack('exactly'),
|
||||
),
|
||||
FALSE,
|
||||
NULL,
|
||||
FALSE,
|
||||
array(
|
||||
'any' => array(),
|
||||
'gt' => array( 'elNumber_' . $element->name . '-qty' ),
|
||||
'lt' => array( 'elNumber_' . $element->name . '-qty' ),
|
||||
'eq' => array( 'elNumber_' . $element->name . '-qty' ),
|
||||
) )
|
||||
. ' '
|
||||
. \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->number( "{$element->name}[1]", $element->value[1], $element->required, NULL, FALSE, NULL, NULL, NULL, 0, NULL, FALSE, NULL, array(), array(), array(), $element->name . '-qty' );
|
||||
}
|
||||
), NULL, NULL, NULL, 'mp_points' ),
|
||||
new \IPS\Helpers\Form\YesNo( 'mf_badges_choose', ( isset( $criteria['achievement_badges'] ) AND $criteria['achievement_badges'] != 'ignore' ), FALSE, array(
|
||||
'togglesOn' => [ 'mf_badges' ]
|
||||
), NULL, NULL, NULL, 'mf_badges_choose' ),
|
||||
new \IPS\Helpers\Form\CheckboxSet( 'mf_badges', ( isset( $criteria['achievement_badges'] ) AND $criteria['achievement_badges'] != 'ignore' ) ? explode( ',', $criteria['achievement_badges'] ) : [], FALSE, array(
|
||||
'options' => $badges['options'],
|
||||
'multiple' => TRUE,
|
||||
'unlimited' => 'all',
|
||||
'unlimitedLang' => 'mf_achievements_all_badges',
|
||||
'impliedUnlimited' => TRUE
|
||||
), NULL, NULL, NULL, 'mf_badges' ),
|
||||
new \IPS\Helpers\Form\YesNo( 'mf_ranks_choose', ( isset( $criteria['achievement_ranks'] ) AND $criteria['achievement_ranks'] != 'ignore' ), FALSE, array(
|
||||
'togglesOn' => [ 'mf_ranks' ]
|
||||
), NULL, NULL, NULL, 'mf_ranks_choose' ),
|
||||
new \IPS\Helpers\Form\CheckboxSet( 'mf_ranks', ( isset( $criteria['achievement_ranks'] ) AND $criteria['achievement_ranks'] != 'ignore' ) ? explode( ',', $criteria['achievement_ranks'] ) : [], FALSE, array(
|
||||
'options' => $ranks['options'],
|
||||
'multiple' => TRUE,
|
||||
'unlimited' => 'all',
|
||||
'unlimitedLang' => 'mf_achievements_all_ranks',
|
||||
'impliedUnlimited' => TRUE
|
||||
), NULL, NULL, NULL, 'mf_ranks' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the filter data
|
||||
*
|
||||
* @param array $post Form values
|
||||
* @return mixed False, or an array of data to use later when filtering the members
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function save( $post )
|
||||
{
|
||||
return array(
|
||||
'achievement_points_operator' => $post['mf_points'][0],
|
||||
'achievement_points_value' => $post['mf_points'][1],
|
||||
'achievement_badges' => ( $post['mf_badges_choose'] ) ? implode( ',', $post['mf_badges'] ) : 'ignore',
|
||||
'achievement_ranks' => ( $post['mf_ranks_choose'] ) ? implode( ',', $post['mf_ranks'] ) : 'ignore'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get where clause to add to the member retrieval database query
|
||||
*
|
||||
* @param mixed $data The array returned from the save() method
|
||||
* @return array|NULL Where clause - must be a single array( "clause" )
|
||||
*/
|
||||
public function getQueryWhereClause( $data )
|
||||
{
|
||||
$where = [];
|
||||
if ( $data['achievement_points_operator'] and $data['achievement_points_value'] )
|
||||
{
|
||||
switch ( $data['achievement_points_operator'] )
|
||||
{
|
||||
case 'gt':
|
||||
$where[] = "achievements_points > " . (int) $data['achievement_points_value'];
|
||||
break;
|
||||
case 'lt':
|
||||
$where[] = "achievements_points < " . (int) $data['achievement_points_value'];
|
||||
break;
|
||||
case 'eq':
|
||||
$where[] = "achievements_points = " . (int) $data['achievement_points_value'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $data['achievement_badges'] != 'ignore' )
|
||||
{
|
||||
$badgeSelect = \IPS\Db::i()->select( 'member', 'core_member_badges', \IPS\Db::i()->in( 'badge', explode( ',', $data['achievement_badges'] ) ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_DISTINCT );
|
||||
$where[] = 'core_members.member_id IN(' . $badgeSelect . ')';
|
||||
}
|
||||
|
||||
if ( $data['achievement_ranks'] != 'ignore' )
|
||||
{
|
||||
$ranks = \IPS\core\Achievements\Rank::getStore();
|
||||
$rankWhere = [];
|
||||
|
||||
foreach( explode( ',', $data['achievement_ranks'] ) as $val )
|
||||
{
|
||||
$minPoints = 0;
|
||||
$maxPoints = 0;
|
||||
$minSet = FALSE;
|
||||
foreach ( $ranks as $rank )
|
||||
{
|
||||
if ( !$minSet and $rank->id == $val )
|
||||
{
|
||||
$minPoints = $rank->points;
|
||||
$minSet = TRUE;
|
||||
}
|
||||
|
||||
if ( $minSet and $rank->points > $minPoints )
|
||||
{
|
||||
$maxPoints = $rank->points;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $minPoints and $maxPoints )
|
||||
{
|
||||
$rankWhere[] = '( achievements_points BETWEEN ' . $minPoints . ' AND ' . ( $maxPoints - 1 ) . ' )';
|
||||
}
|
||||
elseif ( $minPoints )
|
||||
{
|
||||
$rankWhere[] = '( achievements_points >= ' . $minPoints . ' )';
|
||||
}
|
||||
elseif ( $maxPoints )
|
||||
{
|
||||
$rankWhere[] = '( achievements_points < ' . $maxPoints . ' )';
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $rankWhere ) )
|
||||
{
|
||||
$where[] = '(' . implode( ' OR ', $rankWhere ) . ')';
|
||||
}
|
||||
}
|
||||
|
||||
return \count( $where ) ? '(' . implode( ' AND ', $where ) . ')' : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a member matches specified filters
|
||||
*
|
||||
* @note This is only necessary if availableIn() includes group_promotions
|
||||
* @param \IPS\Member $member Member object to check
|
||||
* @param array $filters Previously defined filters
|
||||
* @param object|NULL $object Calling class
|
||||
* @return bool
|
||||
*/
|
||||
public function matches( \IPS\Member $member, $filters, $object=NULL )
|
||||
{
|
||||
if ( ( isset( $filters['achievement_points_operator'] ) and ( $filters['achievement_points_operator'] ) and $filters['achievement_points_operator'] != 'any' ) and ( isset( $filters['achievement_points_value'] ) ) )
|
||||
{
|
||||
$pass = FALSE;
|
||||
switch ( $filters['achievement_points_operator'] )
|
||||
{
|
||||
case 'gt':
|
||||
$pass = (bool) ( $member->achievements_points > (int) $filters['achievement_points_value'] );
|
||||
break;
|
||||
case 'lt':
|
||||
$pass = (bool) ( $member->achievements_points < (int) $filters['achievement_points_value'] );
|
||||
break;
|
||||
case 'eq':
|
||||
$pass = (bool) ( $member->achievements_points = (int) $filters['achievement_points_value'] );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( $pass === FALSE )
|
||||
{
|
||||
/* They don't make it this far */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Lets check badges */
|
||||
if ( isset( $filters['achievement_badges'] ) and $filters['achievement_badges'] != 'ignore' )
|
||||
{
|
||||
if ( ! \IPS\Db::i()->select( 'COUNT(*)', 'core_member_badges', [ 'member=? AND ' . \IPS\Db::i()->in( 'badge', explode( ',', $filters['achievement_badges'] ) ), $member->member_id ] )->first() )
|
||||
{
|
||||
/* Did not find a badge, so they failed the match */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Let's check ranks */
|
||||
if ( isset( $filters['achievement_ranks'] ) and $filters['achievement_ranks'] != 'ignore' )
|
||||
{
|
||||
$ranks = \IPS\core\Achievements\Rank::getStore();
|
||||
$pass = FALSE;
|
||||
|
||||
foreach( explode( ',', $filters['achievement_ranks'] ) as $val )
|
||||
{
|
||||
$minPoints = 0;
|
||||
$maxPoints = 0;
|
||||
$minSet = FALSE;
|
||||
foreach ( $ranks as $rank )
|
||||
{
|
||||
if ( !$minSet and $rank->id == $val )
|
||||
{
|
||||
$minPoints = $rank->points;
|
||||
$minSet = TRUE;
|
||||
}
|
||||
|
||||
if ( $minSet and $rank->points > $minPoints )
|
||||
{
|
||||
$maxPoints = $rank->points;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $minPoints and $maxPoints )
|
||||
{
|
||||
$pass = (bool) $member->achievements_points >= $minPoints AND $member->achievements_points <= ($maxPoints - 1);
|
||||
}
|
||||
elseif ( $minPoints )
|
||||
{
|
||||
$pass = (bool) $member->achievements_points >= $minPoints;
|
||||
}
|
||||
elseif ( $maxPoints )
|
||||
{
|
||||
$pass = (bool) $member->achievements_points < $minPoints;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $pass === FALSE )
|
||||
{
|
||||
/* They don't make it this far */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are still here, then there wasn't an appropriate operator (maybe they selected 'any') so return true */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a lovely human description for this rule if used
|
||||
*
|
||||
* @param mixed $filters The array returned from the save() method
|
||||
* @return string|NULL
|
||||
*/
|
||||
public function getDescription( $filters )
|
||||
{
|
||||
$message = [];
|
||||
if ( ! empty( $filters['achievement_points_value'] ) and $filters['achievement_points_value'] > 0 )
|
||||
{
|
||||
switch ( $filters['achievement_points_operator'] )
|
||||
{
|
||||
case 'gt':
|
||||
$message[] = \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_cheev_points_gt_desc', FALSE, array( 'sprintf' => array( $filters['achievement_points_value'] ) ) );
|
||||
break;
|
||||
case 'lt':
|
||||
$message[] = \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_cheev_points_lt_desc', FALSE, array( 'sprintf' => array( $filters['achievement_points_value'] ) ) );
|
||||
break;
|
||||
case 'eq':
|
||||
$message[] = \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_cheev_points_eq_desc', FALSE, array( 'sprintf' => array( $filters['achievement_points_value'] ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $filters['achievement_badges'] != 'ignore' )
|
||||
{
|
||||
$count = \count( explode( ',', $filters['achievement_badges'] ) );
|
||||
if( $count )
|
||||
{
|
||||
$message[] = \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_cheev_desc', FALSE, [
|
||||
'sprintf' => [
|
||||
\IPS\Member::loggedIn()->language()->addToStack( 'recognize_badges_pluralize', FALSE, ['sprintf' => [$count], 'pluralize' => [$count]] )
|
||||
]] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $filters['achievement_ranks'] != 'ignore' )
|
||||
{
|
||||
$count = \count( explode( ',', $filters['achievement_badges'] ) );
|
||||
if ( $count )
|
||||
{
|
||||
$message[] = \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_cheev_desc', FALSE, [
|
||||
'sprintf' => [
|
||||
\IPS\Member::loggedIn()->language()->addToStack( 'achievement_ranks_pluralize', FALSE, ['sprintf' => [$count], 'pluralize' => [$count]] )
|
||||
]] );
|
||||
}
|
||||
}
|
||||
|
||||
return \count( $message ) ? implode( ', ', $message ) : NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Member Filter Extension
|
||||
* @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 22 Sep 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\MemberFilter;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Filter Extension
|
||||
*/
|
||||
class _Banned
|
||||
{
|
||||
/**
|
||||
* Determine if the filter is available in a given area
|
||||
*
|
||||
* @param string $area Area to check
|
||||
* @return bool
|
||||
*/
|
||||
public function availableIn( $area )
|
||||
{
|
||||
return \in_array( $area, array( 'group_promotions' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Setting Field
|
||||
*
|
||||
* @param mixed $criteria Value returned from the save() method
|
||||
* @return array Array of form elements
|
||||
*/
|
||||
public function getSettingField( $criteria )
|
||||
{
|
||||
$options = array( 'any' => 'any', 'banned' => 'mf_banned_banned', 'notbanned' => 'mf_banned_not_banned' );
|
||||
|
||||
return array(
|
||||
new \IPS\Helpers\Form\Radio( 'mf_banned', isset( $criteria['banned'] ) ? $criteria['banned'] : 'any', FALSE, array( 'options' => $options ) ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a member matches specified filters
|
||||
*
|
||||
* @note This is only necessary if availableIn() includes group_promotions
|
||||
* @param \IPS\Member $member Member object to check
|
||||
* @param array $filters Previously defined filters
|
||||
* @param object|NULL $object Calling class
|
||||
* @return bool
|
||||
*/
|
||||
public function matches( \IPS\Member $member, $filters, $object=NULL )
|
||||
{
|
||||
if( !isset( $filters['banned'] ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
switch ( $filters['banned'] )
|
||||
{
|
||||
case 'banned':
|
||||
return ( (int) $member->temp_ban !== 0 );
|
||||
break;
|
||||
case 'notbanned':
|
||||
return empty( $member->temp_ban );
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the filter data
|
||||
*
|
||||
* @param array $post Form values
|
||||
* @return mixed False, or an array of data to use later when filtering the members
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function save( $post )
|
||||
{
|
||||
return ( isset( $post['mf_banned'] ) and \in_array( $post['mf_banned'], array( 'banned', 'notbanned' ) ) ) ? array( 'banned' => $post['mf_banned'] ) : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get where clause to add to the member retrieval database query
|
||||
*
|
||||
* @param mixed $data The array returned from the save() method
|
||||
* @return array|NULL Where clause - must be a single array( "clause" )
|
||||
*/
|
||||
public function getQueryWhereClause( $data )
|
||||
{
|
||||
if ( isset( $data['banned'] ) )
|
||||
{
|
||||
switch ( $data['banned'] )
|
||||
{
|
||||
case 'banned':
|
||||
return array( "temp_ban<>0" );
|
||||
break;
|
||||
case 'notbanned':
|
||||
return array( "(temp_ban IS NULL OR temp_ban=0)" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -153,13 +153,13 @@ class _Content
|
||||
switch ( $filters['content_count_operator'] )
|
||||
{
|
||||
case 'gt':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_gt_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_gt_desc', FALSE, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
|
||||
break;
|
||||
case 'lt':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_lt_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_lt_desc', FALSE, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
|
||||
break;
|
||||
case 'eq':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_eq_desc', NULL, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_eq_desc', FALSE, array( 'sprintf' => array( $filters['content_count_score'] ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class _Group
|
||||
|
||||
if ( \count( $humanGroups ) )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_group_desc', NULL, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $humanGroups ) ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_group_desc', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $humanGroups ) ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -246,16 +246,16 @@ class _Joined
|
||||
|
||||
if ( $start and $end )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_range_desc', FALSE, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
|
||||
}
|
||||
}
|
||||
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_days_desc', FALSE, array( 'sprintf' => array( $filters['days'] ) ) );
|
||||
}
|
||||
elseif( !empty( $filters['days_lt'] ) AND (int) $filters['days_lt'] )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_days_lt_desc', FALSE, array( 'sprintf' => array( $filters['days_lt'] ) ) );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -260,16 +260,16 @@ class _Lastpost
|
||||
|
||||
if ( $start and $end )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_range_desc', FALSE, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
|
||||
}
|
||||
}
|
||||
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_days_desc', FALSE, array( 'sprintf' => array( $filters['days'] ) ) );
|
||||
}
|
||||
elseif( !empty( $filters['days_lt'] ) AND (int) $filters['days_lt'] )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_days_lt_desc', FALSE, array( 'sprintf' => array( $filters['days_lt'] ) ) );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -245,16 +245,16 @@ class _Lastvisit
|
||||
|
||||
if ( $start and $end )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_range_desc', NULL, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_range_desc', FALSE, array( 'sprintf' => array( $start->localeDate(), $end->localeDate() ) ) );
|
||||
}
|
||||
}
|
||||
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_days_desc', NULL, array( 'sprintf' => array( $filters['days'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_days_desc', FALSE, array( 'sprintf' => array( $filters['days'] ) ) );
|
||||
}
|
||||
elseif( !empty( $filters['days_lt'] ) AND (int) $filters['days_lt'] )
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_days_lt_desc', NULL, array( 'sprintf' => array( $filters['days_lt'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_days_lt_desc', FALSE, array( 'sprintf' => array( $filters['days_lt'] ) ) );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -296,14 +296,9 @@ class _ProfileFields
|
||||
}
|
||||
break;
|
||||
case 'IPS\Helpers\Form\Date':
|
||||
if( !$profileFieldData['core_pfieldgroups_' . $group ][ $langKey ] )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$start = NULL;
|
||||
$end = NULL;
|
||||
|
||||
if ( $filters[ $langKey ]['start'] )
|
||||
{
|
||||
$start = ( new \IPS\DateTime( $filters[ $langKey ]['start'] ) )->getTimestamp();
|
||||
@@ -313,6 +308,11 @@ class _ProfileFields
|
||||
$end = ( new \IPS\DateTime( $filters[ $langKey ]['end'] ) )->getTimestamp();
|
||||
}
|
||||
|
||||
if( ( $start OR $end ) AND !$profileFieldData['core_pfieldgroups_' . $group ][ $langKey ] )
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if( ( $start AND $start > $profileFieldData['core_pfieldgroups_' . $group ][ $langKey ] ) OR ( $end AND $end < $profileFieldData['core_pfieldgroups_' . $group ][ $langKey ] ) )
|
||||
{
|
||||
return FALSE;
|
||||
|
||||
@@ -153,13 +153,13 @@ class _Reputation
|
||||
switch ( $filters['reputation_operator'] )
|
||||
{
|
||||
case 'gt':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_gt_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_gt_desc', FALSE, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
|
||||
break;
|
||||
case 'lt':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_lt_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_lt_desc', FALSE, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
|
||||
break;
|
||||
case 'eq':
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_eq_desc', NULL, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_eq_desc', FALSE, array( 'sprintf' => array( $filters['reputation_score'] ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Member Filter Extension
|
||||
* @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 22 Sep 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\MemberFilter;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Filter Extension
|
||||
*/
|
||||
class _Spammer
|
||||
{
|
||||
/**
|
||||
* Determine if the filter is available in a given area
|
||||
*
|
||||
* @param string $area Area to check
|
||||
* @return bool
|
||||
*/
|
||||
public function availableIn( $area )
|
||||
{
|
||||
return \in_array( $area, array( 'group_promotions' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Setting Field
|
||||
*
|
||||
* @param mixed $criteria Value returned from the save() method
|
||||
* @return array Array of form elements
|
||||
*/
|
||||
public function getSettingField( $criteria )
|
||||
{
|
||||
$options = array( 'any' => 'any', 'spammer' => 'mf_spammer_spammer', 'notspammer' => 'mf_spammer_not_spammer' );
|
||||
|
||||
return array(
|
||||
new \IPS\Helpers\Form\Radio( 'mf_spammer', isset( $criteria['spammer'] ) ? $criteria['spammer'] : 'any', FALSE, array( 'options' => $options ) ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the filter data
|
||||
*
|
||||
* @param array $post Form values
|
||||
* @return mixed False, or an array of data to use later when filtering the members
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function save( $post )
|
||||
{
|
||||
return ( isset( $post['mf_spammer'] ) and \in_array( $post['mf_spammer'], array( 'spammer', 'notspammer' ) ) ) ? array( 'spammer' => $post['mf_spammer'] ) : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get where clause to add to the member retrieval database query
|
||||
*
|
||||
* @param mixed $data The array returned from the save() method
|
||||
* @return array|NULL Where clause - must be a single array( "clause" )
|
||||
*/
|
||||
public function getQueryWhereClause( $data )
|
||||
{
|
||||
if ( isset( $data['spammer'] ) )
|
||||
{
|
||||
switch ( $data['spammer'] )
|
||||
{
|
||||
case 'spammer':
|
||||
return array( \IPS\Db::i()->bitwiseWhere( \IPS\Member::$bitOptions['members_bitoptions'], 'bw_is_spammer' ));
|
||||
break;
|
||||
case 'notspammer':
|
||||
return array( \IPS\Db::i()->bitwiseWhere( \IPS\Member::$bitOptions['members_bitoptions'], 'bw_is_spammer', FALSE ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ class _Staff
|
||||
$not = TRUE;
|
||||
}
|
||||
|
||||
return array( "(" . \IPS\Db::i()->in( 'member_id', $members, $not ) . " OR " . \IPS\Db::i()->in( 'member_group_id', $groups, $not ) . " OR " . \IPS\Db::i()->findInSet( 'mgroup_others', $groups, $not ) . ")" );
|
||||
return array( "(" . \IPS\Db::i()->in( 'core_members.member_id', $members, $not ) . " OR " . \IPS\Db::i()->in( 'member_group_id', $groups, $not ) . " OR " . \IPS\Db::i()->findInSet( 'mgroup_others', $groups, $not ) . ")" );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Member Filter Extension
|
||||
* @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 22 Sep 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\MemberFilter;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member Filter Extension
|
||||
*/
|
||||
class _Validating
|
||||
{
|
||||
/**
|
||||
* Determine if the filter is available in a given area
|
||||
*
|
||||
* @param string $area Area to check
|
||||
* @return bool
|
||||
*/
|
||||
public function availableIn( $area )
|
||||
{
|
||||
return \in_array( $area, array( 'bulkmail' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Setting Field
|
||||
*
|
||||
* @param mixed $criteria Value returned from the save() method
|
||||
* @return array Array of form elements
|
||||
*/
|
||||
public function getSettingField( $criteria )
|
||||
{
|
||||
$options = array( 'any' => 'any', 'validating' => 'mf_validating_validating', 'notvalidating' => 'mf_validating_not_validating' );
|
||||
|
||||
return array(
|
||||
new \IPS\Helpers\Form\Radio( 'mf_validating', isset( $criteria['validating'] ) ? $criteria['validating'] : 'any', FALSE, array( 'options' => $options ) ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the filter data
|
||||
*
|
||||
* @param array $post Form values
|
||||
* @return mixed False, or an array of data to use later when filtering the members
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function save( $post )
|
||||
{
|
||||
return ( isset( $post['mf_validating'] ) and \in_array( $post['mf_validating'], array( 'validating', 'notvalidating' ) ) ) ? array( 'validating' => $post['mf_validating'] ) : FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get where clause to add to the member retrieval database query
|
||||
*
|
||||
* @param mixed $data The array returned from the save() method
|
||||
* @return array|NULL Where clause - must be a single array( "clause" )
|
||||
*/
|
||||
public function getQueryWhereClause( $data )
|
||||
{
|
||||
if ( isset( $data['validating'] ) )
|
||||
{
|
||||
switch ( $data['validating'] )
|
||||
{
|
||||
case 'validating':
|
||||
return array( "( v.lost_pass=0 AND v.forgot_security=0 AND v.vid IS NOT NULL )" );
|
||||
break;
|
||||
case 'notvalidating':
|
||||
return array( "( v.vid IS NULL )" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for member retrieval database query
|
||||
* Can be used to set joins
|
||||
*
|
||||
* @param mixed $data The array returned from the save() method
|
||||
* @param \IPS\Db\Query $query The query
|
||||
* @return void
|
||||
*/
|
||||
public function queryCallback( $data, &$query )
|
||||
{
|
||||
if( isset( $data['validating'] ) )
|
||||
{
|
||||
$query->join( [ 'core_validating', 'v' ], "core_members.member_id=v.member_id" );
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,8 @@ class _Core
|
||||
'admin_mails',
|
||||
'terms_acceptance',
|
||||
'points',
|
||||
'badges'
|
||||
'badges',
|
||||
'club_membership'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -548,7 +549,7 @@ class _Core
|
||||
|
||||
foreach ( \IPS\Application::allExtensions( 'core', 'MemberRestrictions', TRUE, 'core', 'Content', FALSE ) as $class )
|
||||
{
|
||||
foreach ( $class::changesForHistory( $jsonValue['restrictions'] ) as $v )
|
||||
foreach ( $class::changesForHistory( $jsonValue['restrictions'], $row ) as $v )
|
||||
{
|
||||
$changes[] = $v;
|
||||
}
|
||||
@@ -745,8 +746,24 @@ class _Core
|
||||
}
|
||||
|
||||
break;
|
||||
case 'club_membership':
|
||||
if( isset( $jsonValue['club_id'] ))
|
||||
{
|
||||
try
|
||||
{
|
||||
$club = \IPS\Member\Club::load( $jsonValue['club_id'] );
|
||||
switch( $jsonValue['type'] )
|
||||
{
|
||||
case \IPS\Member\Club::STATUS_BANNED:
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'history_removed_from_club', FALSE, array('htmlsprintf' => array( $club->name, $byStaff ) ) );
|
||||
case \IPS\Member\Club::STATUS_MEMBER:
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'history_added_to_club', FALSE, array('htmlsprintf' => array( $club->name, $byStaff ) ) );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
$member->logHistory( 'core', 'badges', [ 'action' => 'manual', 'id' => $values['acp_manual_badge']->_id ] );
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,12 +86,13 @@ class _Content extends \IPS\core\MemberACPProfile\Restriction
|
||||
* Get details of a change to show on history
|
||||
*
|
||||
* @param array $changes Changes as set in save()
|
||||
* @param array $row Row of data from member history table.
|
||||
* @return array
|
||||
*/
|
||||
public static function changesForHistory( $changes )
|
||||
public static function changesForHistory( array $changes, array $row ): array
|
||||
{
|
||||
$return = array();
|
||||
|
||||
|
||||
foreach ( array( 'modq' => 'mod_posts', 'nopost' => 'restrict_post', 'banned' => 'temp_ban' ) as $k => $v )
|
||||
{
|
||||
if ( isset( $changes[ $v ] ) )
|
||||
@@ -101,7 +102,7 @@ class _Content extends \IPS\core\MemberACPProfile\Restriction
|
||||
$c = \IPS\Member::loggedIn()->language()->addToStack( 'moderation_' . $k );
|
||||
if ( $changes[ $v ]['new'] != -1 )
|
||||
{
|
||||
$interval = \IPS\DateTime::formatInterval( ( \is_numeric( $changes[ $v ]['new'] ) ) ? \IPS\DateTime::create()->diff( \IPS\DateTime::ts( $changes[ $v ]['new'] ) ) : new \DateInterval( $changes[ $v ]['new'] ), 2 );
|
||||
$interval = \IPS\DateTime::formatInterval( ( \is_numeric( $changes[ $v ]['new'] ) ) ? \IPS\DateTime::ts( $row['log_date'] )->diff( \IPS\DateTime::ts( $changes[ $v ]['new'] ) ) : new \DateInterval( $changes[ $v ]['new'] ), 2 );
|
||||
$c = \IPS\Member::loggedIn()->language()->addToStack( 'history_received_warning_penalty_time', FALSE, array( 'sprintf' => array( $c, $interval ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,14 +91,15 @@ class _Statuses extends \IPS\core\MemberACPProfile\Restriction
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get details of a change to show on history
|
||||
*
|
||||
* @param array $changes Changes as set in save()
|
||||
* @param array $row Row of data from member history table.
|
||||
* @return array
|
||||
*/
|
||||
public static function changesForHistory( $changes )
|
||||
public static function changesForHistory( array $changes, array $row ): array
|
||||
{
|
||||
if ( isset( $changes['bw_no_status_update'] ) )
|
||||
{
|
||||
|
||||
@@ -93,21 +93,22 @@ class _Tags extends \IPS\core\MemberACPProfile\Restriction
|
||||
{
|
||||
$return[] = 'restriction_no_tagging';
|
||||
}
|
||||
if ( !$this->member->group['bw_disable_prefixes'] and $this->member->members_bitoptions['bw_disable_prefixes'] )
|
||||
if ( !$this->member->group['gbw_disable_prefixes'] and $this->member->members_bitoptions['bw_disable_prefixes'] )
|
||||
{
|
||||
$return[] = 'restriction_no_prefixes';
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get details of a change to show on history
|
||||
*
|
||||
* @param array $changes Changes as set in save()
|
||||
* @param array $row Row of data from member history table.
|
||||
* @return array
|
||||
*/
|
||||
public static function changesForHistory( $changes )
|
||||
public static function changesForHistory( array $changes, array $row ): array
|
||||
{
|
||||
$return = array();
|
||||
|
||||
|
||||
@@ -110,7 +110,11 @@ class _System
|
||||
array( 'core_rss_import', 'rss_import_member' ),
|
||||
array( 'core_anonymous_posts', 'anonymous_member_id' ),
|
||||
array( 'core_member_badges', 'member' ),
|
||||
array( 'core_notifications_pwa_keys', 'member' )
|
||||
array( 'core_notifications_pwa_keys', 'member' ),
|
||||
array( 'core_members_logins', 'member_id' ),
|
||||
array( 'core_stream_subscriptions', 'member_id' ),
|
||||
array( 'core_alerts', 'alert_member_id' ),
|
||||
array( 'core_alerts', 'alert_recipient_user' ),
|
||||
) as $toMerge
|
||||
)
|
||||
{
|
||||
@@ -139,7 +143,10 @@ class _System
|
||||
|
||||
/* Solved */
|
||||
\IPS\Db::i()->update( 'core_solved_index', array( 'member_id' => $member->member_id ), array( 'member_id=?', $member2->member_id ), array(), NULL, \IPS\Db::IGNORE );
|
||||
|
||||
|
||||
/* Achievements */
|
||||
\IPS\Db::i()->update( 'core_achievements_log_milestones', array( 'milestone_member_id' => $member->member_id ), array( 'milestone_member_id=?', $member2->member_id ), array(), NULL, \IPS\Db::IGNORE );
|
||||
|
||||
/* Delete duplicate stuff */
|
||||
\IPS\Db::i()->delete( 'core_item_markers', array( 'item_member_id=?', $member2->member_id ) );
|
||||
\IPS\Db::i()->delete( 'core_members_feature_seen', array( 'member_id=?', $member2->member_id ) );
|
||||
@@ -155,6 +162,7 @@ class _System
|
||||
\IPS\Db::i()->delete( 'core_acp_notifcations_dismissals', array( '`member`=?', $member2->member_id ) );
|
||||
\IPS\Db::i()->delete( 'core_acp_notifications_preferences', array( '`member`=?', $member2->member_id ) );
|
||||
\IPS\Db::i()->delete( 'core_marketplace_tokens', array( 'id=?', $member2->member_id ) );
|
||||
\IPS\Db::i()->delete( 'core_achievements_log_milestones', array( 'milestone_member_id=?', $member2->member_id ) );
|
||||
|
||||
/* Update PM recipient counts */
|
||||
foreach( \IPS\Db::i()->select( 'map_topic_id', 'core_message_topic_user_map', array( 'map_user_id=?', $member->member_id ) ) as $topicId )
|
||||
@@ -250,7 +258,10 @@ class _System
|
||||
\IPS\Db::i()->delete( 'core_notifications', array( 'item_class=? AND item_id=?', 'IPS\\core\\Statuses\\Reply', $reply ) );
|
||||
|
||||
/* Remove item from deletion log */
|
||||
\IPS\Db::i()->delete( 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", "IPS\\core\\Statuses\\Reply", $reply ) );
|
||||
\IPS\Db::i()->delete( 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", "IPS\\core\\Statuses\\Reply", $reply ) );
|
||||
|
||||
/* Remove reports */
|
||||
\IPS\Db::i()->delete( 'core_rc_index', array( "class=? AND content_id=?", "IPS\\core\\Statuses\\Reply", $reply ) );
|
||||
}
|
||||
|
||||
\IPS\Content\Search\Index::i()->removeClassFromSearchIndex( 'IPS\core\Statuses\Reply', NULL, $member->member_id );
|
||||
@@ -261,6 +272,9 @@ class _System
|
||||
|
||||
/* Remove item from deletion log */
|
||||
\IPS\Db::i()->delete( 'core_deletion_log', array( "dellog_content_class=? AND dellog_content_id=?", "IPS\\core\\Statuses\\Status", $status ) );
|
||||
|
||||
/* Remove reports */
|
||||
\IPS\Db::i()->delete( 'core_rc_index', array( "class=? AND content_id=?", "IPS\\core\\Statuses\\Reply", $status ) );
|
||||
}
|
||||
|
||||
\IPS\Content\Search\Index::i()->removeClassFromSearchIndex( 'IPS\core\Statuses\Status', NULL, $member->member_id );
|
||||
@@ -306,7 +320,11 @@ class _System
|
||||
array( 'core_anonymous_posts', 'anonymous_member_id' ),
|
||||
array( 'core_marketplace_tokens', 'id' ),
|
||||
array( 'core_member_badges', 'member' ),
|
||||
array( 'core_notifications_pwa_keys', 'member' )
|
||||
array( 'core_notifications_pwa_keys', 'member' ),
|
||||
array( 'core_achievements_log_milestones', 'milestone_member_id' ),
|
||||
array( 'core_members_logins', 'member_id' ),
|
||||
array( 'core_stream_subscriptions', 'member_id' ),
|
||||
array( 'core_alerts_seen', 'seen_member_id' )
|
||||
) as $toDelete )
|
||||
{
|
||||
\IPS\Db::i()->delete( $toDelete[0], array( '`' . $toDelete[1] . '`=?', $member->member_id ) );
|
||||
@@ -349,6 +367,8 @@ class _System
|
||||
|
||||
$member->deletePhoto();
|
||||
$member->coverPhoto()->delete();
|
||||
|
||||
\IPS\Api\Webhook::fire( 'member_delete', $member );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,5 +406,15 @@ class _System
|
||||
/* Reset profile completion flag to see if there are any items to complete now permissions are elevated */
|
||||
$member->members_bitoptions['profile_completed'] = 0;
|
||||
}
|
||||
|
||||
if( \count( $changes ) )
|
||||
{
|
||||
$params = [
|
||||
'member' => $member->apiOutput(),
|
||||
'changes' => $changes
|
||||
];
|
||||
|
||||
\IPS\Api\Webhook::fire( 'member_edited', $params );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Mobile Navigation Extension: Custom Item
|
||||
* @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
|
||||
* @subpackage Core
|
||||
* @since 12 Jun 2019
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\MobileNavigation;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mobile Navigation Extension: Custom Item
|
||||
*/
|
||||
class _CustomItem extends \IPS\core\MobileNavigation\MobileNavigationAbstract
|
||||
{
|
||||
/**
|
||||
* Get Type Title which will display in the AdminCP Menu Manager
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function typeTitle()
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack('mobilenavigation_CustomItem');
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow multiple instances?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function allowMultiple()
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration fields
|
||||
*
|
||||
* @param array $existingConfiguration The existing configuration, if editing an existing item
|
||||
* @param int $id The ID number of the existing item, if editing
|
||||
* @return array
|
||||
*/
|
||||
public static function configuration( $existingConfiguration, $id = NULL )
|
||||
{
|
||||
$currentUrl = NULL;
|
||||
if ( isset( $existingConfiguration['menu_custom_item_url'] ) )
|
||||
{
|
||||
if ( isset( $existingConfiguration['internal'] ) )
|
||||
{
|
||||
$currentUrl = (string) \IPS\Http\Url::internal( $existingConfiguration['menu_custom_item_url'], 'front', $existingConfiguration['internal'], isset( $existingConfiguration['seoTitles'] ) ? $existingConfiguration['seoTitles'] : array() );
|
||||
}
|
||||
else
|
||||
{
|
||||
$currentUrl = $existingConfiguration['menu_custom_item_url'];
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
new \IPS\Helpers\Form\Translatable( 'menu_custom_item_link', NULL, NULL, array( 'app' => 'core', 'key' => $id ? "mobilemenu_item_{$id}" : NULL ), function( $val )
|
||||
{
|
||||
if ( !trim( $val[ \IPS\Lang::defaultLanguage() ] ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_required');
|
||||
}
|
||||
} ),
|
||||
new \IPS\Helpers\Form\Url( 'menu_custom_item_url', $currentUrl, NULL, array(), function( $val )
|
||||
{
|
||||
if ( isset( \IPS\Request::i()->menu_manager_extension ) and \IPS\Request::i()->menu_manager_extension === 'core_CustomItem' and empty( $val ) )
|
||||
{
|
||||
throw new \InvalidArgumentException('form_required');
|
||||
}
|
||||
} )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse configuration fields
|
||||
*
|
||||
* @param array $configuration The values received from the form
|
||||
* @param int $id The ID number of the existing item, if editing
|
||||
* @return array
|
||||
*/
|
||||
public static function parseConfiguration( $configuration, $id )
|
||||
{
|
||||
$baseUrl = \IPS\Http\Url::internal('', 'front');
|
||||
|
||||
if ( $configuration['menu_custom_item_url'] instanceof \IPS\Http\Url\Friendly )
|
||||
{
|
||||
$configuration['internal'] = $configuration['menu_custom_item_url']->seoTemplate;
|
||||
$configuration['seoTitles'] = $configuration['menu_custom_item_url']->seoTitles;
|
||||
$configuration['menu_custom_item_url'] = http_build_query( $configuration['menu_custom_item_url']->hiddenQueryString + $configuration['menu_custom_item_url']->queryString, '', '&' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$configuration['menu_custom_item_url'] = (string) $configuration['menu_custom_item_url'];
|
||||
}
|
||||
|
||||
\IPS\Lang::saveCustom( 'core', "mobilemenu_item_{$id}", $configuration['menu_custom_item_link'] );
|
||||
unset( $configuration['menu_custom_item_link'] );
|
||||
|
||||
return $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissions can be inherited?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function permissionsCanInherit()
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function title()
|
||||
{
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( "mobilemenu_item_{$this->id}" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Link
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function link()
|
||||
{
|
||||
if ( isset( $this->configuration['menu_custom_item_url'] ) and ( $this->configuration['menu_custom_item_url'] or isset( $this->configuration['internal'] ) ) )
|
||||
{
|
||||
if ( isset( $this->configuration['internal'] ) )
|
||||
{
|
||||
return \IPS\Http\Url::internal( $this->configuration['menu_custom_item_url'], 'front', $this->configuration['internal'], isset( $this->configuration['seoTitles'] ) ? $this->configuration['seoTitles'] : array() );
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\Http\Url::external( $this->configuration['menu_custom_item_url'] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return \IPS\Http\Url::internal('');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Moderator Control Panel Extension: Alerts
|
||||
* @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 12 May 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\ModCp;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Moderator Control Panel Extension: Alerts
|
||||
*/
|
||||
class _Alerts
|
||||
{
|
||||
/**
|
||||
* Returns the primary tab key for the navigation bar
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getTab()
|
||||
{
|
||||
/* Check Permissions */
|
||||
if ( ! \IPS\Member::loggedIn()->modPermission('can_manage_alerts') )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'alerts';
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function manage()
|
||||
{
|
||||
/* Check Permissions */
|
||||
if ( ! \IPS\Member::loggedIn()->modPermission('can_manage_alerts') )
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2C427/1', 403, '' );
|
||||
}
|
||||
|
||||
$table = new \IPS\Helpers\Table\Content( '\IPS\core\Alerts\Alert', \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=alerts' ) );
|
||||
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'modcp', 'core', 'front' ), 'alertRow' );
|
||||
$table->include = array( 'alert_title' );
|
||||
$table->mainColumn = 'alert_title';
|
||||
$table->sortBy = 'alert_id';
|
||||
$table->sortDirection = 'desc';
|
||||
$table->sortOptions = array( 'alert_id' );
|
||||
$table->title = \IPS\Member::loggedIn()->language()->addToStack( 'modcp_alerts' );
|
||||
|
||||
/* Filters */
|
||||
$table->filters = array(
|
||||
'alert_filter_active' => [ 'alert_enabled=1' ],
|
||||
'alert_filter_inactive' => [ 'alert_enabled=0' ],
|
||||
'alert_filter_viewed' => [ 'alert_viewed > 0 ' ],
|
||||
'alert_filter_not_viewed' => [ 'alert_viewed = 0 ' ],
|
||||
'alert_filter_groups' => [ "alert_recipient_type='group'"],
|
||||
'alert_filter_user' => [ "alert_recipient_type='user'"]
|
||||
);
|
||||
|
||||
\IPS\Output::i()->sidebar['enabled'] = FALSE;
|
||||
\IPS\Output::i()->breadcrumb[] = array( NULL, \IPS\Member::loggedIn()->language()->addToStack( 'modcp_alerts' ) );
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'modcp_alerts' );
|
||||
return \IPS\Theme::i()->getTemplate( 'modcp' )->alerts( (string) $table );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/Edit Alert
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$current = NULL;
|
||||
if ( \IPS\Request::i()->id )
|
||||
{
|
||||
$current = \IPS\core\Alerts\Alert::load( \IPS\Request::i()->id );
|
||||
}
|
||||
|
||||
$form = \IPS\core\Alerts\Alert::form( $current );
|
||||
$form->class = 'ipsForm_vertical';
|
||||
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$alert = \IPS\Core\Alerts\Alert::_createFromForm( $values, $current );
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=modcp&tab=alerts", 'front', 'modcp_alerts' ) );
|
||||
}
|
||||
|
||||
if ( !\is_null( $current ) )
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'edit_alert' );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'add_alert' );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=alerts' ), \IPS\Member::loggedIn()->language()->addToStack( 'modcp_alerts' ) );
|
||||
\IPS\Output::i()->breadcrumb[] = array( NULL, ( !\is_null( $current ) ) ? \IPS\Member::loggedIn()->language()->addToStack( 'edit_alert' ) : \IPS\Member::loggedIn()->language()->addToStack( 'add_alert' ) );
|
||||
return $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Alert
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if ( !\IPS\Member::loggedIn()->modPermission( 'can_manage_alerts' ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2C427/2', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
try
|
||||
{
|
||||
$alert = \IPS\core\Alerts\Alert::load( \IPS\Request::i()->id );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C427/3', 404, '' );
|
||||
}
|
||||
|
||||
$alert->delete();
|
||||
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=modcp&tab=alerts", 'front', 'modcp_alerts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Author
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function changeAuthor()
|
||||
{
|
||||
if ( !\IPS\Member::loggedIn()->modPermission( 'can_manage_alerts' ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2C427/4', 403, '' );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$alert = \IPS\core\Alerts\Alert::load( \IPS\Request::i()->id );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C427/5', 404, '' );
|
||||
}
|
||||
|
||||
/* Build form */
|
||||
$form = new \IPS\Helpers\Form;
|
||||
$form->add( new \IPS\Helpers\Form\Member( 'author', $alert->author(), TRUE ) );
|
||||
$form->class .= 'ipsForm_vertical';
|
||||
|
||||
/* Handle submissions */
|
||||
if ( $values = $form->values() )
|
||||
{
|
||||
$alert->changeAuthor( $values['author'] );
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=modcp&tab=alerts", 'front', 'modcp_alerts' ) );
|
||||
}
|
||||
|
||||
/* Display form */
|
||||
\IPS\Output::i()->output = $form->customTemplate( array( \IPS\Theme::i()->getTemplate( 'forms', 'core' ), 'popupTemplate' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Alert Status
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
if ( !\IPS\Member::loggedIn()->modPermission( 'can_manage_alerts' ) )
|
||||
{
|
||||
\IPS\Output::i()->error( 'no_module_permission', '2C427/6', 403, '' );
|
||||
}
|
||||
|
||||
\IPS\Session::i()->csrfCheck();
|
||||
|
||||
try
|
||||
{
|
||||
$alert = \IPS\core\Alerts\Alert::load( \IPS\Request::i()->id );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
\IPS\Output::i()->error( 'node_error', '2C427/7', 404, '' );
|
||||
}
|
||||
|
||||
$alert->enabled = ( $alert->enabled === 1 ? 0 : 1 );
|
||||
$alert->save();
|
||||
|
||||
if ( \IPS\Request::i()->isAjax() )
|
||||
{
|
||||
\IPS\Output::i()->json( array( 'OK' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=modcp&tab=alerts", 'front', 'modcp_alerts' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,7 @@ class _Content
|
||||
|
||||
$table = new \IPS\Helpers\Table\Content( $currentClass, \IPS\Http\Url::internal( "app=core&module=modcp&controller=modcp&tab=hidden&type={$currentType}", 'front', 'modcp_content' ), array( $where ) );
|
||||
$table->title = \IPS\Member::loggedIn()->language()->addToStack( 'modcp_hidden' );
|
||||
$table->classes = array( 'ipsStream' );
|
||||
return \IPS\Theme::i()->getTemplate( 'modcp' )->tableWrapper( (string) $table );
|
||||
}
|
||||
|
||||
|
||||
@@ -85,11 +85,12 @@ class _Content
|
||||
$return['can_manage_announcements'] = 'YesNo';
|
||||
|
||||
$return['can_see_poll_voters'] = 'YesNo';
|
||||
$return['can_edit_poll_votes'] = 'YesNo';
|
||||
$return['can_close_polls'] = 'YesNo';
|
||||
|
||||
$return['can_manage_deleted_content'] = 'YesNo';
|
||||
$return['can_view_anonymous_posters'] = 'YesNo';
|
||||
|
||||
$return['can_manage_alerts'] = 'YesNo';
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -31,18 +31,25 @@ class _Achievements
|
||||
public static function configurationOptions( \IPS\Member $member = NULL )
|
||||
{
|
||||
// Return an array defining each notification type, including the form elements necessary to allow its configuration
|
||||
|
||||
return array(
|
||||
'core_Achievements' => array(
|
||||
'type' => 'standard',
|
||||
'notificationTypes' => array( 'new_rank', 'new_badge', 'new_recognize' ),
|
||||
'title' => 'notifications__core_Achievements',
|
||||
'showTitle' => FALSE,
|
||||
'description' => 'notifications__core_Achievements_desc',
|
||||
'default' => array( 'inline' ),
|
||||
'disabled' => array()
|
||||
)
|
||||
);
|
||||
if ( \IPS\Settings::i()->achievements_enabled )
|
||||
{
|
||||
return array(
|
||||
'core_Achievements' => array(
|
||||
'type' => 'standard',
|
||||
'notificationTypes' => array( 'new_rank', 'new_badge', 'new_recognize' ),
|
||||
'title' => 'notifications__core_Achievements',
|
||||
'showTitle' => FALSE,
|
||||
'description' => 'notifications__core_Achievements_desc',
|
||||
'default' => array( 'inline' ),
|
||||
'disabled' => array()
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,13 +94,13 @@ class _Achievements
|
||||
}
|
||||
else
|
||||
{
|
||||
$title = \IPS\Member::loggedIn()->rank['title'];
|
||||
$title = $notification->member->rank['title'];
|
||||
}
|
||||
|
||||
return array(
|
||||
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_rank', FALSE, array( 'sprintf' => array( $title ) ) ),
|
||||
'url' => \IPS\Member::loggedIn()->url(),
|
||||
'author' => \IPS\Member::loggedIn()
|
||||
'title' => $notification->member->language()->addToStack( 'notification__new_rank', FALSE, array( 'sprintf' => array( $title ) ) ),
|
||||
'url' => $notification->member->url(),
|
||||
'author' => $notification->member
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,7 +111,7 @@ class _Achievements
|
||||
* @param \IPS\Member $member The member that earned the rank
|
||||
* @return array
|
||||
*/
|
||||
public static function parse_mobile_new_rank( \IPS\Lang $language, \IPS\Member $member )
|
||||
public static function parse_mobile_new_rank( \IPS\Lang $language, \IPS\core\Achievements\Rank $rank, \IPS\Member $member )
|
||||
{
|
||||
return array(
|
||||
'body' => $language->addToStack( 'notification__new_rank', FALSE, array( 'sprintf' => array( $member->rank['title'] ) ) ),
|
||||
@@ -149,8 +156,8 @@ class _Achievements
|
||||
try
|
||||
{
|
||||
$badge = \IPS\core\Achievements\Badge::load( $badgeId );
|
||||
$content = \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_badge_content', NULL, [ 'sprintf' => [ $badge->_title ] ] );
|
||||
$title = \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_badge', NULL, [ 'sprintf' => [ $badge->_title ] ] );
|
||||
$content = \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_badge_content', FALSE, [ 'sprintf' => [ $badge->_title ] ] );
|
||||
$title = \IPS\Member::loggedIn()->language()->addToStack( 'notification__new_badge', FALSE, [ 'sprintf' => [ $badge->_title ] ] );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
@@ -165,9 +172,9 @@ class _Achievements
|
||||
|
||||
return array(
|
||||
'title' => $title,
|
||||
'url' => (string) \IPS\Http\Url::internal( "app=core&module=members&controller=profile&do=badges&id=" . \IPS\Member::loggedIn()->member_id, 'front', 'profile_badges', \IPS\Member::loggedIn()->members_seo_name ),
|
||||
'url' => (string) \IPS\Http\Url::internal( "app=core&module=members&controller=profile&do=badges&id=" . $notification->member->member_id, 'front', 'profile_badges', $notification->member->members_seo_name ),
|
||||
'content' => $content,
|
||||
'author' => \IPS\Member::loggedIn()
|
||||
'author' => $notification->member
|
||||
);
|
||||
}
|
||||
|
||||
@@ -182,7 +189,7 @@ class _Achievements
|
||||
public static function parse_mobile_new_badge( \IPS\Lang $language, \IPS\Member $member, \IPS\core\Achievements\Badge $badge )
|
||||
{
|
||||
return array(
|
||||
'body' => $language->addToStack( 'notification__new_badge', NULL, [ 'sprintf' => [ $badge->_title ] ] ),
|
||||
'body' => $language->addToStack( 'notification__new_badge', FALSE, [ 'sprintf' => [ $badge->_title ] ] ),
|
||||
'data' => array(
|
||||
'url' => (string) \IPS\Http\Url::internal( "app=core&module=members&controller=profile&do=badges&id={$member->member_id}", 'front', 'profile_badges', $member->members_seo_name ),
|
||||
'author' => $member,
|
||||
|
||||
@@ -42,7 +42,7 @@ class _Content
|
||||
$autoFollow[] = 'comments';
|
||||
}
|
||||
|
||||
$autoFollowField = new \IPS\Helpers\Form\CheckboxSet( 'auto_track', $autoFollow, FALSE, array( 'options' => array( 'content' => 'auto_track_content', 'comments' => 'auto_track_comments' ), 'multiple' => TRUE ) );
|
||||
$autoFollowField = new \IPS\Helpers\Form\CheckboxSet( 'auto_track', $autoFollow, FALSE, array( 'options' => array( 'content' => 'auto_track_content', 'comments' => 'auto_track_comments' ), 'multiple' => TRUE, 'showAllNone' => FALSE ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -55,7 +55,7 @@ class _Content
|
||||
$autoFollow[] = 'comments';
|
||||
}
|
||||
|
||||
$autoFollowField = new \IPS\Helpers\Form\CheckboxSet( 'auto_follow_defaults', $autoFollow, FALSE, array( 'options' => array( 'content' => 'auto_follow_new_content', 'comments' => 'auto_follow_replied_to' ), 'multiple' => TRUE ) );
|
||||
$autoFollowField = new \IPS\Helpers\Form\CheckboxSet( 'auto_follow_defaults', $autoFollow, FALSE, array( 'options' => array( 'content' => 'auto_follow_new_content', 'comments' => 'auto_follow_replied_to' ), 'multiple' => TRUE, 'showAllNone' => FALSE ) );
|
||||
}
|
||||
|
||||
return array(
|
||||
@@ -445,12 +445,13 @@ class _Content
|
||||
$names = array();
|
||||
foreach ( $commenters as $member )
|
||||
{
|
||||
$name = ( $item->isAnonymous() ) ? \IPS\Member::loggedIn()->language()->addToStack( 'post_anonymously_placename' ) : \IPS\Member::load( $member )->name;
|
||||
if ( \count( $names ) > 2 )
|
||||
{
|
||||
$names[] = \IPS\Member::loggedIn()->language()->addToStack( 'x_others', FALSE, array( 'pluralize' => array( \count( $commenters ) - 3 ) ) );
|
||||
break;
|
||||
}
|
||||
$names[] = \IPS\Member::load( $member )->name;
|
||||
$names[] = $name;
|
||||
}
|
||||
|
||||
$comment = $commentClass::loadAndCheckPerms( $notification->item_sub_id );
|
||||
|
||||
@@ -35,7 +35,7 @@ class _Messenger
|
||||
$module = \IPS\Application\Module::get( 'core', 'messaging', 'front' );
|
||||
if ( $module->_enabled )
|
||||
{
|
||||
if ( !$member or ( $member->canAccessModule( $module ) and !$member->members_disable_pm ) )
|
||||
if ( !$member or ( $member->canAccessModule( $module ) and $member->members_disable_pm != 2 ) )
|
||||
{
|
||||
$return['core_Messenger'] = array(
|
||||
'type' => 'standard',
|
||||
|
||||
@@ -528,7 +528,7 @@ class _Moderation
|
||||
|
||||
return array(
|
||||
'title' => $language->addToStack( 'notification__unapproved_content_title', FALSE, array( 'htmlsprintf' => array(
|
||||
( $content instanceof \IPS\Content\Item ) ? $content->definiteArticle( $language ) : $language->get( $content::$title . '_lc' ),
|
||||
( $content instanceof \IPS\Content ) ? $content->definiteArticle( $language ) : $language->get( $content::$title . '_lc' ),
|
||||
) ) ),
|
||||
'body' => $language->addToStack( 'notification__unapproved_content', FALSE, array( 'htmlsprintf' => array(
|
||||
$content->author()->name,
|
||||
|
||||
@@ -524,7 +524,7 @@ class _MyStuff
|
||||
)
|
||||
) ),
|
||||
'data' => array(
|
||||
'url' => (string) $content->url('find'),
|
||||
'url' => (string) ( $content instanceof \IPS\Content\Comment ) ? $content->url('find') : $content->url(),
|
||||
'author' => $content->author()
|
||||
),
|
||||
'channelId' => 'my-content',
|
||||
@@ -602,7 +602,7 @@ class _MyStuff
|
||||
)
|
||||
) ),
|
||||
'data' => array(
|
||||
'url' => (string) $content->url('find'),
|
||||
'url' => (string) ( $content instanceof \IPS\Content\Comment ) ? $content->url('find') : $content->url(),
|
||||
'author' => $content->author()
|
||||
),
|
||||
'channelId' => 'my-content',
|
||||
@@ -670,8 +670,15 @@ class _MyStuff
|
||||
) ),
|
||||
'data' => array(
|
||||
'url' => (string) $content->url(),
|
||||
'author' => $content->author()
|
||||
'author' => $content->author(),
|
||||
'grouped' => $language->addToStack( 'notification__approved_content_grouped', FALSE, array(
|
||||
'htmlsprintf' => array(
|
||||
mb_strtolower( $content->definiteArticle( $language, TRUE ) )
|
||||
)
|
||||
) ),
|
||||
'groupedTitle' => $language->addToStack( 'notification__approved_content_title', FALSE )
|
||||
),
|
||||
'tag' => md5( 'approved' . \get_class( $item ) . $item::$title ), // Group notification by type
|
||||
'channelId' => 'my-content',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Overview statistics extension: DeletedContent
|
||||
* @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 23 Sep 2021
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\OverviewStatistics;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overview statistics extension: DeletedContent
|
||||
*/
|
||||
class _DeletedContent
|
||||
{
|
||||
/**
|
||||
* @brief Which statistics page (activity or user)
|
||||
*/
|
||||
public $page = 'activity';
|
||||
|
||||
/**
|
||||
* Return the sub-block keys
|
||||
*
|
||||
* @note This is designed to allow one class to support multiple blocks, for instance using the ContentRouter to generate blocks.
|
||||
* @return array
|
||||
*/
|
||||
public function getBlocks()
|
||||
{
|
||||
return array( 'deletedcontent' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return block details (title and description)
|
||||
*
|
||||
* @param string|NULL $subBlock The subblock we are loading as returned by getBlocks()
|
||||
* @return array
|
||||
*/
|
||||
public function getBlockDetails( $subBlock = NULL )
|
||||
{
|
||||
/* Description can be null and will not be shown if so */
|
||||
return array( 'app' => 'core', 'title' => 'stats_deletedcontent_percent', 'description' => 'stats_percentagedeleted_desc', 'refresh' => 60, 'form' => false );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the block HTML to show
|
||||
*
|
||||
* @param array|NULL $dateRange NULL for all time, or an array with 'start' and 'end' \IPS\DateTime objects to restrict to
|
||||
* @param string|NULL $subBlock The subblock we are loading as returned by getBlocks()
|
||||
* @return string
|
||||
*/
|
||||
public function getBlock( $dateRange = NULL, $subBlock = NULL )
|
||||
{
|
||||
if( !\IPS\Settings::i()->dellog_retention_period )
|
||||
{
|
||||
$total = $deleted = $value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$total = \IPS\Db::i()->select( 'COUNT(*)', 'core_search_index', array( 'index_date_created > ?', time() - \IPS\Settings::i()->dellog_retention_period ) )->first();
|
||||
$deleted = \IPS\Db::i()->select( 'COUNT(*)', 'core_deletion_log', array() )->first();
|
||||
$value = $total ? round( $deleted / $total * 100, 2 ) : 0;
|
||||
}
|
||||
|
||||
return \IPS\Theme::i()->getTemplate( 'activitystats', 'core' )->deletedPercentage( $value, $total, $deleted );
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class _ProfileFields
|
||||
$pieBarData[] = array(
|
||||
'name' => \IPS\Member::loggedIn()->language()->addToStack('stats_pfields__yes'),
|
||||
'value' => isset( $results[1] ) ? $results[1]['total'] : 0,
|
||||
'percentage' => ( isset( $results[1] ) AND $results[0]['total'] > 0 ) ? round( ( $results[1]['total'] / $total ) * 100, 2 ) : 0
|
||||
'percentage' => ( isset( $results[1] ) AND $results[1]['total'] > 0 ) ? round( ( $results[1]['total'] / $total ) * 100, 2 ) : 0
|
||||
);
|
||||
}
|
||||
elseif( $field['pf_type'] == 'Rating' )
|
||||
|
||||
@@ -112,8 +112,7 @@ class _ProfileFields
|
||||
foreach( $step->subcompletion_act as $item )
|
||||
{
|
||||
$fieldId = \substr( $item, 12 );
|
||||
|
||||
foreach( $member->profileFields( \IPS\core\ProfileFields\Field::PROFILE_COMPLETION ) AS $group => $field )
|
||||
foreach( $member->profileFields( \IPS\core\ProfileFields\Field::PROFILE_COMPLETION, TRUE ) AS $group => $field )
|
||||
{
|
||||
foreach( $field AS $key => $value )
|
||||
{
|
||||
|
||||
@@ -51,7 +51,8 @@ class _Bulkmail
|
||||
|
||||
/* Reduce the maximum number of emails to be sent for bulk mail to 500 to prevent member specific tag timeouts */
|
||||
$mailPerGo = min( $classToUse::MAX_EMAILS_PER_GO, 500 );
|
||||
|
||||
$existingvalue = \IPS\Db::i()->readWriteSeparation;
|
||||
\IPS\Db::i()->readWriteSeparation = FALSE;
|
||||
$results = $mail->getQuery( array( $offset, $mailPerGo ) );
|
||||
|
||||
if ( !\count( $results ) )
|
||||
@@ -59,10 +60,10 @@ class _Bulkmail
|
||||
$mail->active = 0;
|
||||
$mail->offset = 0;
|
||||
$mail->save();
|
||||
|
||||
\IPS\Db::i()->readWriteSeparation = $existingvalue;
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
|
||||
/* Convert $results into an array with replacement tags */
|
||||
$recipients = array();
|
||||
foreach ( $results as $memberData )
|
||||
@@ -102,7 +103,7 @@ class _Bulkmail
|
||||
}
|
||||
|
||||
/* Send it */
|
||||
$email = \IPS\Email::buildFromContent( $mail->subject, $content, NULL, \IPS\Email::TYPE_BULK, TRUE, 'bulk_mail' )
|
||||
$email = \IPS\Email::buildFromContent( $mail->subject, $content, NULL, \IPS\Email::TYPE_BULK, \IPS\Email::WRAPPER_USE, 'bulk_mail' )
|
||||
->setUnsubscribe( 'core', 'unsubscribeBulk' );
|
||||
$sent = 0;
|
||||
foreach ( $recipients as $languageId => $_recipients )
|
||||
@@ -114,7 +115,7 @@ class _Bulkmail
|
||||
$mail->offset = ( $mail->offset + $mailPerGo );
|
||||
$mail->sentto = ( $mail->sentto + $sent );
|
||||
$mail->save();
|
||||
|
||||
\IPS\Db::i()->readWriteSeparation = $existingvalue;
|
||||
return $offset + $mailPerGo;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
|
||||
@@ -76,8 +76,15 @@ class _DeleteOrMoveContent
|
||||
$moveTo = NULL;
|
||||
if ( isset( $data['moveTo'] ) )
|
||||
{
|
||||
$moveToClass = isset( $data['moveToClass'] ) ? $data['moveToClass'] : $classname;
|
||||
$moveTo = $moveToClass::load( $data['moveTo'] );
|
||||
$moveToClass = $data['moveToClass'] ?? $classname;
|
||||
try
|
||||
{
|
||||
$moveTo = $moveToClass::load( $data['moveTo'] );
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
$moveTo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
$return = $node->massMoveorDelete( $moveTo, $data );
|
||||
|
||||
@@ -70,10 +70,15 @@ class _Follow
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
$existingvalue = \IPS\Db::i()->readWriteSeparation;
|
||||
\IPS\Db::i()->readWriteSeparation = FALSE;
|
||||
|
||||
$sentTo = isset( $data['sentTo'] ) ? $data['sentTo'] : array();
|
||||
$newOffset = $item->sendNotificationsBatch( $offset, $sentTo, isset( $data['extra'] ) ? $data['extra'] : NULL );
|
||||
$data['sentTo'] = $sentTo;
|
||||
|
||||
\IPS\Db::i()->readWriteSeparation = $existingvalue;
|
||||
|
||||
if( $newOffset === NULL )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Background Task
|
||||
* @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 11 Jan 2022
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Queue;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Background Task
|
||||
*/
|
||||
class _IndexNow
|
||||
{
|
||||
/**
|
||||
* Parse data before queuing
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function preQueueData( $data )
|
||||
{
|
||||
if( !\IPS\core\IndexNow::i()->isEnabled() )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( isset( $data ['urls'] ) AND \count($data ['urls'] ) )
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
/* We don't have any URLS to submit, so just return NULL to complete the task */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run Background Task
|
||||
*
|
||||
* @param mixed $data Data as it was passed to \IPS\Task::queue()
|
||||
* @param int $offset Offset
|
||||
* @return int New offset
|
||||
* @throws \IPS\Task\Queue\OutOfRangeException Indicates offset doesn't exist and thus task is complete
|
||||
*/
|
||||
public function run( $data, $offset )
|
||||
{
|
||||
\IPS\core\IndexNow::i()->send( $data['urls'] );
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Progress
|
||||
*
|
||||
* @param mixed $data Data as it was passed to \IPS\Task::queue()
|
||||
* @param int $offset Offset
|
||||
* @return array( 'text' => 'Doing something...', 'complete' => 50 ) Text explaining task and percentage complete
|
||||
* @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete
|
||||
*/
|
||||
public function getProgress( $data, $offset )
|
||||
{
|
||||
if( isset( $data['type']))
|
||||
{
|
||||
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack( 'indexnow_s_submitting', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $data['type'], FALSE ), ) ) ), 'complete' => 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
return array( 'text' => \IPS\Member::loggedIn()->language()->addToStack( 'indexnow_data_submitting' ), 'complete' => 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,19 +48,22 @@ class _MemberContent
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get count */
|
||||
try
|
||||
/* Get count - only do this if we're deleting member content, not guest content */
|
||||
if ( $data['member_id'] )
|
||||
{
|
||||
$data['count'] = $classname::db()->select( 'COUNT(*)', $classname::$databaseTable, static::_getWhere( $data ) )->first();
|
||||
}
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
try
|
||||
{
|
||||
$data['count'] = $classname::db()->select( 'COUNT(*)', $classname::$databaseTable, static::_getWhere( $data ) )->first();
|
||||
}
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( !$data['count'] )
|
||||
{
|
||||
return NULL;
|
||||
if ( !$data['count'] )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
@@ -84,7 +87,9 @@ class _MemberContent
|
||||
}
|
||||
|
||||
$select = $classname::db()->select( '*', $classname::$databaseTable, static::_getWhere( $data ), $classname::$databasePrefix.$classname::$databaseColumnId, array( 0, \IPS\REBUILD_NORMAL ) );
|
||||
|
||||
|
||||
/* Keep track of how many we did since we cannot rely on the count for guests */
|
||||
$did = 0;
|
||||
foreach ( new \IPS\Patterns\ActiveRecordIterator( $select, $classname ) as $item )
|
||||
{
|
||||
/* If this is the first comment on an item where a first comment is required (e.g. posts) do nothing, as when we get to the item, that will handle it */
|
||||
@@ -137,18 +142,24 @@ class _MemberContent
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$did++;
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
|
||||
$did++;
|
||||
}
|
||||
catch( \ErrorException $e )
|
||||
{
|
||||
|
||||
$did++;
|
||||
}
|
||||
}
|
||||
|
||||
if( $offset + \IPS\REBUILD_NORMAL >= $data['count'] )
|
||||
|
||||
if ( !$did )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
else if( isset( $data['count'] ) AND ( $offset + \IPS\REBUILD_NORMAL >= $data['count'] ) )
|
||||
{
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
@@ -186,7 +197,14 @@ class _MemberContent
|
||||
|
||||
$text = \IPS\Member::loggedIn()->language()->addToStack( 'backgroundQueue_membercontent_' . $data['action'], FALSE, $sprintf );
|
||||
|
||||
return array( 'text' => $text, 'complete' => $data['count'] ? ( round( 100 / $data['count'] * $offset, 2 ) ) : 100 );
|
||||
if ( !$data['member_id'] )
|
||||
{
|
||||
return array( 'text' => $text, 'complete' => NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
return array( 'text' => $text, 'complete' => $data['count'] ? ( round( 100 / $data['count'] * $offset, 2 ) ) : 100 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,7 +209,7 @@ class _RebuildAchievements
|
||||
public function postComplete( $data, $processed = TRUE )
|
||||
{
|
||||
/* The core_queue row is deleted before this is run */
|
||||
if ( ! \IPS\Db::i()->select( 'COUNT(*)', 'core_queue', [ [ '`key`=?', 'RebuildAchievements' ] ] )->first() )
|
||||
if ( ! \IPS\Db::i()->select( 'COUNT(*)', 'core_queue', [ [ '`key`=?', 'RebuildAchievements' ] ], NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() )
|
||||
{
|
||||
/* Rebuilding is complete! */
|
||||
\IPS\Settings::i()->changeValues( array( 'achievements_rebuilding' => 0, 'achievements_last_rebuilt' => time() ) );
|
||||
|
||||
@@ -127,6 +127,9 @@ class _RebuildSearchIndex
|
||||
*/
|
||||
public function run( &$data, $offset )
|
||||
{
|
||||
/* We want to allow read/write separation in this task */
|
||||
\IPS\Db::i()->readWriteSeparation = TRUE;
|
||||
|
||||
$classname = $data['class'];
|
||||
$exploded = explode( '\\', $classname );
|
||||
if ( !class_exists( $classname ) or !\IPS\Application::appIsEnabled( $exploded[1] ) )
|
||||
@@ -186,20 +189,32 @@ class _RebuildSearchIndex
|
||||
}
|
||||
catch( \OutOfRangeException $e )
|
||||
{
|
||||
/* Turn off read/write separation before returning */
|
||||
\IPS\Db::i()->readWriteSeparation = FALSE;
|
||||
|
||||
/* Something has gone wrong with iterator attempting to use constructFromData */
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
}
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
/* Turn off read/write separation before returning */
|
||||
\IPS\Db::i()->readWriteSeparation = FALSE;
|
||||
|
||||
/* Something has gone wrong with the query, like the table not existing */
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
if( $indexed === NULL )
|
||||
{
|
||||
/* Turn off read/write separation before returning */
|
||||
\IPS\Db::i()->readWriteSeparation = FALSE;
|
||||
|
||||
throw new \IPS\Task\Queue\OutOfRangeException;
|
||||
}
|
||||
|
||||
/* Turn off read/write separation before returning */
|
||||
\IPS\Db::i()->readWriteSeparation = FALSE;
|
||||
|
||||
/* Return the number indexed so far, so that the rebuild progress bar text makes sense */
|
||||
return $data['indexed'];
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Support Applications in sitemaps
|
||||
* @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 17 Mar 2020
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Sitemap;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Support Applications in sitemaps
|
||||
*/
|
||||
class _Applications
|
||||
{
|
||||
/**
|
||||
* @brief Recommended Settings
|
||||
*/
|
||||
public $recommendedSettings = array();
|
||||
|
||||
/**
|
||||
* Add settings for ACP configuration to the form
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function settings()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings for ACP configuration
|
||||
*
|
||||
* @param array $values Values
|
||||
* @return void
|
||||
*/
|
||||
public function saveSettings( $values )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sitemap filename(s)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFilenames()
|
||||
{
|
||||
return array( 'sitemap_' . mb_strtolower('Applications') );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the sitemap
|
||||
*
|
||||
* @param string $filename The sitemap file to build (should be one returned from getFilenames())
|
||||
* @param \IPS\Sitemap $sitemap Sitemap object reference
|
||||
* @return void
|
||||
*/
|
||||
public function generateSitemap( $filename, $sitemap )
|
||||
{
|
||||
$links = array();
|
||||
|
||||
foreach( \IPS\Application::enabledApplications() as $app )
|
||||
{
|
||||
foreach( $app->sitemapLinks() as $link )
|
||||
{
|
||||
$links[] = array( 'url' => $link );
|
||||
}
|
||||
}
|
||||
|
||||
/* Build the file */
|
||||
$sitemap->buildSitemapFile( $filename, $links );
|
||||
}
|
||||
}
|
||||
@@ -163,6 +163,12 @@ class _Content extends \IPS\Content\ExtensionGenerator
|
||||
/* Check that guests can access the content at all */
|
||||
try
|
||||
{
|
||||
$app = \IPS\Application::load( $class::$application );
|
||||
if ( !$app->canAccess( new \IPS\Member ) )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
}
|
||||
|
||||
$module = \IPS\Application\Module::get( $class::$application, $class::$module, 'front' );
|
||||
if ( !$module->can( 'view', new \IPS\Member ) )
|
||||
{
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Support profiles in sitemaps
|
||||
* @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 29 Aug 2013
|
||||
*/
|
||||
|
||||
namespace IPS\core\extensions\core\Sitemap;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Support profiles in sitemaps
|
||||
*/
|
||||
class _Profiles
|
||||
{
|
||||
/**
|
||||
* @brief Recommended Settings
|
||||
*/
|
||||
public $recommendedSettings = array(
|
||||
'sitemap_profiles_include' => true,
|
||||
'sitemap_profiles_count' => -1,
|
||||
'sitemap_profiles_priority' => 0.6,
|
||||
'sitemap_profiles_content' => 10,
|
||||
);
|
||||
|
||||
/**
|
||||
* Settings for ACP configuration to the form
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function settings()
|
||||
{
|
||||
return array(
|
||||
'sitemap_profiles_include' => new \IPS\Helpers\Form\YesNo( "sitemap_profiles_include", \IPS\Settings::i()->sitemap_profiles_count != 0, FALSE, array( 'togglesOn' => array( "sitemap_profiles_count", "sitemap_profiles_priority", "sitemap_profiles_content" ) ), NULL, NULL, NULL, "sitemap_profiles_include" ),
|
||||
'sitemap_profiles_count' => new \IPS\Helpers\Form\Number( 'sitemap_profiles_count', \IPS\Settings::i()->sitemap_profiles_count, FALSE, array( 'min' => '-1', 'unlimited' => '-1' ), NULL, NULL, NULL, 'sitemap_profiles_count' ),
|
||||
'sitemap_profiles_content' => new \IPS\Helpers\Form\Number( 'sitemap_profiles_content', \IPS\Settings::i()->sitemap_profiles_content, FALSE, array( 'min' => '-1', 'unlimited' => '-1', 'unlimitedLang' => 'sitemap_profiles_content_unlimited' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('sitemap_profiles_content_suffix'), 'sitemap_profiles_content' ),
|
||||
'sitemap_profiles_priority' => new \IPS\Helpers\Form\Select( 'sitemap_profiles_priority', \IPS\Settings::i()->sitemap_profiles_priority, FALSE, array( 'options' => \IPS\Sitemap::$priorities, 'unlimited' => '-1', 'unlimitedLang' => 'sitemap_dont_include' ), NULL, NULL, NULL, 'sitemap_profiles_priority' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings for ACP configuration
|
||||
*
|
||||
* @param array $values Values
|
||||
* @return void
|
||||
*/
|
||||
public function saveSettings( $values )
|
||||
{
|
||||
if ( $values['sitemap_configuration_info'] )
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'sitemap_profiles_count' => $this->recommendedSettings['sitemap_profiles_count'], 'sitemap_profiles_content' => $this->recommendedSettings['sitemap_profiles_content'], 'sitemap_profiles_priority' => $this->recommendedSettings['sitemap_profiles_priority'] ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
\IPS\Settings::i()->changeValues( array( 'sitemap_profiles_count' => $values['sitemap_profiles_include'] ? $values['sitemap_profiles_count'] : 0, 'sitemap_profiles_content' => $values['sitemap_profiles_content'], 'sitemap_profiles_priority' => $values['sitemap_profiles_priority'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sitemap filename(s)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFilenames()
|
||||
{
|
||||
/* First, we need to make sure we are actually including profiles in the sitemap */
|
||||
if ( \IPS\Settings::i()->sitemap_profiles_count == 0 )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/* Then, make sure the module is enabled. */
|
||||
$profileModule = \IPS\Application\Module::get( 'core', 'members', 'front' );
|
||||
if( !$profileModule->visible )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/* And that Guests can view the profile module */
|
||||
if( !$profileModule->can( 'view', new \IPS\Member ) )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$where = NULL;
|
||||
|
||||
if ( \IPS\Settings::i()->sitemap_profiles_content > 0 )
|
||||
{
|
||||
$where = array( array( 'member_posts >= ?', \IPS\Settings::i()->sitemap_profiles_content ) );
|
||||
}
|
||||
|
||||
/* Get a count of how many files we'll be generating */
|
||||
$count = \IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where )->first();
|
||||
$count = ( $count > \IPS\Sitemap::MAX_PER_FILE ) ? ceil( $count / \IPS\Sitemap::MAX_PER_FILE ) : 1;
|
||||
|
||||
/* If we are not storing all profiles, limit the count */
|
||||
if ( \IPS\Settings::i()->sitemap_profiles_count > -1 AND $count > \IPS\Settings::i()->sitemap_profiles_count )
|
||||
{
|
||||
$count = \IPS\Settings::i()->sitemap_profiles_count;
|
||||
}
|
||||
|
||||
/* Generate the file names */
|
||||
$files = array();
|
||||
for( $i=1; $i <= $count; $i++ )
|
||||
{
|
||||
$files[] = "sitemap_profiles_" . $i;
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the sitemap
|
||||
*
|
||||
* @param string $filename The sitemap file to build (should be one returned from getFilenames())
|
||||
* @param \IPS\Sitemap $sitemap Sitemap object reference
|
||||
* @return void
|
||||
*/
|
||||
public function generateSitemap( $filename, $sitemap )
|
||||
{
|
||||
/* Which file are we building? */
|
||||
$_info = explode( '_', $filename );
|
||||
$index = array_pop( $_info ) - 1;
|
||||
$lastId = 0;
|
||||
$entries = array();
|
||||
$start = \IPS\Sitemap::MAX_PER_FILE * $index;
|
||||
$limit = \IPS\Sitemap::MAX_PER_FILE;
|
||||
|
||||
/* Have we already maxed out? We shouldn't really hit this because getFilenames() already factors in the max, but best to check */
|
||||
if( \IPS\Settings::i()->sitemap_profiles_count > -1 AND \IPS\Settings::i()->sitemap_profiles_count < $start )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do we need less than 10k? */
|
||||
if( \IPS\Settings::i()->sitemap_profiles_count > -1 AND \IPS\Settings::i()->sitemap_profiles_count < $start + $limit )
|
||||
{
|
||||
$limit = \IPS\Settings::i()->sitemap_profiles_count - $start;
|
||||
}
|
||||
|
||||
/* Create limit clause */
|
||||
$limitClause = array( $start, $limit );
|
||||
|
||||
/* Generate WHERE clause */
|
||||
$where = array();
|
||||
|
||||
if ( \IPS\Settings::i()->sitemap_profiles_content > 0 )
|
||||
{
|
||||
$where[] = array( 'member_posts >= ?', \IPS\Settings::i()->sitemap_profiles_content );
|
||||
}
|
||||
|
||||
/* Try to fetch the highest ID built in the last sitemap, if it exists */
|
||||
try
|
||||
{
|
||||
$lastId = \IPS\Db::i()->select( 'last_id', 'core_sitemap', array( array( 'sitemap=?', implode( '_', $_info ) . '_' . ( $index ) ) ) )->first();
|
||||
|
||||
if( $lastId > 0 )
|
||||
{
|
||||
$where[] = array( 'core_members.member_id > ?', $lastId );
|
||||
$limitClause = $limit;
|
||||
}
|
||||
}
|
||||
catch( \UnderflowException $e ){}
|
||||
|
||||
/* Retrieve the members */
|
||||
foreach( \IPS\Db::i()->select( 'members_seo_name, name, member_id', 'core_members', $where, 'member_id', $limitClause ) as $row )
|
||||
{
|
||||
$row['members_seo_name'] = $row['members_seo_name'] ?: \IPS\Http\Url\Friendly::seoTitle( $row['name'] );
|
||||
|
||||
$entry = array(
|
||||
'url' => \IPS\Http\Url::internal( "app=core&module=members&controller=profile&id={$row['member_id']}", 'front', 'profile', $row['members_seo_name'] )
|
||||
);
|
||||
|
||||
if( \IPS\Settings::i()->sitemap_profiles_priority > 0 )
|
||||
{
|
||||
$entry['priority'] = \IPS\Settings::i()->sitemap_profiles_priority;
|
||||
}
|
||||
|
||||
$entries[] = $entry;
|
||||
|
||||
$lastId = $row['member_id'];
|
||||
}
|
||||
|
||||
/* Build the file */
|
||||
$sitemap->buildSitemapFile( $filename, $entries, $lastId );
|
||||
|
||||
return $lastId;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user