Version 5.0.14

This commit is contained in:
Neo committed 2025-12-19 19:17:57 -08:00
1 parent 5efe744c5f
commit dbd774b30c
364 files changed
+7048 -1377

No files matched your search

@@ -251,13 +251,16 @@ class Reaction extends ContentAchievementActionAbstract
try
{
$class = $exploded[0];
$content = $class::load( $exploded[1] );
if( \IPS\IPS::classUsesTrait( $content, 'IPS\Content\Reactable' ) )
if( class_exists( $class ) )
{
$contentLink = Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $content->url(), TRUE, $content->indefiniteArticle(), FALSE );
$content = $class::load( $exploded[1] );
if( \IPS\IPS::classUsesTrait( $content, 'IPS\Content\Reactable' ) )
{
$contentLink = Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $content->url(), TRUE, $content->indefiniteArticle(), FALSE );
$reaction = $content->reacted( Member::load( $exploded[2] ) );
$reactionName = $reaction->_title;
$reaction = $content->reacted( Member::load( $exploded[2] ) );
$reactionName = $reaction->_title;
}
}
}
catch ( Exception $e ) { }
@@ -0,0 +1,218 @@
<?php
/**
* @brief Community Enhancements
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 23 Oct 2025
*/
namespace IPS\core\extensions\core\CommunityEnhancements;
/* To prevent PHP errors (extending class does not exist) revealing path */
use Exception;
use IPS\Dispatcher;
use IPS\Extensions\CommunityEnhancementsAbstract;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Stack;
use IPS\Helpers\Form\Text;
use IPS\Helpers\Form\YesNo;
use IPS\Http\Url;
use IPS\Member;
use IPS\Member as MemberClass;
use IPS\Output;
use IPS\Theme;
use IPS\Settings;
use LogicException;
use function defined;
use function explode;
use function implode;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Community Enhancement
*/
class Iframely extends CommunityEnhancementsAbstract
{
/**
* @brief Enhancement is enabled?
*/
public bool $enabled = false;
/**
* @brief IPS-provided enhancement?
*/
public bool $ips = false;
/**
* @brief Enhancement has configuration options?
*/
public bool $hasOptions = true;
/**
* @brief Icon data
*/
public string $icon = "iframely.png";
/**
* Constructor
*
* @return void
*/
public function __construct()
{
foreach ( [ 'iframely_enabled', 'iframely_api_key', 'iframely_url_whitelist' ] as $key )
{
$this->_settings[$key] = Settings::i()->$key;
}
$this->enabled = (bool) Settings::i()->iframely_enabled;
}
/**
* The settings that this extension is currently using
* @var array
*/
protected array $_settings = [];
/**
* Edit
*
* @return void
*/
public function edit() : void
{
$form = new Form;
$message = Member::loggedIn()->language()->addToStack( 'enhancements__core_Iframely_desc_full' );
if ( Dispatcher::i()->checkAcpPermission( "posting_manage_simplified_mode", return: true ) )
{
$message = "<p>{$message}</p><p>" . Member::loggedIn()->language()->addToStack( "enhancements__core_Iframely_embed_settings_link" ) . "</p>";
}
Member::loggedIn()->language()->parseOutputForDisplay( $message );
Member::loggedIn()->language()->words['enhancements__core_Iframely_message'] = $message;
$form->addMessage( 'enhancements__core_Iframely_message' );
$form->add( new YesNo( 'iframely_enabled', Settings::i()->iframely_enabled, false, ['togglesOn' => ['iframely_api_key', 'iframely_url_whitelist', 'iframely_builtin_services']] ) );
$form->add( new Text( 'iframely_api_key', Settings::i()->iframely_api_key, false, id: 'iframely_api_key' ) );
$services = [];
$enabled = [];
foreach ( ['iframely_facebook_enabled', 'iframely_instagram_enabled'] as $serviceSetting )
{
if ( Settings::i()->$serviceSetting )
{
$enabled[] = $serviceSetting;
}
$services[$serviceSetting] = $serviceSetting;
}
$form->add( new Form\CheckboxSet( "iframely_builtin_services", $enabled, false, [
'options' => $services
], id: 'iframely_builtin_services' ) );
$form->add( new Stack( 'iframely_url_whitelist', explode( ',', Settings::i()->iframely_url_whitelist ?: "" ), false, id: 'iframely_url_whitelist' ) );
if ( $values = $form->values() )
{
try
{
$values['iframely_url_whitelist'] = implode( ",", $values['iframely_url_whitelist'] );
$this->_settings = array_replace( $this->_settings, $values );
$this->testSettings();
if ( array_key_exists( 'iframely_builtin_services', $values ) )
{
foreach( $services as $setting )
{
$values[$setting] = in_array( $setting, $values['iframely_builtin_services'] ) ? 1 : 0;
}
}
unset( $values['iframely_builtin_services'] );
Settings::i()->changeValues( $values );
Output::i()->inlineMessage = MemberClass::loggedIn()->language()->addToStack('saved');
}
catch ( LogicException $e )
{
$form->error = $e->getMessage();
}
}
Output::i()->sidebar['actions'] = array(
'help' => array(
'title' => 'help',
'icon' => 'question-circle',
'link' => Url::ips( 'docs/iframely' ),
'target' => '_blank'
),
);
Output::i()->output = Theme::i()->getTemplate( 'global' )->block( 'enhancements__core_Iframely', $form );
}
/**
* Enable/Disable
*
* @param $enabled bool Enable/Disable
* @return void
* @throws LogicException
*/
public function toggle( bool $enabled ) : void
{
if ( $enabled )
{
$this->testSettings();
}
Settings::i()->changeValues( array( 'iframely_enabled' => $enabled ) );
}
/**
* Test Settings
*
* @return void
* @throws LogicException
*/
protected function testSettings() : void
{
/* If this is not enabled, no need to check the api key */
if ( !@$this->_settings['iframely_enabled'] )
{
return;
}
if ( empty( $this->_settings['iframely_api_key'] ) )
{
throw new LogicException( Member::loggedIn()->language()->get("iframely_err_no_api_key" ) );
}
/* We only need to test the key if it's different from the saved value */
if ( $this->_settings['iframely_api_key'] !== Settings::i()->iframely_api_key )
{
try
{
$response = Url::external( "https://iframe.ly/api/iframely" )
->setQueryString([
'key' => $this->_settings['iframely_api_key'],
'url' => 'https://iframely.com',
])
->request()
->get();
if ( $response->httpResponseCode == 403 )
{
throw new Exception();
}
}
catch ( Exception )
{
throw new LogicException( Member::loggedIn()->language()->get( "iframely_err_invalid_api_key" ) );
}
}
}
}
@@ -13,6 +13,7 @@ namespace IPS\core\extensions\core\GroupForm;
/* To prevent PHP errors (extending class does not exist) revealing path */
use IPS\Application;
use IPS\Content\Hideable;
use IPS\Extensions\GroupFormAbstract;
use IPS\Helpers\Form;
use IPS\Helpers\Form\CheckboxSet;
@@ -184,7 +185,7 @@ class Content extends GroupFormAbstract
$form->addHeader( 'group_deleting' );
$form->add( new CheckboxSet( 'g_hide_own_posts', !$group->g_hide_own_posts ? 0 : ( $group->g_hide_own_posts == '1' ? '1' : explode( ',', $group->g_hide_own_posts ) ), FALSE, array(
'options' => array_filter( $contentClasses, function( $class ) {
return IPS::classUsesTrait( $class, 'IPS\Content\hideable' );
return IPS::classUsesTrait( $class, Hideable::class );
}, ARRAY_FILTER_USE_KEY ),
'unlimited' => '1'
) ) );
@@ -196,7 +196,7 @@ class IPTools extends ModCpAbstract
'view' => array(
'icon' => 'search',
'title' => 'see_uses',
'link' => Url::internal( 'app=core&module=modcp&controller=modcp&tab=ip_tools&ip=' ) . $row['ip'],
'link' => Url::internal( 'app=core&module=modcp&controller=modcp&tab=ip_tools', 'front', 'modcp_ip_tools' )->setQueryString( 'ip', $row['ip'] ),
),
);
};
@@ -75,11 +75,22 @@ class Unapproved extends ModCpAbstract
*/
public function getCounters() : array
{
$count = Request::i()->isAjax() ? $this->getApprovalQueueCount(true) : $this->getApprovalQueueCount();
return [
[ 'id' => 'elModCPApprovalCount', 'title' => 'modcp_approval', 'total' => $this->getApprovalQueueCount() ]
[ 'id' => 'elModCPApprovalCount', 'title' => 'modcp_approval', 'total' => $count ]
];
}
/**
* get unapproved counter for the ajax update
*
* @return void
*/
public function getCount(): void
{
Output::i()->json(['total' => $this->getApprovalQueueCount(true) ]);
}
/**
* Execute
*
@@ -245,7 +256,7 @@ class Unapproved extends ModCpAbstract
}
return array(
array_merge( array( "(" . implode( " OR ", $clause ) . ")" ), $binds )
array_merge( array( "(" . implode( " OR ", $clause ) . ")" ), $binds ),
);
}
@@ -263,8 +274,8 @@ class Unapproved extends ModCpAbstract
$rows = iterator_to_array(
new ActiveRecordIterator(
Db::i()->select( '*', 'core_approval_queue', Db::i()->in( 'approval_id', $ids ) ),
Approval::class
)
Approval::class,
),
);
foreach( $rows as $row )
@@ -14,6 +14,7 @@ namespace IPS\core\extensions\core\ModeratorPermissions;
use IPS\Extensions\ModeratorPermissionsAbstract;
use IPS\Member;
use IPS\Platform\Bridge;
use IPS\Settings;
use function defined;
@@ -71,7 +72,6 @@ class Content extends ModeratorPermissionsAbstract
'can_edit_item_message_content' => array( 'YesNo', array( 'togglesOff' => $toggles['edit_item_message'] ) ),
'can_delete_item_message_content'=> array( 'YesNo', array( 'togglesOff' => $toggles['delete_item_message'] ) ),
'can_toggle_item_moderation_content' => array( 'YesNo', array( 'togglesOff' => $toggles['toggle_item_moderation'] ) ),
'can_assign_content' => array( 'YesNo', array( 'togglesOff' => $toggles['assign'] ) ),
'can_recognize_content' => array( 'YesNo', array( 'togglesOn' => [ 'can_recognize_content_options', 'can_recognize_content_no_point_limit' ] ) ),
'can_recognize_content_options' => array( 'CheckboxSet', array(
'options' => [
@@ -81,6 +81,11 @@ class Content extends ModeratorPermissionsAbstract
) ),
'can_recognize_content_no_point_limit' => 'YesNo'
);
if( Bridge::i()->featureIsEnabled( 'assignments' ) )
{
$return['can_assign_content'] = array( 'YesNo', array( 'togglesOff' => $toggles['assign'] ) );
}
if ( Settings::i()->edit_log == 2 )
{
@@ -122,7 +122,7 @@ class Editor extends ProfileStepsAbstract
$wizards[ $step->key ] = function( $data ) use ( $member, $step ) {
$form = new Form( 'profile_profile_fields_' . $step->id, 'profile_complete_next' );
$form->addHtml( $member->language()->addToStack( 'profile_step_subaction_custom_' . $step->id ) );
$form->addHtml( Theme::i()->getTemplate( 'global', 'core', 'global' )->richText( $member->language()->addToStack( 'profile_step_subaction_custom_' . $step->id ), [ 'i-padding_3' ] ) );
/* Because there are no form elements, $values is an empty array - that's ok and means the form was submitted, while FALSE means it wasn't */
if ( ( $values = $form->values() ) !== FALSE )
@@ -19,6 +19,7 @@ use IPS\Content\Tag;
use IPS\Db;
use IPS\Extensions\QueueAbstract;
use IPS\Member;
use IPS\Settings;
use IPS\Task\Queue\OutOfRangeException as QueueOutOfRangeException;
use OutOfRangeException;
use function count;
@@ -93,11 +94,13 @@ class UpdateTaggedItems extends QueueAbstract
}
/* First we remove the old tag from the list */
$index = array_search( $data['tag'], $currentTags );
if( $index !== false )
{
unset( $currentTags[ $index ] );
}
foreach( $currentTags as $index => $_tag )
{
if( mb_strtolower( $_tag ) == mb_strtolower( $data['tag'] ) )
{
unset( $currentTags[ $index ] );
}
}
/* Are we renaming the tag? Put the new name in */
if( isset( $data['new'] ) and $data['new'] )
@@ -48,30 +48,6 @@ class DeviceUsage extends \IPS\core\Statistics\Chart
*/
public function getChart( Url $url ): Chart
{
/* Determine minimum date */
$minimumDate = NULL;
if( Settings::i()->stats_device_usage_prune )
{
$minimumDate = DateTime::create()->sub( new DateInterval( 'P' . Settings::i()->stats_device_usage_prune . 'D' ) );
}
/* We can't retrieve any stats prior to the new tracking being implemented */
try
{
$oldestLog = Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'online_users' ) )->first();
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
{
$minimumDate = DateTime::ts( $oldestLog );
}
}
catch( UnderflowException $e )
{
/* We have nothing tracked, set minimum date to today */
$minimumDate = DateTime::create();
}
$chart = new Database( $url, 'core_statistics', 'time', '', array(
'isStacked' => FALSE,
'backgroundColor' => '#ffffff',
@@ -61,7 +61,10 @@ class EmailClicks extends \IPS\core\Statistics\Chart
try
{
$oldestLog = Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'emails_sent' ) )->first();
if ( is_null( $oldestLog ) )
{
throw new UnderflowException; // If there are no rows, MIN(time) will be null
}
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
{
$minimumDate = DateTime::ts( $oldestLog );
@@ -45,7 +45,6 @@ class Language extends \IPS\core\Statistics\Chart
*/
public function getChart( Url $url ): Chart
{
$chart = new Chart;
$counts = iterator_to_array( Db::i()->select( 'language, COUNT(member_id) as count', 'core_members', array( "language > ?", 0), NULL, NULL, "language" )->setKeyField( 'language' ) );
$chart = new Database( $url, 'core_members', 'language', '', array(
@@ -59,7 +59,10 @@ class OnlineUsers extends \IPS\core\Statistics\Chart
try
{
$oldestLog = Db::i()->select( 'MIN(time)', 'core_statistics', array( 'type=?', 'online_users' ) )->first();
if ( is_null( $oldestLog ) )
{
throw new UnderflowException; // If there are no rows, MIN(time) will be null
}
if( !$minimumDate OR $oldestLog < $minimumDate->getTimestamp() )
{
$minimumDate = DateTime::ts( $oldestLog );