221 lines
5.4 KiB
PHP
221 lines
5.4 KiB
PHP
<?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' );
|
|
}
|
|
}
|
|
} |