Version 4.7.23
This commit is contained in:
1 parent
7124a02564
commit
25ddeb65d6
1791 files changed
+76990
-44452
No files matched your search
@@ -24,6 +24,11 @@ abstract class _AbstractContentAchievementAction extends AbstractAchievementActi
|
||||
protected static $includeItems = TRUE;
|
||||
protected static $includeComments = TRUE;
|
||||
protected static $includeReviews = TRUE;
|
||||
|
||||
/**
|
||||
* @var bool Exclude content items that require a first comment
|
||||
*/
|
||||
protected static $excludeItemsWithRequiredComment = FALSE;
|
||||
|
||||
/**
|
||||
* Get filter form elements
|
||||
@@ -62,7 +67,7 @@ abstract class _AbstractContentAchievementAction extends AbstractAchievementActi
|
||||
$nodeFilterKey = 'achievement_subfilter_' . $classKey . '_type_' . str_replace( '\\', '-', $class );
|
||||
$contentTypeToggles[ $class ][] = $nodeFilterKey;
|
||||
}
|
||||
if ( static::$includeItems )
|
||||
if ( static::$includeItems OR ( !static::$includeItems AND static::$excludeItemsWithRequiredComment AND !$class::$firstCommentRequired ) )
|
||||
{
|
||||
$contentTypeOptions[ $class ] = $class::$title;
|
||||
$contentTypeToggles[ $class ] = $nodeFilterKey ? [ $nodeFilterKey ] : [];
|
||||
|
||||
@@ -55,8 +55,8 @@ class _Acronym extends \IPS\Patterns\ActiveRecord
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'word_a_type', ( $acronym ) ? $acronym->a_type : 'acronym', FALSE, array( 'options' => array( 'acronym' => 'word_type_acronym', 'link' => 'word_type_link' ), 'toggles' => array( 'acronym' => array( 'word_a_long' ), 'link' => array( 'word_a_url' ) ) ), NULL, NULL, NULL, 'word_a_type' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'word_a_short', ( $acronym ) ? $acronym->a_short : NULL, TRUE ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'word_a_long', ( $acronym ) ? $acronym->a_long : NULL, TRUE, array(), NULL, NULL, NULL, 'word_a_long' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'word_a_url', ( $acronym ) ? $acronym->a_long : NULL, TRUE, array(), NULL, NULL, NULL, 'word_a_url' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'word_a_long', ( $acronym AND ( !$acronym->a_type OR $acronym->a_type == 'acronym' ) ) ? $acronym->a_long : NULL, NULL, array(), NULL, NULL, NULL, 'word_a_long' ) );
|
||||
$form->add( new \IPS\Helpers\Form\Url( 'word_a_url', ( $acronym AND $acronym->a_type AND $acronym->a_type == 'link' ) ? $acronym->a_long : NULL, NULL, array(), NULL, NULL, NULL, 'word_a_url' ) );
|
||||
$form->add( new \IPS\Helpers\Form\YesNo( 'word_a_casesensitive', ( $acronym ) ? $acronym->a_casesensitive : NULL, FALSE, array(), NULL, NULL, NULL, 'word_a_casesensitive' ) );
|
||||
|
||||
return $form;
|
||||
|
||||
@@ -80,7 +80,12 @@ class _Alert extends \IPS\Content\Item
|
||||
*/
|
||||
public static $icon = 'bullhorn';
|
||||
|
||||
|
||||
CONST REPLY_NOT_REQUIRED = 0;
|
||||
CONST REPLY_OPTIONAL = 1;
|
||||
CONST REPLY_REQUIRED = 2;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get SEO name
|
||||
*
|
||||
@@ -159,7 +164,7 @@ class _Alert extends \IPS\Content\Item
|
||||
throw new \InvalidArgumentException( 'alert_no_recipient_selected' );
|
||||
}
|
||||
|
||||
if ( ! $member->canUseMessenger() and \IPS\Request::i()->alert_reply == 2 )
|
||||
if ( ! $member->canUseMessenger() and \IPS\Request::i()->alert_reply == static::REPLY_REQUIRED )
|
||||
{
|
||||
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack( 'alert_member_pm_disabled', NULL, [ 'sprintf' => $member->name ] ) );
|
||||
}
|
||||
@@ -169,7 +174,7 @@ class _Alert extends \IPS\Content\Item
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'alert_recipient_group', ( $alert and $alert->recipient_group ) ? explode( ',', $alert->recipient_group ) : array(), FALSE, array( 'options' => $groups, 'multiple' => TRUE ), function( $groups )
|
||||
{
|
||||
if ( \IPS\Request::i()->alert_reply == 2 )
|
||||
if ( \IPS\Request::i()->alert_reply == static::REPLY_REQUIRED )
|
||||
{
|
||||
$module = \IPS\Application\Module::get( 'core', 'messaging' );
|
||||
$names = [];
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
namespace IPS\core\Announcements;
|
||||
|
||||
/* 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' );
|
||||
@@ -233,8 +234,15 @@ class _Announcement extends \IPS\Content\Item
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Text( 'announce_title', ( $announcement ) ? $announcement->title : NULL, TRUE, array( 'maxLength' => 255 ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Date( 'announce_start', ( $announcement ) ? \IPS\DateTime::ts( $announcement->start ) : new \IPS\DateTime ) );
|
||||
$form->add( new \IPS\Helpers\Form\Date( 'announce_end', ( $announcement AND $announcement->end ) ? \IPS\DateTime::ts( $announcement->end ) : 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'indefinitely' ) ) );
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\Date( 'announce_end', ( $announcement AND $announcement->end ) ? \IPS\DateTime::ts( $announcement->end ) : 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'indefinitely' ), function($val){
|
||||
|
||||
/* Compare with announce_start and make sure it's after this */
|
||||
$start = new \IPS\DateTime( \IPS\Request::i()->announce_start );
|
||||
if( $val and $val->getTimestamp() < $start->getTimestamp() )
|
||||
{
|
||||
throw new \InvalidArgumentException( 'invalid_end_date' );
|
||||
}
|
||||
} ) );
|
||||
$form->add( new \IPS\Helpers\Form\Radio( 'announce_type', ( $announcement ) ? $announcement->type : 'content', TRUE, array(
|
||||
'options' => array(
|
||||
static::TYPE_NONE => \IPS\Member::loggedIn()->language()->addToStack( 'announce_type_none' ),
|
||||
|
||||
@@ -1382,6 +1382,14 @@ HTML,
|
||||
$changes
|
||||
);
|
||||
$changes['custom'] = true;
|
||||
if ( !isset( $properties[$key] ) )
|
||||
{
|
||||
$properties[ $key ] = $changes;
|
||||
}
|
||||
else
|
||||
{
|
||||
array_replace_recursive( $properties[ $key ], $changes );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1407,9 +1415,9 @@ HTML,
|
||||
|
||||
/* These should never be set in the setting since other code assumes these properties are what's specified in the arrays */
|
||||
$properties[$key] = $properties[$key] ?? array();
|
||||
unset( $changes['pii'] );
|
||||
if ( !( $properties[$key]['custom'] ?? 0 ) )
|
||||
{
|
||||
unset( $changes['pii'] );
|
||||
unset( $changes['event_keys'] );
|
||||
unset( $changes['description'] );
|
||||
unset( $changes['short'] );
|
||||
|
||||
@@ -292,6 +292,11 @@ class _DeletionLog extends \IPS\Patterns\ActiveRecord
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if( $member->modPermission( 'can_access_all_clubs' ) AND in_array('cm', $this->content_permissions['groups'] ) )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class _Table extends \IPS\Helpers\Table\Table
|
||||
|
||||
/* Where Clause */
|
||||
$where = array();
|
||||
$where[] = array( "(dellog_content_permissions=? OR " . \IPS\Db::i()->findInSet( 'dellog_content_permissions', \IPS\Member::loggedIn()->groups ) . " OR " . \IPS\Db::i()->findInSet( 'dellog_content_permissions', array( 'm' . \IPS\Member::loggedIn()->member_id ) ) . ")", '*' );
|
||||
$where[] = array( \IPS\Db::i()->findInSet( 'dellog_content_permissions', \IPS\Member::loggedIn()->permissionArray() ) . " OR dellog_content_permissions=?", '*' );
|
||||
|
||||
/* Return only content from enabled apps */
|
||||
$where[] = \IPS\Db::i()->in( 'dellog_content_class', array_values( \IPS\Content::routedClasses( TRUE, TRUE ) ) );
|
||||
|
||||
@@ -262,6 +262,7 @@ class _Digest
|
||||
}
|
||||
|
||||
/* Fetch the member's follows so we can build their digest */
|
||||
/* @note SELECT_FROM_WRITE_SERVER: Avoid issue where the follow_notify_sent time below is updated on the writer, but not on the readers on the next task run */
|
||||
$follows = \IPS\Db::i()->select( '*', 'core_follow', array( 'follow_notify_do=1 AND follow_notify_freq=? AND follow_notify_sent < ? AND ' . \IPS\Db::i()->in( 'follow_member_id', $memberIDs ), $frequency, ( $frequency == 'daily' ) ? time() - 86400 : time() - 604800 ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER );
|
||||
|
||||
$groupedFollows = array();
|
||||
|
||||
@@ -81,10 +81,10 @@ class _SocialProfiles extends \IPS\Helpers\Form\KeyValue
|
||||
*/
|
||||
protected function addSocialNetworks( $options )
|
||||
{
|
||||
$options['value']['options'] = array(
|
||||
$values = array(
|
||||
'facebook' => "siteprofilelink_facebook",
|
||||
'twitter' => "siteprofilelink_twitter",
|
||||
'youtube' => "siteprofilelink_youtube",
|
||||
'x' => "siteprofilelink_x",
|
||||
'tumblr' => "siteprofilelink_tumblr",
|
||||
'deviantart' => "siteprofilelink_deviantart",
|
||||
'etsy' => "siteprofilelink_etsy",
|
||||
@@ -100,8 +100,13 @@ class _SocialProfiles extends \IPS\Helpers\Form\KeyValue
|
||||
'vk' => "siteprofilelink_vk",
|
||||
'discord' => "siteprofilelink_discord",
|
||||
'twitch' => "siteprofilelink_twitch",
|
||||
'bluesky' => "siteprofilelink_bluesky",
|
||||
'tiktok' => "siteprofilelink_tiktok",
|
||||
);
|
||||
|
||||
asort( $values );
|
||||
$options['value']['options'] = $values;
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Community Hive Helpers
|
||||
* @author <a href='https://communityhive.com'>CommunityHive.com</a>
|
||||
* @copyright (c) CommunityHive.com
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Community Hive
|
||||
* @since 16 May 2023
|
||||
*/
|
||||
|
||||
namespace IPS\core;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Application;
|
||||
use IPS\Content;
|
||||
use IPS\Content\Item;
|
||||
use IPS\Content\Searchable;
|
||||
use IPS\Http\Url;
|
||||
use IPS\IPS;
|
||||
use IPS\Member;
|
||||
use IPS\Settings;
|
||||
use Jose\Component\Core\JWK;
|
||||
use IPS\Db;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Community Hive Helpers
|
||||
*/
|
||||
class _Hive extends \IPS\Patterns\Singleton
|
||||
{
|
||||
/**
|
||||
* Framework coding standards....
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an API request with a JWT
|
||||
*
|
||||
* @param string $endpoint
|
||||
* @param array $payload
|
||||
* @return array
|
||||
*/
|
||||
public static function api( string $endpoint, array $payload ): array
|
||||
{
|
||||
$hostname = \defined( 'COMMUNITYHIVE_URL' ) ? COMMUNITYHIVE_URL : 'https://communityhive.com';
|
||||
$request = Url::external( $hostname . '/api/v1/' . $endpoint )
|
||||
->request(3)
|
||||
->put( static::_assembleJwt( $payload ) );
|
||||
|
||||
if( (int) $request->httpResponseCode !== 200 )
|
||||
{
|
||||
\IPS\Log::log( [ 'endpoint' => $hostname . '/api/v1/' . $endpoint, 'payload' => $payload, 'response' => $request ], 'hive_error' );
|
||||
throw new \LogicException( $request->decodeJson()['error'] ?? 'an unknown error occurred.' );
|
||||
}
|
||||
|
||||
return $request->decodeJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate group hash in an ordered way
|
||||
*
|
||||
* @param Member $member
|
||||
* @return string
|
||||
*/
|
||||
public static function groupHash( Member $member ): string
|
||||
{
|
||||
if( $member->isBanned() === FALSE )
|
||||
{
|
||||
$hash = array_filter( array_merge( [ $member->member_group_id ], explode( ',', $member->mgroup_others ) ) );
|
||||
natcasesort( $hash );
|
||||
return implode( ',', $hash );
|
||||
}
|
||||
else
|
||||
{
|
||||
return 'guest';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief store any keys for removal in __destruct()
|
||||
*/
|
||||
protected static array $_deleteQueue = [];
|
||||
|
||||
/**
|
||||
* Remove content from Hive - Used if something is moved or content is hidden
|
||||
*
|
||||
* @param Content $content
|
||||
* @return void
|
||||
*/
|
||||
public function removeContent( Content $content )
|
||||
{
|
||||
/* Wrap it all because we don't want it to stop the moderation action */
|
||||
try
|
||||
{
|
||||
if ( !( $content instanceof Searchable ) or empty( Settings::i()->hive_key ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
$idColumn = $content::$databaseColumnId;
|
||||
|
||||
if ( $content instanceof Item )
|
||||
{
|
||||
$key1 = \get_class( $content ) . '/' . $content->$idColumn;
|
||||
}
|
||||
else
|
||||
{
|
||||
$itemIdColumn = $content->item()::$databaseColumnId;
|
||||
$key1 = \get_class( $content->item() ) . '/' . $content->item()->$itemIdColumn;
|
||||
$key2 = \get_class( $content ) . '/' . $content->$idColumn;
|
||||
}
|
||||
|
||||
/* Store IDs for removal */
|
||||
static::$_deleteQueue[] = [ 'key1' => $key1, 'key2' => $key2 ?? NULL ];
|
||||
}
|
||||
catch( \Exception | \Throwable $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'hive_content_removal' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assemble a JWT with payload
|
||||
*
|
||||
* @param array $payload
|
||||
* @return string
|
||||
*/
|
||||
protected static function _assembleJwt( array $payload ): string
|
||||
{
|
||||
IPS::$PSR0Namespaces['Jose'] = \IPS\ROOT_PATH . '/system/3rd_party/JwtFramework/src';
|
||||
IPS::$PSR0Namespaces['Base64Url'] = \IPS\ROOT_PATH . '/system/3rd_party/Base64Url';
|
||||
|
||||
$jwk = new JWK([
|
||||
'kty' => 'oct',
|
||||
'k' => base64_encode( Settings::i()->hive_site_key )
|
||||
]);
|
||||
|
||||
$jwsCompactSerializer = new \Jose\Component\Signature\Serializer\CompactSerializer();
|
||||
$jwsBuilder = new \Jose\Component\Signature\JWSBuilder( new \Jose\Component\Core\AlgorithmManager( [ new \Jose\Component\Signature\Algorithm\HS256() ] ) );
|
||||
|
||||
$defaultPayload = [
|
||||
'sub' => Settings::i()->hive_site_id,
|
||||
'iss' => rtrim( Settings::i()->base_url, '/' ),
|
||||
'iat' => time(),
|
||||
'nbf' => time(),
|
||||
'exp' => time() + 60,
|
||||
'aud' => 'communityhive',
|
||||
'hive_system_version' => Application::load('core')->long_version
|
||||
];
|
||||
|
||||
$jws = $jwsBuilder
|
||||
->create()
|
||||
->withPayload( json_encode( array_merge( $defaultPayload, $payload ) ) )
|
||||
->addSignature( $jwk, [
|
||||
'typ' => 'JWT',
|
||||
'alg' => 'HS256',
|
||||
])
|
||||
->build();
|
||||
|
||||
return $jwsCompactSerializer->serialize( $jws, 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Cache whether the member is subscribed
|
||||
*/
|
||||
protected static ?bool $_showPromotion = NULL;
|
||||
|
||||
/**
|
||||
* Check if we should show the Hive Promotion for the viewing member
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function showPromotion(): bool
|
||||
{
|
||||
if( static::$_showPromotion !== NULL )
|
||||
{
|
||||
return static::$_showPromotion;
|
||||
}
|
||||
|
||||
if ( !Settings::i()->hive_enabled )
|
||||
{
|
||||
return static::$_showPromotion = FALSE;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Db::i()->select('member_id', 'core_hive_subscribers', [ 'member_id=?', Member::loggedIn()->member_id ] )->first();
|
||||
return static::$_showPromotion = FALSE;
|
||||
}
|
||||
catch ( \UnderflowException $e ){}
|
||||
|
||||
return static::$_showPromotion = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send API request to remove any content IDs that have been queued
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
/* Wrap it all because we don't want it to stop the moderation action */
|
||||
try
|
||||
{
|
||||
if( \count( static::$_deleteQueue ) )
|
||||
{
|
||||
static::api( 'contentremove', [ 'keys' => static::$_deleteQueue ] );
|
||||
}
|
||||
}
|
||||
catch( \Exception | \Throwable $e )
|
||||
{
|
||||
\IPS\Log::log( $e, 'hive_content_removal' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,11 +114,11 @@ class _IndexNow extends \IPS\Patterns\Singleton
|
||||
default:
|
||||
$error = 'unknown_error';
|
||||
}
|
||||
\IPS\Log::log($response->httpResponseCode . ' ' . $error . ' ' . print_r($data, TRUE), 'IndexNow');
|
||||
\IPS\Log::log( $response->httpResponseCode . ' ' . $error . ' ' . print_r($response, TRUE) . print_r($data, TRUE), 'IndexNow');
|
||||
}
|
||||
} catch (\IPS\Http\Request\CurlException $e)
|
||||
{
|
||||
\IPS\Log::log($e->getMessage(), 'IndexNow');
|
||||
\IPS\Log::log( $e->getMessage(), 'IndexNow' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
namespace IPS\core\Messenger;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\core\Alerts\Alert;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -60,6 +63,11 @@ class _Conversation extends \IPS\Content\Item
|
||||
* @var bool
|
||||
*/
|
||||
public static bool $skipIndexNow = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Check posts per day limits? Useful for things that use the content system, but aren't necessarily content themselves.
|
||||
*/
|
||||
public static $checkPostsPerDay = FALSE;
|
||||
|
||||
/**
|
||||
* Delete Record
|
||||
@@ -162,7 +170,7 @@ class _Conversation extends \IPS\Content\Item
|
||||
{
|
||||
$alert = \IPS\core\Alerts\Alert::load( \IPS\Request::i()->alert );
|
||||
|
||||
if( $alert->forMember( \IPS\Member::loggedIn() ) AND $alert->reply == 2 )
|
||||
if( $alert->forMember( \IPS\Member::loggedIn() ) AND $alert->reply == Alert::REPLY_REQUIRED )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -641,6 +649,7 @@ class _Conversation extends \IPS\Content\Item
|
||||
*/
|
||||
public static function rebuildMessageCounts( \IPS\Member $member )
|
||||
{
|
||||
/* @note SELECT_FROM_WRITE_SERVER: Avoid issue where the new message rows are on the writer, but not the reader */
|
||||
$total = \IPS\Db::i()->select( 'count(*)', 'core_message_topic_user_map', array( 'map_user_id=? AND map_user_active=1', $member->member_id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
|
||||
$member->msg_count_total = $total;
|
||||
|
||||
|
||||
@@ -47,6 +47,16 @@ class _Table extends \IPS\Helpers\Table\Table
|
||||
{
|
||||
/* Init */
|
||||
parent::__construct( $url );
|
||||
|
||||
$classes = array();
|
||||
|
||||
/* Only include classes for enabled applications. */
|
||||
foreach( \IPS\Content::routedClasses( FALSE, FALSE, FALSE ) AS $class )
|
||||
{
|
||||
$classes[] = $class;
|
||||
}
|
||||
|
||||
$this->where[] = array( \IPS\Db::i()->in( 'promote_class', $classes ) );
|
||||
|
||||
$this->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'promote', 'core', 'front' ), 'promoteTable' );
|
||||
$this->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'promote', 'core', 'front' ), 'promoteTableRows' );
|
||||
|
||||
@@ -26,7 +26,14 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
* @brief Const No type selected
|
||||
*/
|
||||
const TYPE_MESSAGE = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Const Reported content statuses
|
||||
*/
|
||||
const STATUS_NEW = 1;
|
||||
const STATUS_OPEN = 2;
|
||||
const STATUS_CLOSED = 3;
|
||||
const STATUS_REJECTED = 4;
|
||||
|
||||
/* !\IPS\Patterns\ActiveRecord */
|
||||
|
||||
@@ -141,7 +148,208 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
throw new \OutofRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all the report types for this report index
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getReportedTypes(): array
|
||||
{
|
||||
$reportedTypes = [];
|
||||
$reportTypes = [];
|
||||
foreach ( \IPS\core\Reports\Types::roots() as $type )
|
||||
{
|
||||
$reportTypes[ $type->id ] = $type->_title;
|
||||
}
|
||||
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_rc_reports', ['rid=? and report_type > 0', $this->id] ) as $report )
|
||||
{
|
||||
if ( isset( $reportTypes[ $report['report_type'] ] ) )
|
||||
{
|
||||
$reportedTypes[ $report['report_type'] ] = $reportTypes[ $report['report_type'] ];
|
||||
}
|
||||
}
|
||||
|
||||
return $reportedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status of the report
|
||||
*
|
||||
* @param int $status
|
||||
* @param $member
|
||||
* @return void
|
||||
*/
|
||||
public function changeStatus( int $status, int $authorNotification=0, $member=null )
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
$this->status = $status;
|
||||
$this->save();
|
||||
|
||||
/* We only need to notify if there is an outcome */
|
||||
if ( $status == static::STATUS_CLOSED or static::STATUS_REJECTED )
|
||||
{
|
||||
$forNotifications = [];
|
||||
$forEmails = [];
|
||||
|
||||
/* Now go through all the individual reports and see if we need to notify anyone */
|
||||
foreach ( \IPS\Db::i()->select( '*', 'core_rc_reports', ['rid=?', $this->id] ) as $report )
|
||||
{
|
||||
if ( !$report['report_type'] )
|
||||
{
|
||||
/* We have not selected a reason, no nothing to worry about */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get the report type object */
|
||||
try
|
||||
{
|
||||
$reportType = \IPS\core\Reports\Types::load( $report['report_type'] );
|
||||
$reportMember = \IPS\Member::load( $report['report_by'] );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $report['report_by'] )
|
||||
{
|
||||
/* We have a member */
|
||||
$forNotifications[$report['report_type']][] = [$reportMember, $reportType, $report];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( $report['guest_email'] )
|
||||
{
|
||||
/* We have an email */
|
||||
$forEmails[$report['report_type']][] = [$reportType, $report];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Members */
|
||||
foreach ( $forNotifications as $typeId => $data )
|
||||
{
|
||||
foreach ( $data as $reportData )
|
||||
{
|
||||
$reportMember = $reportData[0];
|
||||
$reportType = $reportData[1];
|
||||
$userReport = $reportData[2];
|
||||
|
||||
if ( $this->getReportOutcomeBlurb( $userReport['id'] ) )
|
||||
{
|
||||
$notification = new \IPS\Notification( \IPS\Application::load( 'core' ), 'report_outcome', $this, [$this, $reportType, $userReport], ['reportType' => $reportType->id, 'userReport' => $userReport] );
|
||||
$notification->recipients->attach( $reportMember );
|
||||
$notification->send();
|
||||
\IPS\Log::debug( "Sent notification to {$reportMember->name}", 'reports_notification' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Guests */
|
||||
foreach ( $forEmails as $typeId => $data )
|
||||
{
|
||||
foreach ( $data as $reportData )
|
||||
{
|
||||
$reportType = $reportData[0];
|
||||
$guestEmail = $reportData[1]['guest_email'];
|
||||
$userReport = $reportData[1];
|
||||
|
||||
if ( $guestEmail and $this->getReportOutcomeBlurb( $userReport['id'] ) )
|
||||
{
|
||||
\IPS\Email::buildFromTemplate( 'core', 'notification_report_outcome', [$this, $reportType, $userReport], \IPS\Email::TYPE_LIST )->send( $guestEmail );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Content author? */
|
||||
if ( $authorNotification )
|
||||
{
|
||||
try
|
||||
{
|
||||
$class = $this->_data['class'];
|
||||
$thing = $class::load( $this->_data['content_id'] );
|
||||
$content = ( $thing instanceof \IPS\Content\Comment ) ? $thing->truncated() : $thing->mapped( 'title' );
|
||||
|
||||
if ( $thing->author() and $thing->author()->email )
|
||||
{
|
||||
$authorContent = static::getAuthorNotifications();
|
||||
|
||||
if ( isset( $authorContent[ $authorNotification ] ) )
|
||||
{
|
||||
$notificationContent = $authorContent[$authorNotification]['text'];
|
||||
|
||||
/* Substitutions */
|
||||
$subs = [
|
||||
'name' => $thing->author()->name,
|
||||
'content' => trim( $content ),
|
||||
'link' => (string) $thing->url(),
|
||||
'reason' => implode( ', ', $this->getReportedTypes() )
|
||||
];
|
||||
|
||||
foreach( $subs as $key => $value )
|
||||
{
|
||||
$notificationContent = str_replace( '{' . $key . '}', $value, $notificationContent );
|
||||
}
|
||||
|
||||
\IPS\Email::buildFromTemplate( 'core', 'notification_report_content_author', [$this, $thing, $notificationContent], \IPS\Email::TYPE_LIST )->send( $thing->author()->email );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Post a comment on the report */
|
||||
$content = $member->language()->addToStack( 'update_report_status_content', FALSE, array( 'sprintf' => array( $member->language()->addToStack( 'report_status_' . $this->status ) ) ) );
|
||||
$member->language()->parseOutputForDisplay( $content );
|
||||
|
||||
$comment = \IPS\core\Reports\Comment::create( $this, $content, TRUE, NULL, NULL, $member, new \IPS\DateTime );
|
||||
$comment->save();
|
||||
|
||||
/* And add to the moderator log */
|
||||
\IPS\Session::i()->modLog( 'modlog__action_update_report_status', array( $this->url()->__toString() => FALSE ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the author notifications
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getAuthorNotifications(): array
|
||||
{
|
||||
$return = [];
|
||||
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_rc_author_notification_text', [], 'title ASC' ) as $row )
|
||||
{
|
||||
$return[ $row['id'] ] = $row;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the report outcome blurb
|
||||
*
|
||||
* @param int $userReportId
|
||||
* @return string
|
||||
*/
|
||||
public function getReportOutcomeBlurb( int $userReportId ): string
|
||||
{
|
||||
try
|
||||
{
|
||||
$userReport = \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'id=?', $userReportId ) )->first();
|
||||
$reportType = \IPS\core\Reports\Types::load( $userReport['report_type'] );
|
||||
return $this->status === static::STATUS_REJECTED ? $reportType->getRejectedNotificationText( \IPS\Member::load( $userReport['report_by'] ) ) : $reportType->getCompletedNotificationText( \IPS\Member::load( $userReport['report_by'] ) );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mapped value
|
||||
*
|
||||
@@ -359,18 +567,7 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
{
|
||||
if ( mb_substr( $action, 0, -1 ) === 'report_status_' )
|
||||
{
|
||||
$this->status = mb_substr( $action, -1 );
|
||||
$this->save();
|
||||
|
||||
/* Post a comment on the report */
|
||||
$content = \IPS\Member::loggedIn()->language()->addToStack( 'update_report_status_content', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( 'report_status_' . $this->status ) ) ) );
|
||||
\IPS\Member::loggedIn()->language()->parseOutputForDisplay( $content );
|
||||
|
||||
$comment = \IPS\core\Reports\Comment::create( $this, $content, TRUE, NULL, NULL, \IPS\Member::loggedIn(), new \IPS\DateTime );
|
||||
$comment->save();
|
||||
|
||||
/* And add to the moderator log */
|
||||
\IPS\Session::i()->modLog( 'modlog__action_update_report_status', array( $this->url()->__toString() => FALSE ) );
|
||||
$this->changeStatus( (int) mb_substr( $action, -1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -385,7 +582,7 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
*/
|
||||
public function customMultimodActions()
|
||||
{
|
||||
return array_diff( array( 'report_status_1', 'report_status_2', 'report_status_3', ), array( 'report_status_' . $this->status ) );
|
||||
return array_diff( array( 'report_status_1', 'report_status_2', 'report_status_3', 'report_status_4'), array( 'report_status_' . $this->status ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,6 +613,11 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
'action' => 'report_status_3',
|
||||
'icon' => 'check-circle',
|
||||
'label' => 'report_status_3'
|
||||
),
|
||||
array(
|
||||
'action' => 'report_status_4',
|
||||
'icon' => 'archive',
|
||||
'label' => 'report_status_4'
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -574,6 +776,7 @@ class _Report extends \IPS\Content\Item implements \IPS\Content\ReadMarkers
|
||||
$typeCounts = array();
|
||||
$total = 0;
|
||||
$seen = array();
|
||||
/* @note SELECT_FROM_WRITE_SERVER added in 7fd46ad949b0a9b03a5c67c70afee26184c2930c pull #316 (The problem was, that the R/W separation.The new report was added and in the same step we fetched(counted) the existing reports, where the recent one wasn't taken into account.) */
|
||||
foreach( \IPS\Db::i()->select( '*', 'core_rc_reports', array( 'rid=? and report_type > 0', $this->id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER ) as $row )
|
||||
{
|
||||
if ( isset( $seen[ $row['report_by'] ] ) )
|
||||
|
||||
@@ -86,12 +86,14 @@ class _Rules extends \IPS\Node\Model
|
||||
$form->add( new \IPS\Helpers\Form\Number( 'automaticmoderation_points', $this->id ? $this->points_needed : 10, TRUE, array( 'min' => 1, 'max' => 9999 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('automaticmoderation_points_suffix') ) );
|
||||
|
||||
$options = array();
|
||||
foreach( \IPS\core\Reports\Types::roots() as $type )
|
||||
if( count( \IPS\core\Reports\Types::roots() ) > 0 )
|
||||
{
|
||||
$options[ $type->id ] = $type->_title;
|
||||
foreach( \IPS\core\Reports\Types::roots() as $type )
|
||||
{
|
||||
$options[ $type->id ] = $type->_title;
|
||||
}
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'automaticmoderation_type', $this->types ? explode( ',', $this->types ) : array(), TRUE, array( 'options' => $options ) ) );
|
||||
}
|
||||
|
||||
$form->add( new \IPS\Helpers\Form\CheckboxSet( 'automaticmoderation_type', $this->types ? explode( ',', $this->types ) : array(), TRUE, array( 'options' => $options ) ) );
|
||||
|
||||
/* Loop over our member filters */
|
||||
$form->addHeader( 'automaticmoderation_generic_filters' );
|
||||
@@ -183,7 +185,11 @@ class _Rules extends \IPS\Node\Model
|
||||
$values['rule_filters'] = json_encode( $_options );
|
||||
$values['rule_points_needed'] = $values['automaticmoderation_points'];
|
||||
$values['rule_enabled'] = $values['automaticmoderation_enabled'];
|
||||
$values['rule_types'] = $values['automaticmoderation_type'];
|
||||
if( isset( $values['automaticmoderation_type'] ) )
|
||||
{
|
||||
$values['rule_types'] = $values['automaticmoderation_type'];
|
||||
}
|
||||
|
||||
|
||||
/* Now we have to remove any fields that aren't valid... */
|
||||
foreach( $values as $k => $v )
|
||||
@@ -360,17 +366,21 @@ class _Rules extends \IPS\Node\Model
|
||||
|
||||
/* Loop over the filters */
|
||||
$descriptions = array();
|
||||
foreach( $this->_filters as $key => $filter )
|
||||
if( $this->_filters )
|
||||
{
|
||||
if( isset( static::$extensions[ $key ] ) )
|
||||
foreach( $this->_filters as $key => $filter )
|
||||
{
|
||||
/* Ask the extension if this member matches the defined rule...if not, just return FALSE now */
|
||||
if( method_exists( static::$extensions[ $key ], 'getDescription' ) and $description = static::$extensions[ $key ]->getDescription( $filter ) )
|
||||
if( isset( static::$extensions[ $key ] ) )
|
||||
{
|
||||
$descriptions[] = $description; /* Is this descriptive enough? */
|
||||
/* Ask the extension if this member matches the defined rule...if not, just return FALSE now */
|
||||
if( method_exists( static::$extensions[ $key ], 'getDescription' ) and $description = static::$extensions[ $key ]->getDescription( $filter ) )
|
||||
{
|
||||
$descriptions[] = $description; /* Is this descriptive enough? */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return \IPS\Member::loggedIn()->language()->addToStack( 'automaticmoderation_row_desc', TRUE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( $descriptions ) ) ) );
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
namespace IPS\core\Reports;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\Member;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -66,7 +69,29 @@ class _Types extends \IPS\Node\Model
|
||||
* @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key
|
||||
*/
|
||||
public static $titleLangPrefix = 'automaticmod_types_';
|
||||
|
||||
|
||||
/**
|
||||
* Get the completed notification text (hits the DB to get the real value, not the hash key)
|
||||
* @param Member|null $member
|
||||
* @return string
|
||||
*/
|
||||
public function getCompletedNotificationText( \IPS\Member|null $member=null ): string
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
return $member->language()->get( 'automaticmod_types_notify_complete_' . $this->id, 'core' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rejected notification text (hits the DB to get the real value, not the hash key)
|
||||
* @param Member|null $member
|
||||
* @return string
|
||||
*/
|
||||
public function getRejectedNotificationText( \IPS\Member|null $member=null ): string
|
||||
{
|
||||
$member = $member ?: \IPS\Member::loggedIn();
|
||||
return $member->language()->get( 'automaticmod_types_notify_reject_' . $this->id, 'core' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Form
|
||||
*
|
||||
@@ -76,6 +101,8 @@ class _Types extends \IPS\Node\Model
|
||||
public function form( &$form )
|
||||
{
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'automaticmod_types_title', NULL, TRUE, array( 'app' => 'core', 'key' => ( $this->id ? 'automaticmod_types_' . $this->id : NULL ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'automaticmod_types_notify_complete', NULL, FALSE, array( 'textArea' => true, 'app' => 'core', 'key' => ( $this->id ? 'automaticmod_types_notify_complete_' . $this->id : NULL ) ) ) );
|
||||
$form->add( new \IPS\Helpers\Form\Translatable( 'automaticmod_types_notify_reject', NULL, FALSE, array( 'textArea' => true, 'app' => 'core', 'key' => ( $this->id ? 'automaticmod_types_notify_reject_' . $this->id : NULL ) ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,8 +120,12 @@ class _Types extends \IPS\Node\Model
|
||||
|
||||
/* Save the title */
|
||||
\IPS\Lang::saveCustom( 'core', 'automaticmod_types_' . $this->id, $values['automaticmod_types_title'] );
|
||||
|
||||
/* Save the notification text */
|
||||
\IPS\Lang::saveCustom( 'core', 'automaticmod_types_notify_complete_' . $this->id, $values['automaticmod_types_notify_complete'] );
|
||||
\IPS\Lang::saveCustom( 'core', 'automaticmod_types_notify_reject_' . $this->id, $values['automaticmod_types_notify_reject'] );
|
||||
|
||||
unset( $values['automaticmod_types_title'] );
|
||||
unset( $values['automaticmod_types_title'], $values['automaticmod_types_notify_complete'], $values['automaticmod_types_notify_reject'] );
|
||||
|
||||
return parent::formatFormValues( $values );
|
||||
}
|
||||
|
||||
@@ -46,6 +46,16 @@ class _Install
|
||||
'success' => TRUE,
|
||||
'message' => \IPS\Member::loggedIn()->language()->addToStack( 'requirements_php_version_success', FALSE, array( 'sprintf' => array( $phpVersion ) ) )
|
||||
);
|
||||
|
||||
$php = explode( '.', $requirements['php']['recommended'] );
|
||||
$maxRecommended = implode( '.', [ $php[0], $php[1], 99 ] );
|
||||
$humanReadable = implode( '.', [ $php[0], $php[1], 'x' ] );
|
||||
|
||||
if( version_compare( $phpVersion, $maxRecommended ) >= 0 )
|
||||
{
|
||||
$return['advice']['PHP']['php'] = \IPS\Member::loggedIn()->language()->addToStack( 'requirements_php_version_advice_max', FALSE, array( 'sprintf' => array( $phpVersion, $humanReadable ) ) );
|
||||
$return['requirements']['PHP']['version']['downgrade'] = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@ class _Group extends \IPS\Node\Model
|
||||
|
||||
/* Build the layout selection radios */
|
||||
$templates = array();
|
||||
foreach ( \IPS\Theme::i()->getRawTemplates( 'core', 'front', 'staffdirectory', \IPS\Theme::RETURN_ARRAY_BIT_NAMES | \IPS\Theme::RETURN_NATIVE ) as $template )
|
||||
foreach ( \IPS\Theme::load( \IPS\Theme::defaultTheme() )->getRawTemplates( 'core', 'front', 'staffdirectory', \IPS\Theme::RETURN_ARRAY_BIT_NAMES | \IPS\Theme::RETURN_NATIVE ) as $template )
|
||||
{
|
||||
if ( mb_strpos( $template, 'layout_' ) === 0 && mb_strpos( $template, '_preview' ) === FALSE )
|
||||
{
|
||||
|
||||
@@ -365,6 +365,7 @@ class _User extends \IPS\Node\Model
|
||||
{
|
||||
if ( $value == NULL )
|
||||
{
|
||||
/* @note SELECT_FROM_WRITE_SERVER Added in fa59a59 to account for a race condition */
|
||||
$value = !( (bool) \IPS\Db::i()->select( 'COUNT(*)', 'core_leaders', NULL, NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first() );
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,20 @@ abstract class _Chart
|
||||
|
||||
$extension = static::loadFromController( $data['chart_controller'] );
|
||||
$chart = $extension->getChart( $url );
|
||||
$chart->currentFilters = json_decode( $data['chart_configuration'], true );
|
||||
$currentFilters = array();
|
||||
foreach( json_decode( $data['chart_configuration'], true ) AS $k => $v )
|
||||
{
|
||||
if ( mb_substr( $k, 0, 11 ) == 'customform_' )
|
||||
{
|
||||
$currentFilters[ mb_substr( $k, 11 ) ] = $v;
|
||||
}
|
||||
else
|
||||
{
|
||||
$currentFilters[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
$chart->savedCustomFilters = $currentFilters;
|
||||
$chart->timescale = $data['chart_timescale'] ?? $chart->timescale;
|
||||
$chart->title = $data['chart_title'];
|
||||
$chart->showFilterTabs = FALSE;
|
||||
|
||||
@@ -381,6 +381,7 @@ class _Status extends \IPS\Content\Item implements \IPS\Content\Lockable, \IPS\C
|
||||
'key' => 'Members',
|
||||
'autoSaveKey' => 'status' . $member->member_id . ( ( $item !== NULL ) ? '_' . $item->id : '' ),
|
||||
'minimize' => $minimize,
|
||||
'minimizeAfterReset' => TRUE,
|
||||
'maxLength' => 65535,
|
||||
'attachIds' => ( $item === NULL ) ? NULL : array( $item->id, NULL, $item->member_id )
|
||||
), '\IPS\Helpers\Form::floodCheck' );
|
||||
@@ -421,6 +422,7 @@ class _Status extends \IPS\Content\Item implements \IPS\Content\Lockable, \IPS\C
|
||||
'maxLength' => 65535,
|
||||
'autoSaveKey' => 'reply-' . static::$application . '/' . static::$module . '-' . $this->$idColumn,
|
||||
'minimize' => isset( \IPS\Request::i()->$submitted ) ? NULL : static::$formLangPrefix . '_comment_placeholder',
|
||||
'minimizeAfterReset' => true,
|
||||
'contentClass' => \get_called_class(),
|
||||
'contentId' => $this->$idColumn
|
||||
), function() use( $self ) {
|
||||
|
||||
@@ -67,7 +67,7 @@ class _StoredReplies extends \IPS\Node\Model implements \IPS\Node\Permissions
|
||||
protected static $restrictions = array(
|
||||
'app' => 'core',
|
||||
'module' => 'editor',
|
||||
'prefix' => 'storedReplies_'
|
||||
'all' => 'stored_replies_manage'
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -196,6 +196,7 @@ class _Stream extends \IPS\Node\Model
|
||||
$stream->date_type = 'relative';
|
||||
$stream->default_view = 'expanded';
|
||||
$stream->baseUrl = \IPS\Http\Url::internal( "app=core&module=discover&controller=streams", 'front', 'discover_all' );
|
||||
$stream->read = "all";
|
||||
return $stream;
|
||||
}
|
||||
|
||||
@@ -394,7 +395,7 @@ class _Stream extends \IPS\Node\Model
|
||||
$nodeClass = $class::$containerNodeClass;
|
||||
$value = isset( $containers->$class ) ? $containers->$class : 0;
|
||||
|
||||
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, $value, FALSE, array( 'class' => $nodeClass, 'clubs' => TRUE, 'multiple' => TRUE, 'zeroVal' => 'all', 'permissionCheck' => $nodeClass::searchableNodesPermission(), 'forceOwner' => FALSE, 'subnodes' => FALSE ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
|
||||
$field = new \IPS\Helpers\Form\Node( 'stream_containers_' . $class::$title, $value, FALSE, array( 'class' => $nodeClass, 'clubs' => TRUE, 'multiple' => TRUE, 'zeroVal' => 'all', 'forceOwner' => FALSE, 'subnodes' => FALSE ), NULL, NULL, NULL, 'stream_containers_' . $class::$title );
|
||||
$field->label = \IPS\Member::loggedIn()->language()->addToStack( $nodeClass::$nodeTitle );
|
||||
$form->add( $field );
|
||||
}
|
||||
@@ -560,6 +561,12 @@ class _Stream extends \IPS\Node\Model
|
||||
*/
|
||||
protected function _getOurClubs()
|
||||
{
|
||||
/* If we're in the ACP, just return all clubs */
|
||||
if( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation === 'admin' )
|
||||
{
|
||||
return iterator_to_array( \IPS\Member\Club::clubs( NULL, NULL, 'name' ) );
|
||||
}
|
||||
|
||||
if( static::$ourClubs === NULL )
|
||||
{
|
||||
static::$ourClubs = \IPS\Member\Club::clubs( \IPS\Member::loggedIn(), NULL, 'name', TRUE, array(), \IPS\Settings::i()->clubs_require_approval ? array( 'approved=1' ) : NULL );
|
||||
@@ -871,7 +878,7 @@ class _Stream extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->tags )
|
||||
if ( $this->tags and \IPS\Settings::i()->tags_enabled )
|
||||
{
|
||||
$terms[] = \IPS\Member::loggedIn()->language()->addToStack( 'stream_includes_tags', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->formatList( array_map( function( $val ){
|
||||
return '\'' . $val . '\'';
|
||||
@@ -1098,7 +1105,7 @@ class _Stream extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
if( $this->tags )
|
||||
if( $this->tags and \IPS\Settings::i()->tags_enabled )
|
||||
{
|
||||
$config['stream_tags'] = $this->tags;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
*/
|
||||
public static function sendBatch( $frequency = 'daily', $numberToSend = 25 )
|
||||
{
|
||||
/* @note SELECT_FROM_WRITE_SERVER to prevent further cycles from having data on the write but not read servers */
|
||||
$subscriptions = iterator_to_array(
|
||||
\IPS\Db::i()->select( 'core_stream_subscriptions.*, core_members.last_visit', 'core_stream_subscriptions', array('frequency = ? AND sent < ? and last_visit > ?', $frequency, ( $frequency == 'daily' ) ? time() - 86400 : time() - 604800, time() - \IPS\Settings::i()->activity_stream_subscriptions_inactive_limit * 86400), 'sent ASC', array(0, $numberToSend), NULL, NULL, \IPS\Db::SELECT_DISTINCT | \IPS\Db::SELECT_FROM_WRITE_SERVER )
|
||||
->join( 'core_members', 'core_stream_subscriptions.member_id=core_members.member_id') );
|
||||
@@ -174,7 +175,9 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
$containerUrl = $containerClass::urlFromIndexData( $data['indexData'], $data['itemData'], $data['containerData'] );
|
||||
}
|
||||
|
||||
$items[] = array_merge($data, [
|
||||
try
|
||||
{
|
||||
$items[] = array_merge($data, [
|
||||
'title' => $object instanceof \IPS\Content\Comment ? $object->item()->searchIndexTitle() : $object->searchIndexTitle(),
|
||||
'url' => $object->url(),
|
||||
'content' => $object->content(),
|
||||
@@ -183,7 +186,13 @@ class _Subscription extends \IPS\Patterns\ActiveRecord
|
||||
'itemClass' => $itemClass,
|
||||
'containerUrl' => $containerUrl,
|
||||
'containerTitle' => $containerTitle
|
||||
]);
|
||||
]);
|
||||
}
|
||||
catch( \OutOfRangeException|\BadMethodCallException $e )
|
||||
{
|
||||
/* If the item is no longer available, we'll just skip it */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $items;
|
||||
|
||||
@@ -459,7 +459,6 @@ class _Warning extends \IPS\Content\Item
|
||||
|
||||
if ( $this->cheev_point_reduction )
|
||||
{
|
||||
$member->achievements_points -= $this->cheev_point_reduction;
|
||||
|
||||
if ( $member->achievements_points >= $this->cheev_point_reduction )
|
||||
{
|
||||
@@ -470,6 +469,9 @@ class _Warning extends \IPS\Content\Item
|
||||
{
|
||||
/* Eg points to deduct is 50, user has 10, so set points deducted to 10 so undo doesn't add 50 back */
|
||||
$this->cheev_point_reduction = $member->achievements_points;
|
||||
$member->achievements_points -= $this->cheev_point_reduction;
|
||||
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$consequences['cheev_point_reduction'] = $this->cheev_point_reduction;
|
||||
@@ -657,6 +659,20 @@ class _Warning extends \IPS\Content\Item
|
||||
if ( $values[ 'warn_' . $f ] instanceof \IPS\DateTime )
|
||||
{
|
||||
$difference = \IPS\DateTime::create()->setTimezone( $values[ 'warn_' . $f ]->getTimezone() )->diff( $values[ 'warn_' . $f ] );
|
||||
|
||||
/**
|
||||
* Due to a bug in PHP 8.0, DateTime::diff() can return -1 for the hour, and an extra day. We need to look for this specifically and set hour to 23, and reduce days by 1. This bug is fixed in 8.1, so this should be removed when that version is required.
|
||||
* @see <a href='https://bugs.php.net/bug.php?id=66545'>#66545</a>
|
||||
*/
|
||||
if ( version_compare( PHP_VERSION, '8.1.0', '<' ) )
|
||||
{
|
||||
if ( $difference->h === -1 )
|
||||
{
|
||||
$difference->h = 23;
|
||||
$difference->d = $difference->d - 1;
|
||||
}
|
||||
}
|
||||
|
||||
$period = 'P';
|
||||
foreach ( array( 'y' => 'Y', 'm' => 'M', 'd' => 'D' ) as $k => $v )
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user