Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+62 -5
View File
@@ -22,6 +22,16 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Application extends \IPS\Application
{
/**
* @brief Cached advertisement count
*/
protected $advertisements = NULL;
/**
* @brief Cached clubs pending approval count
*/
protected $clubs = NULL;
/**
* ACP Menu Numbers
*
@@ -34,9 +44,21 @@ class _Application extends \IPS\Application
switch ( $queryString['controller'] )
{
case 'advertisements':
return \IPS\Db::i()->select( 'COUNT(*)', 'core_advertisements', array( 'ad_active=-1' ) )->first();
break;
if( $this->advertisements === NULL )
{
$this->advertisements = \IPS\Db::i()->select( 'COUNT(*)', 'core_advertisements', array( 'ad_active=-1' ) )->first();
}
return $this->advertisements;
case 'clubs':
if( $this->clubs === NULL )
{
$this->clubs = \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs', array( 'approved=0' ) )->first();
}
return $this->clubs;
}
return 0;
}
/**
@@ -67,6 +89,18 @@ class _Application extends \IPS\Application
{
return 'cogs';
}
/**
* Install Other
*
* @return void
*/
public function installOther()
{
/* Save installed domain to spam defense whitelist */
$domain = rtrim( str_replace( 'www.', '', parse_url( \IPS\Settings::i()->base_url, PHP_URL_HOST ) ), '/' );
\IPS\Db::i()->insert( 'core_spam_whitelist', array( 'whitelist_type' => 'domain', 'whitelist_content' => $domain, 'whitelist_date' => time(), 'whitelist_reason' => 'Invision Community Domain' ) );
}
/**
* Can view page even when user is a guest when guests cannot access the site
@@ -81,7 +115,7 @@ class _Application extends \IPS\Application
return (
$module->key == 'system'
and
in_array( $controller, array( 'login', 'register', 'lostpass', 'terms', 'ajax', 'privacy', 'editor', 'language', 'theme' ) )
in_array( $controller, array( 'login', 'register', 'lostpass', 'terms', 'ajax', 'privacy', 'editor', 'language', 'theme', 'redirect', 'guidelines' ) )
)
or
(
@@ -111,7 +145,9 @@ class _Application extends \IPS\Application
) )
or
(
$controller === 'ajax' and $do === 'states' // Makes sure address input still works within the ACP otherwise the form to turn site back online is broken
$controller === 'ajax' and
( $do === 'states' OR // Makes sure address input still works within the ACP otherwise the form to turn site back online is broken
$do === 'passwordStrength' ) // Makes sure the password strength meter still works because it is used in the AdminCP and registration
)
)
);
@@ -224,7 +260,7 @@ class _Application extends \IPS\Application
/* redirect old messenger link */
if( isset( \IPS\Request::i()->module ) AND \IPS\Request::i()->module == 'global' AND isset( \IPS\Request::i()->controller ) AND (\IPS\Request::i()->controller == 'register' ) )
{
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=register', 'front', 'register', NULL, \IPS\Settings::i()->logins_over_https ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=system&controller=register', 'front', 'register' ) );
}
/* redirect old reports */
@@ -235,4 +271,25 @@ class _Application extends \IPS\Application
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=modcp&controller=modcp&tab=reports&action=view&id=' . \IPS\Request::i()->rid , 'front', 'modcp_report' ) );
}
}
/**
* Imports an IN_DEV email template into the database
*
* @param string $path Path to file
* @param object $file DirectoryIterator File Object
* @param string|null $namePrefix Name prefix
* @return array
*/
protected function _buildEmailTemplateFromInDev( $path, $file, $namePrefix='' )
{
$return = parent::_buildEmailTemplateFromInDev( $path, $file, $namePrefix );
/* Make sure that the email wrapper is pinned to the top of the list */
if( $file->getFilename() == 'emailWrapper.phtml' )
{
$return['template_pinned'] = 1;
}
return $return;
}
}
+8 -4
View File
@@ -26,7 +26,8 @@ class _groups extends \IPS\Api\Controller
* GET /core/groups
* Get list of groups
*
* @apiparam int page Page number
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\Member\Group>
*/
public function GETindex()
@@ -37,10 +38,12 @@ class _groups extends \IPS\Api\Controller
/* Return */
return new \IPS\Api\PaginatedResponse(
200,
\IPS\Db::i()->select( '*', 'core_groups', $where, "g_id asc", NULL, NULL, NULL, \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS ),
\IPS\Db::i()->select( '*', 'core_groups', $where, "g_id asc" ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
'IPS\Member\Group',
\IPS\Db::i()->select( 'COUNT(*)', 'core_groups', $where )->first()
\IPS\Db::i()->select( 'COUNT(*)', 'core_groups', $where )->first(),
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
);
}
@@ -62,7 +65,7 @@ class _groups extends \IPS\Api\Controller
throw new \OutOfRangeException;
}
return new \IPS\Api\Response( 200, $group->apiOutput() );
return new \IPS\Api\Response( 200, $group->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
@@ -74,6 +77,7 @@ class _groups extends \IPS\Api\Controller
* DELETE /core/groups/{id}
* Deletes a group
*
* @apiclientonly
* @param int $id ID Number
* @throws 1C358/2 INVALID_ID The group ID does not exist
* @return void
+57
View File
@@ -0,0 +1,57 @@
<?php
/**
* @brief Me API
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @since 3 Dec 2015
* @version SVN_VERSION_NUMBER
*/
namespace IPS\core\api;
/* 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 Me API
*/
class _me extends \IPS\Api\Controller
{
/**
* GET /core/me
* Get basic information about the authorized user
*
* @apimemberonly
* @return \IPS\Member
*/
public function GETindex()
{
$output = $this->member->apiOutput( $this->member );
if ( $this->canAccess( 'core', 'me', 'GETitem' ) )
{
$output['email'] = $this->member->email;
}
return new \IPS\Api\Response( 200, $output );
}
/**
* GET /core/me/email
* Get authorized user's email address
*
* @apimemberonly
* @return array
* @apiresponse string email Email address
*/
public function GETitem( $path )
{
return new \IPS\Api\Response( 200, array( 'email' => $this->member->email ) );
}
}
+368 -20
View File
@@ -26,15 +26,43 @@ class _members extends \IPS\Api\Controller
* GET /core/members
* Get list of members
*
* @apiparam string sortBy What to sort by. Can be 'joined', 'name' or leave unspecified for ID
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam int page Page number
* @apiparam string sortBy What to sort by. Can be 'joined', 'name' or leave unspecified for ID
* @apiparam string sortDir Sort direction. Can be 'asc' or 'desc' - defaults to 'asc'
* @apiparam string name (Partial) user name to search for
* @apiparam string email (Partial) Email address to search for
* @apiparam int|array group Group ID or IDs to search for
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\Member>
*/
public function GETindex()
{
/* Where clause */
$where = array();
$where = array( array( 'core_members.name<>?', '' ) );
/* Are we searching? */
if( isset( \IPS\Request::i()->name ) )
{
$where[] = array( "name LIKE CONCAT( '%', ?, '%' )", \IPS\Request::i()->name );
}
if( isset( \IPS\Request::i()->email ) )
{
$where[] = array( "email LIKE CONCAT( '%', ?, '%' )", \IPS\Request::i()->email );
}
if( isset( \IPS\Request::i()->group ) )
{
if( is_array( \IPS\Request::i()->group ) )
{
$groups = array_map( function( $value ){ return intval( $value ); }, \IPS\Request::i()->group );
$where[] = array( "(member_group_id IN(" . implode( ',', $groups ) . ") OR " . \IPS\Db::i()->findInSet( 'mgroup_others', $groups ) . ")" );
}
elseif( \IPS\Request::i()->group )
{
$where[] = array( "(member_group_id=" . intval( \IPS\Request::i()->group ) . ") OR (" . \IPS\Db::i()->findInSet( 'mgroup_others', array( intval( \IPS\Request::i()->group ) ) ) . ")" );
}
}
/* Sort */
$sortBy = ( isset( \IPS\Request::i()->sortBy ) and in_array( \IPS\Request::i()->sortBy, array( 'name', 'joined' ) ) ) ? \IPS\Request::i()->sortBy : 'member_id';
@@ -43,10 +71,12 @@ class _members extends \IPS\Api\Controller
/* Return */
return new \IPS\Api\PaginatedResponse(
200,
\IPS\Db::i()->select( '*', 'core_members', $where, "{$sortBy} {$sortDir}", NULL, NULL, NULL, \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS ),
\IPS\Db::i()->select( '*', 'core_members', $where, "{$sortBy} {$sortDir}" ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
'IPS\Member',
\IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where )->first()
\IPS\Db::i()->select( 'COUNT(*)', 'core_members', $where )->first(),
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
);
}
@@ -55,6 +85,7 @@ class _members extends \IPS\Api\Controller
* Get information about a specific member
*
* @param int $id ID Number
* @apiparam array otherFields An array of additional non-standard fields to return via the REST API
* @throws 1C292/2 INVALID_ID The member ID does not exist
* @return \IPS\Member
*/
@@ -68,7 +99,7 @@ class _members extends \IPS\Api\Controller
throw new \OutOfRangeException;
}
return new \IPS\Api\Response( 200, $member->apiOutput() );
return new \IPS\Api\Response( 200, $member->apiOutput( $this->member, ( isset( \IPS\Request::i()->otherFields ) ) ? \IPS\Request::i()->otherFields : NULL ) );
}
catch ( \OutOfRangeException $e )
{
@@ -79,8 +110,7 @@ class _members extends \IPS\Api\Controller
/**
* Create or update member
*
* @param \IPS\Member $member The member
* @apiparam int group Group ID number
* @param \IPS\Member $member The member
* @throws 1C292/4 USERNAME_EXISTS The username provided is already in use
* @throws 1C292/5 EMAIL_EXISTS The email address provided is already in use
* @throws 1C292/6 INVALID_GROUP The group ID provided is not valid
@@ -92,7 +122,8 @@ class _members extends \IPS\Api\Controller
{
$existingUsername = \IPS\Member::load( \IPS\Request::i()->name, 'name' );
if ( !$existingUsername->member_id )
{
{
$member->logHistory( 'core', 'display_name', array( 'old' => $member->name, 'new' => \IPS\Request::i()->name, 'by' => 'api' ) );
$member->name = \IPS\Request::i()->name;
}
else
@@ -106,6 +137,7 @@ class _members extends \IPS\Api\Controller
$existingEmail = \IPS\Member::load( \IPS\Request::i()->email, 'email' );
if ( !$existingEmail->member_id )
{
$member->logHistory( 'core', 'email_change', array( 'old' => $member->name, 'new' => \IPS\Request::i()->name, 'by' => 'api' ) );
$member->email = \IPS\Request::i()->email;
$member->invalidateSessionsAndLogins();
}
@@ -128,16 +160,57 @@ class _members extends \IPS\Api\Controller
}
}
if ( isset( \IPS\Request::i()->password ) )
if( isset( \IPS\Request::i()->secondaryGroups ) AND is_array( \IPS\Request::i()->secondaryGroups ) )
{
foreach ( \IPS\Login::handlers( TRUE ) as $handler )
foreach( \IPS\Request::i()->secondaryGroups as $groupId )
{
try
{
$handler->changePassword( $member, \IPS\Request::i()->password );
$group = \IPS\Member\Group::load( $groupId );
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_GROUP', '1C292/7', 403 );
}
catch( \BadMethodCallException $e ){}
}
$member->mgroup_others = implode( ',', \IPS\Request::i()->secondaryGroups );
}
elseif( isset( \IPS\Request::i()->secondaryGroups ) AND \IPS\Request::i()->secondaryGroups == '' )
{
$member->mgroup_others = '';
}
if( isset( \IPS\Request::i()->registrationIpAddress ) AND filter_var( \IPS\Request::i()->registrationIpAddress, FILTER_VALIDATE_IP ) )
{
$member->ip_address = \IPS\Request::i()->registrationIpAddress;
}
if( isset( \IPS\Request::i()->rawProperties ) AND is_array( \IPS\Request::i()->rawProperties ) )
{
foreach( \IPS\Request::i()->rawProperties as $property => $value )
{
$member->$property = $value;
}
}
if ( isset( \IPS\Request::i()->password ) )
{
/* Setting the password for the just created member shouldn't be logged to the member history and shouldn't fire the onPassChange Sync call */
$logPasswordChange = TRUE;
if ( $member->member_id )
{
$logPasswordChange = FALSE;
}
$member->setLocalPassword( \IPS\Request::i()->password );
$member->save();
if ( $logPasswordChange )
{
$member->memberSync( 'onPassChange', array( \IPS\Request::i()->password ) );
$member->logHistory( 'core', 'password_change', 'api' );
}
$member->invalidateSessionsAndLogins();
}
else
@@ -145,6 +218,21 @@ class _members extends \IPS\Api\Controller
$member->save();
}
/* Validation stuff */
if( isset( \IPS\Request::i()->validated ) )
{
/* If the member is currently validating and we are setting the validated flag to true, then complete the validation */
if( \IPS\Request::i()->validated == 1 AND $member->members_bitoptions['validating'] )
{
$member->validationComplete();
}
/* If the member is not currently validating, and we set the validated flag to false AND validation is enabled, mark the member validating */
elseif( \IPS\Request::i()->validated == 0 AND !$member->members_bitoptions['validating'] AND \IPS\Settings::i()->reg_auth_type != 'none' )
{
$member->postRegistration();
}
}
/* Any custom fields? */
if( isset( \IPS\Request::i()->customFields ) )
{
@@ -182,16 +270,22 @@ class _members extends \IPS\Api\Controller
/**
* POST /core/members
* Create a member
* Create a member. Requires the standard login handler to be enabled
*
* @apiclientonly
* @apiparam string name Username
* @apiparam string email Email address
* @apiparam string password Password
* @apiparam string password Password (standard login handler only)
* @apiparam int group Group ID number
* @apiparam string registrationIpAddress IP Address
* @apiparam array secondaryGroups Secondary group IDs, or empty value to reset secondary groups
* @apiparam object customFields Array of custom fields as fieldId => fieldValue
* @apiparam int validated Flag to indicate if the account is validated (1) or not (0)
* @apiparam array rawProperties Key => value object of member properties to set. Note that values will be set exactly as supplied without validation. USE AT YOUR OWN RISK.
* @throws 1C292/4 USERNAME_EXISTS The username provided is already in use
* @throws 1C292/5 EMAIL_EXISTS The email address provided is already in use
* @throws 1C292/6 INVALID_GROUP The group ID provided is not valid
* @throws 1C292/7 INVALID_GROUP A secondary group ID provided is not valid
* @throws 1C292/8 NO_USERNAME_OR_EMAIL No Username or Email Address was provided for the account
* @throws 1C292/9 NO_PASSWORD No password was provided for the account
* @return \IPS\Member
@@ -215,22 +309,29 @@ class _members extends \IPS\Api\Controller
$member = $this->_createOrUpdate( $member );
return new \IPS\Api\Response( 201, $member->apiOutput() );
return new \IPS\Api\Response( 201, $member->apiOutput( $this->member ) );
}
/**
* POST /core/members/{id}
* Edit a member
*
* @apiclientonly
* @apiparam string name Username
* @apiparam string email Email address
* @apiparam string password Password
* @apiparam string password Password (standard login handler only)
* @apiparam int group Group ID number
* @apiparam string registrationIpAddress IP Address
* @apiparam array secondaryGroups Secondary group IDs, or empty value to reset secondary groups
* @apiparam object customFields Array of custom fields as fieldId => fieldValue
* @apiparam int validated Flag to indicate if the account is validated (1) or not (0)
* @apiparam array rawProperties Key => value object of member properties to set. Note that values will be set exactly as supplied without validation. USE AT YOUR OWN RISK.
* @param int $id ID Number
* @throws 2C292/7 INVALID_ID The member ID does not exist
* @throws 1C292/4 USERNAME_EXISTS The username provided is already in use
* @throws 1C292/5 EMAIL_EXISTS The email address provided is already in use
* @throws 1C292/6 INVALID_GROUP The group ID provided is not valid
* @throws 1C292/7 INVALID_GROUP A secondary group ID provided is not valid
* @return \IPS\Member
*/
public function POSTitem( $id )
@@ -242,10 +343,22 @@ class _members extends \IPS\Api\Controller
{
throw new \OutOfRangeException;
}
$oldPrimaryGroup = $member->member_group_id;
$oldSecondaryGroups = array_unique( array_filter( explode( ',', $member->mgroup_others ) ) );
$member = $this->_createOrUpdate( $member );
if ( $oldPrimaryGroup != $member->member_group_id )
{
$member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'api', 'apiKey' => $this->apiKey ? $this->apiKey->id : NULL, 'client' => $this->client ? $this->client->client_id : NULL, 'old' => $oldPrimaryGroup, 'new' => $member->member_group_id ), $this->member ?: FALSE );
}
$newSecondaryGroups = array_unique( array_filter( explode( ',', $member->mgroup_others ) ) );
if ( array_diff( $oldSecondaryGroups, $newSecondaryGroups ) or array_diff( $newSecondaryGroups, $oldSecondaryGroups ) )
{
$member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'api', 'apiKey' => $this->apiKey ? $this->apiKey->id : NULL, 'client' => $this->client ? $this->client->client_id : NULL, 'old' => $oldSecondaryGroups, 'new' => $newSecondaryGroups ), $this->member ?: FALSE );
}
return new \IPS\Api\Response( 200, $member->apiOutput() );
return new \IPS\Api\Response( 200, $member->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
@@ -257,6 +370,7 @@ class _members extends \IPS\Api\Controller
* DELETE /core/members/{id}
* Deletes a member
*
* @apiclientonly
* @param int $id ID Number
* @throws 1C292/2 INVALID_ID The member ID does not exist
* @return void
@@ -280,4 +394,238 @@ class _members extends \IPS\Api\Controller
throw new \IPS\Api\Exception( 'INVALID_ID', '1C292/2', 404 );
}
}
/**
* GET /core/members/{id}/follows
* Get list of items a member is following
*
* @param int $id ID Number
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @return \IPS\Api\PaginatedResponse<IPS\core\Followed\Follow>
* @throws 2C292/F NO_PERMISSION The authorized user does not have permission to view the follows
* @throws 2C292/I INVALID_ID The member could not be found
*/
public function GETitem_follows( $id )
{
try
{
/* Load member */
$member = \IPS\Member::load( $id );
if( !$member->member_id )
{
throw new \OutOfRangeException;
}
/* We can only adjust follows for ourself, if we are an authorized member */
if ( $this->member and $member->member_id != $this->member->member_id )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2C292/F', 403 );
}
/* Return */
return new \IPS\Api\PaginatedResponse(
200,
\IPS\Db::i()->select( '*', 'core_follow', array( 'follow_member_id=?', $member->member_id ), "follow_added ASC" ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
'IPS\core\Followed\Follow',
\IPS\Db::i()->select( 'COUNT(*)', 'core_follow', array( 'follow_member_id=?', $member->member_id ) )->first(),
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
);
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '2C292/I', 404 );
}
}
/**
* POST /core/members/{id}/follows
* Store a new follow for the member
*
* @param int $id ID Number
* @reqapiparam string followApp Application of the content to follow
* @reqapiparam string followArea Area of the content to follow
* @reqapiparam int followId ID of the content to follow
* @apiparam bool followAnon Whether or not to follow anonymously
* @apiparam bool followNotify Whether or not to receive notifications
* @apiparam string followType Type of notification to receive (immediate=send a notification immediately, daily=daily notification digest, weekly=weekly notification digest)
* @return \IPS\core\Followed\Follow
* @throws 2C292/G NO_PERMISSION The authorized user does not have permission to view the follows
* @throws 2C292/H INVALID_ID The member could not be found
* @throws 2C292/J INVALID_CONTENT The app, area or content ID could not be found
*/
public function POSTitem_follows( $id )
{
try
{
/* Load member */
$member = \IPS\Member::load( $id );
if( !$member->member_id )
{
throw new \OutOfRangeException;
}
/* We can only adjust follows for ourself, if we are an authorized member */
if ( $this->member and $member->member_id != $this->member->member_id )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2C292/G', 403 );
}
/* Make sure follow app/area/id is valid (Phil I'm looking at you) */
try
{
$classToFollow = 'IPS\\' . \IPS\Request::i()->followApp . '\\' . mb_ucfirst( \IPS\Request::i()->followArea );
if( !class_exists( $classToFollow ) )
{
throw new \OutOfRangeException;
}
$thingToFollow = $classToFollow::load( \IPS\Request::i()->followId );
}
catch( \Exception $e )
{
throw new \IPS\Api\Exception( 'INVALID_CONTENT', '2C292/J', 404 );
}
/* If we are already following this, update instead of insert */
try
{
$follow = \IPS\core\Followed\Follow::load( md5( \IPS\Request::i()->followApp . ';' . \IPS\Request::i()->followArea . ';' . \IPS\Request::i()->followId . ';' . $member->member_id ) );
}
catch( \OutOfRangeException $e )
{
$follow = new \IPS\core\Followed\Follow;
$follow->member_id = $member->member_id;
$follow->app = \IPS\Request::i()->followApp;
$follow->area = \IPS\Request::i()->followArea;
$follow->rel_id = \IPS\Request::i()->followId;
}
$follow->is_anon = ( isset( \IPS\Request::i()->followAnon ) ) ? (int) \IPS\Request::i()->followAnon : 0;
$follow->notify_do = ( isset( \IPS\Request::i()->followType ) AND \IPS\Request::i()->followType == 'none' ) ? 0 : ( ( isset( \IPS\Request::i()->followNotify ) ) ? (int) \IPS\Request::i()->followNotify : 1 );
$follow->notify_freq = ( isset( \IPS\Request::i()->followType ) AND in_array( \IPS\Request::i()->followType, array( 'none', 'immediate', 'daily', 'weekly' ) ) ) ? \IPS\Request::i()->followType : 'immediate';
$follow->save();
/* If we're following a club, follow all nodes in the club automatically */
if( $follow->app == 'core' and $follow->area == 'club' )
{
$thing = \IPS\Member\Club::loadAndCheckPerms( $follow->rel_id );
foreach ( $thing->nodes() as $node )
{
$itemClass = $node['node_class']::$contentItemClass;
$followApp = $itemClass::$application;
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
/* If we are already following this, update instead of insert */
try
{
$nodeFollow = \IPS\core\Followed\Follow::load( md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . $member->member_id ) );
}
catch( \OutOfRangeException $e )
{
$nodeFollow = new \IPS\core\Followed\Follow;
$nodeFollow->member_id = $member->member_id;
$nodeFollow->app = $followApp;
$nodeFollow->area = $followArea;
$nodeFollow->rel_id = $node['node_id'];
}
$nodeFollow->is_anon = $follow->is_anon;
$nodeFollow->notify_do = $follow->notify_do;
$nodeFollow->notify_freq = $follow->notify_freq;
$nodeFollow->save();
}
}
return new \IPS\Api\Response( 200, $follow->apiOutput( $this->member ) );
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '2C292/H', 404 );
}
}
/**
* DELETE /core/members/{id}/follows
* Delete a follow for the member
*
* @param int $id ID Number
* @apiparam string followKey Follow Key
* @throws 2C292/C INVALID_ID The member could not be found
* @throws 2C292/E INVALID_FOLLOW_KEY The follow does not exist or does not belong to this member
* @throws 2C292/D NO_PERMISSION The authorized user does not have permission to delete the follow
* @return void
*/
public function DELETEitem_follows( $id )
{
try
{
/* Load member */
$member = \IPS\Member::load( $id );
if( !$member->member_id )
{
throw new \OutOfRangeException;
}
/* We can only adjust follows for ourself, if we are an authorized member */
if ( $this->member and $member->member_id != $this->member->member_id )
{
throw new \IPS\Api\Exception( 'NO_PERMISSION', '2C292/D', 403 );
}
/* Load our follow, and make sure it belongs to the specified member */
try
{
if( !isset( \IPS\Request::i()->followKey ) )
{
throw new \UnderflowException;
}
$follow = \IPS\Db::i()->select( '*', 'core_follow', array( 'follow_id=?', \IPS\Request::i()->followKey ) )->first();
if( $follow['follow_member_id'] != $member->member_id )
{
throw new \UnderflowException;
}
}
catch( \UnderflowException $e )
{
throw new \IPS\Api\Exception( 'INVALID_FOLLOW_KEY', '2C292/E', 404 );
}
/* Unfollow */
\IPS\Db::i()->delete( 'core_follow', array( 'follow_id=?', \IPS\Request::i()->followKey ) );
/* If this is a club, unfollow all nodes in the club too */
if( $follow['follow_app'] == 'core' AND $follow['follow_area'] == 'club' )
{
$class = 'IPS\Member\Club';
try
{
$thing = $class::loadAndCheckPerms( $follow['follow_rel_id'] );
foreach ( $thing->nodes() as $node )
{
$itemClass = $node['node_class']::$contentItemClass;
$followApp = $itemClass::$application;
$followArea = mb_strtolower( mb_substr( $node['node_class'], mb_strrpos( $node['node_class'], '\\' ) + 1 ) );
\IPS\Db::i()->delete( 'core_follow', array( 'follow_id=? AND follow_member_id=?', md5( $followApp . ';' . $followArea . ';' . $node['node_id'] . ';' . $member->member_id ), $member->member_id ) );
}
}
catch ( \OutOfRangeException $e ){}
}
return new \IPS\Api\Response( 200, NULL );
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '2C292/C', 404 );
}
}
}
+165
View File
@@ -0,0 +1,165 @@
<?php
/**
* @brief Personal Conversations API
* @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 1 Dec 2017
* @note We intentionally have not added any way to fetch messages to match the built in privacy functionality
*/
namespace IPS\core\api;
/* 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 Personal Conversations API
*/
class _messages extends \IPS\Api\Controller
{
/**
* POST /core/messages
* Create a new personal conversation
*
* @apiclientonly
* @apiparam int from User ID conversation is from
* @apiparam array to One or more user IDs conversation is sent to
* @apiparam string title Conversation title
* @apiparam string body Conversation body
* @throws 1C374/2 INVALID_SENDER Sender was not supplied or is invalid
* @throws 1C374/3 INVALID_RECIPIENT No recipients were supplied
* @throws 1C374/4 INVALID_RECIPIENT One or more recipients are invalid
* @throws 1C374/5 MISSING_TITLE_OR_BODY The title and/or body of the conversation were not supplied
* @return int Conversation ID
*/
public function POSTindex()
{
/* Make sure there is a valid sender */
if ( !isset( \IPS\Request::i()->from ) OR !\IPS\Member::load( (int) \IPS\Request::i()->from )->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_SENDER', '1C374/2', 404 );
}
/* Verify there are recipients and all the recipients are valid */
if( !isset( \IPS\Request::i()->to ) OR !is_array( \IPS\Request::i()->to ) OR !count( \IPS\Request::i()->to ) )
{
throw new \IPS\Api\Exception( 'INVALID_RECIPIENT', '1C374/3', 404 );
}
else
{
foreach( \IPS\Request::i()->to as $to )
{
if( !\IPS\Member::load( (int) $to )->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_RECIPIENT', '1C374/4', 404 );
}
}
}
/* Make sure we have a title and body */
if( !isset( \IPS\Request::i()->title ) OR !isset( \IPS\Request::i()->body ) )
{
throw new \IPS\Api\Exception( 'MISSING_TITLE_OR_BODY', '1C374/5', 404 );
}
/* Create the conversation */
$item = \IPS\core\Messenger\Conversation::createItem( \IPS\Member::load( (int) \IPS\Request::i()->from ), \IPS\Request::i()->ipAddress(), \IPS\DateTime::create(), NULL );
$item->title = \IPS\Request::i()->title;
$item->to_count = count( \IPS\Request::i()->to );
$item->save();
/* Create the first message */
$postContents = \IPS\Text\Parser::parseStatic( \IPS\Request::i()->body, TRUE, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), 'core_Messaging' );
$commentClass = $item::$commentClass;
$post = $commentClass::create( $item, $postContents, TRUE, NULL, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), \IPS\DateTime::create() );
$item->first_msg_id = $post->id;
$item->save();
/* Authorize sender and recipients */
$item->authorize( array_map( function( $member ) { return (int) $member; }, array_merge( array( \IPS\Request::i()->from ), \IPS\Request::i()->to ) ) );
/* Send notifications */
$post->sendNotifications();
return new \IPS\Api\Response( 201, $item->id );
}
/**
* POST /core/messages/{id}
* Add a reply to a personal conversation
*
* @apiclientonly
* @apiparam string body Message body
* @apiparam int from Person responding to message (must be part of conversation)
* @param int $id ID Number
* @throws 1C374/6 INVALID_ID The personal conversation ID does not exist
* @throws 1C374/7 INVALID_SENDER The sender ID supplied was not valid
* @throws 1C374/8 SENDER_NO_PERMISSON The sender supplied does not have permmission to reply to the conversation
* @return bool
*/
public function POSTitem( $id )
{
try
{
$message = \IPS\core\Messenger\Conversation::load( $id );
/* Make sure we have a member, and the member is authorized to reply */
if( !isset( \IPS\Request::i()->from ) OR !\IPS\Member::load( (int) \IPS\Request::i()->from )->member_id )
{
throw new \IPS\Api\Exception( 'INVALID_SENDER', '1C374/7', 404 );
}
if( !$message->canView( \IPS\Member::load( (int) \IPS\Request::i()->from ) ) )
{
throw new \IPS\Api\Exception( 'SENDER_NO_PERMISSON', '1C374/8', 403 );
}
/* Create the reply */
$postContents = \IPS\Text\Parser::parseStatic( \IPS\Request::i()->body, TRUE, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), 'core_Messaging' );
$commentClass = $message::$commentClass;
$post = $commentClass::create( $message, $postContents, TRUE, NULL, NULL, \IPS\Member::load( (int) \IPS\Request::i()->from ), \IPS\DateTime::create() );
/* Send notifications */
$post->sendNotifications();
return new \IPS\Api\Response( 200, TRUE );
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C374/6', 404 );
}
}
/**
* DELETE /core/messages/{id}
* Deletes a personal conversation
*
* @apiclientonly
* @param int $id ID Number
* @throws 1C292/2 INVALID_ID The personal conversation ID does not exist
* @return void
*/
public function DELETEitem( $id )
{
try
{
$message = \IPS\core\Messenger\Conversation::load( $id );
$message->delete();
return new \IPS\Api\Response( 200, NULL );
}
catch ( \OutOfRangeException $e )
{
throw new \IPS\Api\Exception( 'INVALID_ID', '1C374/1', 404 );
}
}
}
+212
View File
@@ -0,0 +1,212 @@
<?php
/**
* @brief Search API
* @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 Oct 2017
*/
namespace IPS\core\api;
/* 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 Search API
*/
class _search extends \IPS\Api\Controller
{
/**
* GET /core/search
* Perform a search and get a list of results
*
* @apiparam int page Page number
* @apiparam int perPage Number of results per page - defaults to 25
* @apiparam string q String to search for
* @apiparam string tags Comma-separated list of tags to search for
* @apiparam string type Content type class to restrict searches to
* @apiparam int item Restrict searches to comments or reviews made to the specified item
* @apiparam string nodes Comma-separated list of node IDs to restrict searches to
* @apiparam int search_min_comments Minimum number of comments search results must have for content types that support comments
* @apiparam int search_min_replies Minimum number of comments search results must have for content types that require the first comment (e.g. topics)
* @apiparam int search_min_reviews Minimum number of reviews search results must have
* @apiparam int search_min_views Minimum number of views search results must have
* @apiparam string author Restrict searches to results posted by this member (name)
* @apiparam string club Comma-separated list of club IDs to restrict searches to
* @apiparam string start_before Date period (from current time) that search results should start before
* @apiparam string start_after Date period (from current time) that search results should start after
* @apiparam string updated_before Date period (from current time) that search results should last be updated before
* @apiparam string updated_after Date period (from current time) that search results should last be updated after
* @apiparam string sortby Sort by method (newest or relevancy)
* @apiparam string eitherTermsOrTags Whether to search both tags and search term ("and") or either ("or")
* @apiparam string search_and_or Whether to perform an "and" search or an "or" search for all search terms
* @apiparam string search_in Specify "titles" to search in titles only, otherwise titles and content are both searched
* @return array
* @apiresponse int page api_int_page
* @apiresponse int perPage api_int_perpage
* @apiresponse int totalResults api_int_totalresults
* @apiresponse int totalPages api_int_totalpages
* @apiresponse [\IPS\Content\Search\Result] results api_results_thispage
*/
public function GETindex()
{
/* Get valid content types */
$contentTypes = \IPS\core\modules\front\search\search::contentTypes();
/* Initialize search */
$query = \IPS\Content\Search\Query::init();
/* Set content type */
if ( isset( \IPS\Request::i()->type ) and array_key_exists( \IPS\Request::i()->type, $contentTypes ) )
{
if ( isset( \IPS\Request::i()->item ) )
{
$class = $contentTypes[ \IPS\Request::i()->type ];
try
{
$item = $class::loadAndCheckPerms( \IPS\Request::i()->item );
$query->filterByContent( array( \IPS\Content\Search\ContentFilter::init( $class )->onlyInItems( array( \IPS\Request::i()->item ) ) ) );
}
catch ( \OutOfRangeException $e ) { }
}
else
{
$filter = \IPS\Content\Search\ContentFilter::init( $contentTypes[ \IPS\Request::i()->type ] );
if ( isset( \IPS\Request::i()->nodes ) )
{
$filter->onlyInContainers( explode( ',', \IPS\Request::i()->nodes ) );
}
if ( isset( \IPS\Request::i()->search_min_comments ) )
{
$filter->minimumComments( \IPS\Request::i()->search_min_comments );
}
if ( isset( \IPS\Request::i()->search_min_replies ) )
{
$filter->minimumComments( \IPS\Request::i()->search_min_replies + 1 );
}
if ( isset( \IPS\Request::i()->search_min_reviews ) )
{
$filter->minimumReviews( \IPS\Request::i()->search_min_reviews );
}
if ( isset( \IPS\Request::i()->search_min_views ) )
{
$filter->minimumViews( \IPS\Request::i()->search_min_views );
}
$query->filterByContent( array( $filter ) );
}
}
/* Filter by author */
if ( isset( \IPS\Request::i()->author ) )
{
$author = \IPS\Member::load( \IPS\Request::i()->author, 'name' );
if ( $author->member_id )
{
$query->filterByAuthor( $author );
}
}
/* Filter by club */
if ( isset( \IPS\Request::i()->club ) AND \IPS\Settings::i()->clubs )
{
$query->filterByClub( explode( ',', \IPS\Request::i()->club ) );
}
/* Set time cutoffs */
foreach ( array( 'start' => 'filterByCreateDate', 'updated' => 'filterByLastUpdatedDate' ) as $k => $method )
{
$beforeKey = "{$k}_before";
$afterKey = "{$k}_after";
if ( isset( \IPS\Request::i()->$beforeKey ) or isset( \IPS\Request::i()->$afterKey ) )
{
foreach ( array( 'before', 'after' ) as $l )
{
$$l = NULL;
$key = "{$l}Key";
if ( isset( \IPS\Request::i()->$$key ) AND \IPS\Request::i()->$$key != 'any' )
{
switch ( \IPS\Request::i()->$$key )
{
case 'day':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1D' ) );
break;
case 'week':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1W' ) );
break;
case 'month':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1M' ) );
break;
case 'six_months':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P6M' ) );
break;
case 'year':
$$l = \IPS\DateTime::create()->sub( new \DateInterval( 'P1Y' ) );
break;
default:
$$l = \IPS\DateTime::ts( \IPS\Request::i()->$$key );
break;
}
}
}
$query->$method( $after, $before );
}
}
/* Set Order */
if ( ! isset( \IPS\Request::i()->sortby ) )
{
\IPS\Request::i()->sortby = $query->getDefaultSortMethod();
}
switch( \IPS\Request::i()->sortby )
{
case 'newest':
$query->setOrder( \IPS\Content\Search\Query::ORDER_NEWEST_CREATED );
break;
case 'relevancy':
$query->setOrder( \IPS\Content\Search\Query::ORDER_RELEVANCY );
break;
}
$flags = ( isset( \IPS\Request::i()->eitherTermsOrTags ) and \IPS\Request::i()->eitherTermsOrTags === 'and' ) ? \IPS\Content\Search\Query::TERM_AND_TAGS : \IPS\Content\Search\Query::TERM_OR_TAGS;
$operator = NULL;
if ( isset( \IPS\Request::i()->search_and_or ) and in_array( \IPS\Request::i()->search_and_or, array( \IPS\Content\Search\Query::OPERATOR_OR, \IPS\Content\Search\Query::OPERATOR_AND ) ) )
{
$operator = \IPS\Request::i()->search_and_or;
}
if ( isset( \IPS\Request::i()->search_in ) and \IPS\Request::i()->search_in === 'titles' )
{
$flags = $flags | \IPS\Content\Search\Query::TERM_TITLES_ONLY;
}
/* Return */
return new \IPS\Content\Search\ApiResponse(
200,
array( $query, $flags, isset( \IPS\Request::i()->q ) ? ( \IPS\Request::i()->q ) : NULL, isset( \IPS\Request::i()->tags ) ? explode( ',', \IPS\Request::i()->tags ) : NULL, $operator ),
isset( \IPS\Request::i()->page ) ? \IPS\Request::i()->page : 1,
NULL,
0,
$this->member,
isset( \IPS\Request::i()->perPage ) ? \IPS\Request::i()->perPage : NULL
);
}
}
+61 -29
View File
@@ -6,12 +6,6 @@
"do": "",
"restriction": "view_dashboard"
},
"security": {
"tab": "core",
"controller": "security",
"do": "",
"restriction": "security_manage"
},
"files": {
"tab": "core",
"controller": "files",
@@ -48,7 +42,7 @@
"tab": "core",
"controller": "api",
"do": "",
"restriction": "api_manage"
"restriction": "api_splash"
}
},
"settings": {
@@ -86,7 +80,7 @@
"tab": "core",
"controller": "login",
"do": "",
"restriction": "login_manage"
"restriction": "login_access"
},
"contactus": {
"tab": "core",
@@ -186,18 +180,6 @@
}
},
"membersettings": {
"registration": {
"tab": "members",
"controller": "registration",
"do": "",
"restriction": "registration_manage"
},
"spam": {
"tab": "members",
"controller": "spam",
"do": "",
"restriction": "spam_manage"
},
"profiles": {
"tab": "members",
"controller": "profiles",
@@ -210,12 +192,6 @@
"do": "",
"restriction": "reps_manage"
},
"warnings": {
"tab": "members",
"controller": "warnings",
"do": "",
"restriction": "warn_settings"
},
"notifications": {
"tab": "members",
"controller": "notifications",
@@ -235,6 +211,26 @@
"restriction": "ranks_manage"
}
},
"moderation": {
"reportedContent": {
"tab": "members",
"controller": "reportedContent",
"do": "",
"restriction": "reportedContent_manage"
},
"spam": {
"tab": "members",
"controller": "spam",
"do": "",
"restriction": "spam_manage"
},
"warnings": {
"tab": "members",
"controller": "warnings",
"do": "",
"restriction": "warn_settings"
}
},
"staff": {
"moderators": {
"tab": "members",
@@ -327,25 +323,49 @@
"do": "",
"restriction": "membervisits_manage"
},
"membersactivity": {
"tab": "stats",
"controller": "membersactivity",
"do": "",
"restriction": "membersactivity_manage"
},
"onlineusers": {
"tab": "stats",
"controller": "onlineusers",
"do": "",
"restriction": "onlineusers_manage"
},
"deviceusage": {
"tab": "stats",
"controller": "deviceusage",
"do": "",
"restriction": "deviceusage_manage"
}
},
"activitystats": {
"memberactivity": {
"communityactivity": {
"tab": "stats",
"controller": "memberactivity",
"controller": "communityactivity",
"do": "",
"restriction": "memberactivity_manage"
"restriction": "communityactivity_manage"
},
"keywords": {
"tab": "stats",
"controller": "keywords",
"do": "",
"restriction": "keywords_manage"
},
"reactions": {
"tab": "stats",
"controller": "reactions",
"do": "",
"restriction": "reactions_manage"
},
"tags": {
"tab": "stats",
"controller": "tags",
"do": "",
"restriction": "tags_manage"
}
},
"messengerstats": {
@@ -363,6 +383,18 @@
"controller": "clubs",
"do": "",
"restriction": "clubs_manage"
},
"fields": {
"tab": "community",
"controller": "fields",
"do": "",
"restriction": "fields_manage"
},
"settings": {
"tab": "community",
"controller": "settings",
"do": "",
"restriction": "clubs_settings_manage"
}
}
}
+61 -32
View File
@@ -19,6 +19,13 @@
"menu_manage": "menu_manage"
},
"api": {
"api_splash": "api_splash",
"oauth_manage": "oauth_manage",
"oauth_add": "oauth_add",
"oauth_edit": "oauth_edit",
"oauth_delete": "oauth_delete",
"oauth_tokens": "oauth_tokens",
"oauth_secrets": "oauth_secrets",
"api_manage": "api_manage",
"api_add": "api_add",
"api_edit": "api_edit",
@@ -52,7 +59,11 @@
},
"settings": {
"login": {
"login_manage": "login_manage"
"login_access": "login_access",
"login_manage": "login_manage",
"login_settings": "login_settings",
"login_account_management": "login_account_management",
"registration_settings": "registration_settings"
},
"email": {
"email_manage": "email_manage",
@@ -72,9 +83,6 @@
"files": {
"files_manage": "files_manage"
},
"security": {
"security_manage": "security_manage"
},
"posting": {
"posting_manage": "posting_manage",
"posting_manage_profanity": "posting_manage_profanity",
@@ -173,31 +181,9 @@
"ban": {
"ban_manage": "ban_manage"
},
"spam": {
"spam_manage": "spam_manage",
"spam_service_log": "spam_service_log",
"qanda_manage": "qanda_manage",
"qanda_add": "qanda_add",
"qanda_edit": "qanda_edit",
"qanda_delete": "qanda_delete"
},
"notifications": {
"notifications_manage": "notifications_manage"
},
"warnings": {
"warn_settings": "warn_settings",
"reasons_view": "reasons_view",
"reasons_edit": "reasons_edit",
"reasons_add": "reasons_add",
"reasons_delete": "reasons_delete",
"actions_view": "actions_view",
"actions_edit": "actions_edit",
"actions_add": "actions_add",
"actions_delete": "actions_delete"
},
"registration": {
"registration_manage": "registration_manage"
},
"reactions": {
"reactions_manage": "reactions_manage"
}
@@ -285,9 +271,6 @@
"dashboard": {
"view_dashboard": "view_dashboard"
},
"security": {
"security_manage": "security_manage"
},
"files": {
"files_view": "files_view",
"files_delete": "files_delete",
@@ -324,14 +307,60 @@
"membervisits": {
"membervisits_manage": "membervisits_manage"
},
"memberactivity": {
"memberactivity_manage": "memberactivity_manage"
},
"onlineusers": {
"onlineusers_manage": "onlineusers_manage"
},
"membersactivity": {
"membersactivity_manage": "membersactivity_manage"
},
"deviceusage": {
"deviceusage_manage": "deviceusage_manage"
}
},
"system": [],
"moderation": {
"reportedContent": {
"reportedContent_manage": "reportedContent_manage"
},
"spam": {
"spam_manage": "spam_manage",
"spam_service_log": "spam_service_log",
"qanda_manage": "qanda_manage",
"qanda_add": "qanda_add",
"qanda_edit": "qanda_edit",
"qanda_delete": "qanda_delete",
"spam_whitelist_manage": "spam_whitelist_manage",
"spam_whitelist_add": "spam_whitelist_add",
"spam_whitelist_edit": "spam_whitelist_edit",
"spam_whitelist_delete": "spam_whitelist_delete"
},
"warnings": {
"warn_settings": "warn_settings",
"reasons_view": "reasons_view",
"reasons_edit": "reasons_edit",
"reasons_add": "reasons_add",
"reasons_delete": "reasons_delete",
"actions_view": "actions_view",
"actions_edit": "actions_edit",
"actions_add": "actions_add",
"actions_delete": "actions_delete"
},
"reportedContentTypes": {
"reportedContentTypes_manage": "reportedContentTypes_manage"
}
},
"activitystats": {
"communityactivity": {
"communityactivity_manage": "communityactivity_manage"
},
"keywords": {
"keywords_manage": "keywords_manage"
},
"reactions": {
"reactionsstats_manage": "reactionsstats_manage"
},
"tagsusage": {
"tagsusage_manage": "tagsusage_manage"
}
}
}
+227 -197
View File
@@ -1,4 +1,15 @@
{
"app=core&module=activitystats&controller=communityactivity": {
"lang_key": "menu__core_activitystats_communityactivity",
"restriction": null,
"keywords": [
"active members",
"activity",
"community activity",
"member activity",
"new posts"
]
},
"app=core&module=activitystats&controller=keywords": {
"lang_key": "menu__core_activitystats_keywords",
"restriction": null,
@@ -8,15 +19,6 @@
"tracking"
]
},
"app=core&module=activitystats&controller=memberactivity": {
"lang_key": "menu__core_activitystats_memberactivity",
"restriction": null,
"keywords": [
"active members",
"activity",
"member activity"
]
},
"app=core&module=applications&controller=api": {
"lang_key": "menu__core_applications_api",
"restriction": "api_manage",
@@ -73,6 +75,15 @@
"viglink"
]
},
"app=core&module=applications&controller=enhancements&do=edit&id=core_FacebookPixel": {
"lang_key": "menu__core_applications_enhancements",
"restriction": "enhancements_manage",
"keywords": [
"facebook",
"facebook pixel",
"pixel"
]
},
"app=core&module=applications&controller=enhancements&do=edit&id=core_GoogleMaps&searchResult=form_googlemaps": {
"lang_key": "googlemaps",
"restriction": "enhancements_manage",
@@ -81,15 +92,6 @@
"maps"
]
},
"app=core&module=applications&controller=enhancements&do=edit&id=core_Sparkpost": {
"lang_key": "enhancements__core_Sparkpost",
"restriction": "enhancements_manage",
"keywords": [
"bulk mail",
"email",
"sparkpost"
]
},
"app=core&module=applications&controller=enhancements&do=edit&id=core_Viglink&searchResult=form_viglink_account_type": {
"lang_key": "viglink_account_type",
"restriction": "enhancements_manage",
@@ -239,6 +241,22 @@
"smilies"
]
},
"app=core&module=editor&controller=emoticons&tab=custom": {
"lang_key": "custom_emoji",
"restriction": "emoticons_manage",
"keywords": [
"emoticon",
"smiley",
"smilie"
]
},
"app=core&module=editor&controller=emoticons&tab=standard": {
"lang_key": "standard_emoji",
"restriction": "emoticons_manage",
"keywords": [
"emoji"
]
},
"app=core&module=editor&controller=settings": {
"lang_key": "menu__core_editor_settings",
"restriction": "settings_manage",
@@ -909,27 +927,6 @@
]
},
"app=core&module=membersettings&controller=profiles": {
"lang_key": "menu__core_membersettings_profiles",
"restriction": "profiles_manage",
"keywords": [
"about me",
"custom profile fields"
]
},
"app=core&module=membersettings&controller=profiles&tab=completion": {
"lang_key": "profile_completion",
"restriction": "profiles_manage",
"keywords": [
"complete my profile",
"complete profile",
"complete your profile",
"profile completion",
"register completion",
"registration compltion",
"sign up completion"
]
},
"app=core&module=membersettings&controller=profiles&tab=fields": {
"lang_key": "profile_fields",
"restriction": "profiles_manage",
"keywords": [
@@ -941,14 +938,27 @@
"profile fields"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=form_allow_gravatars": {
"app=core&module=membersettings&controller=profiles&tab=profilecompletion": {
"lang_key": "profile_completion",
"restriction": "profiles_manage",
"keywords": [
"complete my profile",
"complete profile",
"complete your profile",
"profile completion",
"register completion",
"registration completion",
"sign up completion"
]
},
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=form_allow_gravatars": {
"lang_key": "allow_gravatars",
"restriction": "profiles_manage",
"keywords": [
"gravatar"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=form_letter_photos": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=form_letter_photos": {
"lang_key": "letter_photos",
"restriction": "profiles_manage",
"keywords": [
@@ -962,7 +972,7 @@
"profile photo"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=form_profile_comments": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=form_profile_comments": {
"lang_key": "profile_comments",
"restriction": "profiles_manage",
"keywords": [
@@ -970,28 +980,28 @@
"profile statuses"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=form_signatures_enabled": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=form_signatures_enabled": {
"lang_key": "signatures_enabled",
"restriction": "profiles_manage",
"keywords": [
"signatures"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=form_user_name_length": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=form_user_name_length": {
"lang_key": "user_name_length",
"restriction": "profiles_manage",
"keywords": [
"name length"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=form_username_characters": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=form_username_characters": {
"lang_key": "username_characters",
"restriction": "profiles_manage",
"keywords": [
"username characters"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=ignore_system_on": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=ignore_system_on": {
"lang_key": "ignore_system_on",
"restriction": "profiles_manage",
"keywords": [
@@ -999,14 +1009,14 @@
"ignoring"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=profile_birthday_type": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=profile_birthday_type": {
"lang_key": "profile_birthday_type",
"restriction": "profiles_manage",
"keywords": [
"birthday"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=profile_comment_approval": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=profile_comment_approval": {
"lang_key": "profile_comment_approval",
"restriction": "profiles_manage",
"keywords": [
@@ -1014,7 +1024,7 @@
"profile statuses"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=prune_member_history": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=prune_member_history": {
"lang_key": "prune_member_history",
"restriction": "profiles_manage",
"keywords": [
@@ -1022,7 +1032,7 @@
"member history"
]
},
"app=core&module=membersettings&controller=profiles&tab=settings&searchResult=user_name_length": {
"app=core&module=membersettings&controller=profiles&tab=profilesettings&searchResult=user_name_length": {
"lang_key": "user_name_length",
"restriction": "profiles_manage",
"keywords": [
@@ -1049,55 +1059,6 @@
"titles"
]
},
"app=core&module=membersettings&controller=registration": {
"lang_key": "menu__core_membersettings_registration",
"restriction": "registration_manage",
"keywords": [
"approve",
"confirmation",
"coppa",
"registration",
"validate",
"validation"
]
},
"app=core&module=membersettings&controller=registration&searchResult=form_allow_reg": {
"lang_key": "allow_reg",
"restriction": "registration_manage",
"keywords": [
"allow registrations",
"new registrations",
"registration",
"registrations"
]
},
"app=core&module=membersettings&controller=registration&searchResult=new_reg_notify": {
"lang_key": "new_reg_notify",
"restriction": "registration_manage",
"keywords": [
"email on",
"email on new registration",
"new registrations",
"notify",
"notify new registration",
"registrations"
]
},
"app=core&module=membersettings&controller=registration&searchResult=quick_register": {
"lang_key": "quick_register",
"restriction": "registration_manage",
"keywords": [
"quick register",
"register"
]
},
"app=core&module=membersettings&controller=registration&searchResult=use_coppa": {
"lang_key": "use_coppa",
"restriction": "registration_manage",
"keywords": [
"coppa"
]
},
"app=core&module=membersettings&controller=reputation": {
"lang_key": "menu__core_membersettings_reputation",
"restriction": "reps_manage",
@@ -1230,7 +1191,24 @@
"winner"
]
},
"app=core&module=membersettings&controller=spam": {
"app=core&module=messengerstats&controller=pmstats": {
"lang_key": "menu__core_members_pmstats",
"restriction": null,
"keywords": [
"message statistics",
"Messenger Statistics",
"private messages"
]
},
"app=core&module=moderation&controller=reportedContent": {
"lang_key": "menu__core_moderation_reportedContent",
"restriction": "reportedContent_manage",
"keywords": [
"moderation",
"report"
]
},
"app=core&module=moderation&controller=spam": {
"lang_key": "menu__core_membersettings_spam",
"restriction": "spam_manage",
"keywords": [
@@ -1238,14 +1216,14 @@
"spammers"
]
},
"app=core&module=membersettings&controller=spam&do=serviceLogs": {
"app=core&module=moderation&controller=spam&do=serviceLogs": {
"lang_key": "spamlogs",
"restriction": "spam_service_log",
"keywords": [
"spam logs"
]
},
"app=core&module=membersettings&controller=spam&searchResult=bot_antispam_type": {
"app=core&module=moderation&controller=spam&searchResult=bot_antispam_type": {
"lang_key": "bot_antispam_type",
"restriction": "spam_manage",
"keywords": [
@@ -1254,7 +1232,7 @@
"security question"
]
},
"app=core&module=membersettings&controller=spam&searchResult=guest_captcha": {
"app=core&module=moderation&controller=spam&searchResult=guest_captcha": {
"lang_key": "guest_captcha_search_target",
"restriction": "spam_manage",
"keywords": [
@@ -1264,7 +1242,7 @@
"guest posts"
]
},
"app=core&module=membersettings&controller=spam&tab=captcha": {
"app=core&module=moderation&controller=spam&tab=captcha": {
"lang_key": "spamprevention_captcha",
"restriction": "spam_manage",
"keywords": [
@@ -1273,14 +1251,14 @@
"recaptcha"
]
},
"app=core&module=membersettings&controller=spam&tab=flagging": {
"app=core&module=moderation&controller=spam&tab=flagging": {
"lang_key": "spamprevention_flagging",
"restriction": "spam_manage",
"keywords": [
"flagging"
]
},
"app=core&module=membersettings&controller=spam&tab=qanda": {
"app=core&module=moderation&controller=spam&tab=qanda": {
"lang_key": "qanda_settings",
"restriction": "qanda_manage",
"keywords": [
@@ -1290,8 +1268,8 @@
"registration"
]
},
"app=core&module=membersettings&controller=spam&tab=service": {
"lang_key": "enhancements__core_SpamMonitoring",
"app=core&module=moderation&controller=spam&tab=service": {
"lang_key": "moderation",
"restriction": "spam_manage",
"keywords": [
"ips spam monitor",
@@ -1299,8 +1277,8 @@
"ips spam service"
]
},
"app=core&module=membersettings&controller=warnings": {
"lang_key": "menu__core_membersettings_warnings",
"app=core&module=moderation&controller=warnings": {
"lang_key": "menu__core_moderation_warnings",
"restriction": "warn_settings",
"keywords": [
"acknowledge",
@@ -1309,7 +1287,7 @@
"warnings"
]
},
"app=core&module=membersettings&controller=warnings&tab=settings&searchResult=form_warn_on": {
"app=core&module=moderation&controller=warnings&tab=settings&searchResult=form_warn_on": {
"lang_key": "warn_on",
"restriction": "warn_settings",
"keywords": [
@@ -1318,7 +1296,7 @@
"warning system"
]
},
"app=core&module=membersettings&controller=warnings&tab=settings&searchResult=warn_protected": {
"app=core&module=moderation&controller=warnings&tab=settings&searchResult=warn_protected": {
"lang_key": "warn_protected",
"restriction": "warn_settings",
"keywords": [
@@ -1326,14 +1304,14 @@
"protected"
]
},
"app=core&module=membersettings&controller=warnings&tab=settings&searchResult=warn_show_own": {
"app=core&module=moderation&controller=warnings&tab=settings&searchResult=warn_show_own": {
"lang_key": "warn_show_own",
"restriction": "warn_settings",
"keywords": [
"own"
]
},
"app=core&module=membersettings&controller=warnings&tab=settings&searchResult=warnings_acknowledge": {
"app=core&module=moderation&controller=warnings&tab=settings&searchResult=warnings_acknowledge": {
"lang_key": "warnings_acknowledge",
"restriction": "warn_settings",
"keywords": [
@@ -1385,67 +1363,6 @@
"uploads"
]
},
"app=core&module=overview&controller=security": {
"lang_key": "menu__core_overview_security",
"restriction": "security_manage",
"keywords": [
"acp",
"admin",
"conf",
"hack",
"htaccess",
"http",
"security"
]
},
"app=core&module=overview&controller=security&do=settings&searchResult=form_device_management": {
"lang_key": "device_management",
"restriction": "security_manage",
"keywords": [
"manage authorised browsers",
"manage authorised devices",
"manage authorized browsers",
"manage authorized devices",
"manage recently used browsers",
"manage recently used devices"
]
},
"app=core&module=overview&controller=security&do=settings&searchResult=form_match_ipaddress": {
"lang_key": "match_ipaddress",
"restriction": "security_manage",
"keywords": [
"ip address",
"session"
]
},
"app=core&module=overview&controller=security&do=settings&searchResult=form_security_remove_acp_link": {
"lang_key": "security_remove_acp_link",
"restriction": "security_manage",
"keywords": [
"acp",
"admin cp",
"admincp"
]
},
"app=core&module=overview&controller=security&do=settings&searchResult=form_xforward_matching": {
"lang_key": "xforward_matching",
"restriction": "security_manage",
"keywords": [
"http_x_forwarded_for",
"ip address",
"proxies",
"proxy"
]
},
"app=core&module=overview&controller=security&do=settings&searchResult=password_strength_meter": {
"lang_key": "password_strength_meter",
"restriction": "security_manage",
"keywords": [
"meter",
"password",
"strength"
]
},
"app=core&module=promotion&controller=advertisements": {
"lang_key": "menu__core_promotion_advertisements",
"restriction": "advertisements_manage",
@@ -1638,6 +1555,14 @@
"seo"
]
},
"app=core&module=settings&controller=advanced&tab=settings&searchResult=match_ipaddress": {
"lang_key": "match_ipaddress",
"restriction": "security_manage",
"keywords": [
"ip address",
"session"
]
},
"app=core&module=settings&controller=advanced&tab=settings&searchResult=task_use_cron": {
"lang_key": "task_use_cron",
"restriction": "advanced_manage_server",
@@ -1647,6 +1572,16 @@
"tasks"
]
},
"app=core&module=settings&controller=advanced&tab=settings&searchResult=xforward_matching": {
"lang_key": "xforward_matching",
"restriction": "security_manage",
"keywords": [
"http_x_forwarded_for",
"ip address",
"proxies",
"proxy"
]
},
"app=core&module=settings&controller=contactus": {
"lang_key": "menu__core_settings_contactus",
"restriction": "contactus_manage",
@@ -1774,6 +1709,15 @@
"twitter"
]
},
"app=core&module=settings&controller=general&searchResult=form_site_twitter_id": {
"lang_key": "site_twitter_id",
"restriction": "general_manage",
"keywords": [
"site twitter",
"twitter",
"twitter id"
]
},
"app=core&module=settings&controller=general&searchResult=form_upgrade_email": {
"lang_key": "upgrade_email",
"restriction": "general_manage",
@@ -1806,7 +1750,85 @@
"windows"
]
},
"app=core&module=settings&controller=login&do=settings&searchResult=disable_anonymous": {
"app=core&module=settings&controller=login&tab=accountsettings&searchResult=device_management": {
"lang_key": "device_management",
"restriction": "security_manage",
"keywords": [
"manage authorised browsers",
"manage authorised devices",
"manage authorized browsers",
"manage authorized devices",
"manage recently used browsers",
"manage recently used devices"
]
},
"app=core&module=settings&controller=login&tab=accountsettings&searchResult=password_strength_meter": {
"lang_key": "password_strength_meter",
"restriction": "security_manage",
"keywords": [
"meter",
"password",
"strength"
]
},
"app=core&module=settings&controller=login&tab=registration": {
"lang_key": "menu__core_membersettings_registration",
"restriction": "registration_manage",
"keywords": [
"approve",
"confirmation",
"coppa",
"registration",
"validate",
"validation"
]
},
"app=core&module=settings&controller=login&tab=registration&searchResult=allow_reg": {
"lang_key": "allow_reg",
"restriction": "registration_manage",
"keywords": [
"allow registrations",
"new registrations",
"registration",
"registrations"
]
},
"app=core&module=settings&controller=login&tab=registration&searchResult=new_reg_notify": {
"lang_key": "new_reg_notify",
"restriction": "registration_manage",
"keywords": [
"email on",
"email on new registration",
"new registrations",
"notify",
"notify new registration",
"registrations"
]
},
"app=core&module=settings&controller=login&tab=registration&searchResult=quick_register": {
"lang_key": "quick_register",
"restriction": "registration_manage",
"keywords": [
"quick register",
"register"
]
},
"app=core&module=settings&controller=login&tab=registration&searchResult=reg_auth_type": {
"lang_key": "reg_auth_type",
"restriction": "login_access",
"keywords": [
"email",
"validation"
]
},
"app=core&module=settings&controller=login&tab=registration&searchResult=use_coppa": {
"lang_key": "use_coppa",
"restriction": "registration_manage",
"keywords": [
"coppa"
]
},
"app=core&module=settings&controller=login&tab=settings&searchResult=disable_anonymous": {
"lang_key": "disable_anonymous",
"restriction": "login_manage",
"keywords": [
@@ -1814,16 +1836,7 @@
"anonymous login"
]
},
"app=core&module=settings&controller=login&do=settings&searchResult=form_logins_over_https": {
"lang_key": "logins_over_https",
"restriction": "login_manage",
"keywords": [
"https",
"secure logins",
"ssl"
]
},
"app=core&module=settings&controller=login&do=settings&searchResult=form_new_device_email": {
"app=core&module=settings&controller=login&tab=settings&searchResult=form_new_device_email": {
"lang_key": "new_device_email",
"restriction": "login_manage",
"keywords": [
@@ -1837,7 +1850,7 @@
"new signin email"
]
},
"app=core&module=settings&controller=login&do=settings&searchResult=ipb_bruteforce_attempts": {
"app=core&module=settings&controller=login&tab=settings&searchResult=ipb_bruteforce_attempts": {
"lang_key": "ipb_bruteforce_attempts",
"restriction": "login_manage",
"keywords": [
@@ -1848,6 +1861,22 @@
"logins"
]
},
"app=core&module=settings&controller=login&tab=settings&searchResult=ipb_bruteforce_period": {
"lang_key": "ipb_bruteforce_period",
"restriction": "login_access",
"keywords": [
"failed",
"login"
]
},
"app=core&module=settings&controller=login&tab=settings&searchResult=ipb_bruteforce_unlock": {
"lang_key": "ipb_bruteforce_unlock",
"restriction": "login_access",
"keywords": [
"failed",
"login"
]
},
"app=core&module=settings&controller=mfa": {
"lang_key": "menu__core_settings_mfa",
"restriction": "mfa_manage",
@@ -2134,8 +2163,10 @@
"restriction": "posting_manage_profanity",
"keywords": [
"bad words",
"filter",
"profanity",
"swear words"
"swear words",
"word filters"
]
},
"app=core&module=settings&controller=posting&tab=tags": {
@@ -2234,9 +2265,17 @@
"lang_key": "guest_terms_bar",
"restriction": "terms_manage",
"keywords": [
"cookie control",
"terms bar"
]
},
"app=core&module=settings&controller=terms&searchResult=guest_terms_bar_text": {
"lang_key": "guest_terms_bar_text",
"restriction": "terms_manage",
"keywords": [
"cookie control"
]
},
"app=core&module=staff&controller=admin": {
"lang_key": "menu__core_staff_admin",
"restriction": "restrictions_manage",
@@ -2313,15 +2352,6 @@
"user activity"
]
},
"app=core&module=stats&controller=pmstats": {
"lang_key": "menu__core_members_pmstats",
"restriction": null,
"keywords": [
"message statistics",
"Messenger Statistics",
"private messages"
]
},
"app=core&module=stats&controller=registrationstats": {
"lang_key": "menu__core_members_registrationstats",
"restriction": null,
@@ -103,8 +103,7 @@ class _{class}
$this->testSettings();
}
//\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $enabled ), array( 'conf_key=?', 'xxxxxx' ) );
//unset( \IPS\Data\Store::i()->settings );
//\IPS\Settings::i()->changeValues( array( 'xxxxxx' => $enabled ) );
}
/**
@@ -0,0 +1,55 @@
<?php
/**
* @brief DesignersMode extension: {class}
* @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}
* @since {date}
*/
namespace IPS\{app}\extensions\core\DesignersMode;
/* 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 DesignersMode extension: {class}
*/
class _{class}
{
/**
* Anything need building?
*
* @return bool
*/
public function toBuild()
{
}
/**
* Designer's mode on
*
* @return bool
*/
public function on( $data=NULL )
{
}
/**
* Designer's mode off
*
* @return bool
*/
public function off( $data=NULL )
{
}
}
@@ -0,0 +1,35 @@
<?php
/**
* @brief ACP Member Profile Block
* @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}
* @since {date}
*/
namespace IPS\{app}\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile Block
*/
class _{class} extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
return "Hello, World!";
}
}
@@ -0,0 +1,35 @@
<?php
/**
* @brief Member ACP Profile - Content Statistics Tab
* @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}
* @since {date}
*/
namespace IPS\{app}\extensions\core\MemberACPProfileContentTab;
/* 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 ACP Profile - Content Statistics Tab: Gallery
*/
class _{class} extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
return "Hello, World!";
}
}
@@ -0,0 +1,61 @@
<?php
/**
* @brief ACP Member Profile Tab
* @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}
* @since {date}
*/
namespace IPS\{app}\extensions\core\MemberACPProfileTabs;
/* 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 ACP Member Profile Tab
*/
class _Main extends \IPS\core\MemberACPProfile\MainTab
{
/**
* Get left-column blocks
*
* @return array
*/
public function leftColumnBlocks()
{
return array(
'IPS\{app}\extensions\core\MemberACPProfileBlocks\BlockName',
);
}
/**
* Get main-column blocks
*
* @return array
*/
public function mainColumnBlocks()
{
return array(
'IPS\{app}\extensions\core\MemberACPProfileBlocks\BlockName',
);
}
/**
* Get right-column blocks
*
* @return array
*/
public function rightColumnBlocks()
{
return array(
'IPS\{app}\extensions\core\MemberACPProfileBlocks\BlockName',
);
}
}
@@ -87,9 +87,10 @@ class _{class}
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
return TRUE;
}
@@ -23,24 +23,6 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _{class}
{
/**
* Action Buttons
*
* @param \IPS\Member $member The Member
* @return array
*/
public function actionButtons( $member )
{
return array(
'id' => array(
'title' => 'title',
'icon' => 'cog',
'link' => \IPS\Http\Url::internal( "app={app}&module=&controller=&do=&id={$member->member_id}" ),
'class' => ''
)
);
}
/**
* Process Form
*
@@ -0,0 +1,87 @@
<?php
/**
* @brief Member Restrictions
* @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}
* @since {date}
*/
namespace IPS\{app}\extensions\core\MemberRestrictions;
/* 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 Restrictions
*/
class _{class} extends \IPS\core\MemberACPProfile\Restriction
{
/**
* Modify Edit Restrictions form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( \IPS\Helpers\Form $form )
{
// $form->add( ... );
}
/**
* Save Form
*
* @param array $values Values from form
* @return array
*/
public function save( $values )
{
$return = array();
// $return['my_restriction'] = array( 'old' => $oldValue, 'new' => $values['my_restriction'] ); // used in changesForHistory()
// \IPS\Db::i()->update( ... ); // Actually save the value
return $return;
}
/**
* What restrictions are active on the account?
*
* @return array
*/
public function activeRestrictions()
{
$return = array();
/*if ( ... )
{
$return[] = 'my_restricion';
}*/
return $return;
}
/**
* Get details of a change to show on history
*
* @param array $changes Changes as set in save()
* @return array
*/
public static function changesForHistory( $changes )
{
$return = array();
if ( isset( $changes['my_restriction'] ) )
{
$return[] = \IPS\Member::loggedIn()->language()->addToStack( 'changed_my_restriction', FALSE, array( 'sprintf' => array( $changes['my_restriction']['new'], $changes['my_restriction']['new'] ) ) );
}
return $return;
}
}
@@ -49,10 +49,9 @@ class _{class}
* Member has logged on
*
* @param \IPS\Member $member Member that logged in
* @param \IPS\Http\Url $redirectUrl The URL to send the user back to
* @return void
*/
public function onLogin( $member, $returnUrl )
public function onLogin( $member )
{
}
@@ -61,7 +60,7 @@ class _{class}
* Member has logged out
*
* @param \IPS\Member $member Member that logged out
* @param \IPS\Http\Url $redirectUrl The URL to send the user back to
* @param \IPS\Http\Url $returnUrl The URL to send the user back to
* @return void
*/
public function onLogout( $member, $returnUrl )
@@ -35,6 +35,7 @@ class _{class}
*/
public function settings()
{
return array();
}
/**
@@ -57,4 +58,5 @@ class _{class}
public function generateSitemap( $filename, $sitemap )
{
}
}
+295 -91
View File
@@ -45,7 +45,7 @@
{$email->language->addToStack("email_view_other_profile", FALSE, array( 'sprintf' => array( $member->name ) ) )}: {$member->url()}
======
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_new_private_message</template_name><template_content_html><![CDATA[{$email->language->addToStack("messenger_notify_title", FALSE, array( 'sprintf' => array( $message->author()->name ) ) )}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_private_message</template_name><template_content_html><![CDATA[{$email->language->addToStack("messenger_notify_title", FALSE, array( 'sprintf' => array( $message->author()->name ) ) )}
<br /><br />
<a href='{$message->url()}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 12px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("messenger_inline_button", FALSE)}</a>
@@ -141,7 +141,7 @@
{$email->language->addToStack("email_url_to_message", FALSE)}: {$message->url()}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>registration_validate</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>registration_validate</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_registration_validate", FALSE)}
<br /><br />
@@ -160,7 +160,7 @@
{$email->language->addToStack("email_validate_link", FALSE)}: {url="app=core&module=system&controller=register&do=validate&vid={$vid}&mid={$member->member_id}" plain="true" base="front"}
=====
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_club_request</template_name><template_content_html><![CDATA[<br />
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_club_request</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack("notification__club_request", FALSE, array( 'sprintf' => array( $member->name, $club->name ) ) )}
<br /><br />
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f9f9f9;'>
@@ -171,17 +171,8 @@
</tr>
</table>]]></template_content_html><template_data>$club, $member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("notification__club_request", FALSE, array( 'sprintf' => array( $member->name, $club->name ) ) )}
{$email->language->addToStack("view_club", FALSE)}: {$club->url()}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_warning_mods</template_name><template_content_html><![CDATA[
{{$content = $warning->content();}}
{{if $content && $content instanceof \IPS\Content\Comment}}
{{$item = $content->item();}}
{$email->language->addToStack("email_mod_warned_comment", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->url(), \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name, $item->indefiniteArticle( $email->language ) ) ) )} <a href='{$content->url()->setQueryString( '_warn', $warning->id )}'>{$content->item()->mapped('title')}</a>
{{elseif $content}}
{$email->language->addToStack("email_mod_warned_item", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->url(), \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name, $content->indefiniteArticle( $email->language ) ) ) )} <a href='{$content->url()->setQueryString( '_warn', $warning->id )}'>{$content->mapped('title')}</a>
{{else}}
{$email->language->addToStack("email_mod_warned_other", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->url(), \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name ) ) )}
{{endif}}
{$email->language->addToStack("view_club", FALSE)}: {$club->url()}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_warning_mods</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_mod_warned_other", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->url(), \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name ) ) )}
<br />
<br />
@@ -195,14 +186,6 @@
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;">{$email->language->addToStack("core_warn_reason_" . $warning->reason, FALSE)}</td>
</tr>
{{endif}}
{{if $content}}
<tr class='responsive_row'>
<td dir='{dir}' width='150' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;" valign='top'><strong>{$email->language->addToStack("email_warn_source", FALSE)}:</strong></td>
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;">
<a href='{$content->url()->setQueryString( '_warn', $warning->id )}'>{{if $content instanceof \IPS\Content\Comment}}{$content->item()->mapped('title')}{{else}}{$content->mapped('title')}{{endif}}</a>
</td>
</tr>
{{endif}}
{{if $warning->canViewDetails() or $warning->mq or $warning->rpa or $warning->suspend}}
<tr class='responsive_row'>
<td dir='{dir}' width='150' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;" valign='top'><strong>{$email->language->addToStack("email_warn_penalty", FALSE)}:</strong></td>
@@ -280,19 +263,10 @@
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>
]]></template_content_html><template_data>$warning, $email</template_data><template_content_plaintext><![CDATA[
{{$content = $warning->content();}}
{{if $content && $content instanceof \IPS\Content\Comment}}
{{$item = $content->item();}}
{$email->language->addToStack("email_mod_warned_comment_plain", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name, $item->indefiniteArticle( $email->language ) ) ) )} {$content->item()->mapped('title')}
{{elseif $content}}
{$email->language->addToStack("email_mod_warned_item_plain", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name, $content->indefiniteArticle( $email->language ) ) ) )} {$content->mapped('title')}
{{else}}
{$email->language->addToStack("email_mod_warned_other_plain", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name ) ) )}
{{endif}}
{$email->language->addToStack("email_mod_warned_other_plain", FALSE, array( 'sprintf' => array( \IPS\Member::load( $warning->member )->name, \IPS\Member::load( $warning->moderator )->name ) ) )}
=====
{{if $warning->canViewDetails()}}{$email->language->addToStack("email_warn_reason", FALSE)}:{$email->language->addToStack("core_warn_reason_" . $warning->reason, FALSE)}{{endif}}
{{if $content}}{$email->language->addToStack("email_warn_source", FALSE)}: {{if $content instanceof \IPS\Content\Comment}}{$content->item()->mapped('title')}{{else}}{$content->mapped('title')}{{endif}}{{endif}}
{{if $warning->canViewDetails() or $warning->mq or $warning->rpa or $warning->suspend}}{$email->language->addToStack("email_warn_penalty", FALSE)}:
{{if $warning->canViewDetails()}}
{{if $warning->expire_date}}
@@ -321,7 +295,7 @@
{{endif}}
=====
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_private_message_added</template_name><template_content_html><![CDATA[{$email->language->addToStack("messenger_notify_add_title", FALSE, array( 'sprintf' => array( $added->name ) ) )}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_private_message_added</template_name><template_content_html><![CDATA[{$email->language->addToStack("messenger_notify_add_title", FALSE, array( 'sprintf' => array( $added->name ) ) )}
<br /><br />
<a href='{$message->url()}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 12px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("messenger_inline_button", FALSE)}</a>
@@ -337,14 +311,14 @@
<table width='100%' cellpadding='10' cellspacing='0' border='0'>
<tr>
<td dir='{dir}'>
<strong>{$email->language->addToStack("members_in_convo", FALSE, array( 'pluralize' => array( $message->item()->activeParticipants ) ) )}</strong>
<strong>{$email->language->addToStack("members_in_convo", FALSE, array( 'pluralize' => array( $message->activeParticipants ) ) )}</strong>
</td>
</tr>
<tr>
<td dir='{dir}'>
<table width='100%' cellpadding='0' cellspacing='0' border='0' class='responsive_table'>
{{$i = 0;}}
{{foreach $message->item()->maps() as $map}}
{{foreach $message->maps() as $map}}
{{if $map['map_user_active']}}
{{if ( $i % 4 ) == 0}}
<tr>
@@ -387,7 +361,7 @@
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f5f5f5'>
<tr>
<td dir='{dir}'>
<h2 style='margin: 0; font-size: 19px; font-weight: 500'>{$message->item()->title}</h2>
<h2 style='margin: 0; font-size: 19px; font-weight: 500'>{$message->title}</h2>
</td>
</tr>
</table>
@@ -417,7 +391,7 @@
{$email->language->addToStack("email_url_to_message", FALSE)}: {$message->url()}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_unapproved_content</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_unapproved_content</template_name><template_content_html><![CDATA[
{{if $content->author()->member_id}}
{$email->language->addToStack("email_new_content_unapproved", FALSE, array( 'sprintf' => array( $content->author()->url(), $content->author()->name, $content->indefiniteArticle( $email->language ) ) ) )} <a href='{$content->url()}'>{{if $content instanceof \IPS\Content\Comment OR $content instanceof \IPS\Content\Review}}{$content->item()->mapped('title')}{{else}}{$content->mapped('title')}{{endif}}</a>
{{elseif $content->author()->real_name}}
@@ -446,7 +420,7 @@
{{if ( ( $content instanceof \IPS\Content\Comment or $content instanceof \IPS\Content\Review ) and $container = $content->item()->containerWrapper() ) or ( $content instanceof \IPS\Content\Item and $container = $content->containerWrapper() )}}
<tr>
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; line-height: 1.5; font-size: 14px; color: #8d8d8d">
{$email->language->addToStack("email_posted_in", FALSE, array( 'sprintf' => array( $email->language->addToStack( $container->_titleLanguageKey ) ) ) )}
{$email->language->addToStack("email_posted_in", FALSE, array( 'sprintf' => array( $content->container()->getTitleForLanguage( $email->language ) ) ) )}
</td>
</tr>
{{endif}}
@@ -479,7 +453,51 @@
{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $content::$title, FALSE ) ) ) ) )}: {$content->url()}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>account_locked</template_name><template_content_html><![CDATA[<br />
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_automatic_moderation</template_name><template_content_html><![CDATA[{{$item = $reportedContent instanceof \IPS\Content\Item ? $reportedContent : $reportedContent->item();}}
{$email->language->addToStack("email_content_automatic_moderation", FALSE, array( 'sprintf' => array( $reportedContent->indefiniteArticle( $email->language ), $reportedContent->url(), $item->mapped('title') ) ) )}
<br />
<br />
<table width='100%' cellpadding='0' cellspacing='0' border='0'>
<tr>
<td dir='{dir}' width='40' valign='top' class='hidePhone' style='width: 0; max-height: 0; overflow: hidden; float: left;'>
<img src='{$report->author()->get_photo( true, true )}' width='40' height='40' style='border: 1px solid #777777; vertical-align: middle;'>
</td>
<td dir='{dir}' width='30' valign='top' class='hidePhone' style='width: 0; max-height: 0; overflow: hidden; float: left;'>
<br />
<span style='display: block; width: 0px; height: 0px; border-width: 15px; border-color: transparent #f9f9f9 transparent transparent; border-style: solid'></span>
</td>
<td dir='{dir}' valign='top' style='background: #f9f9f9;'>
<table width='100%' cellpadding='10' cellspacing='0' border='0'>
<tr>
<td dir='{dir}'>
<table width='100%' cellpadding='5' cellspacing='0' border='0'>
<tr>
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; line-height: 1.5; font-size: 14px;">
{$email->parseTextForEmail( $latestReport['report'], $email->language )|raw}
</td>
</tr>
<tr>
<td dir='{dir}'>
<a href='{$report->url()}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 14px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $report::$title, FALSE ) ) ) ) )}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br /><br />
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>]]></template_content_html><template_data>$report, $latestReport, $reportedContent, $email</template_data><template_content_plaintext><![CDATA[{{$item = $reportedContent instanceof \IPS\Content\Item ? $reportedContent : $reportedContent->item();}}
{$email->language->addToStack("email_content_automatic_moderation_plain", FALSE, array( 'sprintf' => array( $reportedContent->indefiniteArticle( $email->language ), $item->mapped('title') ) ) )}
{$email->language->addToStack("go_to_the_report", FALSE)}: {$report->url()}
{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $reportedContent::$title, FALSE ) ) ) ) )}: {$reportedContent->url()}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>account_locked</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack("email_account_locked", FALSE, array( 'sprintf' => array( \IPS\Settings::i()->ipb_bruteforce_attempts, $location ) ) )}
<br /><br />
{$email->language->addToStack("email_account_locked_change_password")}
@@ -493,7 +511,9 @@
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f9f9f9;'>
<tr>
<td dir='{dir}' align='center'>
<a href='{url="app=core&module=system&controller=settings&area=password" seoTemplate="settings_password" base="front"}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 14px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("email_reset_password", FALSE)}</a>
<a href='{url="app=core&module=system&controller=settings&do=secureAccount" seoTemplate="settings_secure" base="front"}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 14px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("email_secure_account", FALSE)}</a>
{$email->language->addToStack("or", FALSE)}
<a href='{url="app=core&module=system&controller=lostpass" seoTemplate="lostpassword" base="front"}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 14px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("email_reset_password", FALSE)}</a>
</td>
</tr>
</table>]]></template_content_html><template_data>$member, $location, $unlockTime=NULL, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("email_account_locked", FALSE, array( 'sprintf' => array( \IPS\Settings::i()->ipb_bruteforce_attempts, $location ) ) )}
@@ -505,14 +525,15 @@
{$email->language->addToStack("email_account_locked_no_unlock")}
{{endif}}
{$email->language->addToStack("change_password", FALSE)}: {url="app=core&module=system&controller=settings&area=password" seoTemplate="settings_password" base="front"}
]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>unsubscribeDigest</template_name><template_content_html><![CDATA[<tr>
{$email->language->addToStack("email_secure_account", FALSE)}: {url="app=core&module=system&controller=settings&do=secureAccount" seoTemplate="settings_secure" base="front"}
{$email->language->addToStack("or", FALSE)}
{$email->language->addToStack("email_reset_password", FALSE)}: {url="app=core&module=system&controller=lostpass" seoTemplate="lostpassword" base="front"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>unsubscribeDigest</template_name><template_content_html><![CDATA[<tr>
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}' valign='middle' align='center' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 12px; line-height: 18px; padding-left: 10px;">
{$email->language->addToStack("unsubscribe_blurb", FALSE)} <a href="{url="app=core&module=system&controller=followed&filter=digest" base="front" seoTemplate="followed_content"}" style='color: #4a8aca; text-decoration: none; display: inline-block'>{$email->language->addToStack( 'adjust_digest_prefs', FALSE )}</a>.
<br />
</td>
</tr>]]></template_content_html><template_data>$member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'adjust_digest_prefs', FALSE )}: {url="app=core&module=system&controller=followed&filter=digest" base="front" seoTemplate="followed_content"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>digests__areaWrapper</template_name><template_content_html><![CDATA[<!-- Add item counts and link to remaining items on site -->
</tr>]]></template_content_html><template_data>$member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'adjust_digest_prefs', FALSE )}: {url="app=core&module=system&controller=followed&filter=digest" base="front" seoTemplate="followed_content"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>digests__areaWrapper</template_name><template_content_html><![CDATA[<!-- Add item counts and link to remaining items on site -->
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f5f5f5'>
<tr>
<td dir='{dir}'>
@@ -529,7 +550,7 @@
{$output|raw}
</table>
<br /><br />]]></template_content_html><template_data>$output, $app, $area, $max, $count, $email</template_data><template_content_plaintext>{$output|raw}
</template_content_plaintext></template><template><template_app>core</template_app><template_name>upgrade</template_name><template_content_html><![CDATA[
</template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>upgrade</template_name><template_content_html><![CDATA[
{$email->language->addToStack("dashboard_version_info", FALSE, array( 'sprintf' => $version ))}
<br />
<br />
@@ -559,7 +580,7 @@
{$email->language->addToStack('this_is_a_security_update')}
{{endif}}
{$email->language->addToStack("upgrade_now")}: {url="&app=core&module=system&controller=upgrade" base="admin"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_club_join</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack("upgrade_now")}: {url="&app=core&module=system&controller=upgrade" base="admin"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_club_join</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack("notification__club_join", FALSE, array( 'sprintf' => array( $member->name, $club->name ) ) )}
<br /><br />
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f9f9f9;'>
@@ -570,7 +591,18 @@
</tr>
</table>]]></template_content_html><template_data>$club, $member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("notification__club_join", FALSE, array( 'sprintf' => array( $member->name, $club->name ) ) )}
{$email->language->addToStack("view_club", FALSE)}: {$club->url()}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_new_likes</template_name><template_content_html><![CDATA[
{$email->language->addToStack("view_club", FALSE)}: {$club->url()}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_unapproved_club</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack("notification__new_club_unapproved", FALSE, array( 'sprintf' => array( $club->owner->name, $club->name ) ) )}
<br /><br />
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f9f9f9;'>
<tr>
<td dir='{dir}' align='center'>
<a href='{$club->url()}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 14px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("club_view", FALSE)}</a>
</td>
</tr>
</table>]]></template_content_html><template_data>$club, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("notification__new_club_unapproved", FALSE, array( 'sprintf' => array( $club->owner->name, $club->name ) ) )}
{$email->language->addToStack("club_view", FALSE)}: {$club->url()}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_likes</template_name><template_content_html><![CDATA[
{{$item = ( $comment instanceof \IPS\Content\Item ) ? $comment : $comment->item();}}
{$email->language->addToStack("email_new_likes", FALSE, array( 'sprintf' => array( ( $comment->author()->group['gbw_view_reps'] ) ? $member->name : $email->language->get( 'notifications_anonymous' ), $comment->indefiniteArticle( $email->language ) ) ) )} <a href='{$item->url()}'>{$item->mapped('title')}</a>
@@ -614,7 +646,7 @@
{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $item::$title, FALSE ) ) ) ) )}: {$item->url()}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>email_address_changed</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>email_address_changed</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_email_address_changed", FALSE, array( 'sprintf' => \IPS\Settings::i()->board_name ) )}
<br />
<br />
@@ -657,7 +689,7 @@
{$email->language->addToStack("email_old_email", FALSE)}: {$oldEmail}
=====
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_unapproved_content_bulk</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_unapproved_content_bulk</template_name><template_content_html><![CDATA[
{{if $member->member_id}}
{$email->language->addToStack("email_new_content_unapproved_bulk", FALSE, array( 'sprintf' => array( $member->url(), $member->name, $email->language->addToStack( $contentClass::$title . '_pl', FALSE ) ) ) )} <a href='{$container->url()}'>{$container->_title}</a>
{{else}}
@@ -668,7 +700,7 @@
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>]]></template_content_html><template_data>$container, $member, $contentClass, $email</template_data><template_content_plaintext><![CDATA[
{$email->language->addToStack("email_new_content_unapproved_bulk_plain", FALSE, array( 'sprintf' => array( $member->name, $email->language->addToStack( $contentClass::$title . '_pl', FALSE ) ) ) )} {$container->_title}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>contact_form</template_name><template_content_html><![CDATA[{$email->language->addToStack("email_contact_form", FALSE)}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>contact_form</template_name><template_content_html><![CDATA[{$email->language->addToStack("email_contact_form", FALSE)}
<br />
<br />
@@ -711,7 +743,7 @@
{$email->language->addToStack("email_contact_said", FALSE, array( 'sprintf' => array( $name, $fromEmail ) ) )}
{expression="\IPS\Email::buildPlaintextBody( $message )"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_profile_comment</template_name><template_content_html><![CDATA[
{expression="\IPS\Email::buildPlaintextBody( $message )"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_profile_comment</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_new_profile_comment", FALSE, array( 'sprintf' => array( $comment->author()->url(), $comment->author()->name ) ) )}
<br />
@@ -764,7 +796,7 @@
{$email->language->addToStack("email_ignore_user_pm", FALSE, array( 'sprintf' => array( $comment->author()->name ) ) )}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>unsubscribeNotification</template_name><template_content_html><![CDATA[{{if $member instanceof \IPS\Member}}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>unsubscribeNotification</template_name><template_content_html><![CDATA[{{if $member instanceof \IPS\Member}}
<tr>
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}' valign='middle' align='center' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 12px; line-height: 18px; padding-left: 10px;">
@@ -772,7 +804,7 @@
<br />
</td>
</tr>
{{endif}}]]></template_content_html><template_data>$key, $member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'adjust_notification_prefs', FALSE )}: {url="app=core&module=system&controller=notifications&do=options&type={$key}" seoTemplate="notifications_options"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_new_content</template_name><template_content_html><![CDATA[
{{endif}}]]></template_content_html><template_data>$key, $member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'adjust_notification_prefs', FALSE )}: {url="app=core&module=system&controller=notifications&do=options&type={$key}" seoTemplate="notifications_options"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_content</template_name><template_content_html><![CDATA[
{{if $content->author()->member_id}}
{$email->language->addToStack("email_new_content", FALSE, array( 'sprintf' => array( $content->author()->url(), $content->author()->name, $content->indefiniteArticle( $email->language ) ) ) )} <a href='{$content->url()}'>{$content->mapped('title')}</a>
{{elseif $content->author()->real_name}}
@@ -800,7 +832,7 @@
<table width='100%' cellpadding='5' cellspacing='0' border='0'>
<tr>
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; line-height: 1.5; font-size: 14px; color: #8d8d8d">
{$email->language->addToStack("email_posted_in", FALSE, array( 'sprintf' => array( $email->language->addToStack( $content->container()->_titleLanguageKey ) ) ) )}
{$email->language->addToStack("email_posted_in", FALSE, array( 'sprintf' => array( $content->container()->getTitleForLanguage( $email->language ) ) ) )}
</td>
</tr>
<tr>
@@ -826,7 +858,7 @@
{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $content::$title, FALSE ) ) ) ) )}: {$content->url()}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_club_response</template_name><template_content_html><![CDATA[<br />
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_club_response</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack( $response ? "notification__club_response_accepted" : "notification__club_response_declined", FALSE, array( 'sprintf' => array( $club->name ) ) )}
<br /><br />
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f9f9f9;'>
@@ -837,7 +869,7 @@
</tr>
</table>]]></template_content_html><template_data>$club, $response, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack( $response ? "notification__club_response_accepted" : "notification__club_response_declined", FALSE, array( 'sprintf' => array($club->name ) ) )}
{$email->language->addToStack("view_club", FALSE)}: {$club->url()}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>email_change</template_name><template_content_html><![CDATA[
{$email->language->addToStack("view_club", FALSE)}: {$club->url()}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>email_change</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_complete_email", FALSE)}
<br />
<br />
@@ -877,7 +909,7 @@
=====
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>lost_password_init</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>lost_password_init</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_lost_pass", FALSE)}
<br /><br />
@@ -893,7 +925,7 @@
{$email->language->addToStack("email_reset_password", FALSE)}: {url="app=core&module=system&controller=lostpass&do=validate&vid={$vid}&mid={$member->member_id}" seoTemplate="lostpassword" plain="true" base="front"}
=====
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>admin_reg</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>admin_reg</template_name><template_content_html><![CDATA[
{expression="$email->language->addToStack( 'email_admin_reg', FALSE, array( 'sprintf' => array( \IPS\Settings::i()->board_name ) ) )"}
<br />
<br />
@@ -945,7 +977,7 @@
{expression="$email->language->addToStack( 'email_go_to_site', FALSE, array( 'sprintf' => array( \IPS\Settings::i()->board_name ) ) )"}: {setting="base_url"}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_quote</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_quote</template_name><template_content_html><![CDATA[
{{$item = $comment instanceof \IPS\Content\Item ? $comment : $comment->item();}}
{$email->language->addToStack("email_quoted_you", FALSE, array( 'sprintf' => array( $comment->author()->url(), $comment->author()->name, $item->indefiniteArticle( $email->language ) ) ) )}
@@ -994,7 +1026,7 @@
{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $comment::$title, FALSE ) ) ) ) )}: {$comment->url()}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_new_content_bulk</template_name><template_content_html><![CDATA[{{if $member->member_id}}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_content_bulk</template_name><template_content_html><![CDATA[{{if $member->member_id}}
{$email->language->addToStack("email_new_content_bulk", FALSE, array( 'sprintf' => array( $member->url(), $member->name, $email->language->addToStack( $contentClass::$title . '_pl_lc', FALSE ) ) ) )} <a href='{$container->url()}'>{$container->_title}</a>
{{else}}
{$email->language->addToStack("email_new_content_bulk_guest", FALSE, array( 'sprintf' => array( $member->name, $email->language->addToStack( $contentClass::$title . '_pl_lc', FALSE ) ) ) )} <a href='{$container->url()}'>{$container->_title}</a>
@@ -1003,7 +1035,7 @@
<br /><br />
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>]]></template_content_html><template_data>$container, $member, $contentClass, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("email_new_content_bulk_plain", FALSE, array( 'sprintf' => array( $member->name, $email->language->addToStack( $contentClass::$title . '_pl_lc', FALSE ) ) ) )} {$container->_title}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>registration_notify</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>registration_notify</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_registration_notify", FALSE, array( 'sprintf' => array( \IPS\Settings::i()->board_name ) ) )}
<br />
<br />
@@ -1051,7 +1083,7 @@
{$email->language->addToStack("go_to_this_user", FALSE, array( 'sprintf' => array( $member->name ) ) )}: {setting="base_url"}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>emailWrapper</template_name><template_content_html><![CDATA[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>emailWrapper</template_name><template_content_html><![CDATA[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="{dir}">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
@@ -1109,7 +1141,7 @@
display: table-cell !important;
}
@media only screen and (max-device-width: 480px) {
@media only screen and (max-width: 480px) {
a[href^="tel"], a[href^="sms"] {
text-decoration: none;
color: blue;
@@ -1146,9 +1178,15 @@
.responsive_fullwidth {
width: 100% !important;
}
.cOurPicksRow {
margin-bottom: 30px;
padding-bottom: 30px;
border-bottom: 1px solid #e0e0e0;
}
}
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) {
@media only screen and (min-width: 768px) and (max-width: 1024px) {
a[href^="tel"], a[href^="sms"] {
text-decoration: none;
color: blue;
@@ -1163,6 +1201,11 @@
cursor: default;
}
}
/* Share Links */
.cShareLink:hover {
color: white;
}
</style>
</head>
<body bgcolor="#f1f1f1" style='direction: {dir}'>
@@ -1224,6 +1267,111 @@
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
{{if $ourPicks AND count( $ourPicks )}}
<tr>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}' width='800' bgcolor='#ffffff' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 15px; color: #333333; line-height: 21px;">
<table width='100%' cellpadding='0' cellspacing='0' border='0' style='border-bottom: 1px solid #e0e0e0;'>
<tr height='15'>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
<tr>
<td dir='{dir}' width='30'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><strong>{$email->language->addToStack("email_also_interesting")}</strong></td>
<td dir='{dir}' width='30'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
<tr height='15'>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
</table>
<table width='100%' cellpadding='30' cellspacing='0' border='0' class='responsive_table'>
<tr>
<td>
<table width='100%' cellpadding='0' cellspacing='0' border='0' class='responsive_table'>
<tr>
{{$count = 0;}}
{{foreach $ourPicks as $item}}
{{if $count == 2}}
</tr>
<tr height='30'>
<td colspan='3' dir='{dir}' class='hidePhone' style='border-bottom: 1px solid #e0e0e0;'>
<img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''>
</td>
</tr>
<tr height='30'>
<td colspan='3' dir='{dir}' class='hidePhone'>
<img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''>
</td>
</tr>
<tr>
{{endif}}
<td class='responsive_fullwidth cOurPicksRow' dir='{dir}' width='48%' valign='top' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 15px;">
{{$staff = \IPS\Member::load( $item->added_by );}}
{{$title = $item->objectTitle;}}
<h2 style='font-size: 20px; margin: 0;'>
<a href="{$item->object()->url()}" style='color: #4a8aca; text-decoration: none;'>{$item->objectTitle}</a>
</h2>
<p style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 13px; color: #8c8c8c; line-height: 21px; margin: 0">
{$item->objectMetaDescription( $email->language )}
</p>
{{if $text = $item->getText('internal', false)}}
<p style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 15px; line-height: 21px; margin-bottom: 0">
{truncate="$text" length="100"}
</p>
{{endif}}
<p style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 15px; color: #8c8c8c; line-height: 21px; margin-top: 5px; margin-bottom: 0">
<a href="{$item->object()->url()}" style='color: #4a8aca; text-decoration: none;'>{$email->language->addToStack("digests_read_more")}</a>
{{if $counts = $item->objectDataCount}}
&nbsp;&nbsp;({$counts['words']})
{{endif}}
</p>
<br>
<table width='100%' cellpadding='0' cellspacing='0' border='0'>
<tr>
<td dir='{dir}' width='40' valign='top'>
<img src='{$staff->get_photo( true, true )}' width='40' height='40' style='border: 1px solid #777777; vertical-align: middle;'>
</td>
<td dir='{dir}' width='10'>
<img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='10' height='1' alt=''>
</td>
<td dir='{dir}' width='100%' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 13px; color: #8c8c8c;">
<strong>{$email->language->addToStack("promoted_by")}</strong><br>
{$staff->name}, {datetime="$item->sent" relative="false"}
</td>
</tr>
</table>
</td>
{{$count++;}}
{{if $count == 1 || $count == 3}}
<td dir='{dir}' width='4%' class='hidePhone'>
<img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''>
</td>
{{endif}}
{{if ( count( $ourPicks ) === 1 && $count == 1 ) || ( count( $ourPicks ) == 3 && $count == 3 )}}
<td width='48%' class='hidePhone'>
<img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''>
</td>
{{endif}}
{{endforeach}}
</tr>
</table>
</td>
</tr>
</table>
</td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
<tr height='25'>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}'><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
{{endif}}
{{if $unsubscribe}}
{$unsubscribe|raw}
{{endif}}
@@ -1236,6 +1384,19 @@
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
{{endif}}
{{if \IPS\Settings::i()->site_social_profiles AND \IPS\Settings::i()->social_links_in_email AND $links = json_decode( \IPS\Settings::i()->site_social_profiles, TRUE ) AND count( $links )}}
{{$socialColors = array('internal' => '#008b00;', 'delicious' => '#3274D1;', 'deviantart' => '#16A085;', 'digg' => '#14589E;', 'email' => '#444;', 'facebook' => '#3B5998;', 'google' => '#D14836;', 'google-plus' => '#D14836;', 'linkedin' => '#007FB1;', 'reddit' => '#FF4500;', 'stumble' => '#EB4924;', 'twitter' => '#00ACED;', 'vk' => '#507299;', 'weibo' => '#D32F2F;', 'youtube' => '#E62117;', 'foursquare' => '#2D5BE3;', 'etsy' => '#F56400;', 'flickr' => '#202022;', 'github' => '#000000;', 'instagram' => '#E13D62;', 'pinterest' => '#BD081C;', 'slack' => '#42C299;', 'xing' => '#B0D400;');}}
<tr>
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}' valign='top' width='800' align='center' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 12px; color: #bdbdbd; line-height: 18px; padding-left: 10px;">
{{foreach $links as $profile}}
<a href='{$profile['key']}' target='_blank' class='cShareLink' style='width: 28px; height: 28px; line-height: 34px; text-align: center; border-radius: 16px; display: inline-block; color: white !important; font-size: 15px; margin: 4px 2px; background-color: {$socialColors[ $profile['value'] ]}'><img src='{setting="base_url"}applications/core/interface/email/{$profile['value']}.png' width='18' height='18' alt="{$profile['value']}"></a>
{{endforeach}}
</td>
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
</tr>
{{endif}}
{{if \IPS\Settings::i()->board_name}}
<tr>
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
@@ -1248,7 +1409,7 @@
</table>
</body>
</html>
]]></template_content_html><template_data>$subject, $member, $content, $unsubscribe, $placeholderRecipient=FALSE, $introText='', $email</template_data><template_content_plaintext><![CDATA[
]]></template_content_html><template_data>$subject, $member, $content, $unsubscribe, $placeholderRecipient=FALSE, $introText='', $email, $ourPicks=NULL</template_data><template_content_plaintext><![CDATA[
{{if $member->member_id }}{$email->language->addToStack("email_greeting_member", FALSE, array( 'sprintf' => array( $member->name ) ) )}{{elseif $placeholderRecipient}}{$email->language->addToStack("email_greeting_member", FALSE, array( 'sprintf' => array( '*|member_name|*' ) ) )}{{endif}}
{$content|raw}
@@ -1257,7 +1418,7 @@
{{if \IPS\Settings::i()->privacy_type != "none"}}{{if \IPS\Settings::i()->privacy_type == "internal"}}{url="app=core&module=system&controller=privacy" seoTemplate="privacy" base="front" plain="true"}{{else}}{setting="privacy_link"}){{endif}}{{endif}}
{setting="board_name"}{{if \IPS\Settings::i()->site_address and \IPS\Settings::i()->site_address != 'null'}}, {address="\IPS\Settings::i()->site_address"}{{endif}}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_new_status</template_name><template_content_html><![CDATA[
{setting="board_name"}{{if \IPS\Settings::i()->site_address and \IPS\Settings::i()->site_address != 'null'}}, {address="\IPS\Settings::i()->site_address"}{{endif}}]]></template_content_plaintext><template_pinned>1</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_status</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_new_status", FALSE, array( 'sprintf' => array( $comment->author()->url(), $comment->author()->name ) ) )}
<br />
@@ -1305,7 +1466,7 @@
{$email->language->addToStack("email_ignore_user_pm", FALSE, array( 'sprintf' => array( $comment->author()->name ) ) )}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_member_follow</template_name><template_content_html><![CDATA[{$email->language->addToStack("notification__member_follow", FALSE, array( 'sprintf' => array( $member->name ) ) )}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_member_follow</template_name><template_content_html><![CDATA[{$email->language->addToStack("notification__member_follow", FALSE, array( 'sprintf' => array( $member->name ) ) )}
<br />
<br />
@@ -1359,7 +1520,7 @@
{$email->language->addToStack("email_member_following_tip", FALSE)}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_mention</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_mention</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_mentioned", FALSE, array( 'sprintf' => array( $content->author()->name, $content->indefiniteArticle( $email->language ), ( $content instanceof \IPS\Content\Comment ) ? $content->item()->mapped('title') : $content->mapped('title') ) ) )}
<br />
@@ -1405,7 +1566,7 @@
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>]]></template_content_html><template_data>$content, $email</template_data><template_content_plaintext><![CDATA[
{$email->language->addToStack("email_mentioned", FALSE, array( 'sprintf' => array( $content->author()->name, $content->indefiniteArticle( $email->language ), ( $content instanceof \IPS\Content\Comment ) ? $content->item()->mapped('title') : $content->mapped('title') ) ) )}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>registration_complete</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>registration_complete</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_reg_complete", FALSE, array( 'sprintf' => array( \IPS\Settings::i()->board_name ) ) )}
<br />
@@ -1417,7 +1578,7 @@
{$email->language->addToStack("email_go_to_site", FALSE, array( 'sprintf' => array( \IPS\Settings::i()->board_name ) ) )}: {setting="base_url"}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_club_invitation</template_name><template_content_html><![CDATA[<br />
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_club_invitation</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack("notification__club_invitation_by", FALSE, array( 'sprintf' => array( $invitedBy->name, $club->name ) ) )}
<br /><br />
<table width='100%' cellpadding='15' cellspacing='0' border='0' style='background: #f9f9f9;'>
@@ -1428,13 +1589,13 @@
</tr>
</table>]]></template_content_html><template_data>$club, $invitedBy, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("notification__club_invitation_by", FALSE, array( 'sprintf' => array( $invitedBy->name, $club->name ) ) )}
{$email->language->addToStack("club_join", FALSE)}: {$club->url()}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>unsubscribeBulk</template_name><template_content_html><![CDATA[<tr>
{$email->language->addToStack("club_join", FALSE)}: {$club->url()}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>unsubscribeBulk</template_name><template_content_html><![CDATA[<tr>
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}' valign='middle' align='center' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 12px; line-height: 18px; padding-left: 10px;">
{$email->language->addToStack("unsubscribe_blurb", FALSE)} <a href="{{if $member instanceof \IPS\Member}}{url="app=core&module=system&controller=unsubscribe&email=" seoTemplate="unsubscribe" base="front"}{$member->email}&key={expression="md5( $member->email . ':' . $member->members_pass_hash )"}{{else}}{url="app=core&module=system&controller=unsubscribe&email=" seoTemplate="unsubscribe" base="front"}*|member_email|*&key=*|unsubscribe_key|*{{endif}}" style='color: #4a8aca; text-decoration: none; display: inline-block'>{$email->language->addToStack( 'unsubscribe', FALSE )}</a>.
<br />
</td>
</tr>]]></template_content_html><template_data>$member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'unsubscribe', FALSE )}: {{if $member instanceof \IPS\Member}}{url="app=core&module=system&controller=unsubscribe&email=" seoTemplate="unsubscribe" base="front"}{$member->email}&key={expression="md5( $member->email . ':' . $member->members_pass_hash )"}{{else}}*|unsubscribe_url|*{{endif}}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_new_review</template_name><template_content_html><![CDATA[
</tr>]]></template_content_html><template_data>$member, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'unsubscribe', FALSE )}: {{if $member instanceof \IPS\Member}}{url="app=core&module=system&controller=unsubscribe&email=" seoTemplate="unsubscribe" base="front"}{$member->email}&key={expression="md5( $member->email . ':' . $member->members_pass_hash )"}{{else}}*|unsubscribe_url|*{{endif}}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_review</template_name><template_content_html><![CDATA[
{{$item = $comment->item();}}
{{if $comment->author()->member_id}}
@@ -1492,7 +1653,7 @@
{$email->language->addToStack("go_to_this_review", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $comment::$title, FALSE ) ) ) ) )}: {$comment->url('find')}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_profile_reply</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_profile_reply</template_name><template_content_html><![CDATA[
{{$item = $comment->item();}}
{$email->language->addToStack("email_new_profile_reply", FALSE, array( 'sprintf' => array( $comment->author()->url(), $comment->author()->name ) ) )}
@@ -1582,7 +1743,7 @@
{$email->language->addToStack("email_ignore_user_pm", FALSE, array( 'sprintf' => array( $comment->author()->name ) ) )}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>new_device</template_name><template_content_html><![CDATA[<br />
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>new_device</template_name><template_content_html><![CDATA[<br />
{$email->language->addToStack('new_device_email_intro')}
<br />
<br />
@@ -1622,7 +1783,8 @@
<tr>
{{if in_array( $device->userAgent()->browser, array( 'Android Browser', 'AppleWebKit', 'Camino', 'Chrome', 'Edge', 'Firefox', 'IEMobile', 'Midori', 'MSIE', 'Opera', 'Puffin', 'Safari', 'SamsungBrowser', 'Silk', 'UCBrowser', 'Vivaldi' ) )}}
<td dir='{dir}' width="40" valign="top" class='hidePhone' style='width: 0; max-height: 0; overflow: hidden; float: left;'>
&nbsp;<img src="{resource="logos/browsers/{expression="str_replace( ' ', '', $device->userAgent()->browser )"}.png" location="interface" app="core"}" width="20" height="20" alt="{$device->userAgent()->browser}" style='border: 0; vertical-align: middle;'>
{{$img = str_replace( ' ', '', $device->userAgent()->browser );}}
&nbsp;<img src="{resource="logos/browsers/{$img}.png" location="interface" app="core"}" width="20" height="20" alt="{$device->userAgent()->browser}" style='border: 0; vertical-align: middle;'>
</td>
{{endif}}
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;">
@@ -1633,19 +1795,17 @@
</td>
</td>
</tr>
{{if $device->login_handler and $handlers = \IPS\Login::handlers( TRUE ) and isset( $handlers[ $device->login_handler ] ) and $handler = $handlers[ $device->login_handler ] and method_exists( $handler, 'loginForm' )}}
{{if $loginMethod = $device->loginMethod() and $logo = $loginMethod->logoForDeviceInformation()}}
<tr class='responsive_row' height="35">
<td dir='{dir}' width='150' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;"><strong>{$email->language->addToStack('device_table_login_handler')}</strong></td>
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f9f9f9">
<tr>
{{if $handler::$logo}}
<td dir='{dir}' width="40" valign="top" class='hidePhone' style='width: 0; max-height: 0; overflow: hidden; float: left;'>
&nbsp;<img src="{resource="logos/login/{$handler::$logo}.png" location="interface" app="core"}" width="20" height="20" alt="{$device->userAgent()->browser}" style='border: 0; vertical-align: middle;'>
</td>
{{endif}}
<td dir='{dir}' width="40" valign="top" class='hidePhone' style='width: 0; max-height: 0; overflow: hidden; float: left;'>
&nbsp;<img src="{$logo}" width="20" height="20" alt="{$email->language->addToStack( 'login_method_' . $loginMethod->id )}" style='border: 0; vertical-align: middle;'>
</td>
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 14px;">
{$email->language->addToStack( 'login_handler_' . ucfirst( $handler->key ) )}
{$email->language->addToStack( 'login_method_' . $loginMethod->id )}
</td>
</tr>
</table>
@@ -1687,13 +1847,13 @@
{$email->language->addToStack('members_device')}: {$device->userAgent()->platform}
{$email->language->addToStack('device_table_user_agent')}: {$device->userAgent()->browser}
{{if $device->login_handler and $handlers = \IPS\Login::handlers( TRUE ) and isset( $handlers[ $device->login_handler ] ) and $handler = $handlers[ $device->login_handler ] and method_exists( $handler, 'loginForm' )}}{$email->language->addToStack('device_table_login_handler')}: {$email->language->addToStack( 'login_handler_' . ucfirst( $handler->key ) )}{{endif}}
{{if $loginMethod = $device->loginMethod() and $logo = $loginMethod->logoForDeviceInformation()}}{$email->language->addToStack('device_table_login_handler')}: {$email->language->addToStack( 'login_method_' . $loginMethod->id )}{{endif}}
{{if $location}}{$email->language->addToStack('device_table_location')}: {$location}
* {$email->language->addToStack('ip_geolocation_info')}{{endif}}
{$email->language->addToStack('new_device_email_footer')}
{{if settings.device_management}}{$email->language->addToStack("new_device_email_action_1", FALSE)}: {url="app=core&module=system&controller=settings&area=devices" seoTemplate="settings_devices" base="front"}{{else}}{$email->language->addToStack("new_device_email_action_2", FALSE)}: {url="app=core&module=system&controller=settings&do=secureAccount" seoTemplate="settings_secure" base="front"}{{endif}}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>digest</template_name><template_content_html><![CDATA[{$email->language->addToStack("digest_$frequency", FALSE)}
{{if settings.device_management}}{$email->language->addToStack("new_device_email_action_1", FALSE)}: {url="app=core&module=system&controller=settings&area=devices" seoTemplate="settings_devices" base="front"}{{else}}{$email->language->addToStack("new_device_email_action_2", FALSE)}: {url="app=core&module=system&controller=settings&do=secureAccount" seoTemplate="settings_secure" base="front"}{{endif}}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>digest</template_name><template_content_html><![CDATA[{$email->language->addToStack("digest_$frequency", FALSE)}
<br />
<br />
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>
@@ -1703,7 +1863,7 @@
<br />
<%digest%>]]></template_content_html><template_data>$member, $frequency, $email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("digest_$frequency", FALSE)}
<%digest%>]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_report_center</template_name><template_content_html><![CDATA[{{$item = $reportedContent instanceof \IPS\Content\Item ? $reportedContent : $reportedContent->item();}}
<%digest%>]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_report_center</template_name><template_content_html><![CDATA[{{$item = $reportedContent instanceof \IPS\Content\Item ? $reportedContent : $reportedContent->item();}}
{$email->language->addToStack("email_content_reported", FALSE, array( 'sprintf' => array( \IPS\Member::load( $latestReport['report_by'] )->name, $reportedContent->indefiniteArticle( $email->language ), $reportedContent->url(), $item->mapped('title') ) ) )}
<br />
@@ -1747,7 +1907,7 @@
{$email->language->addToStack("go_to_the_report", FALSE)}: {$report->url()}
{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $reportedContent::$title, FALSE ) ) ) ) )}: {$reportedContent->url()}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>unsubscribeFollow</template_name><template_content_html><![CDATA[<tr>
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>unsubscribeFollow</template_name><template_content_html><![CDATA[<tr>
<td dir='{dir}' valign="top"><img src='{setting="base_url"}applications/core/interface/email/spacer.png' width='1' height='1' alt=''></td>
<td dir='{dir}' valign='middle' align='center' style="font-family: 'Helvetica Neue', helvetica, sans-serif; font-size: 12px; line-height: 18px; padding-left: 10px;">
*|unsubscribe_blurb|*<br>
@@ -1756,7 +1916,7 @@
</td>
</tr>
]]></template_content_html><template_data>$key, $member, $email</template_data><template_content_plaintext><![CDATA[*|unsubscribe_blurb|*
{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'unfollow', FALSE )} ( *|unfollow_link|* ) {$email->language->addToStack( 'or', FALSE )} {$email->language->addToStack( 'adjust_notification_prefs', FALSE )} ( {url="app=core&module=system&controller=notifications&do=options&type={$key}" base="front" seoTemplate="notifications_options"} )]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>notification_new_comment</template_name><template_content_html><![CDATA[
{$email->language->addToStack("unsubscribe_blurb", FALSE)} {$email->language->addToStack( 'unfollow', FALSE )} ( *|unfollow_link|* ) {$email->language->addToStack( 'or', FALSE )} {$email->language->addToStack( 'adjust_notification_prefs', FALSE )} ( {url="app=core&module=system&controller=notifications&do=options&type={$key}" base="front" seoTemplate="notifications_options"} )]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_comment</template_name><template_content_html><![CDATA[
{{$item = $comment->item();}}
{{if $comment->author()->member_id}}
@@ -1812,7 +1972,7 @@
{$email->language->addToStack("go_to_this_post", FALSE)}: {$comment->url('find')}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>warning</template_name><template_content_html><![CDATA[{{$content = $warning->content();}}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>warning</template_name><template_content_html><![CDATA[{{$content = $warning->content();}}
{{if $content && $content instanceof \IPS\Content\Comment}}
{{$item = $content->item();}}
@@ -1970,7 +2130,7 @@
{{endif}}
-- {setting="board_name"}
]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>error_log</template_name><template_content_html><![CDATA[
]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>error_log</template_name><template_content_html><![CDATA[
{expression="$email->language->addToStack( 'email_error_log', FALSE, array( 'sprintf' => array( \IPS\Settings::i()->board_name ) ) )"}
<br />
<br />
@@ -2021,7 +2181,7 @@
{$email->language->addToStack("email_error_log_info", FALSE)}
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>mfaRecovery</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>mfaRecovery</template_name><template_content_html><![CDATA[
{$email->language->addToStack("mfa_recovery_email_html", FALSE)}
<br /><br />
@@ -2037,7 +2197,7 @@
{$email->language->addToStack("mfa_recovery_email_confirm", FALSE)}: {url="app=core&module=system&controller=settings&do=mfarecoveryvalidate&vid={$vid}&mid={$member->member_id}" seoTemplate="mfarecoveryvalidate" plain="true" base="front"}
=====
-- {setting="board_name"}]]></template_content_plaintext></template><template><template_app>core</template_app><template_name>password_changed</template_name><template_content_html><![CDATA[
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>password_changed</template_name><template_content_html><![CDATA[
{$email->language->addToStack("email_password_changed", FALSE, array( 'sprintf' => \IPS\Settings::i()->board_name ) )}
@@ -2045,4 +2205,48 @@
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>]]></template_content_html><template_data>$email</template_data><template_content_plaintext><![CDATA[{$email->language->addToStack("email_password_changed", FALSE, array( 'sprintf' => \IPS\Settings::i()->board_name ) )}
-- {setting="board_name"}]]></template_content_plaintext></template></emails>
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template><template><template_app>core</template_app><template_name>notification_new_react</template_name><template_content_html><![CDATA[
{{$item = ( $comment instanceof \IPS\Content\Item ) ? $comment : $comment->item();}}
{$email->language->addToStack("email_new_reacted", FALSE, array( 'sprintf' => array( ( $comment->author()->group['gbw_view_reps'] ) ? $member->name : $email->language->get( 'notifications_anonymous' ), $comment->indefiniteArticle( $email->language ) ) ) )} <a href='{$item->url()}'>{$item->mapped('title')}</a>
<br />
<br />
<table width='100%' cellpadding='0' cellspacing='0' border='0'>
<tr>
<td dir='{dir}' width='40' valign='top' class='hidePhone' style='width: 0; max-height: 0; overflow: hidden; float: left;'>
<img src='{$comment->author()->get_photo( true, true )}' width='40' height='40' style='border: 1px solid #777777; vertical-align: middle;'>
</td>
<td dir='{dir}' width='30' valign='top' class='hidePhone' style='width: 0; max-height: 0; overflow: hidden; float: left;'>
<br />
<span style='display: block; width: 0px; height: 0px; border-width: 15px; border-color: transparent #f9f9f9 transparent transparent; border-style: solid'></span>
</td>
<td dir='{dir}' valign='top' style='background: #f9f9f9;'>
<table width='100%' cellpadding='10' cellspacing='0' border='0'>
<tr>
<td dir='{dir}'>
<table width='100%' cellpadding='5' cellspacing='0' border='0'>
<tr>
<td dir='{dir}' style="font-family: 'Helvetica Neue', helvetica, sans-serif; line-height: 1.5; font-size: 14px;">
{$email->parseTextForEmail( $comment->content(), $email->language )|raw}
</td>
</tr>
<tr>
<td dir='{dir}'>
<a href='{$comment->url()}' style="color: #ffffff; font-family: 'Helvetica Neue', helvetica, sans-serif; text-decoration: none; font-size: 14px; background: {setting="email_color"}; line-height: 32px; padding: 0 10px; display: inline-block; border-radius: 3px;">{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $comment::$title, FALSE ) ) ) ) )}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<br /><br />
<em style='color: #8c8c8c'>&mdash; {setting="board_name"}</em>]]></template_content_html><template_data>$comment, $member, $email</template_data><template_content_plaintext><![CDATA[
{{$item = ( $comment instanceof \IPS\Content\Item ) ? $comment : $comment->item();}}{$email->language->addToStack("email_new_reacted", FALSE, array( 'sprintf' => array( ( $comment->author()->group['gbw_view_reps'] ) ? $member->name : $email->language->get( 'notifications_anonymous' ), $item->indefiniteArticle( $email->language ) ) ) )} {$item->mapped('title')}
{$email->language->addToStack("go_to_this_x", FALSE, array( 'sprintf' => array( mb_strtolower( $email->language->addToStack( $item::$title, FALSE ) ) ) ) )}: {$item->url()}
-- {setting="board_name"}]]></template_content_plaintext><template_pinned>0</template_pinned></template></emails>
File diff suppressed because it is too large. Load diff
+13 -20
View File
@@ -75,26 +75,14 @@
"settings_signature": {
"friendly": "settings/signature",
"real": "app=core&module=system&controller=settings&area=signature"
},
"settings_Facebook": {
"friendly": "settings/facebook",
"real": "app=core&module=system&controller=settings&area=profilesync&service=Facebook"
},
"settings_Google": {
"friendly": "settings/google",
"real": "app=core&module=system&controller=settings&area=profilesync&service=Google"
},
"settings_Linkedin": {
"friendly": "settings/linkedin",
"real": "app=core&module=system&controller=settings&area=profilesync&service=LinkedIn"
},
"settings_Microsoft": {
"friendly": "settings/microsoft",
"real": "app=core&module=system&controller=settings&area=profilesync&service=Microsoft"
},
"settings_Twitter": {
"friendly": "settings/twitter",
"real": "app=core&module=system&controller=settings&area=profilesync&service=Twitter"
},
"settings_login": {
"friendly": "settings/login",
"real": "app=core&module=system&controller=settings&area=login"
},
"settings_apps": {
"friendly": "settings/apps",
"real": "app=core&module=system&controller=settings&area=apps"
},
"settings": {
"friendly": "settings",
@@ -117,6 +105,11 @@
"friendly": "attachments",
"real": "app=core&module=system&controller=attachments"
},
"cookies": {
"friendly": "cookies",
"real": "app=core&module=system&controller=cookies",
"alias": "cookies"
},
/* Messenger */
"messenger_compose": {
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+7 -15
View File
@@ -86,7 +86,11 @@
},
"messengerstats": {
"default_controller": "",
"protected": false
"protected": 0
},
"moderation": {
"default_controller": "",
"protected": 0
}
},
"front": {
@@ -119,7 +123,7 @@
"protected": 0
},
"contact": {
"default_controller": null,
"default_controller": "contact",
"protected": 0
},
"status": {
@@ -139,19 +143,7 @@
"protected": 0
},
"promote": {
"default_controller": "promote",
"protected": 0
},
"activity": {
"default_controller": "",
"protected": 0
},
"dashboard": {
"default_controller": "dashboard",
"protected": 1
},
"discovery": {
"default_controller": "",
"default_controller": "ourpicks",
"protected": 0
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1 +1 @@
{"profilesync":"P0Y0M0DT1H0M0S","updatecheck":"P0Y0M1DT0H0M0S","cleanup":"P0Y0M1DT0H0M0S","sitemapgenerator":"P0Y0M0DT0H15M0S","connect":"P0Y0M0DT1H0M0S","queue":"P0Y0M0DT0H1M0S","pop":"P0Y0M0DT0H10M0S","digest":"P0Y0M0DT0H10M0S","publish":"P0Y0M0DT0H10M0S","clearcache":"P0Y0M0DT0H10M0S","clearsessions":"P0Y0M0DT0H30M0S","viewupdates":"P0Y0M0DT0H5M0S","imageproxy":"P0Y0M0DT0H30M0S","cleanupattachments":"P0Y0M0DT12H0M0S","cleanupfiles":"P0Y0M1DT0H0M0S","unlockmembers":"P0Y0M0DT1H0M0S","leaderboard":"P0Y0M0DT6H0M0S","deletecontent":"P0Y0M1DT0H0M0S","clubrebuild":"P0Y0M0DT0H20M0S","onlineusers":"P0Y0M0DT0H15M0S","promote":"P0Y0M0DT0H5M0S"}
{"profilesync":"P0Y0M0DT1H0M0S","updatecheck":"P0Y0M1DT0H0M0S","cleanup":"P0Y0M1DT0H0M0S","sitemapgenerator":"P0Y0M0DT0H15M0S","queue":"P0Y0M0DT0H1M0S","pop":"P0Y0M0DT0H10M0S","digest":"P0Y0M0DT0H10M0S","publish":"P0Y0M0DT0H10M0S","clearcache":"P0Y0M0DT0H10M0S","clearsessions":"P0Y0M0DT0H30M0S","viewupdates":"P0Y0M0DT0H5M0S","imageproxy":"P0Y0M0DT0H30M0S","cleanupattachments":"P0Y0M0DT12H0M0S","cleanupfiles":"P0Y0M1DT0H0M0S","unlockmembers":"P0Y0M0DT1H0M0S","leaderboard":"P0Y0M0DT6H0M0S","deletecontent":"P0Y0M1DT0H0M0S","clubrebuild":"P0Y0M0DT0H20M0S","onlineusers":"P0Y0M0DT0H15M0S","promote":"P0Y0M0DT0H5M0S","prunesearchindex":"P0Y0M1DT0H0M0S","prunesystemlogs":"P0Y0M1DT0H0M0S","automaticmoderation":"P0Y0M0DT0H10M0S","usagereporting":"P0Y0M7DT0H0M0S","userAgentStatistics":"P0Y0M0DT0H30M0S"}
File diff suppressed because it is too large. Load diff
+52 -1
View File
@@ -212,5 +212,56 @@
"101117": "4.2.0 Beta 8",
"102000": "4.2.0",
"102001": "4.2.0",
"102002": "4.2.0"
"102002": "4.2.0",
"102003": "4.2.0",
"102004": "4.2.1",
"102005": "4.2.2 Beta 1",
"102006": "4.2.2 Beta 2",
"102007": "4.2.2",
"102008": "4.2.2",
"102009": "4.2.2",
"102010": "4.2.3 Beta 1",
"102011": "4.2.3",
"102012": "4.2.3",
"102013": "4.2.3",
"102014": "4.2.4 Beta 1",
"102015": "4.2.4 Beta 2",
"102016": "4.2.4",
"102017": "4.2.4",
"102018": "4.2.4",
"102019": "4.2.4",
"102020": "4.2.4",
"102021": "4.2.5 Beta 1",
"102022": "4.2.5 Beta 2",
"102023": "4.2.5 Beta 3",
"102024": "4.2.5",
"102025": "4.2.5",
"102026": "4.2.5",
"102027": "4.2.5",
"102028": "4.2.5",
"102029": "4.2.5",
"102030": "4.2.6 Beta 1",
"102031": "4.2.6 Beta 1",
"102032": "4.2.6 Beta 2",
"102033": "4.2.6 Beta 3",
"102034": "4.2.6",
"102035": "4.2.6",
"102036": "4.2.6",
"102037": "4.2.7 Beta 1",
"102038": "4.2.7 Beta 2",
"102039": "4.2.7 Beta 3",
"102040": "4.2.7 Beta 4",
"102041": "4.2.7 Beta 5",
"102042": "4.2.7",
"102043": "4.2.7",
"102044": "4.2.7",
"102045": "4.2.7",
"102046": "4.2.7",
"102047": "4.2.8",
"102048": "4.2.8",
"103000": "4.3.0 Beta 1",
"103002": "4.3.0 Beta 2",
"103003": "4.3.0 Beta 3",
"103004": "4.3.0 Beta 4",
"103005": "4.3.0"
}
+12 -12
View File
@@ -7,17 +7,6 @@
"menu_style": "menu",
"embeddable": true
},
"announcements": {
"class": "\\IPS\\Widget\\PermissionCache",
"restrict": [
"sidebar",
"cms"
],
"default_area": "header",
"allow_reuse": false,
"menu_style": "menu",
"embeddable": true
},
"stats": {
"class": "\\IPS\\Widget\\StaticCache",
"restrict": [],
@@ -101,5 +90,16 @@
"allow_reuse": true,
"menu_style": "modal",
"embeddable": true
},
"newsletter": {
"class": "\\IPS\\Widget",
"restrict": [
"sidebar",
"cms"
],
"default_area": "sidebar",
"allow_reuse": false,
"menu_style": "modal",
"embeddable": true
}
}
}
@@ -0,0 +1,119 @@
<?php
/**
* @brief Community Enhancement: Facebook Pixel
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @since 16 May 2017
*/
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: Facebook Pixel
*/
class _FacebookPixel
{
/**
* @brief IPS-provided enhancement?
*/
public $ips = FALSE;
/**
* @brief Enhancement is enabled?
*/
public $enabled = FALSE;
/**
* @brief Enhancement has configuration options?
*/
public $hasOptions = TRUE;
/**
* @brief Icon data
*/
public $icon = "facebook.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$this->enabled = ( \IPS\Settings::i()->fb_pixel_enabled and \IPS\Settings::i()->fb_pixel_id );
}
/**
* Edit
*
* @return void
*/
public function edit()
{
$validation = function( $val ) {
if ( $val and !\IPS\Request::i()->fb_pixel_id )
{
throw new \DomainException('fb_pixel_id_req');
}
};
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Text( 'fb_pixel_id', \IPS\Settings::i()->fb_pixel_id ? \IPS\Settings::i()->fb_pixel_id : '', FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'fb_pixel_enabled', \IPS\Settings::i()->fb_pixel_enabled, FALSE, array(), $validation ) );
$form->add( new \IPS\Helpers\Form\Number( 'fb_pixel_delay', \IPS\Settings::i()->fb_pixel_delay, FALSE, array(), $validation, NULL, \IPS\Member::loggedIn()->language()->addToStack('fb_pixel_delay_seconds') ) );
if ( $form->values() )
{
$form->saveAsSettings();
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_FacebookPixel' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
\IPS\Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'learn_more',
'icon' => 'question-circle',
'link' => \IPS\Http\Url::ips( 'docs/facebookpixel' ),
'target' => '_blank'
),
);
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_FacebookPixel', $form );
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
*/
public function toggle( $enabled )
{
if ( $enabled )
{
if ( \IPS\Settings::i()->fb_pixel_id )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'fb_pixel_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
}
else
{
throw new \DomainException;
}
}
else
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'fb_pixel_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
}
}
}
@@ -79,7 +79,6 @@ class _GeoIP
*/
public function toggle( $enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'ipsgeoip' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'ipsgeoip' => 0 ) );
}
}
@@ -112,9 +112,15 @@ class _GoogleMaps
} ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'googlemaps', \IPS\Settings::i()->googlemaps, FALSE, array(), $validation ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'googleplacesautocomplete', \IPS\Settings::i()->googleplacesautocomplete, FALSE, array(), $validation ) );
if ( $form->values() )
if ( $values = $form->values() )
{
$form->saveAsSettings();
if( $values['googlemaps'] > 0 )
{
$values['mapbox'] = 0;
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_GoogleMaps' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
@@ -139,22 +145,21 @@ class _GoogleMaps
*/
public function toggle( $enabled )
{
if ( $enabled )
/* If we're disabling, just disable */
if( !$enabled )
{
if ( \IPS\Settings::i()->google_maps_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( '( conf_key=? OR conf_key=? )', 'googlemaps', 'googleplacesautocomplete' ) );
unset( \IPS\Data\Store::i()->settings );
}
else
{
throw new \DomainException;
}
\IPS\Settings::i()->changeValues( array( 'googlemaps' => 0, 'googleplacesautocomplete' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle on */
if( $enabled && \IPS\Settings::i()->google_maps_api_key )
{
\IPS\Settings::i()->changeValues( array( 'googlemaps' => 1, 'googleplacesautocomplete' => 1, 'mapbox' => 0 ) );
}
else
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( '( conf_key=? OR conf_key=? )', 'googlemaps', 'googleplacesautocomplete' ) );
unset( \IPS\Data\Store::i()->settings );
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
throw new \DomainException;
}
}
}
@@ -0,0 +1,147 @@
<?php
/**
* @brief Community Enhancement: Mapbox
* @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 1 Nov 2017
*/
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: Mapbox
*/
class _Mapbox
{
/**
* @brief IPS-provided enhancement?
*/
public $ips = FALSE;
/**
* @brief Enhancement is enabled?
*/
public $enabled = FALSE;
/**
* @brief Enhancement has configuration options?
*/
public $hasOptions = TRUE;
/**
* @brief Icon data
*/
public $icon = "mapbox.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$this->enabled = ( \IPS\Settings::i()->mapbox_api_key and ( \IPS\Settings::i()->mapbox ) );
}
/**
* Edit
*
* @return void
*/
public function edit()
{
$validation = function( $val ) {
if ( $val and !\IPS\Request::i()->mapbox_api_key )
{
throw new \DomainException('mapbox_api_key_req');
}
};
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Text( 'mapbox_api_key', \IPS\Settings::i()->mapbox_api_key, FALSE, array(), function( $val ) {
if ( $val )
{
/* Check API */
try
{
$location = '-73.961452,40.714224';
$response = \IPS\Http\Url::external( "https://api.mapbox.com/geocoding/v5/mapbox.places/{$location}.json" )->setQueryString( array(
'access_token' => $val,
) )->request()->get()->decodeJson();
}
catch ( \Exception $e )
{
throw new \DomainException('mapbox_api_error');
}
if ( isset( $response['message'] ) )
{
throw new \DomainException('mapbox_api_key_invalid');
}
}
} ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'mapbox', \IPS\Settings::i()->mapbox, FALSE, array(), $validation ) );
if ( $values = $form->values() )
{
if( $values['mapbox'] > 0 )
{
$values['googlemaps'] = 0;
$values['googleplacesautocomplete'] = 0;
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_MapboxMaps' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
\IPS\Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'learn_more',
'icon' => 'question-circle',
'link' => \IPS\Http\Url::ips( 'docs/mapboxmaps' ),
'target' => '_blank'
),
);
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_MapboxMaps', $form );
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
*/
public function toggle( $enabled )
{
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Settings::i()->changeValues( array( 'mapbox' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle on */
if( $enabled && \IPS\Settings::i()->mapbox_api_key )
{
\IPS\Settings::i()->changeValues( array( 'mapbox' => 1, 'googlemaps' => 0, 'googleplacesautocomplete' => 0 ) );
}
else
{
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
throw new \DomainException;
}
}
}
@@ -76,18 +76,23 @@ class _SendGrid
$form->add( new \IPS\Helpers\Form\YesNo( 'sendgrid_click_tracking', \IPS\Settings::i()->sendgrid_click_tracking, FALSE, array(), NULL, NULL, NULL, 'sendgrid_click_tracking' ) );
$form->add( new \IPS\Helpers\Form\Text( 'sendgrid_ip_pool', \IPS\Settings::i()->sendgrid_ip_pool ?: NULL, FALSE, array( 'nullLang' => 'sendgrid_ip_pool_none' ), NULL, NULL, NULL, 'sendgrid_ip_pool' ) );
if ( $form->values() )
if ( $values = $form->values() )
{
try
{
$this->testSettings( $form->values() );
$this->testSettings( $values );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( $e->getMessage(), '2C339/1', 500 );
}
$form->saveAsSettings();
if( $values['sendgrid_use_for'] > 0 )
{
$values['sparkpost_use_for'] = 0;
}
$form->saveAsSettings( $values );
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_SendGrid' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
@@ -116,15 +121,13 @@ class _SendGrid
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'sendgrid_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'sendgrid_use_for' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle bulk mail on */
if( $enabled && \IPS\Settings::i()->sendgrid_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'sendgrid_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'sendgrid_use_for' => 1, 'sparkpost_use_for' => 0 ) );
}
else
{
@@ -147,8 +150,7 @@ class _SendGrid
{
if( \IPS\Settings::i()->mail_method == 'sendgrid' )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'mail' ), array( 'conf_key=?', 'mail_method' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'mail_method' => 'mail' ) );
}
return;
@@ -50,7 +50,7 @@ class _SpamMonitoring
public function __construct()
{
$licenseData = \IPS\IPS::licenseKey();
if( !$licenseData or !isset( $licenseData['products']['spam'] ) or !$licenseData['products']['spam'] or strtotime( $licenseData['expires'] ) < time() )
if( !$licenseData or !isset( $licenseData['products']['spam'] ) or !$licenseData['products']['spam'] or ( !$licenseData['cloud'] AND strtotime( $licenseData['expires'] ) < time() ) )
{
$this->enabled = false;
}
@@ -77,7 +77,7 @@ class _SpamMonitoring
}
catch ( \LogicException $e )
{
//Do not show error message. We will see error message in Settings
// \IPS\Output::i()->error( $e->getMessage(), '2C116/2', 403, '' );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( 'app=core&module=membersettings&controller=spam&tab=service' ) );
@@ -97,8 +97,7 @@ class _SpamMonitoring
$this->testSettings();
}
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $enabled ), array( 'conf_key=?', 'spam_service_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'spam_service_enabled' => $enabled ) );
}
/**
@@ -115,7 +114,7 @@ class _SpamMonitoring
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('spam_service_nokey', FALSE, array( 'sprintf' => array( \IPS\Http\Url::internal( 'app=core&module=settings&controller=licensekey', null ) ) ) ) );
}
if ( strtotime( $licenseData['expires'] ) < time() )
if ( !$licenseData['cloud'] AND strtotime( $licenseData['expires'] ) < time() )
{
throw new \DomainException('licensekey_expired');
}
@@ -1,198 +1,3 @@
<?php
/**
* @brief Community Enhancements: Sparkpost integration
* @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 March 2016
*/
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 Enhancements: Sparkpost integration
*/
class _Sparkpost
{
/**
* @brief IPS-provided enhancement?
*/
public $ips = FALSE;
/**
* @brief Enhancement is enabled?
*/
public $enabled = FALSE;
/**
* @brief Enhancement has configuration options?
*/
public $hasOptions = TRUE;
/**
* @brief Icon data
*/
public $icon = "sparkpost.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$this->enabled = ( \IPS\Settings::i()->sparkpost_api_key && \IPS\Settings::i()->sparkpost_use_for );
}
/**
* Edit
*
* @return void
*/
public function edit()
{
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Radio( 'sparkpost_use_for', \IPS\Settings::i()->sparkpost_use_for, TRUE, array(
'options' => array(
'0' => 'sparkpost_donot_use',
'1' => 'sparkpost_bulkmail_use',
'2' => 'sparkpost_all_use'
),
'toggles' => array(
'0' => array(),
'1' => array( 'sparkpost_api_key', 'sparkpost_click_tracking', 'sparkpost_ip_pool' ),
'2' => array( 'sparkpost_api_key', 'sparkpost_click_tracking', 'sparkpost_ip_pool' ),
)
) ) );
$form->add( new \IPS\Helpers\Form\Text( 'sparkpost_api_key', \IPS\Settings::i()->sparkpost_api_key, FALSE, array(), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('sparkpost_api_key_suffix'), 'sparkpost_api_key' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'sparkpost_click_tracking', \IPS\Settings::i()->sparkpost_click_tracking, FALSE, array(), NULL, NULL, NULL, 'sparkpost_click_tracking' ) );
$form->add( new \IPS\Helpers\Form\Text( 'sparkpost_ip_pool', \IPS\Settings::i()->sparkpost_ip_pool ?: NULL, FALSE, array( 'nullLang' => 'sparkpost_ip_pool_none' ), NULL, NULL, NULL, 'sparkpost_ip_pool' ) );
if ( $form->values() )
{
try
{
$this->testSettings( $form->values() );
}
catch ( \Exception $e )
{
\IPS\Output::i()->error( $e->getMessage(), '2S123/1', 500 );
}
$form->saveAsSettings();
\IPS\Session::i()->log( 'acplog__enhancements_edited', array( 'enhancements__core_Sparkpost' => TRUE ) );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('saved');
}
\IPS\Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'learn_more',
'icon' => 'question-circle',
'link' => \IPS\Http\Url::ips( 'docs/sparkpost' ),
'target' => '_blank'
),
);
\IPS\Output::i()->output = $form;
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
* @throws \DomainException
*/
public function toggle( $enabled )
{
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'sparkpost_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
}
/* Otherwise if we already have an API key, just toggle bulk mail on */
if( $enabled && \IPS\Settings::i()->sparkpost_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'sparkpost_use_for' ) );
unset( \IPS\Data\Store::i()->settings );
}
else
{
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
throw new \DomainException;
}
}
/**
* Test Settings
*
* @param array $values Form values
* @return void
* @throws \LogicException
*/
protected function testSettings( $values )
{
/* If we've disabled, just shut off */
if( (int) $values['sparkpost_use_for'] === 0 )
{
if( \IPS\Settings::i()->mail_method == 'sparkpost' )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'mail' ), array( 'conf_key=?', 'mail_method' ) );
unset( \IPS\Data\Store::i()->settings );
}
return;
}
/* If we enable Sparkpost but do not supply an API key, this is a problem */
if( !$values['sparkpost_api_key'] )
{
throw new \InvalidArgumentException( "sparkpost_enable_need_details" );
}
/* Test Sparkpost settings */
try
{
$domain = mb_substr( \IPS\Settings::i()->email_out, mb_strpos( \IPS\Settings::i()->email_out, '@' ) + 1 );
$sparkpost = new \IPS\Email\Outgoing\SparkPost( $values['sparkpost_api_key'] );
$sendingDomains = $sparkpost->sendingDomains();
if ( isset( $sendingDomains['errors'] ) )
{
throw new \DomainException('sparkpost_bad_credentials');
}
$isOkay = FALSE;
foreach ( $sendingDomains['results'] as $sendingDomain )
{
if ( $sendingDomain['domain'] == $domain )
{
if ( $sendingDomain['status']['ownership_verified'] )
{
$isOkay = TRUE;
}
else
{
throw new \DomainException('sparkpost_domain_not_verified');
}
}
}
if ( !$isOkay )
{
throw new \DomainException('sparkpost_domain_not_registered');
}
}
catch ( \IPS\Http\Request\Exception $e )
{
throw new \DomainException( 'sparkpost_bad_credentials' );
}
}
}
// this file intentionally left blank
@@ -201,15 +201,13 @@ class _Viglink
/* If we're disabling, just disable */
if( !$enabled )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'viglink_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'viglink_enabled' => 0 ) );
}
/* Otherwise if we already have an API key, just toggle on */
if( $enabled && \IPS\Settings::i()->viglink_api_key )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 1 ), array( 'conf_key=?', 'viglink_enabled' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'viglink_enabled' => 1 ) );
}
/* Otherwise we need to let them enter an API key before we can enable. Throwing an exception causes you to be redirected to the settings page. */
else
@@ -76,7 +76,7 @@ class _Email
$sender = explode( ',', \IPS\Settings::i()->contact_emails );
}
$mail = \IPS\Email::buildFromTemplate( 'core', 'contact_form', array( \IPS\Member::loggedIn(), $fromName, $fromEmail, $content ), \IPS\Email::TYPE_TRANSACTIONAL );
$mail->send( $sender , array(), array(), NULL, $fromName, array( 'Reply-To' => \IPS\Email::encodeHeader( $fromName, ( \IPS\Member::loggedIn()->member_id ? \IPS\Member::loggedIn()->email : $values['email_address'] ) ) ) );
$mail->send( $sender , array(), array(), NULL, $fromName, array( 'Reply-To' => \IPS\Email::encodeHeader( $fromName, ( \IPS\Member::loggedIn()->member_id ? \IPS\Member::loggedIn()->email : $values['email_address'] ) ) ), FALSE );
return TRUE;
}
@@ -40,13 +40,11 @@ class _AdminNotes
public function getBlock()
{
$form = new \IPS\Helpers\Form( 'form', 'save', \IPS\Http\Url::internal( "app=core&module=overview&controller=dashboard&do=getBlock&appKey=core&blockKey=core_AdminNotes" )->csrf() );
$form->add( new \IPS\Helpers\Form\TextArea( 'admin_notes', ( isset( \IPS\Settings::i()->acp_notes ) ) ? htmlspecialchars( \IPS\Settings::i()->acp_notes, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) : '' ) );
$form->add( new \IPS\Helpers\Form\TextArea( 'admin_notes', ( isset( \IPS\Settings::i()->acp_notes ) ) ? htmlspecialchars( \IPS\Settings::i()->acp_notes, ENT_DISALLOWED, 'UTF-8', FALSE ) : '' ) );
if( $values = $form->values() )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $values['admin_notes'] ), array( 'conf_key=?', 'acp_notes' ) );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'acp_notes_updated' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'acp_notes' => $values['admin_notes'], 'acp_notes_updated' => time() ) );
if( \IPS\Request::i()->isAjax() )
{
@@ -40,8 +40,8 @@ class _BackgroundQueue
public function getBlock()
{
$rows = array();
$select = \IPS\Db::i()->select( '*', 'core_queue', NULL, 'priority ASC, date ASC' );
$select = \IPS\Db::i()->select( '*', 'core_queue', array( 'app_enabled=?', 1 ), 'priority ASC, date ASC' )->join( 'core_applications', "app=app_directory" );
if ( count( $select ) )
{
foreach ( $select as $queueData )
@@ -1,94 +1,18 @@
<?php
/**
* @brief Dashboard extension: Show a notice if there are failed IPS Connect communications
* @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 03 Dec 2013
*/
// Removed
namespace IPS\core\extensions\core\Dashboard;
/* 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 Dashboard extension: Show a notice if there are failed IPS Connect communications
*/
class _IpsconnectMaster
{
/**
* Can the current user view this dashboard item?
*
* @return bool
*/
public function canView()
{
$handlers = \IPS\Login::handlers( TRUE );
return (bool) ( array_key_exists( 'Ipsconnect', $handlers ) and $handlers['Ipsconnect']->enabled );
return FALSE;
}
/**
* Return the block HTML show on the dashboard
*
* @return string
* @note We may want to consider doing something when there are failed queued requests but the slave record no longer exists. For now I don't want to automatically
clear them because it may be indicative of a bug elsewhere, but down the road we may want to simply delete the requests from the queue table and not show the failure.
*/
public function getBlock()
{
/* Are we deleting the slave? */
if( \IPS\Request::i()->deleteSlave )
{
\IPS\Session::i()->csrfCheck();
\IPS\Db::i()->delete( 'core_ipsconnect_queue', array( 'slave_id=?', intval( \IPS\Request::i()->deleteSlave ) ) );
\IPS\Db::i()->delete( 'core_ipsconnect_slaves', array( 'slave_id=?', intval( \IPS\Request::i()->deleteSlave ) ) );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => \IPS\Db::i()->select( 'COUNT(*)', 'core_ipsconnect_slaves', array( 'slave_enabled=?', 1 ) )->first() ), array( 'conf_key=?', 'connect_slaves' ) );
unset( \IPS\Data\Store::i()->settings );
\IPS\Output::i()->inlineMessage = \IPS\Member::loggedIn()->language()->addToStack('connect_slave_deleted');
\IPS\Session::i()->log( 'acplog__slave_deleted' );
}
/* Do we have any fails to show? */
$fails = \IPS\Db::i()->select( 'DISTINCT(slave_id)', 'core_ipsconnect_queue' );
if( count($fails) )
{
$failures = array();
foreach( $fails as $slave )
{
try
{
$slave = \IPS\Db::i()->select( '*', 'core_ipsconnect_slaves', array( 'slave_id=? AND slave_enabled=?', $slave, 1 ) )->first();
$fails = count( \IPS\Db::i()->select( 'id', 'core_ipsconnect_queue', array( 'slave_id=?', $slave['slave_id'] ) ) );
$failures[] = array(
'slave' => $slave,
'count' => $fails
);
}
catch( \UnderflowException $e )
{
/* This means that the slave record doesn't actually exist or is disabled */
}
}
if( count( $failures ) )
{
return \IPS\Theme::i()->getTemplate( 'dashboard' )->connectFailures( $failures, count($failures) );
}
}
else
{
return \IPS\Theme::i()->getTemplate( 'dashboard' )->noConnectFailures();
}
return '';
}
}
@@ -39,7 +39,7 @@ class _LatestNews
*/
public function getBlock()
{
return NULL;
//
}
/**
@@ -50,6 +50,9 @@ class _LatestNews
*/
protected function refreshNews()
{
return NULL;
\IPS\Data\Store::i()->ips_news = json_encode( array(
'content' => \IPS\Http\Url::ips( 'news' )->request()->get()->decodeJson(),
'time' => time()
) );
}
}
@@ -49,7 +49,7 @@ class _OnlineUsers
/* Add Rows */
$online = array();
$seen = array();
foreach( \IPS\Db::i()->select( "*", 'core_sessions', array( 'running_time>?', \IPS\DateTime::create()->sub( new \DateInterval('PT30M') )->getTimestamp() ), 'running_time DESC' ) as $row )
foreach( \IPS\Session\Store::i()->getOnlineUsers( \IPS\Session\Store::ONLINE_MEMBERS | \IPS\Session\Store::ONLINE_GUESTS, 'desc' ) as $row )
{
$key = ( $row['member_id'] ? $row['member_id'] : $row['id'] );
@@ -29,7 +29,7 @@ class _Registrations
*/
public function canView()
{
return TRUE;
return \IPS\Member::loggedIn()->hasAcpRestriction( 'core' , 'members', 'registrations_manage' );
}
/**
@@ -39,80 +39,31 @@ class _Registrations
*/
public function getBlock()
{
/* Init Chart */
$chart = new \IPS\Helpers\Chart;
/* Specify headers */
$chart->addHeader( \IPS\Member::loggedIn()->language()->addToStack('date'), 'string' ); // Since we're displaying a column chart, it makes more sense to use a string so they plotted as discreet columns rather than in the middle of dates
$chart->addHeader( \IPS\Member::loggedIn()->language()->addToStack('members'), 'number' );
$data = array();
/* We use midnight as a basis so we can include todays registrations */
$date = new \IPS\DateTime( 'tomorrow', new \DateTimeZone( \IPS\Member::loggedIn()->timezone ) );
/* We need to clone $date here for use in the query later */
$today = clone $date;
/* And we do this over a period of seven days - the query will also do this on the cloned object later. */
$date->sub( new \DateInterval( 'P7D' ) );
while ( $date->getTimestamp() < time() )
{
$data[ $date->format( 'Y-n-j' ) ] = 0;
$date->add( new \DateInterval( 'P1D' ) );
}
/* We can use the registration stats controller for this */
$chart = new \IPS\Helpers\Chart\Database(
\IPS\Http\Url::internal( 'app=core&module=stats&controller=registrationstats' ),
'core_members',
'joined',
'',
array(
'isStacked' => FALSE,
'backgroundColor' => '#ffffff',
'colors' => array( '#10967e' ),
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
'lineWidth' => 1,
'areaOpacity' => 0.4
),
'ColumnChart',
'weekly',
array( 'start' => \IPS\DateTime::create()->sub( new \DateInterval( 'P30D' ) ), 'end' => \IPS\DateTime::ts( time() ) )
);
$chart->addSeries( \IPS\Member::loggedIn()->language()->addToStack('stats_new_registrations'), 'number', 'COUNT(*)', FALSE );
$chart->availableTypes = array( 'AreaChart', 'ColumnChart', 'BarChart' );
/* fetch only successful registered members ; if this needs to be changed, please review the other areas where we have the name<>? AND email<>? condition */
$where = array( 'joined>? AND name<>? AND email<>?', $today->sub( new \DateInterval( 'P7D' ) )->getTimestamp(), '', '' );
/* Add Rows */
foreach( \IPS\Db::i()->select( 'joined', 'core_members', $where, 'joined DESC' ) AS $joined )
{
$time = \IPS\DateTime::ts( $joined )->format( 'Y-n-j' );
/* Occasionally, timezone differences mean that we fall just over the beginning or end, which results in an undefined index. We can
ignore any registrations for dates that haven't been initialized. */
if( isset( $data[ $time ] ) )
{
$data[ $time ] += 1;
}
}
uksort( $data, function( $a, $b )
{
return strnatcmp( $a, $b );
} );
/* Add to graph */
foreach ( $data as $time => $d )
{
$datetime = new \IPS\DateTime;
$exploded = explode( '-', $time );
$datetime->setDate( $exploded[0], $exploded[1], $exploded[2] );
$chart->addRow( array( $datetime->format( 'j M Y' ), $d ) );
}
/* Work out the ticks */
$ticks = array();
$increment = ceil( max($data) / 5 );
for ($i = 1; $i <= 5; $i++)
{
$v = $increment * $i;
$ticks[] = array( 'v' => $v, 'f' => (string) $v );
}
$chart->where[] = array( '( name<>? AND email<>? )', '', '' );
/* Output */
return \IPS\Theme::i()->getTemplate( 'dashboard' )->registrations( $chart->render( 'ColumnChart', array(
'legend' => array( 'position' => 'none' ),
'areaOpacity' => 0.4,
'lineWidth' => 1,
'backgroundColor' => '#ffffff',
'colors' => array( '#10967e' ),
'vAxis' => array( 'ticks' => $ticks ),
'hAxis' => array( 'gridlines' => array( 'color' => '#f5f5f5' ) ),
) ) );
return \IPS\Theme::i()->getTemplate( 'dashboard' )->registrations( $chart );
}
}
@@ -238,8 +238,7 @@ class _Admin
if( $newMessage !== FALSE )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $newMessage ), "conf_key='site_offline_message'" );
unset( \IPS\Data\Store::i()->settings );
\IPS\Settings::i()->changeValues( array( 'site_offline_message' => $newMessage ) );
}
/* Application disabled messages */
@@ -0,0 +1,87 @@
<?php
/**
* @brief Editor Extension: Review Responses
* @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 08 Jan 2018
*/
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: Review Responses
*/
class _ReviewResponses
{
/**
* Can we use HTML in this editor?
*
* @param \IPS\Member $member The member
* @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 )
{
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
* @return bool
*/
public function attachmentPermissionCheck( $member, $id1, $id2, $id3, $attachment )
{
try
{
$class = $id3 . '\\Review';
$item = $class::load( $id1 );
return $item->canView( $member );
}
catch ( \OutOfRangeException $e )
{
return FALSE;
}
}
/**
* 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 )
{
$class = $id3 . '\\Review';
return $class::load( $id2 );
}
}
@@ -0,0 +1,251 @@
<?php
/**
* @brief Editor Extension: Widget
* @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 Feb 2018
*/
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: Widget, Used for all the core widgets which need an editor
*/
class _Widget
{
/**
* Array containing all widgets utilizing this Extension
*/
protected static $widgets = array(
'guestSignUp',
'newsletter',
);
/**
* Can we use HTML in this editor?
*
* @param \IPS\Member $member The member
* @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 )
{
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
* @return bool
*/
public function attachmentPermissionCheck( $member, $id1, $id2, $id3, $attachment )
{
return TRUE;
}
/**
* 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 NULL;
}
/**
* 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 Store image proxy status ( true = enabled )
*/
protected $_imageProxyStatus = null;
/**
* 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 $status Enable/Disable Image Proxy
* @return int Number completed
* @note This method is optional and will only be called if it exists
*/
public function rebuildImageProxy( $offset, $max, $status=TRUE )
{
$this->_imageProxyStatus = $status;
return $this->performRebuild( $offset, $max, 'parseImageProxy' );
}
/**
* 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;
$areas = array( 'core_widget_areas' );
if ( \IPS\Application::appIsEnabled('cms') )
{
$areas[] = 'cms_page_widget_areas';
}
foreach ( $areas as $table )
{
foreach ( \IPS\Db::i()->select( '*', $table ) as $area )
{
$did++;
$widgetsColumn = $table == 'core_widget_areas' ? 'widgets' : 'area_widgets';
$whereClause = $table == 'core_widget_areas' ? array( 'id=? AND area=?', $area['id'], $area['area'] ) : array( 'area_page_id=? AND area_area=?', $area['area_page_id'], $area['area_area'] );
$widgets = json_decode( $area[ $widgetsColumn ], TRUE );
$update = FALSE;
foreach ( $widgets as $k => $widget )
{
if ( in_array( $widget['key'], static::$widgets ) )
{
$appOrPlugin = isset( $widget['plugin'] ) ? \IPS\Plugin::load( $widget['plugin'] ) : \IPS\Application::load( $widget['app'] );
$widgetObject = \IPS\Widget::load( $appOrPlugin, $widget['key'], $widget['unique'] );
$key = $widgetObject::$editorKey;
if ( isset( $widget['configuration'][$key ] ) )
{
try
{
if( $callback == 'parseImageProxy' )
{
$rebuilt = \IPS\Text\Parser::parseImageProxy( $widget['configuration'][$key ], $this->_imageProxyStatus );
}
else
{
$rebuilt = call_user_func( $callback, $widget['configuration'][$key ] );
}
}
catch( \InvalidArgumentException $e )
{
if( $callback[1] == 'parseStatic' AND $e->getcode() == 103014 )
{
$rebuilt = preg_replace( "#\[/?([^\]]+?)\]#", '', $widget['configuration'][$key ] );
}
else
{
throw $e;
}
}
if ( $rebuilt )
{
$widgets[ $k ]['configuration'][$key] =$rebuilt;
$update = TRUE;
}
}
}
if ( $update )
{
\IPS\Db::i()->update( $table, array( $widgetsColumn => json_encode( $widgets ) ), $whereClause );
}
}
}
}
return $did;
}
/**
* Total content count to be used in progress indicator
*
* @return int Total Count
*/
public function contentCount()
{
$count = 0;
$areas = array( 'core_widget_areas' );
if ( \IPS\Application::appIsEnabled('cms') )
{
$areas[] = 'cms_page_widget_areas';
}
foreach ( $areas as $table )
{
foreach ( \IPS\Db::i()->select( '*', $table ) as $area )
{
$widgetsColumn = $table == 'core_widget_areas' ? 'widgets' : 'area_widgets';
$widgets = json_decode( $area[ $widgetsColumn ], TRUE );
foreach ( $widgets as $k => $widget )
{
if ( in_array( $widget['key'], static::$widgets ) )
{
$count++;
}
}
}
}
return $count;
}
}
@@ -0,0 +1,135 @@
<?php
/**
* @brief File Storage Extension: ClubField
* @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 Aug 2017
*/
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: ClubField
*/
class _ClubField
{
/**
* Count stored files
*
* @return int
*/
public function count()
{
$count = 0;
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
$count += \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL", '' ) )->first();
}
return $count;
}
/**
* 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 )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL", '' ) ) AS $field )
{
try
{
$moved = \IPS\File::get( $oldConfiguration ?: 'core_ClubField', $field[ "field_{$row['f_id']}" ] )->move( $storageConfiguration );
\IPS\Db::i()->update( 'core_clubs_fieldvalues', array( "field_{$row['f_id']}" => (string) $moved ), array( "club_id=?", $field['club_id'] ) );
}
catch( \Exception $e )
{
// Any issues are logged
}
catch( \Throwable $e )
{
// Any issues are logged
}
}
}
throw new \UnderflowException;
}
/**
* 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 )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL" ) ) AS $field )
{
$new = \IPS\File::repairUrl( $field[ "field_{$row['f_id']}" ] );
\IPS\Db::i()->update( 'core_clubs_fieldvalues', array( "field_{$row['f_id']}" => $new ), array( "club_id=?", $field['club_id'] ) );
}
}
throw new \UnderflowException;
}
/**
* Check if a file is valid
*
* @param string $file The file path to check
* @return bool
*/
public function isValidFile( $file )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}=?", (string) $file ) )->first() )
{
return TRUE;
}
}
return FALSE;
}
/**
* Delete all stored files
*
* @return void
*/
public function delete()
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fields', array( "f_type=?", 'Upload' ) ) AS $row )
{
foreach( \IPS\Db::i()->select( '*', 'core_clubs_fieldvalues', array( "field_{$row['f_id']}<>? AND field_{$row['f_id']} IS NOT NULL" ) ) AS $field )
{
try
{
\IPS\File::get( 'core_ClubField', $field[ "field_{$row['f_id']}" ] )->delete();
}
catch( \OutOfRangeException $e ) {}
\IPS\Db::i()->update( 'core_clubs_fieldvalues', array( "field_{$row['f_id']}" => NULL ), array( "club_id=?", $field['club_id'] ) );
}
}
}
}
@@ -135,7 +135,7 @@ class _Clubs
{
if ( $new = \IPS\File::repairUrl( $club[ $photoKey ] ) )
{
$fixed[ $location ] = $new;
$fixed[ $photoKey ] = $new;
}
}
}
@@ -60,7 +60,8 @@ class _Emoticons
\IPS\Db::i()->update( 'core_emoticons', array( 'image' => (string) $file, 'image_2x' => (string) $image_2x ), array( 'id=?', $emoticon['id'] ) );
}
unset( \IPS\Data\Store::i()->emoticons );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
}
catch( \Exception $e )
{
@@ -93,7 +94,8 @@ class _Emoticons
{
\IPS\Db::i()->update( 'core_emoticons', $fixed, array( 'id=?', $emoticon['id'] ) );
unset( \IPS\Data\Store::i()->emoticons );
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => time() ), array( 'conf_key=?', 'emoji_cache' ) );
unset( \IPS\Data\Store::i()->settings );
}
}
catch( \Exception $e ) { }
@@ -45,6 +45,12 @@ class _Imageproxycache
{
$cache = \IPS\Db::i()->select( '*', 'core_image_proxy', array(), 'md5_url', array( $offset, 1 ) )->first();
/* Don't move a file if it doesn't have a filename */
if( $cache['location'] === NULL )
{
return;
}
try
{
$file = \IPS\File::get( $oldConfiguration ?: 'core_Imageproxycache', $cache['location'] )->move( $storageConfiguration );
@@ -0,0 +1,111 @@
<?php
/**
* @brief File Storage Extension: Login Methods
* @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 31 May 2017
*/
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: Login Methods
*/
class _Login
{
/**
* Count stored files
*
* @return int
*/
public function count()
{
$count = 0;
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] )
{
$count++;
}
}
return $count;
}
/**
* 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 )
{
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] )
{
try
{
$settings['button_icon'] = (string) \IPS\File::get( $oldConfiguration ?: 'core_Login', $settings['button_icon'] )->move( $storageConfiguration );
\IPS\Db::i()->update( 'core_login_methods', array( 'login_settings' => json_encode( $settings ) ), array( 'login_id=?', $method['login_id'] ) );
}
catch( \Exception $e )
{
/* Any issues are logged */
}
}
}
unset( \IPS\Data\Store::i()->loginMethods );
throw new \UnderflowException;
}
/**
* Check if a file is valid
*
* @param string $file The file path to check
* @return bool
*/
public function isValidFile( $file )
{
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] == (string) $file )
{
return TRUE;
}
}
return FALSE;
}
/**
* Delete all stored files
*
* @return void
*/
public function delete()
{
foreach ( \IPS\Db::i()->select( '*', 'core_login_methods' ) as $method )
{
$settings = json_decode( $method['login_settings'], TRUE );
if ( isset( $settings['button_icon'] ) and $settings['button_icon'] == (string) $file )
{
\IPS\File::get( 'core_Login', $settings['button_icon'] )->delete();
}
}
}
}
@@ -144,7 +144,7 @@ class _Profile
\IPS\File::get( 'core_Profile', $member['pp_cover_photo'] )->delete();
}
if ( mb_substr( $member['pp_photo_type'], 0, 5 ) === 'sync-' or $member['pp_photo_type'] === 'custom' )
if ( mb_substr( $member['pp_photo_type'], 0, 5 ) === 'sync-' or $member['pp_photo_type'] === 'custom' or $member['pp_photo_type'] === 'letter' )
{
if( $member['pp_main_photo'] )
{
@@ -52,7 +52,7 @@ class _Promote
{
try
{
\IPS\File::get( $oldConfiguration ?: 'core_Profile', $location )->move( $storageConfiguration );
\IPS\File::get( $oldConfiguration ?: 'core_Promote', $location )->move( $storageConfiguration );
}
catch( \Exception $e )
{
@@ -36,6 +36,9 @@ class _Theme
{
/* Clear out CSS as custom URL may have changed */
\IPS\Theme::deleteCompiledCss();
/* Trash this JS */
\IPS\Output::clearJsFiles();
}
/**
@@ -52,7 +52,7 @@ class _Clubs extends \IPS\core\FrontNavigation\FrontNavigationAbstract
*/
public function canAccessContent()
{
return \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'online' ) );
return \IPS\Member::loggedIn()->canAccessModule( \IPS\Application\Module::get( 'core', 'clubs' ) );
}
/**
@@ -31,15 +31,19 @@ class _Content
*/
public function process( &$form, $group )
{
$contentClasses = array();
/* Set up available content classes for customizations - add in conversations which do not have a content router extension */
$contentClasses = array( 'IPS\core\Messenger\Conversation' => 'personal_conversation_pl' );
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter', $group ) as $class )
{
foreach ( $class->classes as $class )
{
$contentClasses[ $class ] = $class::$title . '_pl';
if ( isset( $class::$databaseColumnMap['author'] ) )
{
$contentClasses[ $class ] = $class::$title . '_pl';
}
}
}
/* Uploading */
if ( \IPS\Settings::i()->attach_allowed_types != 'none' )
{
@@ -47,9 +51,9 @@ class _Content
$form->add( new \IPS\Helpers\Form\YesNo( 'g_attach', ( $group->g_attach_max != 0 ), FALSE, array( 'togglesOn' => array( 'g_attach_max', 'g_attach_per_post', 'gbw_delete_attachments' ) ) ) );
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_max', $group->g_attach_max, FALSE, array( 'unlimited' => -1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_max' ) );
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_max', $group->g_id ? $group->g_attach_max : 500000, FALSE, array( 'unlimited' => -1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_max' ) );
}
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_per_post', $group->g_attach_per_post, FALSE, array( 'unlimited' => 0 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_per_post' ) );
$form->add( new \IPS\Helpers\Form\Number( 'g_attach_per_post', $group->g_id ? $group->g_attach_per_post : 50000, FALSE, array( 'unlimited' => 0 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack( 'filesize_raw_k' ), 'g_attach_per_post' ) );
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'gbw_delete_attachments', $group->g_bitoptions['gbw_delete_attachments'], FALSE, array(), NULL, NULL, NULL, 'gbw_delete_attachments' ) );
@@ -134,6 +138,8 @@ class _Content
if( $group->g_id != \IPS\Settings::i()->guest_group )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'gbw_immune_auto_mod', $group->g_bitoptions['gbw_immune_auto_mod'] ) );
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'g_lock_unlock_own', !$group->g_lock_unlock_own ? 0 : ( $group->g_lock_unlock_own == '1' ? '1' : explode( ',', $group->g_lock_unlock_own ) ), FALSE, array(
'options' => array_filter( $contentClasses, function( $class ) {
return in_array( 'IPS\Content\Lockable', class_implements( $class ) );
@@ -144,7 +150,7 @@ class _Content
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'g_can_report', !$group->g_can_report ? 0 : ( $group->g_can_report == '1' ? '1' : explode( ',', $group->g_can_report ) ), FALSE, array(
'options' => array_filter( $contentClasses, function( $class ) {
return in_array( 'IPS\Content\ReportCenter', class_implements( $class ) );
return \IPS\IPS::classUsesTrait( $class, 'IPS\Content\Reportable' );
}, ARRAY_FILTER_USE_KEY ),
'unlimited' => '1'
) ) );
@@ -218,7 +224,7 @@ class _Content
$values['gbw_disable_tagging'] = \IPS\Settings::i()->tags_enabled ? !$values['gbw_disable_tagging'] : $group->g_bitoptions['gbw_disable_tagging'];
$values['gbw_disable_prefixes'] = ( \IPS\Settings::i()->tags_enabled AND \IPS\Settings::i()->tags_can_prefix ) ? !$values['gbw_disable_prefixes'] : $group->g_bitoptions['gbw_disable_prefixes'];
$bwKeys = array( 'gbw_disable_tagging', 'gbw_disable_prefixes', 'gbw_delete_attachments', 'gbw_post_highlight', 'gbw_promote' );
$bwKeys = array( 'gbw_disable_tagging', 'gbw_disable_prefixes', 'gbw_delete_attachments', 'gbw_post_highlight', 'gbw_promote', 'gbw_immune_auto_mod' );
foreach ( $bwKeys as $k )
{
@@ -67,7 +67,7 @@ class _GroupSettings
{
return array( 'prefix' => $element->value['prefix'], 'suffix' => $element->value['suffix'] );
}
elseif ( isset( $element->value['color'] ) )
elseif ( isset( $element->value['color'] ) AND $element->value['color'] )
{
$color = mb_strtolower( $element->value['color'] );
if ( mb_substr( $color, 0, 1 ) !== '#' )
@@ -101,7 +101,7 @@ class _GroupSettings
}
}
$form->add( new \IPS\Helpers\Form\YesNo( 'g_view_board', $group->g_id ? $group->g_view_board : TRUE, FALSE, array( 'togglesOn' => array_merge( $tabs, array( 'group_username', 'g_dohtml', 'g_use_search', "{$form->id}_header_group_privacy", 'g_hide_online_list', 'gbw_hide_group', "{$form->id}_header_group_signatures", 'g_use_signatures', "{$form->id}_header_group_staff", 'g_access_offline', 'g_search_flood', 'gbw_cannot_be_ignored' ) ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'g_view_board', $group->g_id ? $group->g_view_board : TRUE, FALSE, array( 'togglesOn' => array_merge( $tabs, array( 'group_username', 'g_dohtml', 'g_use_search', "gbw_cannot_be_ignored", "{$form->id}_header_group_signatures", 'g_use_signatures', "{$form->id}_header_group_staff", 'g_access_offline', 'g_search_flood', 'gbw_cannot_be_ignored' ) ) ) ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'g_access_offline', $group->g_access_offline, FALSE, array(), NULL, NULL, NULL, 'g_access_offline' ) );
/* Usernames */
@@ -130,24 +130,35 @@ class _GroupSettings
{
$value[0] = 0;
}
if ( isset( $value['unlimited'] ) AND $value['unlimited'] )
else
{
$value[0] = -1;
$value[3] = 0;
}
elseif ( !isset( $value[0] ) )
{
$value[0] = 0;
}
if ( isset( $value['always'] ) AND $value['always'] )
{
$value[1] = 0;
$value[2] = 0;
if ( isset( $value['unlimited'] ) AND $value['unlimited'] )
{
$value[0] = -1;
$value[3] = 0;
}
elseif ( !isset( $value[0] ) )
{
$value[0] = 0;
}
if ( isset( $value['always'] ) AND $value['always'] )
{
$value[1] = 0;
$value[2] = 0;
}
}
return $value;
},
'validate' => function( $element )
{
$value = $element->value;
if( ( isset( $value[1] ) AND $value[1] < 0 ) OR ( isset( $value[2] ) AND $value[2] < 0 ) OR ( isset( $value[3] ) AND $value[3] < 0 ) )
{
throw new \DomainException( 'only_positive_values' );
}
}
), NULL, NULL, NULL, 'group_username' ) );
}
@@ -240,7 +251,7 @@ class _GroupSettings
{
$group->g_dname_changes = ( isset( $values['group_username']['canchange'] ) and $values['group_username']['canchange'] ) ? $values['group_username'][0] : 0;
$group->g_displayname_unit = ( isset( $values['group_username']['canchange'] ) ) ? (int) $values['group_username'][1] : 0;
$group->g_bitoptions['gbw_displayname_unit_type'] = $values['group_username'][2];
$group->g_bitoptions['gbw_displayname_unit_type'] = ( isset( $values['group_username'][2] ) ) ? $values['group_username'][2] : 0;
$group->g_dname_date = ( isset( $values['group_username']['canchange'] ) ) ? $values['group_username'][3] : 1;
}
@@ -291,4 +302,48 @@ class _GroupSettings
$group->g_bitoptions['gbw_cannot_be_ignored'] = !$values['can_be_ignored'];
}
}
/**
* Delete
*
* @param \IPS\Member\Group $group The group
* @return void
*/
public function delete( $group )
{
if ( $group->g_icon )
{
try
{
\IPS\File::get( 'core_Theme', $group->g_icon )->delete();
}
catch( \Exception $ex ) { }
}
}
/**
* Copy Group
*
* @param \IPS\Member\Group $oldGroup The old, just cloned group
* @param \IPS\Member\Group $newGroup The new group
* @return void
*/
public function cloneGroup( $oldGroup, $newGroup )
{
if ( $oldGroup->g_icon )
{
try
{
$icon = \IPS\File::get( 'core_Theme', $oldGroup->g_icon );
$newIcon = \IPS\File::create( 'core_Theme', $icon->originalFilename, $icon->contents() );
$newGroup->g_icon = (string) $newIcon;
}
catch ( \Exception $e )
{
$newGroup->icon = NULL;
}
$newGroup->save();
}
}
}
@@ -83,9 +83,17 @@ class _Social
\IPS\Member\Club::TYPE_OPEN => 'club_type_open',
\IPS\Member\Club::TYPE_CLOSED => 'club_type_closed',
\IPS\Member\Club::TYPE_PRIVATE => 'club_type_private',
\IPS\Member\Club::TYPE_READONLY => 'club_type_readonly',
),
), NULL, NULL, NULL, 'g_create_clubs' ) );
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'gbw_paid_clubs', $group->g_id ? ( $group->g_bitoptions['gbw_paid_clubs'] ) : FALSE ) );
}
$form->add( new \IPS\Helpers\Form\Number( 'g_club_limit', $group->g_club_limit ?: -1, FALSE, array( 'unlimited' => -1 ) ) );
$availableClubNodes = array();
foreach ( \IPS\Member\Club::availableNodeTypes() as $class )
{
@@ -176,6 +184,12 @@ class _Social
{
$group->g_create_clubs = implode( ',', $values['g_create_clubs'] );
$group->g_club_allowed_nodes = ( count( $values['g_club_allowed_nodes'] ) === count( \IPS\Member\Club::availableNodeTypes() ) ) ? '*' : implode( ',', $values['g_club_allowed_nodes'] );
$group->g_club_limit = $values['g_club_limit'] == -1 ? NULL : $values['g_club_limit'];
if ( \IPS\Application::appIsEnabled( 'nexus' ) and \IPS\Settings::i()->clubs_paid_on )
{
$bwKeys[] = 'gbw_paid_clubs';
}
}
}
@@ -49,6 +49,7 @@ class _GroupLimits
'g_delete_own_posts' => array( $this, '_perAppSettings' ),
'g_lock_unlock_own' => array( $this, '_perAppSettings' ),
'g_can_report' => array( $this, '_perAppSettings' ),
'g_club_allowed_nodes' => array( $this, '_clubNodes' ),
)
);
}
@@ -134,7 +135,6 @@ class _GroupLimits
/* If signatures are currently enabled but aren't in the group we are checking, signatures should not be disabled */
elseif ( $index == 0 and $_cur[ $index ] < $values[ $index ] )
{
$_new[ $index ] = $_cur[ $index ];
}
else if( $_cur[ $index ] > $values[ $index ] )
@@ -145,6 +145,13 @@ class _GroupLimits
{
$_new[ $index ] = $values[ $index ];
}
/* If we are not allowed to use any images, ignore the dimensions specified.
Otherwise group A with 0 images and no limits on sizes and group B with 1 images at 200x200 results in 1 image allowed unlimited sizes. */
if( ( $index == 2 OR $index == 3 ) AND $values[1] == 0 )
{
$_new[ $index ] = $_cur[ $index ];
}
}
ksort($_new);
@@ -353,4 +360,26 @@ class _GroupLimits
);
}
}
/**
* Available node types for clubs
*
* @param array $a Group A's values
* @param array $b Group B's values
* @param string $k The key we want to get the combined value for
* @param array $member Member data
* @return mixed
*/
public function _clubNodes( $a, $b, $k, $member )
{
if ( $a[$k] == '*' or $b[$k] == '*' )
{
return '*';
}
/* We need to merge all content types */
$nodeTypes = array_unique( array_merge( explode( ",", $a[$k]), explode( ",", $b[$k]) ) );
return implode( ",", $nodeTypes );
}
}
@@ -94,7 +94,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
try
{
$comment = $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] );
return "<a href='" . $comment->url() . "' target='_blank'>" . htmlspecialchars( $comment->item()->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . '</a>';
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $comment->url(), TRUE, $comment->item()->mapped('title') );
}
catch ( \OutOfRangeException $e )
{
@@ -135,7 +135,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
}
}
return "<a href='" . $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] )->url() . "' target='_blank'>" . htmlspecialchars( $val, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . '</a>';
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $class::load( $data[ $class::$databasePrefix . $class::$databaseColumnId ] )->url(), TRUE, $val );
},
);
if( isset( $class::$databaseColumnMap['container'] ) )
@@ -144,7 +144,7 @@ class _Content extends \IPS\Content\ExtensionGenerator
{
$nodeClass = $class::$containerNodeClass;
$node = $nodeClass::load( $val );
return "<a href='" . $node->url() . "' target='_blank'>" . htmlspecialchars( $node->_title, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . '</a>';
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $node->url(), TRUE, $node->_title );
};
}
@@ -97,6 +97,6 @@ class _MemberHistory
*/
public function findByMember( $member )
{
return \IPS\Db::i()->select( "log_ip_address AS ip, COUNT(*) AS count, MIN(log_date) AS first, MAX(log_date) AS last", 'core_member_history', array( 'log_member=?', $member->member_id ), NULL, NULL, 'log_ip_address' )->setKeyField( 'ip' );
return \IPS\Db::i()->select( "log_ip_address AS ip, COUNT(*) AS count, MIN(log_date) AS first, MAX(log_date) AS last", 'core_member_history', array( 'log_by=?', $member->member_id ), NULL, NULL, 'log_ip_address' )->setKeyField( 'ip' );
}
}
@@ -87,7 +87,7 @@ class _Ratings
{
$class = $row['class'];
$rated = $class::load( $row['item_id'] );
return "<a href='" . $rated->url() . "' target='_blank'>" . htmlspecialchars( $rated->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $rated->url(), TRUE, $rated->mapped('title') );
}
catch ( \OutOfRangeException $e )
{
@@ -95,7 +95,7 @@ class _Registration
'name' => function( $val, $row )
{
$link = ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation === 'front' ) ? \IPS\Member::constructFromData( $row )->url() : \IPS\Member::constructFromData( $row )->acpUrl();
return "<a href='" . $link . "'>" . htmlentities( $val, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $link, TRUE, $val );
},
);
@@ -90,7 +90,7 @@ class _ReportComments
try
{
$report = \IPS\core\Reports\Report::load( $row['rid'] );
return "<a href='" . $report->url() . "' target='_blank'>" . htmlspecialchars( $report->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $report->url(), TRUE, $report->mapped('title') );
}
catch( \OutOfRangeException $e )
{
@@ -90,7 +90,7 @@ class _Reports
try
{
$report = \IPS\core\Reports\Report::load( $row['rid'] );
return "<a href='" . $report->url() . "' target='_blank'>" . htmlspecialchars( $report->mapped('title'), \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $report->url(), TRUE, $reort->mapped('title') );
}
catch( \OutOfRangeException $e )
{
@@ -87,7 +87,7 @@ class _Validations
{
$member = \IPS\Member::load( $row['member_id'] );
$link = ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation === 'front' ) ? $member->url() : $member->acpUrl();
return "<a href='" . $link . "'>" . htmlentities( $member->name, \IPS\HTMLENTITIES, 'UTF-8', FALSE ) . "</a>";
return \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $link, FALSE, $member->name );
},
'validatingType' => function( $val, $row )
{
@@ -90,7 +90,7 @@ class _Votes
try
{
$poll = \IPS\Poll::load( $val );
return htmlspecialchars( $poll->poll_question, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
return htmlspecialchars( $poll->poll_question, ENT_DISALLOWED, 'UTF-8', FALSE );
}
catch ( \OutOfRangeException $e )
{
@@ -0,0 +1,89 @@
<?php
/**
* @brief ACP Member Profile: Basic Information Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Basic Information Block
*/
class _BasicInformation extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$hasPassword = FALSE;
$canChangePassword = \IPS\Login\Handler::findMethod( 'IPS\Login\Handler\Standard' );
$activeIntegrations = array();
foreach ( \IPS\Login::methods() as $method )
{
if ( $method->canProcess( $this->member ) )
{
if ( !( $method instanceof \IPS\Login\Handler\Standard ) )
{
$activeIntegrations[] = $method->_title;
}
if ( $method->canChangePassword( $this->member ) )
{
$hasPassword = TRUE;
$canChangePassword = TRUE;
}
}
}
$accountActions = array();
if ( \IPS\Member::loggedIn()->member_id != $this->member->member_id AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_login' ) AND !$this->member->isBanned() )
{
$accountActions[] = array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'login_as_x', FALSE, array( 'sprintf' => array( $this->member->name ) ) ),
'icon' => 'key',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=login&id={$this->member->member_id}" ),
'class' => '',
'target' => '_blank'
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'members_merge' ) )
{
$accountActions[] = array(
'title' => 'merge_with_another_account',
'icon' => 'level-up',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=merge&id={$this->member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('merge') )
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_delete' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_delete_admin' ) or !$this->member->isAdmin() ) and $this->member->member_id != \IPS\Member::loggedIn()->member_id )
{
$accountActions[] = array(
'title' => 'delete',
'icon' => 'times-circle',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=delete&id={$this->member->member_id}" ),
'data' => array( 'delete' => '', 'delete-warning' => \IPS\Member::loggedIn()->language()->addToStack( 'member_delete_confirm_desc' ), 'noajax' => '' )
);
}
$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
{
$activeSubscription = \IPS\nexus\Subscription::loadActiveByMember( $this->member );
}
return \IPS\Theme::i()->getTemplate('memberprofile')->basicInformation( $this->member, $canChangePassword, $hasPassword, $activeIntegrations, $accountActions, $activeSubscription );
}
}
@@ -0,0 +1,200 @@
<?php
/**
* @brief ACP Member Profile: Content Statistics Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Content Statistics
*/
class _ContentStatistics extends \IPS\core\MemberACPProfile\TabbedBlock
{
/**
* @brief Percentages
*/
protected $percentages = array();
/**
* @brief Raw Counts
*/
protected $rawCounts = array();
/**
* Get Block Title
*
* @return string
*/
public function blockTitle()
{
return 'content_statistics';
}
/**
* Constructor
*
* @param \IPS\Member $member Member
* @return void
*/
public function __construct( \IPS\Member $member )
{
parent::__construct( $member );
$classes = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter' ) as $contentRouter )
{
foreach ( $contentRouter->classes as $class )
{
$include = FALSE;
$exploded = explode( '\\', $class );
if ( in_array( 'IPS\Content\Item', class_parents( $class ) ) )
{
if ( $class::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['items'][] = $class;
}
if ( isset( $class::$commentClass ) )
{
$commentClass = $class::$commentClass;
if ( $commentClass::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['comments'][] = $commentClass;
}
}
if ( isset( $class::$reviewClass ) )
{
$reviewClass = $class::$reviewClass;
if ( $reviewClass::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['comments'][] = $class::$reviewClass;
}
}
}
elseif ( in_array( 'IPS\Content\Comment', class_parents( $class ) ) )
{
if ( $class::incrementPostCount() )
{
$include = TRUE;
$classes[ $exploded[1] ]['comments'][] = $class;
}
}
if ( $include and !isset( $this->percentages[ $exploded[1] ] ) )
{
$this->percentages[ $exploded[1] ] = 0;
}
}
}
$sum = 0;
$counts = array();
foreach ( $classes as $app => $types )
{
foreach ( $types as $type => $typeClasses )
{
foreach ( $typeClasses as $class )
{
if( !isset( $class::$databaseColumnMap['author'] ) )
{
continue;
}
$authorColumn = $class::$databasePrefix . $class::$databaseColumnMap['author'];
if ( !isset( $counts[ $app ][ $type ] ) )
{
$counts[ $app ][ $type ] = 0;
}
if ( !isset( $this->rawCounts[ $app ][ $class ] ) )
{
$this->rawCounts[ $app ][ $class ] = 0;
}
$count = $class::memberPostCount( $member );
$sum += $count;
$counts[ $app ][ $type ] += $count;
$this->rawCounts[ $app ][ $class ] += $count;
}
}
}
if ( $sum )
{
foreach ( $counts as $app => $types )
{
if ( array_sum( $types ) )
{
$this->percentages[ $app ] = round( 100 / $sum * array_sum( $types ), 2 );
}
}
}
}
/**
* Get Tab Names
*
* @return string
*/
public function tabs()
{
$return = array();
if ( array_sum( $this->percentages ) )
{
$return['breakdown'] = 'content_count_breakdown';
}
foreach ( \IPS\Application::allExtensions( 'core', 'MemberACPProfileContentTab', TRUE, NULL, NULL, FALSE ) as $class )
{
try
{
$ext = new $class( $this->member );
$exploded = explode( '\\', $class );
$return[ $exploded[1] . '_' . $exploded[5] ] = 'content_stats__' . $exploded[1] . '_' . $exploded[5];
}
catch ( \Exception $e ) { }
}
return $return;
}
/**
* Get output
*
* @return string
*/
public function tabOutput( $tab )
{
if ( $tab == 'breakdown' )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->contentBreakdown( $this->member, $this->percentages, $this->rawCounts );
}
else
{
$exploded = explode( '_', $tab );
$class = 'IPS\\' . $exploded[0] . '\\extensions\\core\\MemberACPProfileContentTab\\' . $exploded[1];
if ( class_exists( $class ) )
{
$ext = new $class( $this->member );
return $ext->output();
}
return '';
}
}
}
@@ -0,0 +1,37 @@
<?php
/**
* @brief ACP Member Profile: Devices & IP Addresses Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Devices & IP Addresses Block
*/
class _DevicesAndIPAddresses extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$lastUsedIp = $this->member->lastUsedIp();
$devices = new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_members_known_devices', array( 'member_id=?', $this->member->member_id ), 'last_seen DESC', 5 ), 'IPS\Member\Device' );
return \IPS\Theme::i()->getTemplate('memberprofile')->devicesAndIPAddresses( $this->member, $lastUsedIp, $devices );
}
}
@@ -0,0 +1,120 @@
<?php
/**
* @brief ACP Member Profile: Groups Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Groups Block
*/
class _Groups extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$secondaryGroups = array();
foreach ( array_filter( array_map( "intval", explode( ',', $this->member->mgroup_others ) ) ) as $secondaryGroupId )
{
try
{
$secondaryGroups[] = \IPS\Member\Group::load( $secondaryGroupId );
}
catch ( \OutOfRangeException $e ) { }
}
return \IPS\Theme::i()->getTemplate('memberprofile')->groups( $this->member, $secondaryGroups );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Check permission */
if ( $this->member->isAdmin() )
{
\IPS\Dispatcher::i()->checkAcpPermission( 'member_move_admin1' );
}
/* If we are editing ourselves, we can only move ourselves into a group with the same restrictions as what we have now... */
if ( $this->member->member_id == \IPS\Member::loggedIn()->member_id )
{
/* Get the row... */
try
{
$currentRestrictions = \IPS\Db::i()->select( 'row_perm_cache', 'core_admin_permission_rows', array( 'row_id=? AND row_id_type=?', $this->member->member_group_id, 'group' ) )->first();
$availableGroups = array();
foreach( \IPS\Db::i()->select( 'row_id', 'core_admin_permission_rows', array( 'row_perm_cache=? AND row_id_type=?', $currentRestrictions, 'group' ) ) AS $groupId )
{
$availableGroups[ $groupId ] = \IPS\Member\Group::load( $groupId );
}
}
/* If we don't have a row in core_admin_permission_rows, we're an admin as a member rather than apart of our group, so we can be moved anywhere and it won't matter because member-level restrictions override group-level */
catch ( \UnderflowException $e )
{
$availableGroups = \IPS\Member\Group::groups( TRUE, FALSE );
}
}
/* Not editing ourselves - do we have the Can move members into admin groups"" restriction? */
else
{
$availableGroups = \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE );
}
/* Build form */
$form = new \IPS\Helpers\Form( 'group_form', 'save', NULL, array(
'data-controller' => 'core.admin.members.form',
'data-adminGroups' => json_encode( iterator_to_array( \IPS\Db::i()->select( 'row_id', 'core_admin_permission_rows', array( 'row_id_type=?', 'group' ) ) ) )
)) ;
$form->add( new \IPS\Helpers\Form\Select( 'group', $this->member->member_group_id, TRUE, array( 'options' => $availableGroups, 'parse' => 'normal' ) ) );
$form->add( new \IPS\Helpers\Form\Select( 'secondary_groups', array_filter( explode( ',', $this->member->mgroup_others ) ), FALSE, array( 'options' => \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE ), 'multiple' => TRUE, 'parse' => 'normal' ) ) );
/* Handle submissions */
if ( $values = $form->values() )
{
$oldPrimary = $this->member->member_group_id;
$oldSecondary = array_filter( explode( ',', $this->member->mgroup_others ) );
$changes = array();
if ( $this->member->member_group_id != $values['group'] )
{
$this->member->logHistory( 'core', 'group', array( 'type' => 'primary', 'by' => 'manual', 'old' => $this->member->member_group_id, 'new' => $values['group'] ) );
}
$currentSecondary = array_filter( explode( ',', $this->member->mgroup_others ) );
if ( array_diff( $currentSecondary, $values['secondary_groups'] ) or array_diff( $values['secondary_groups'], $currentSecondary ) )
{
$this->member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'manual', 'old' => $currentSecondary, 'new' => $values['secondary_groups'] ) );
}
$this->member->member_group_id = $values['group'];
$this->member->mgroup_others = implode( ',', $values['secondary_groups'] );
$this->member->save();
\IPS\Session::i()->log( 'acplog__members_edited_groups', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,169 @@
<?php
/**
* @brief ACP Member Profile: Header Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Header Block
*/
class _Header extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
/* Sparkline */
$rows = array();
$cutoff = \IPS\DateTime::create()->setTime( 0, 0, 0 )->sub( new \DateInterval('P30D') );
$date = clone $cutoff;
while ( $date->getTimestamp() < time() )
{
$rows[ $date->format( 'Y-n-j' ) ] = 0;
$date->add( new \DateInterval( 'P1D' ) );
}
$sparkline = new \IPS\Helpers\Chart;
$classes = array();
foreach ( \IPS\Application::allExtensions( 'core', 'ContentRouter' ) as $contentRouter )
{
foreach ( $contentRouter->classes as $class )
{
$classes[] = $class;
if ( isset( $class::$commentClass ) )
{
$classes[] = $class::$commentClass;
}
if ( isset( $class::$reviewClass ) )
{
$classes[] = $class::$reviewClass;
}
}
}
foreach ( $classes as $class )
{
if( !isset( $class::$databaseColumnMap['author'] ) )
{
continue;
}
$dateColumn = $class::$databasePrefix . ( isset( $class::$databaseColumnMap['updated'] ) ? $class::$databaseColumnMap['updated'] : $class::$databaseColumnMap['date'] );
$authorColumn = $class::$databasePrefix . $class::$databaseColumnMap['author'];
foreach ( \IPS\Db::i()->select( "COUNT(*) AS count, DATE_FORMAT( FROM_UNIXTIME( IFNULL( {$dateColumn}, 0 ) ), '%Y-%c-%e' ) as time", $class::$databaseTable, array( "{$dateColumn}>? AND {$authorColumn}=?", $cutoff->getTimestamp(), $this->member->member_id ), NULL, NULL, array( $authorColumn, $dateColumn ) ) as $row )
{
$rows[ $row['time'] ] += $row['count'];
}
}
$sparkline->addHeader( \IPS\Member::loggedIn()->language()->addToStack('date'), 'date' );
$sparkline->addHeader( \IPS\Member::loggedIn()->language()->addToStack('members_posts'), 'number' );
foreach ( $rows as $time => $val )
{
$datetime = new \IPS\DateTime;
$datetime->setTime( 0, 0, 0 );
$exploded = explode( '-', $time );
$datetime->setDate( $exploded[0], $exploded[1], $exploded[2] );
$sparkline->addRow( array( $datetime, $val ) );
}
$sparkline = $sparkline->render( 'AreaChart', array(
'areaOpacity' => 0.4,
'backgroundColor' => '#fafafa',
'colors' => array( '#10967e' ),
'chartArea' => array(
'left' => 0,
'top' => 0,
'width' => '100%',
'height' => '100%',
),
'hAxis' => array(
'baselineColor' => '#F3F3F3',
'gridlines' => array(
'count' => 0,
)
),
'height' => 60,
'legend' => array(
'position' => 'none',
),
'lineWidth' => 1,
'vAxis' => array(
'baselineColor' => '#F3F3F3',
'gridlines' => array(
'count' => 0,
)
),
) );
/* Validating */
$validatingRow = NULL;
if ( $this->member->members_bitoptions['validating'] )
{
try
{
$validatingRow = \IPS\Db::i()->select( '*', 'core_validating', array( 'member_id=?', $this->member->member_id ) )->first();
}
catch ( \UnderflowException $e ) { }
}
/* Output */
return \IPS\Theme::i()->getTemplate('memberprofile')->header( $this->member, $validatingRow, $sparkline );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Build Form */
$form = new \IPS\Helpers\Form;
if ( \IPS\Request::i()->type == 'content' )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_content_items', $this->member->member_posts, FALSE ) );
}
elseif ( \IPS\Settings::i()->reputation_enabled and \IPS\Request::i()->type == 'reputation' )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_reputation', $this->member->pp_reputation_points, FALSE ) );
}
else
{
\IPS\Output::i()->error( 'node_error', '2C114/U', 404, '' );
}
/* Handle submissions */
if ( $values = $form->values() )
{
if ( \IPS\Request::i()->type == 'content' )
{
$this->member->member_posts = $values['member_content_items'];
\IPS\Session::i()->log( 'acplog__members_edited_content', array( $this->member->name => FALSE ) );
}
elseif ( \IPS\Settings::i()->reputation_enabled and \IPS\Request::i()->type == 'reputation' )
{
$this->member->pp_reputation_points = $values['member_reputation'];
\IPS\Session::i()->log( 'acplog__members_edited_rep', array( $this->member->name => FALSE ) );
}
$this->member->save();
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,52 @@
<?php
/**
* @brief ACP Member Profile: Locations Map Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Locations Map Block
*/
class _Locations extends \IPS\core\MemberACPProfile\LazyLoadingBlock
{
/**
* Get output
*
* @return string
*/
public function lazyOutput()
{
$mapMarkers = array();
if ( \IPS\Settings::i()->ipsgeoip and \IPS\Settings::i()->googlemaps and \IPS\Settings::i()->google_maps_api_key )
{
foreach ( \IPS\Db::i()->select( 'DISTINCT ip_address', 'core_members_known_ip_addresses', array( 'member_id=?', $this->member->member_id ) ) as $ipAddress )
{
try
{
$location = \IPS\GeoLocation::getByIp( $ipAddress );
$mapMarkers[ $ipAddress ] = array(
'lat' => floatval( $location->lat ),
'long' => floatval( $location->long ),
'title' => $ipAddress
);
}
catch ( \Exception $e ) { }
}
}
return \IPS\Theme::i()->getTemplate('memberprofile')->locations( $this->member, $mapMarkers );
}
}
@@ -0,0 +1,95 @@
<?php
/**
* @brief ACP Member Profile: Login Methods Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Login Methods Block
*/
class _LoginMethods extends \IPS\core\MemberACPProfile\LazyLoadingBlock
{
/**
* Get output
*
* @return string
*/
public function lazyOutput()
{
$loginMethods = array();
foreach ( \IPS\Login::methods() as $method )
{
if ( $method->canProcess( $this->member ) and !( $method instanceof \IPS\Login\Handler\Standard ) )
{
$link = NULL;
try
{
$link = $method->userLink( $method->userId( $this->member ), $method->userProfileName( $this->member ) );
$syncOptions = FALSE;
foreach ( $method->syncOptions( $this->member ) as $option )
{
if ( $option == 'photo' and !$this->member->group['g_edit_profile'] )
{
continue;
}
if ( $option == 'cover' and ( !$this->member->group['g_edit_profile'] or !$this->member->group['gbw_allow_upload_bgimage'] ) )
{
continue;
}
if ( $option == 'status' and ( !$this->member->canAccessModule( \IPS\Application\Module::get( 'core', 'status' ) ) or !\IPS\core\Statuses\Status::canCreateFromCreateMenu( $this->member ) or !\IPS\Settings::i()->profile_comments or $this->member->group['gbw_no_status_update'] ) )
{
continue;
}
$syncOptions = TRUE;
break;
}
$canDisassociate = FALSE;
foreach ( \IPS\Login::methods() as $_method )
{
if ( $_method->id != $method->id and $_method->canProcess( $this->member ) )
{
$canDisassociate = TRUE;
break;
}
}
$loginMethods[ $method->id ] = array(
'title' => $method->_title,
'blurb' => \IPS\Member::loggedIn()->language()->addToStack( 'profilesync_headline', FALSE, array( 'sprintf' => array( $method->userProfileName( $this->member ) ) ) ),
'icon' => $method->userProfilePhoto( $this->member ),
'logo' => $method->logoForUcp(),
'link' => $link,
'edit' => $syncOptions,
'delete'=> $canDisassociate
);
}
catch ( \IPS\Login\Exception $e )
{
$loginMethods[ $method->id ] = array( 'title' => $method->_title, 'blurb' => \IPS\Member::loggedIn()->language()->addToStack('profilesync_reauth_needed'), 'logo' => $method->logoForUcp(), 'link' => $link );
}
catch( \IPS\Http\Request\Exception $e )
{
\IPS\Log::log( $e, 'login_method_connect' );
}
}
}
return \IPS\Theme::i()->getTemplate('memberprofile')->loginMethods( $this->member, $loginMethods );
}
}
@@ -0,0 +1,176 @@
<?php
/**
* @brief ACP Member Profile: MFA Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: MFA Block
*/
class _MFA extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_mfa' ) )
{
return '';
}
$mfaEnabled = FALSE;
$configuredHandlers = array();
$hasSecurityQuestions = FALSE;
$showEditButton = ( \IPS\Settings::i()->mfa_required_groups != '*' and !$this->member->inGroup( explode( ',', \IPS\Settings::i()->mfa_required_groups ) ) );
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $this->member ) )
{
$mfaEnabled = TRUE;
if ( $handler->memberHasConfiguredHandler( $this->member ) )
{
if ( $handler instanceof \IPS\MFA\SecurityQuestions\Handler )
{
$hasSecurityQuestions = TRUE;
}
else
{
$configuredHandlers[ $key ] = $handler;
}
}
if ( !$showEditButton and count( $handler->acpConfiguration( $this->member ) ) )
{
$showEditButton = TRUE;
}
}
}
if ( $mfaEnabled )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->mfa( $this->member, $configuredHandlers, $hasSecurityQuestions, $showEditButton );
}
return '';
}
/**
* Edit Window
*
* @return string
*/
function edit()
{
/* Check permission */
\IPS\Dispatcher::i()->checkAcpPermission( 'member_mfa' );
/* Get all the fields we'll need */
$fields = array();
$optOutToggles = array();
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $this->member ) )
{
foreach ( $handler->acpConfiguration( $this->member ) as $id => $field )
{
$fields[] = $field;
}
$optOutToggles[] = "mfa_{$key}_title";
}
}
/* Build form */
$form = new \IPS\Helpers\Form;
if ( \IPS\Settings::i()->mfa_required_groups != '*' and !$this->member->inGroup( explode( ',', \IPS\Settings::i()->mfa_required_groups ) ) )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'mfa_opt_out_admin', $this->member->members_bitoptions['security_questions_opt_out'], FALSE, array( 'togglesOff' => $optOutToggles ) ) );
}
foreach ( $fields as $id => $field )
{
if ( $field instanceof \IPS\Helpers\Form\Matrix )
{
$form->addMatrix( $field->id, $field );
}
else
{
$form->add( $field );
}
}
/* Handle submissions */
if ( $values = $form->values() )
{
/* Reset the failure count and unlock if necessary */
$this->member->failed_mfa_attempts = 0;
$mfaDetails = $this->member->mfa_details;
if ( isset( $mfaDetails['_lockouttime'] ) )
{
unset( $mfaDetails['_lockouttime'] );
$this->member->mfa_details = $mfaDetails;
}
/* Did we opt out? */
if ( isset( $values['mfa_opt_out_admin'] ) )
{
/* Opt-Out: Disable all handlers */
if ( $values['mfa_opt_out_admin'] )
{
if ( !$this->member->members_bitoptions['security_questions_opt_out'] )
{
$this->member->members_bitoptions['security_questions_opt_out'] = TRUE;
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->memberHasConfiguredHandler( $this->member ) )
{
$handler->disableHandlerForMember( $this->member );
}
}
$this->member->logHistory( 'core', 'mfa', array( 'handler' => 'questions', 'enable' => FALSE, 'optout' => TRUE ) );
$this->member->save();
}
}
/* Opt-In */
elseif ( $this->member->members_bitoptions['security_questions_opt_out'] )
{
$this->member->members_bitoptions['security_questions_opt_out'] = FALSE;
$this->member->save();
$this->member->logHistory( 'core', 'mfa', array( 'handler' => 'questions', 'enable' => FALSE, 'optout' => FALSE ) );
}
}
/* Save each of the handlers */
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $this->member ) )
{
$handler->acpConfigurationSave( $this->member, $values );
}
}
/* Log and Redirect */
\IPS\Session::i()->log( 'acplog__members_edited_mfa', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,55 @@
<?php
/**
* @brief ACP Member Profile: OAuth Apps
* @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 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Groups Block
*/
class _OAuth extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'applications', 'oauth_tokens' ) and \IPS\Db::i()->select( 'COUNT(*)', 'core_oauth_clients', array( \IPS\Db::i()->findInSet( 'oauth_grant_types', array( 'authorization_code', 'implicit', 'password' ) ) ) )->first() )
{
$tokens = array();
foreach ( \IPS\Db::i()->select( '*', 'core_oauth_server_access_tokens', array( 'member_id=?', $this->member->member_id ), 'issued DESC' ) as $token )
{
try
{
$client = \IPS\Api\OAuthClient::load( $token['client_id'] );
$tokens[] = array(
'title' => $client->_title,
'use_refresh_tokens' => $client->use_refresh_tokens,
'data' => $token
);
}
catch ( \Exception $e ) {}
}
return \IPS\Theme::i()->getTemplate('memberprofile')->oauth( $this->member, $tokens );
}
}
}
@@ -0,0 +1,211 @@
<?php
/**
* @brief ACP Member Profile: Profile Data Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Profile Data Block
*/
class _ProfileData extends \IPS\core\MemberACPProfile\TabbedBlock
{
/**
* @brief Fields
*/
protected $fields = array();
/**
* @brief Clubs
*/
protected $clubs = NULL;
/**
* Constructor
*
* @param \IPS\Member $member Member
* @return void
*/
public function __construct( \IPS\Member $member )
{
parent::__construct( $member );
$this->fields = $this->member->profileFields( \IPS\core\ProfileFields\Field::STAFF );
$this->clubs = \IPS\Settings::i()->clubs ? \IPS\Member\Club::clubs( NULL, NULL, 'last_activity', array( 'member' => $this->member, 'statuses' => array( \IPS\Member\Club::STATUS_MODERATOR, \IPS\Member\Club::STATUS_EXPIRED_MODERATOR, \IPS\Member\Club::STATUS_LEADER ) ) ) : array();
}
/**
* Get Block Title
*
* @return string
*/
public function blockTitle()
{
return 'profile_data';
}
/**
* Get Tab Names
*
* @return string
*/
public function tabs()
{
$return = array();
if ( count( $this->fields ) || $this->member->member_title || $this->member->rank['title'] || $this->member->rank['image'] || \IPS\Settings::i()->profile_birthday_type != 'none' || \IPS\Settings::i()->signatures_enabled )
{
$return['fields'] = 'profile_fields';
}
if ( count( $this->clubs ) )
{
$return['clubs'] = 'club_ownership';
}
return $return;
}
/**
* Show Edit Link?
*
* @return bool
*/
protected function showEditLink()
{
return true;
}
/**
* Get output
*
* @return string
*/
public function tabOutput( $tab )
{
if ( $tab == 'fields' )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->profileData( $this->member, $this->fields );
}
elseif ( $tab == 'clubs' )
{
return \IPS\Theme::i()->getTemplate('memberprofile')->clubs( $this->member, $this->clubs );
}
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Build basic form */
$form = new \IPS\Helpers\Form;
$form->addHeader('profile_data');
$form->add( new \IPS\Helpers\Form\Text( 'member_title', $this->member->member_title, FALSE, array( 'maxLength' => 64 ) ) );
if ( \IPS\Settings::i()->profile_birthday_type !== 'none' )
{
$form->add( new \IPS\Helpers\Form\Custom( 'bday', array( 'year' => $this->member->bday_year, 'month' => $this->member->bday_month, 'day' => $this->member->bday_day ), FALSE, array( 'getHtml' => function( $element )
{
return strtr( \IPS\Member::loggedIn()->language()->preferredDateFormat(), array(
'DD' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_day( $element->name, $element->value, $element->error ),
'MM' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_month( $element->name, $element->value, $element->error ),
'YY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
'YYYY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
) );
} ) ) );
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$form->add( new \IPS\Helpers\Form\Editor( 'signature', $this->member->signature, FALSE, array( 'app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => "sig-{$this->member->member_id}", 'attachIds' => array( $this->member->member_id ) ) ) );
}
/* Profile Fields */
try
{
$values = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $this->member->member_id ) )->first();
}
catch( \UnderflowException $e )
{
$values = array();
}
if( count( $values ) )
{
foreach ( \IPS\core\ProfileFields\Field::fields( $values, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
$form->addHeader( "core_pfieldgroups_{$group}" );
foreach ( $fields as $field )
{
$form->add( $field );
}
}
}
/* Handle submissions */
if ( $values = $form->values() )
{
/* Profile Fields */
try
{
$profileFields = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $this->member->member_id ) )->first();
if ( !is_array( $profileFields ) ) // If \IPS\Db::i()->select()->first() has only one column, then the contents of that column is returned. We do not want this here
{
$profileFields = array();
}
}
catch( \UnderflowException $e )
{
$profileFields = array();
}
$profileFields['member_id'] = $this->member->member_id;
foreach ( \IPS\core\ProfileFields\Field::fields( $profileFields, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
foreach ( $fields as $id => $field )
{
$profileFields[ "field_{$id}" ] = $field::stringValue( !empty( $values[ $field->name ] ) ? $values[ $field->name ] : NULL );
}
}
$this->member->changedCustomFields = $profileFields;
\IPS\Db::i()->replace( 'core_pfields_content', $profileFields );
/* Profile Preferences */
$this->member->member_title = $values['member_title'];
if ( $values['bday'] )
{
$this->member->bday_day = $values['bday']['day'];
$this->member->bday_month = $values['bday']['month'];
$this->member->bday_year = $values['bday']['year'];
}
else
{
$this->member->bday_day = NULL;
$this->member->bday_month = NULL;
$this->member->bday_year = NULL;
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$this->member->signature = $values['signature'];
}
$this->member->save();
/* Log and Redirect */
\IPS\Session::i()->log( 'acplog__members_edited_profile', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,100 @@
<?php
/**
* @brief ACP Member Profile: Quotas Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Quotas Block
*/
class _Quotas extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$messengerCount = NULL;
$messengerPercent = NULL;
if ( $this->member->canAccessModule( \IPS\Application\Module::get( 'core', 'messaging', 'front' ) ) and !$this->member->members_disable_pm )
{
$messengerCount = \IPS\Db::i()->select( 'count(*)', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1', $this->member->member_id ) )->first();
if ( $this->member->group['g_max_messages'] > 0 )
{
$messengerPercent = floor( 100 / $this->member->group['g_max_messages'] * $messengerCount );;
}
}
$attachmentStorage = NULL;
$attachmentPercent = NULL;
if ( $this->member->group['g_attach_max'] != 0 )
{
$attachmentStorage = \IPS\Db::i()->select( 'SUM(attach_filesize)', 'core_attachments', array( 'attach_member_id=?', $this->member->member_id ) )->first();
if ( !$attachmentStorage )
{
$attachmentStorage = 0;
}
if ( $this->member->group['g_attach_max'] > 0 )
{
$attachmentPercent = floor( 100 / ( $this->member->group['g_attach_max'] * 1024 ) * $attachmentStorage );
}
}
$viewAttachmentsLink = NULL;
if ( $attachmentStorage and \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'overview', 'files_view' ) )
{
$viewAttachmentsLink = \IPS\Http\Url::internal('app=core&module=overview&controller=files&advanced_search_submitted=1')->setQueryString( 'attach_member_id', $this->member->name )->csrf();
}
return \IPS\Theme::i()->getTemplate('memberprofile')->quotas( $this->member, $messengerCount, $messengerPercent, $attachmentStorage, $attachmentPercent, $viewAttachmentsLink );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
$old = $this->member->members_disable_pm;
if ( \IPS\Request::i()->enable )
{
$this->member->members_disable_pm = 0;
}
else
{
if ( \IPS\Request::i()->prompt ) // Member cannot re-enable
{
$this->member->members_disable_pm = 2;
}
else // Member can re-enable
{
$this->member->members_disable_pm = 1;
}
}
$this->member->save();
if ( $old != $this->member->members_disable_pm )
{
$this->member->logHistory( 'core', 'warning', array( 'restrictions' => array( 'members_disable_pm' => array( 'old' => $old, 'new' => $this->member->members_disable_pm ) ) ) );
}
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
}
@@ -0,0 +1,149 @@
<?php
/**
* @brief ACP Member Profile: Warnings Block
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileBlocks;
/* 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 ACP Member Profile: Warnings Block
*/
class _Warnings extends \IPS\core\MemberACPProfile\Block
{
/**
* Get output
*
* @return string
*/
public function output()
{
$restrictions = array();
foreach ( \IPS\Application::allExtensions( 'core', 'MemberRestrictions', TRUE, 'core', 'Content', FALSE ) as $class )
{
try
{
$ext = new $class( $this->member );
if ( $ext->enabled() )
{
foreach ( $ext->activeRestrictions() as $v )
{
$restrictions[] = $v;
}
}
}
catch ( \Exception $e ) { }
}
$flagActions = array();
$spamOption = explode( ',', \IPS\Settings::i()->spm_option );
if ( in_array( 'delete', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_delete');
}
elseif ( in_array( 'unapprove', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_unapprove');
}
if ( in_array( 'ban', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_ban');
}
elseif ( in_array( 'disable', $spamOption ) )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_disable');
}
if ( \IPS\Settings::i()->spam_service_enabled and \IPS\Settings::i()->spam_service_send_to_ips )
{
$flagActions[] = \IPS\Member::loggedIn()->language()->addToStack('spam_flag_confirm_report');
}
if ( count( $flagActions ) )
{
$flagMessage = sprintf( \IPS\Member::loggedIn()->language()->get('spam_flag_confirm'), \IPS\Member::loggedIn()->language()->formatList( $flagActions ) );
}
return \IPS\Theme::i()->getTemplate('memberprofile')->warnings( $this->member, $restrictions, $flagMessage );
}
/**
* Edit Window
*
* @return string
*/
public function edit()
{
/* Get extensions */
$extensions = array();
foreach ( \IPS\Application::allExtensions( 'core', 'MemberRestrictions', TRUE, 'core', 'Content', FALSE ) as $class )
{
try
{
$ext = new $class( $this->member );
if ( $ext->enabled() )
{
$exploded = explode( '\\', $class );
$extensions[ $exploded[1] . '_' . $exploded[5] ] = $ext;
}
}
catch ( \Exception $e ) { }
}
/* Build Form */
$form = new \IPS\Helpers\Form;
if ( \IPS\Settings::i()->warn_on )
{
$form->addHeader( 'warnings' );
$form->add( new \IPS\Helpers\Form\Number( 'member_warnings', $this->member->warn_level, FALSE ) );
}
foreach ( $extensions as $key => $ext )
{
$form->addHeader( 'member_restrictions__' . $key );
$ext->form( $form );
}
/* Handle Submissions */
if ( $values = $form->values() )
{
$changes = array();
if ( \IPS\Settings::i()->warn_on )
{
if ( $this->member->warn_level != $values['member_warnings'] )
{
$changes['member_warnings'] = array( 'old' => $this->member->warn_level, 'new' => $values['member_warnings'] );
$this->member->warn_level = $values['member_warnings'];
}
}
foreach ( $extensions as $key => $ext )
{
foreach ( $ext->save( $values ) as $k => $v )
{
$changes[ $k ] = $v;
}
}
if ( count( $changes ) )
{
$this->member->logHistory( 'core', 'warning', array( 'restrictions' => $changes ) );
}
$this->member->save();
\IPS\Session::i()->log( 'acplog__members_edited_restrictions', array( $this->member->name => FALSE ) );
\IPS\Output::i()->redirect( \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=view&id={$this->member->member_id}" ), 'saved' );
}
/* Display */
return $form;
}
}
@@ -0,0 +1,56 @@
<?php
/**
* @brief ACP Member Profile: Main Tab
* @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 20 Nov 2017
*/
namespace IPS\core\extensions\core\MemberACPProfileTabs;
/* 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 ACP Member Profile: Main Tab
*/
class _Main extends \IPS\core\MemberACPProfile\MainTab
{
/**
* Get left-column blocks
*
* @return array
*/
public function leftColumnBlocks()
{
return array(
'IPS\core\extensions\core\MemberACPProfileBlocks\BasicInformation',
'IPS\core\extensions\core\MemberACPProfileBlocks\Groups',
'IPS\core\extensions\core\MemberACPProfileBlocks\DevicesAndIPAddresses',
'IPS\core\extensions\core\MemberACPProfileBlocks\MFA',
'IPS\core\extensions\core\MemberACPProfileBlocks\OAuth'
);
}
/**
* Get main-column blocks
*
* @return array
*/
public function mainColumnBlocks()
{
return array(
'IPS\core\extensions\core\MemberACPProfileBlocks\Header',
'IPS\core\extensions\core\MemberACPProfileBlocks\ContentStatistics',
'IPS\core\extensions\core\MemberACPProfileBlocks\Quotas',
'IPS\core\extensions\core\MemberACPProfileBlocks\Warnings',
'IPS\core\extensions\core\MemberACPProfileBlocks\ProfileData',
);
}
}
@@ -30,7 +30,7 @@ class _Content
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -112,9 +112,10 @@ class _Content
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['content_count_operator'] ) OR !isset( $filters['content_count_score'] ) )
@@ -138,4 +139,31 @@ class _Content
/* 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 )
{
if ( ! empty( $filters['content_count_score'] ) and $filters['content_count_score'] > 0 )
{
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'] ) ) );
break;
case 'lt':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_content_lt_desc', NULL, 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'] ) ) );
break;
}
}
return NULL;
}
}
@@ -30,7 +30,7 @@ class _Group
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -70,6 +70,41 @@ class _Group
);
}
/**
* Return a lovely human description for this rule if used
*
* @param mixed $data The array returned from the save() method
* @return string|NULL
*/
public function getDescription( $data )
{
if ( $data['groups'] )
{
$_groups = explode( ',', $data['groups'] );
$humanGroups = array();
foreach( $_groups as $gid )
{
/* Uses datastore so not as bad as it looks mkay */
try
{
$humanGroups[] = \IPS\Member\Group::load( $gid )->name;
}
catch( \Exception $e )
{
continue;
}
}
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 NULL;
}
/**
* Save the filter data
*
@@ -115,9 +150,10 @@ class _Group
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['groups'] ) OR !$filters['groups'] )
@@ -127,7 +163,15 @@ class _Group
$_groups = explode( ',', $filters['groups'] );
/* This intentionally checks secondary groups, as SQL filtering also checks secondary groups */
return (bool) count( array_intersect( $_groups, $member->groups ) );
/* If no object is passed, or the property is not set, then we will check secondary groups */
if( $object === NULL OR !isset( $object->memberFilterCheckSecondaryGroups ) OR $object->memberFilterCheckSecondaryGroups === TRUE )
{
/* This checks secondary groups */
return (bool) count( array_intersect( $_groups, $member->groups ) );
}
else
{
return in_array( $member->member_group_id, $_groups );
}
}
}
@@ -30,7 +30,7 @@ class _Joined
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -42,7 +42,7 @@ class _Joined
public function getSettingField( $criteria )
{
return array(
new \IPS\Helpers\Form\Custom( 'bmf_members_joined', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL ), FALSE, array(
new \IPS\Helpers\Form\Custom( 'bmf_members_joined', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL, 3 => isset( $criteria['days_lt'] ) ? $criteria['days_lt'] : NULL ), FALSE, array(
'getHtml' => function( $element )
{
$dateRange = new \IPS\Helpers\Form\DateRange( "{$element->name}[0]", $element->value[0], FALSE );
@@ -62,9 +62,16 @@ class _Joined
*/
public function save( $post )
{
if( isset( $post['bmf_members_joined'][2] ) AND $post['bmf_members_joined'][2] == 'days' )
if ( isset( $post['bmf_members_joined'][2] ) )
{
return $post['bmf_members_joined'][1] ? array( 'days' => intval( $post['bmf_members_joined'][1] ) ) : FALSE;
if ( $post['bmf_members_joined'][2] == 'days' )
{
return $post['bmf_members_joined'][1] ? array( 'days' => intval( $post['bmf_members_joined'][1] ) ) : FALSE;
}
else if ( $post['bmf_members_joined'][2] == 'days_lt' )
{
return $post['bmf_members_joined'][3] ? array( 'days_lt' => intval( $post['bmf_members_joined'][3] ) ) : FALSE;
}
}
else
{
@@ -83,20 +90,26 @@ class _Joined
{
if( !empty($data['range']) AND !empty($data['range']['end']) )
{
$start = ( $data['range']['start']['date'] ) ? new \IPS\DateTime( $data['range']['start']['date'] ) : NULL;
$end = ( $data['range']['end']['date'] ) ? new \IPS\DateTime( $data['range']['end']['date'] ) : NULL;
$start = ( $data['range']['start'] ) ? new \IPS\DateTime( $data['range']['start'] ) : NULL;
$end = ( $data['range']['end'] ) ? new \IPS\DateTime( $data['range']['end'] ) : NULL;
if( $start and $end )
{
return "core_members.joined BETWEEN {$start->getTimestamp()} AND {$end->getTimestamp()}";
}
}
elseif( !empty($data['days']) )
elseif( !empty( $data['days'] ) AND (int) $data['days'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $data['days'] . 'D' ) );
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days'] . 'D' ) );
return "core_members.joined < {$date->getTimestamp()}";
}
elseif( !empty( $data['days_lt'] ) AND (int) $data['days_lt'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days_lt'] . 'D' ) );
return "core_members.joined > {$date->getTimestamp()}";
}
return NULL;
}
@@ -107,9 +120,10 @@ class _Joined
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( ( !isset( $filters['range'] ) OR !$filters['range'] OR empty( $filters['range']['end'] ) ) AND ( !isset( $filters['days'] ) OR !$filters['days'] ) )
@@ -130,9 +144,39 @@ class _Joined
return (bool) ( $joinedDate->getTimestamp() > $start->getTimestamp() AND $joinedDate->getTimestamp() < $end->getTimestamp() );
}
}
elseif( !empty( $filters['days'] ) )
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return (bool) ( $joinedDate->add( new \DateInterval( 'P' . $filters['days'] . 'D' ) )->getTimestamp() < time() );
return (bool) ( $joinedDate->add( new \DateInterval( 'P' . (int) $filters['days'] . 'D' ) )->getTimestamp() < time() );
}
}
/**
* 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 )
{
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
{
$start = ( $filters['range']['start'] ) ? new \IPS\DateTime( $filters['range']['start'] ) : NULL;
$end = ( $filters['range']['end'] ) ? new \IPS\DateTime( $filters['range']['end'] ) : NULL;
if ( $start and $end )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_joined_range_desc', NULL, 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'] ) ) );
}
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 NULL;
}
}
@@ -30,7 +30,7 @@ class _Lastpost
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -42,7 +42,7 @@ class _Lastpost
public function getSettingField( $criteria )
{
return array(
new \IPS\Helpers\Form\Custom( 'mf_members_last_post', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL ), FALSE, array(
new \IPS\Helpers\Form\Custom( 'mf_members_last_post', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL, 3 => isset( $criteria['days_lt'] ) ? $criteria['days_lt'] : NULL ), FALSE, array(
'getHtml' => function( $element )
{
$dateRange = new \IPS\Helpers\Form\DateRange( "{$element->name}[0]", $element->value[0], FALSE );
@@ -62,9 +62,16 @@ class _Lastpost
*/
public function save( $post )
{
if( isset( $post['mf_members_last_post'][2] ) AND $post['mf_members_last_post'][2] == 'days' )
if ( isset( $post['mf_members_last_post'][2] ) )
{
return $post['mf_members_last_post'][1] ? array( 'days' => intval( $post['mf_members_last_post'][1] ) ) : FALSE;
if ( $post['mf_members_last_post'][2] == 'days' )
{
return $post['mf_members_last_post'][1] ? array( 'days' => intval( $post['mf_members_last_post'][1] ) ) : FALSE;
}
else if ( $post['mf_members_last_post'][2] == 'days_lt' )
{
return $post['mf_members_last_post'][3] ? array( 'days_lt' => intval( $post['mf_members_last_post'][3] ) ) : FALSE;
}
}
else
{
@@ -83,20 +90,27 @@ class _Lastpost
{
if( !empty($data['range']) AND !empty($data['range']['end']) )
{
$start = ( $data['range']['start']['date'] ) ? new \IPS\DateTime( $data['range']['start']['date'] ) : NULL;
$end = ( $data['range']['end']['date'] ) ? new \IPS\DateTime( $data['range']['end']['date'] ) : NULL;
$start = ( $data['range']['start'] ) ? new \IPS\DateTime( $data['range']['start'] ) : NULL;
$end = ( $data['range']['end'] ) ? new \IPS\DateTime( $data['range']['end'] ) : NULL;
if( $start and $end )
{
return "core_members.member_last_post BETWEEN {$start} AND {$end}";
return "core_members.member_last_post BETWEEN {$start->getTimestamp()} AND {$end->getTimestamp()}";
}
}
elseif( !empty($data['days']) )
elseif( !empty( $data['days'] ) AND (int) $data['days'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $data['days'] . 'D' ) );
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days'] . 'D' ) );
return "core_members.member_last_post < {$date->getTimestamp()}";
return "( core_members.member_last_post < {$date->getTimestamp()} OR core_members.member_last_post IS NULL )";
}
elseif( !empty( $data['days_lt'] ) AND (int) $data['days_lt'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days_lt'] . 'D' ) );
return "( core_members.member_last_post > {$date->getTimestamp()} )";
}
return NULL;
}
@@ -107,9 +121,10 @@ class _Lastpost
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( ( !isset( $filters['range'] ) OR !$filters['range'] OR empty( $filters['range']['end'] ) ) AND ( !isset( $filters['days'] ) OR !$filters['days'] ) )
@@ -117,6 +132,20 @@ class _Lastpost
return TRUE;
}
/* If the member hasn't posted anything yet, he has NULL as member_last_post */
if ( !$member->member_last_post )
{
/* But if we want members who haven't posted in more than X days, return anyone who hasn't posted at all as well */
if( !empty( $filters['days'] ) )
{
return TRUE;
}
else
{
return FALSE;
}
}
$lastPost = \IPS\DateTime::ts( $member->member_last_post );
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
@@ -129,9 +158,39 @@ class _Lastpost
return (bool) ( $lastPost->getTimestamp() > $start->getTimestamp() AND $lastPost->getTimestamp() < $end->getTimestamp() );
}
}
elseif( !empty( $filters['days'] ) )
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return (bool) ( $lastPost->add( new \DateInterval( 'P' . $filters['days'] . 'D' ) )->getTimestamp() < time() );
return (bool) ( $lastPost->add( new \DateInterval( 'P' . (int) $filters['days'] . 'D' ) )->getTimestamp() < time() );
}
}
/**
* 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 )
{
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
{
$start = ( $filters['range']['start'] ) ? new \IPS\DateTime( $filters['range']['start'] ) : NULL;
$end = ( $filters['range']['end'] ) ? new \IPS\DateTime( $filters['range']['end'] ) : NULL;
if ( $start and $end )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastpost_range_desc', NULL, 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'] ) ) );
}
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 NULL;
}
}
@@ -30,7 +30,7 @@ class _Lastvisit
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -42,7 +42,7 @@ class _Lastvisit
public function getSettingField( $criteria )
{
return array(
new \IPS\Helpers\Form\Custom( 'bmf_members_last_visit', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL ), FALSE, array(
new \IPS\Helpers\Form\Custom( 'bmf_members_last_visit', array( 0 => isset( $criteria['range'] ) ? $criteria['range'] : '', 1 => isset( $criteria['days'] ) ? $criteria['days'] : NULL, 3 => isset( $criteria['days_lt'] ) ? $criteria['days_lt'] : NULL ), FALSE, array(
'getHtml' => function( $element )
{
$dateRange = new \IPS\Helpers\Form\DateRange( "{$element->name}[0]", $element->value[0], FALSE );
@@ -62,9 +62,16 @@ class _Lastvisit
*/
public function save( $post )
{
if( isset( $post['bmf_members_last_visit'][2] ) AND $post['bmf_members_last_visit'][2] == 'days' )
if ( isset( $post['bmf_members_last_visit'][2] ) )
{
return $post['bmf_members_last_visit'][1] ? array( 'days' => intval( $post['bmf_members_last_visit'][1] ) ) : FALSE;
if ( $post['bmf_members_last_visit'][2] == 'days' )
{
return $post['bmf_members_last_visit'][1] ? array( 'days' => intval( $post['bmf_members_last_visit'][1] ) ) : FALSE;
}
else if ( $post['bmf_members_last_visit'][2] == 'days_lt' )
{
return $post['bmf_members_last_visit'][3] ? array( 'days_lt' => intval( $post['bmf_members_last_visit'][3] ) ) : FALSE;
}
}
else
{
@@ -91,12 +98,18 @@ class _Lastvisit
return "core_members.last_visit BETWEEN {$start->getTimestamp()} AND {$end->getTimestamp()}";
}
}
elseif( !empty($data['days']) )
elseif( !empty( $data['days'] ) AND (int) $data['days'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . $data['days'] . 'D' ) );
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days'] . 'D' ) );
return "core_members.last_visit < {$date->getTimestamp()}";
}
elseif( !empty( $data['days_lt'] ) AND (int) $data['days_lt'] )
{
$date = \IPS\DateTime::create()->sub( new \DateInterval( 'P' . (int) $data['days_lt'] . 'D' ) );
return "core_members.last_visit > {$date->getTimestamp()}";
}
return NULL;
}
@@ -107,9 +120,10 @@ class _Lastvisit
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( ( !isset( $filters['range'] ) OR !$filters['range'] OR empty( $filters['range']['end'] ) ) AND ( !isset( $filters['days'] ) OR !$filters['days'] ) )
@@ -129,9 +143,39 @@ class _Lastvisit
return (bool) ( $lastVisit->getTimestamp() > $start->getTimestamp() AND $lastVisit->getTimestamp() < $end->getTimestamp() );
}
}
elseif( !empty( $filters['days'] ) )
elseif( !empty( $filters['days'] ) AND (int) $filters['days'] )
{
return (bool) ( $lastVisit->add( new \DateInterval( 'P' . $filters['days'] . 'D' ) )->getTimestamp() < time() );
return (bool) ( $lastVisit->add( new \DateInterval( 'P' . (int) $filters['days'] . 'D' ) )->getTimestamp() < time() );
}
}
/**
* 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 )
{
if( !empty( $filters['range'] ) AND !empty( $filters['range']['end'] ) )
{
$start = ( $filters['range']['start'] ) ? new \IPS\DateTime( $filters['range']['start'] ) : NULL;
$end = ( $filters['range']['end'] ) ? new \IPS\DateTime( $filters['range']['end'] ) : NULL;
if ( $start and $end )
{
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_lastvisit_range_desc', NULL, 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'] ) ) );
}
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 NULL;
}
}
@@ -83,9 +83,10 @@ class _Leaderboard
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['leaderboard'] ) OR !$filters['leaderboard'] )
@@ -1,114 +1,4 @@
<?php
/**
* @brief Member filter extension: Linked accounts
* @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 Mar 2017
*/
namespace IPS\core\extensions\core\MemberFilter;
// Removed
/* 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: Member last visit date
*/
class _Linkedaccounts
{
/**
* 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 )
{
$services = array();
$options = array( 'any' => 'any', 'linked' => 'mf_profile_linked', 'unlinked' => 'mf_profile_unlinked' );
foreach ( \IPS\core\ProfileSync\ProfileSyncAbstract::services() as $key => $class )
{
if( $class::memberFilterLinkedWhere() )
{
$services[] = new \IPS\Helpers\Form\Radio( 'profilesync__' . $key, isset( $criteria['profilesync__' . $key] ) ? $criteria['profilesync__' . $key] : 'any', FALSE, array( 'options' => $options ) );
}
}
return $services;
}
/**
* 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();
foreach ( \IPS\core\ProfileSync\ProfileSyncAbstract::services() as $key => $class )
{
if( isset( $post['profilesync__' . $key] ) and in_array( $post['profilesync__' . $key], array( 'linked', 'unlinked' ) ) )
{
$return['profilesync__' . $key] = $post['profilesync__' . $key];
}
}
return count( $return ) ? $return : FALSE;
}
/**
* Get where clause to add to the member retrieval database query
*
* @param mixed $data The array returned from the save() method
* @return string|array|NULL Where clause
*/
public function getQueryWhereClause( $data )
{
$return = array();
foreach ( \IPS\core\ProfileSync\ProfileSyncAbstract::services() as $key => $class )
{
if( !isset( $data['profilesync__' . $key] ) )
{
continue;
}
switch( $data['profilesync__' . $key] )
{
case 'linked':
$where = $class::memberFilterLinkedWhere();
break;
case 'unlinked':
$where = $class::memberFilterLinkedWhere( TRUE );
break;
default:
break;
}
if( $where )
{
$return[] = $where;
}
}
return $return;
}
}
@@ -30,7 +30,7 @@ class _Reputation
*/
public function availableIn( $area )
{
return in_array( $area, array( 'bulkmail', 'group_promotions' ) );
return in_array( $area, array( 'bulkmail', 'group_promotions', 'automatic_moderation' ) );
}
/**
@@ -112,9 +112,10 @@ class _Reputation
* @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 )
public function matches( \IPS\Member $member, $filters, $object=NULL )
{
/* If we aren't filtering by this, then any member matches */
if( !isset( $filters['reputation_operator'] ) OR !$filters['reputation_operator'] OR !isset( $filters['reputation_score'] ) OR !$filters['reputation_score'] )
@@ -138,4 +139,31 @@ class _Reputation
/* 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 )
{
if ( ! empty( $filters['reputation_score'] ) and $filters['reputation_score'] > 0 )
{
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'] ) ) );
break;
case 'lt':
return \IPS\Member::loggedIn()->language()->addToStack( 'member_filter_core_reputation_lt_desc', NULL, 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'] ) ) );
break;
}
}
return NULL;
}
}
@@ -41,7 +41,6 @@ class _Twofactor
*/
public function getSettingField( $criteria )
{
$options = array( 'any' => 'any', 'active' => 'mf_two_factor_active', 'inactive' => 'mf_two_factor_inactive' );
return array(
@@ -8,250 +8,4 @@
* @since 08 Apr 2013
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form
*/
class _BasicInformation
{
/**
* Action Buttons
*
* @param \IPS\Member $member The Member
* @return array
*/
public function actionButtons( $member )
{
$return = array();
/* If we can sign in as, flag as spammer, merge, ban or delete content */
if ( ( \IPS\Member::loggedIn()->member_id != $member->member_id AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_login' ) AND !$member->isBanned() ) || ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id ) || \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'members_merge' ) || ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id ) || ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'membertools_delete' ) ) )
{
$return['actions'] = array(
'title' => 'member_account_actions',
'icon' => 'user',
'primary' => TRUE,
'link' => '#',
'menu' => array()
);
if ( \IPS\Member::loggedIn()->member_id != $member->member_id AND \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_login' ) AND !$member->isBanned() )
{
$return['actions']['menu'][] = array(
'title' => \IPS\Member::loggedIn()->language()->addToStack( 'login_as_x', FALSE, array( 'sprintf' => array( $member->name ) ) ),
'icon' => 'key',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=login&id={$member->member_id}" ),
'class' => '',
'target' => '_blank'
);
}
/* Flag */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_edit_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id )
{
$return['actions']['menu'][] = array(
'title' => $member->members_bitoptions['bw_is_spammer'] ? 'spam_unflag' : 'spam_flag',
'icon' => 'flag',
'link' => \IPS\Http\Url::internal( 'app=core&module=members&controller=members&do=spam&id=' )->setQueryString( array( 'id' => $member->member_id, 'status' => $member->members_bitoptions['bw_is_spammer'] ? 0 : 1 ) ),
'class' => ''
);
}
/* Merge */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'members_merge' ) )
{
$return['actions']['menu'][] = array(
'title' => 'merge',
'icon' => 'level-up',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=merge&id={$member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('merge') )
);
}
/* Ban */
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban' ) and ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_ban_admin' ) or !$member->isAdmin() ) AND $member->member_id != \IPS\Member::loggedIn()->member_id )
{
$return['actions']['menu'][] = array(
'title' => $member->temp_ban ? 'adjust_ban' : 'ban',
'icon' => 'times',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=ban&id={$member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => $member->temp_ban ? \IPS\Member::loggedIn()->language()->addToStack('adjust_ban') : \IPS\Member::loggedIn()->language()->addToStack('ban') )
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'membertools_delete' ) )
{
$return['actions']['menu'][] = array(
'title' => 'member_delete_content',
'icon' => 'trash-o',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=deleteContent&id={$member->member_id}" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('member_delete_content') )
);
}
}
$return['view'] = array(
'title' => 'profile_view_profile',
'icon' => 'search',
'link' => $member->url(),
'class' => '',
'target' => '_blank'
);
if ( \IPS\Settings::i()->warn_on AND \IPS\Member::loggedIn()->modPermission('mod_see_warn') )
{
$return['warnings'] = array(
'title' => 'modcp_view_warnings',
'icon' => 'exclamation-triangle',
'link' => \IPS\Http\Url::internal( "app=core&module=system&controller=warnings&id=" . $member->member_id, 'front', 'warn_list', array( $member->members_seo_name ) ),
'target' => '_blank'
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_history' ) )
{
$return['history'] = array(
'title' => 'member_history',
'icon' => 'history',
'link' => \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=history&id=" . $member->member_id )
);
}
return $return;
}
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* Username */
$form->addHeader('member_basic_information');
$form->add( new \IPS\Helpers\Form\Text( 'ips_member_name', $member->name, TRUE, array( 'accountUsername' => $member ), NULL, NULL, '<a data-ipsDialog data-ipsDialog-title="' . \IPS\Member::loggedIn()->language()->addToStack( 'dname_history' ) . '" data-ipsDialog-url="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=viewDnameHistory&id={$member->member_id}" ) . '" href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=viewDnameHistory&id={$member->member_id}" ) . '">' . \IPS\Member::loggedIn()->language()->addToStack( 'view_username_history' ) . '</a>' ) );
/* Password */
$form->add( new \IPS\Helpers\Form\Custom( 'password', NULL, FALSE, array(
'getHtml' => function( $element ) use ( $member )
{
return \IPS\Theme::i()->getTemplate('members')->changePassword( $element->name, $member->member_id );
}
) ) );
/* Email */
$form->add( new \IPS\Helpers\Form\Email( 'ips_address_mail', $member->email, TRUE, array( 'accountEmail' => $member ) ) );
/* Group - if this member is an admin, we need the "Can move admins into other groups" restriction */
if ( !$member->isAdmin() or \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin1' ) )
{
/* If we are editing ourselves, we can only move ourselves into a group with the same restrictions as what we have now... */
if ( $member->member_id == \IPS\Member::loggedIn()->member_id )
{
/* Get the row... */
try
{
$currentRestrictions = \IPS\Db::i()->select( 'row_perm_cache', 'core_admin_permission_rows', array( 'row_id=? AND row_id_type=?', $member->member_group_id, 'group' ) )->first();
$availableGroups = array();
foreach( \IPS\Db::i()->select( 'row_id', 'core_admin_permission_rows', array( 'row_perm_cache=? AND row_id_type=?', $currentRestrictions, 'group' ) ) AS $groupId )
{
$availableGroups[ $groupId ] = \IPS\Member\Group::load( $groupId );
}
}
/* If we don't have a row in core_admin_permission_rows, we're an admin as a member rather than apart of our group, so we can be moved anywhere and it won't matter because member-level restrictions override group-level */
catch ( \UnderflowException $e )
{
$availableGroups = \IPS\Member\Group::groups( TRUE, FALSE );
}
}
/* Not editing ourselves - do we have the Can move members into admin groups"" restriction? */
else
{
$availableGroups = \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE );
}
$form->add( new \IPS\Helpers\Form\Select( 'group', $member->member_group_id, TRUE, array( 'options' => $availableGroups, 'parse' => 'normal' ) ) );
$form->add( new \IPS\Helpers\Form\Select( 'secondary_groups', array_filter( explode( ',', $member->mgroup_others ) ), FALSE, array( 'options' => \IPS\Member\Group::groups( \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin2' ), FALSE ), 'multiple' => TRUE, 'parse' => 'normal' ) ) );
}
/* Counts */
$confirmButtons = json_encode( array(
'yes' => \IPS\Member::loggedIn()->language()->addToStack('yes'),
'no' => \IPS\Member::loggedIn()->language()->addToStack('recount_all'),
'cancel' => \IPS\Member::loggedIn()->language()->addToStack('cancel'),
) );
$form->addHeader('member_counts');
$form->add( new \IPS\Helpers\Form\Number( 'member_content_items', $member->member_posts, FALSE, array(), NULL, NULL, '<a data-confirm data-confirmType="verify" data-confirmButtons=\'' . $confirmButtons . '\' data-confirmSubMessage="' . \IPS\Member::loggedIn()->language()->addToStack('member_content_items_recount') . '" href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=recountContent&id={$member->member_id}") . '">' . \IPS\Member::loggedIn()->language()->addToStack('recount') . '</a>' ) );
if ( \IPS\Settings::i()->reputation_enabled )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_reputation', $member->pp_reputation_points, FALSE, array( 'min' => NULL ), NULL, NULL, '<a data-confirm data-confirmType="verify" data-confirmButtons=\'' . $confirmButtons . '\' data-confirmSubMessage="' . \IPS\Member::loggedIn()->language()->addToStack('member_reputation_recount') . '" href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=recountReputation&id={$member->member_id}") . '">' . \IPS\Member::loggedIn()->language()->addToStack('recount') . '</a>' . ' &middot; <a data-confirm href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=removeReputation&id={$member->member_id}&type=given") . '">' . \IPS\Member::loggedIn()->language()->addToStack('reputation_remove_given') . '</a>' . ' &middot; <a data-confirm href="' . \IPS\Http\Url::internal( "app=core&module=members&controller=members&do=removeReputation&id={$member->member_id}&type=received") . '">' . \IPS\Member::loggedIn()->language()->addToStack('reputation_remove_received') . '</a>' ) );
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
if ( $values['ips_member_name'] != $member->name )
{
foreach ( \IPS\Login::handlers( TRUE ) as $handler )
{
try
{
$handler->changeUsername( $member, $member->name, $values['ips_member_name'] );
}
catch( \BadMethodCallException $e ) {}
}
}
if ( $values['ips_address_mail'] != $member->email )
{
$oldEmail = $member->email;
foreach ( \IPS\Login::handlers( TRUE ) as $handler )
{
try
{
$handler->changeEmail( $member, $oldEmail, $values['ips_address_mail'] );
}
catch( \BadMethodCallException $e ) {}
}
$member->invalidateSessionsAndLogins( TRUE, \IPS\Session::i()->id );
}
if ( !$member->isAdmin() or \IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_move_admin1' ) )
{
$member->member_group_id = $values['group'];
$member->mgroup_others = implode( ',', $values['secondary_groups'] );
}
$member->member_posts = $values['member_content_items'];
if ( \IPS\Settings::i()->reputation_enabled )
{
$member->pp_reputation_points = $values['member_reputation'];
}
if ( $values['password'] )
{
$member->members_pass_salt = $member->generateSalt();
$member->members_pass_hash = $member->encryptedPassword( $values['password'] );
$member->invalidateSessionsAndLogins( TRUE, \IPS\Session::i()->id );
}
/* Reset Profile Complete flag in case this was an optional step */
$member->members_bitoptions['profile_completed'] = FALSE;
}
}
// Removed
@@ -64,7 +64,12 @@ class _Notifications
*/
public function save( $values, &$member )
{
if ( $member->allow_admin_mails != $values['allow_admin_mails'] )
{
$member->logHistory( 'core', 'admin_mails', array( 'enabled' => (bool) $values['allow_admin_mails'] ) );
}
$member->allow_admin_mails = (int) $values['allow_admin_mails'];
$member->auto_track = json_encode( array(
'content' => ( is_array( $values['auto_track'] ) AND in_array( 'content', $values['auto_track'] ) ) ? 1 : 0,
'comments' => ( is_array( $values['auto_track'] ) AND in_array( 'comments', $values['auto_track'] ) ) ? 1 : 0,
@@ -35,7 +35,7 @@ class _Preferences
$form->addHeader('member_preferences_system');
$form->add( new \IPS\Helpers\Form\YesNo( 'member_timezone_override',!$member->members_bitoptions['timezone_override'], FALSE, array( 'togglesOff' => array( 'timezone' ) ) ) );
$timezones = array();
foreach ( \DateTimeZone::listIdentifiers() as $tz )
foreach ( \IPS\DateTime::getTimezoneIdentifiers() as $tz )
{
if ( $pos = mb_strpos( $tz, '/' ) )
{
@@ -78,6 +78,11 @@ class _Preferences
/* Content */
$form->addHeader('member_preferences_content');
$form->add( new \IPS\Helpers\Form\YesNo( 'view_sigs', $member->members_bitoptions['view_sigs'], FALSE ) );
/* Profile */
$form->addHeader('member_preferences_profile');
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_count_visitors', $member->members_bitoptions['pp_setting_count_visitors'], FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_moderate_followers', !$member->members_bitoptions['pp_setting_moderate_followers'] ) );
}
/**
@@ -107,5 +112,7 @@ class _Preferences
/* Other */
$member->members_bitoptions['view_sigs'] = $values['view_sigs'];
$member->members_bitoptions['pp_setting_count_visitors'] = $values['pp_setting_count_visitors'];
$member->members_bitoptions['pp_setting_moderate_followers'] = $values['pp_setting_moderate_followers'] ? FALSE : TRUE;
}
}
@@ -8,146 +8,4 @@
* @since 08 Apr 2013
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form
*/
class _Profile
{
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* Profile Preferences */
$form->addHeader('member_preferences_profile');
$form->add( new \IPS\Helpers\Form\Upload( 'pp_cover_photo', $member->pp_cover_photo ? \IPS\File::get( 'core_Profile', $member->pp_cover_photo ) : NULL, FALSE, array( 'storageExtension' => 'core_Profile', 'image' => TRUE ) ) );
$form->add( new \IPS\Helpers\Form\Text( 'member_title', $member->member_title, FALSE, array( 'maxLength' => 64 ) ) );
if ( \IPS\Settings::i()->profile_birthday_type !== 'none' )
{
$form->add( new \IPS\Helpers\Form\Custom( 'bday', array( 'year' => $member->bday_year, 'month' => $member->bday_month, 'day' => $member->bday_day ), FALSE, array( 'getHtml' => function( $element )
{
return strtr( \IPS\Member::loggedIn()->language()->preferredDateFormat(), array(
'DD' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_day( $element->name, $element->value, $element->error ),
'MM' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_month( $element->name, $element->value, $element->error ),
'YY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
'YYYY' => \IPS\Theme::i()->getTemplate( 'members', 'core', 'global' )->bdayForm_year( $element->name, $element->value, $element->error ),
) );
} ) ) );
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$form->add( new \IPS\Helpers\Form\Editor( 'signature', $member->signature, FALSE, array( 'app' => 'core', 'key' => 'Signatures', 'autoSaveKey' => "sig-{$member->member_id}", 'attachIds' => array( $member->member_id ) ) ) );
}
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_count_visitors', $member->members_bitoptions['pp_setting_count_visitors'], FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'pp_setting_moderate_followers', !$member->members_bitoptions['pp_setting_moderate_followers'] ) );
/* Profile Fields */
try
{
$values = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
}
catch( \UnderflowException $e )
{
$values = array();
}
if( count( $values ) )
{
foreach ( \IPS\core\ProfileFields\Field::fields( $values, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
$form->addHeader( "core_pfieldgroups_{$group}" );
foreach ( $fields as $field )
{
$form->add( $field );
}
}
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
/* Profile Preferences */
$member->member_title = $values['member_title'];
if ( $values['bday'] )
{
$member->bday_day = $values['bday']['day'];
$member->bday_month = $values['bday']['month'];
$member->bday_year = $values['bday']['year'];
}
else
{
$member->bday_day = NULL;
$member->bday_month = NULL;
$member->bday_year = NULL;
}
if ( \IPS\Settings::i()->signatures_enabled )
{
$member->signature = $values['signature'];
}
$member->members_bitoptions['pp_setting_count_visitors'] = $values['pp_setting_count_visitors'];
$member->members_bitoptions['pp_setting_moderate_followers'] = $values['pp_setting_moderate_followers'] ? FALSE : TRUE;
/* Cover photo */
if ( $values['pp_cover_photo'] )
{
$member->pp_cover_photo = (string) $values['pp_cover_photo'];
}
else
{
$member->pp_cover_photo = '';
}
/* Profile Fields */
try
{
$profileFields = \IPS\Db::i()->select( '*', 'core_pfields_content', array( 'member_id=?', $member->member_id ) )->first();
}
catch( \UnderflowException $e )
{
$profileFields = array();
}
/* If \IPS\Db::i()->select()->first() has only one column, then the contents of that column is returned. We do not want this here. */
if ( !is_array( $profileFields ) )
{
$profileFields = array();
}
$profileFields['member_id'] = $member->member_id;
foreach ( \IPS\core\ProfileFields\Field::fields( $profileFields, \IPS\core\ProfileFields\Field::STAFF ) as $group => $fields )
{
foreach ( $fields as $id => $field )
{
$profileFields[ "field_{$id}" ] = $field::stringValue( !empty( $values[ $field->name ] ) ? $values[ $field->name ] : NULL );
}
}
\IPS\Db::i()->replace( 'core_pfields_content', $profileFields );
$member->changedCustomFields = $profileFields;
}
}
// Removed
@@ -1,137 +1,3 @@
<?php
/**
* @brief Admin CP Member Form
* @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 15 Apr 2013
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form
*/
class _Restrictions
{
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* Moderation */
$form->addHeader( 'member_moderation' );
if ( \IPS\Settings::i()->warn_on )
{
$form->add( new \IPS\Helpers\Form\Number( 'member_warnings', $member->warn_level, FALSE ) );
}
$form->add( new \IPS\Helpers\Form\Date( 'restrict_post', $member->restrict_post, FALSE, array( 'time' => TRUE, 'unlimited' => -1, 'unlimitedLang' => 'indefinitely' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('until') ) );
$form->add( new \IPS\Helpers\Form\Date( 'mod_posts', $member->mod_posts, FALSE, array( 'time' => TRUE, 'unlimited' => -1, 'unlimitedLang' => 'indefinitely' ), NULL, \IPS\Member::loggedIn()->language()->addToStack('until') ) );
/* Restrictions */
$form->addHeader( 'member_restrictions' );
if ( \IPS\Settings::i()->tags_enabled )
{
if ( !$member->group['gbw_disable_tagging'] )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_tagging', !$member->members_bitoptions['bw_disable_tagging'] ) );
}
if ( !$member->group['gbw_disable_prefixes'] )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'bw_disable_prefixes', !$member->members_bitoptions['bw_disable_prefixes'] ) );
}
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
{
/* There are two columns for "can post status updates". The first is a user-level option (the user can turn status updates on and
off in their account), and the second is an admin-level option (block user from posting status updates). Having two options is
confusing so we merge both options into one select list which matches the PM dropdown below */
$value = ( !$member->members_bitoptions['bw_no_status_update'] AND $member->pp_setting_count_comments ) ? 0 :
( !$member->members_bitoptions['bw_no_status_update'] ? 1 : 2 );
$form->add( new \IPS\Helpers\Form\Select( 'bw_no_status_update', $value, FALSE, array( 'options' => array(
0 => 'members_disable_pm_on',
1 => 'members_disable_pm_member_disable',
2 => 'members_disable_pm_admin_disable',
) ) ) );
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'messaging', 'front' ) ) )
{
$form->add( new \IPS\Helpers\Form\Select( 'members_disable_pm', $member->members_disable_pm, FALSE, array( 'options' => array(
0 => 'members_disable_pm_on',
1 => 'members_disable_pm_member_disable',
2 => 'members_disable_pm_admin_disable',
) ) ) );
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
if ( \IPS\Settings::i()->warn_on )
{
$member->warn_level = $values['member_warnings'];
}
$member->mod_posts = is_object( $values['mod_posts'] ) ? $values['mod_posts']->getTimestamp() : ( $values['mod_posts'] ?: 0 );
$member->restrict_post = is_object( $values['restrict_post'] ) ? $values['restrict_post']->getTimestamp() : ( $values['restrict_post'] ?: 0 );
if ( \IPS\Settings::i()->tags_enabled )
{
if ( !$member->group['gbw_disable_tagging'] AND array_key_exists( 'bw_disable_tagging', $values ) )
{
$member->members_bitoptions['bw_disable_tagging'] = !$values['bw_disable_tagging'];
}
if ( !$member->group['gbw_disable_prefixes'] AND array_key_exists( 'bw_disable_prefixes', $values ) )
{
$member->members_bitoptions['bw_disable_prefixes'] = !$values['bw_disable_prefixes'];
}
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'members' ) ) )
{
/* We may be adding a secondary group which DOES have access - we should just use the default in this instance */
if ( array_key_exists( 'bw_no_status_update', $values ) )
{
/* 0 means 'yes can post', 1 means 'no, but user can enable' and 2 means 'no and user cannot re-enable'. */
$member->members_bitoptions['bw_no_status_update'] = ( $values['bw_no_status_update'] == 2 ) ? 1 : 0;
$member->pp_setting_count_comments = ( $values['bw_no_status_update'] == 0 ) ? 1 : 0;
}
else
{
$member->members_bitoptions['bw_no_status_update'] = FALSE;
}
}
if ( $member->canAccessModule( \IPS\Application\Module::get( 'core', 'messaging', 'front' ) ) )
{
/* We may be adding a secondary group which DOES have access - we should just use the default in this instance */
if ( array_key_exists( 'members_disable_pm', $values ) )
{
$member->members_disable_pm = $values['members_disable_pm'];
}
else
{
$member->members_disable_pm = 0;
}
}
}
}
// Removed
@@ -8,128 +8,4 @@
* @since 28 Sep 2016
*/
namespace IPS\core\extensions\core\MemberForm;
/* 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;
}
/**
* Admin CP Member Form: MFA
*/
class _SecurityAnswers // It's called this for backwards compatibility from when we just had security answers
{
/**
* Process Form
*
* @param \IPS\Helpers\Form $form The form
* @param \IPS\Member $member Existing Member
* @return void
*/
public function process( &$form, $member )
{
/* ACP restriction check */
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_mfa' ) )
{
return;
}
/* Get all the fields we'll need */
$fields = array();
$optOutToggles = array();
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $member ) )
{
foreach ( $handler->acpConfiguration( $member ) as $id => $field )
{
$fields[] = $field;
}
$optOutToggles[] = "mfa_{$key}_title";
}
}
/* If we don't have have any, we need don't to display this tab */
if ( !count( $fields ) )
{
return;
}
/* We need the opt-out field if the user is in a group which can opt out */
if ( \IPS\Settings::i()->mfa_required_groups != '*' and !$member->inGroup( explode( ',', \IPS\Settings::i()->mfa_required_groups ) ) )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'mfa_opt_out_admin', $member->members_bitoptions['security_questions_opt_out'], FALSE, array( 'togglesOff' => $optOutToggles ) ) );
}
/* Now add all the other fields */
foreach ( $fields as $id => $field )
{
if ( $field instanceof \IPS\Helpers\Form\Matrix )
{
$form->addMatrix( $field->id, $field );
}
else
{
$form->add( $field );
}
}
}
/**
* Save
*
* @param array $values Values from form
* @param \IPS\Member $member The member
* @return void
*/
public function save( $values, &$member )
{
/* ACP restriction check */
if ( !\IPS\Member::loggedIn()->hasAcpRestriction( 'core', 'members', 'member_mfa' ) )
{
return;
}
/* Reset the failure count and unlock if necessary */
$member->failed_mfa_attempts = 0;
$mfaDetails = $member->mfa_details;
if ( isset( $mfaDetails['_lockouttime'] ) )
{
unset( $mfaDetails['_lockouttime'] );
$member->mfa_details = $mfaDetails;
}
/* Did we opt out? */
if ( isset( $values['mfa_opt_out_admin'] ) )
{
/* Opt-Out: Disable all handlers */
if ( $values['mfa_opt_out_admin'] )
{
$member->members_bitoptions['security_questions_opt_out'] = TRUE;
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
$handler->disableHandlerForMember( $member );
}
$member->save();
}
/* Opt-In */
else
{
$member->members_bitoptions['security_questions_opt_out'] = FALSE;
}
}
/* Save each of the handlers */
foreach ( \IPS\MFA\MFAHandler::handlers() as $key => $handler )
{
if ( $handler->isEnabled() and $handler->memberCanUseHandler( $member ) )
{
$handler->acpConfigurationSave( $member, $values );
}
}
}
}
// Removed
Loaded 100 of 1349 files, more files were not shown because too many files have changed in this diff. Show more