Skip to content
Commits on Source (3)
......@@ -2,6 +2,8 @@
namespace Minds\Api;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Domain\Security as ProDomainSecurity;
use Minds\Interfaces;
use Minds\Helpers;
use Minds\Core\Security;
......@@ -32,6 +34,7 @@ class Factory
$loop = count($segments);
while ($loop >= 0) {
$offset = $loop -1;
if ($loop < count($segments)) {
$slug_length = strlen($segments[$offset+1].'\\');
$route_length = strlen($route);
......@@ -42,32 +45,51 @@ class Factory
$actual = str_replace('\\', '/', $route);
if (isset(Routes::$routes[$actual])) {
$class_name = Routes::$routes[$actual];
if (class_exists($class_name)) {
$handler = new $class_name();
if (property_exists($handler, 'request')) {
$handler->request = $request;
}
if ($handler instanceof Interfaces\ApiAdminPam) {
self::adminCheck();
}
if (!$handler instanceof Interfaces\ApiIgnorePam) {
self::pamCheck($request, $response);
}
$pages = array_splice($segments, $loop) ?: [];
return $handler->$method($pages);
}
}
//autloaded routes
$class_name = "\\Minds\\Controllers\api\\$route";
if (class_exists($class_name)) {
$handler = new $class_name();
if (property_exists($handler, 'request')) {
$handler->request = $request;
}
if ($handler instanceof Interfaces\ApiAdminPam) {
self::adminCheck();
}
if (!$handler instanceof Interfaces\ApiIgnorePam) {
self::pamCheck($request, $response);
}
$pages = array_splice($segments, $loop) ?: [];
return $handler->$method($pages);
}
--$loop;
}
}
......@@ -78,15 +100,18 @@ class Factory
*/
public static function pamCheck($request, $response)
{
if ($request->getAttribute('oauth_user_id')
|| Security\XSRF::validateRequest()
if (
$request->getAttribute('oauth_user_id') ||
Security\XSRF::validateRequest()
) {
return true;
} else {
//error_log('failed authentication:: OAUTH via API');
ob_end_clean();
static::setCORSHeader();
header('Content-type: application/json');
header("Access-Control-Allow-Origin: *");
header('HTTP/1.1 401 Unauthorized', true, 401);
echo json_encode([
'error' => 'Sorry, you are not authenticated',
......@@ -108,8 +133,10 @@ class Factory
} else {
error_log('security: unauthorized access to admin api');
ob_end_clean();
static::setCORSHeader();
header('Content-type: application/json');
header("Access-Control-Allow-Origin: *");
header('HTTP/1.1 401 Unauthorized', true, 401);
echo json_encode(['error'=>'You are not an admin', 'code'=>401]);
exit;
......@@ -126,8 +153,10 @@ class Factory
return true;
} else {
ob_end_clean();
static::setCORSHeader();
header('Content-type: application/json');
header("Access-Control-Allow-Origin: *");
header('HTTP/1.1 401 Unauthorized', true, 401);
echo json_encode([
'status' => 'error',
......@@ -151,11 +180,26 @@ class Factory
ob_end_clean();
static::setCORSHeader();
header('Content-type: application/json');
header("Access-Control-Allow-Origin: *");
echo json_encode($data);
}
/**
* Sets the CORS header, if not already set
*/
public static function setCORSHeader(): void
{
$wasSet = count(array_filter(headers_list(), function ($header) {
return stripos($header, 'Access-Control-Allow-Origin:') === 0;
})) > 0;
if (!$wasSet) {
header("Access-Control-Allow-Origin: *");
}
}
/**
* Returns the exportable form of the entities
* @param array $entities - an array of entities
......
<?php
/**
* Jwt
* @author edgebal
*/
namespace Minds\Common;
use Exception;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Claim;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
class Jwt
{
/** @var string */
protected $key;
/**
* @param string $key
* @return Jwt
*/
public function setKey(string $key): Jwt
{
$this->key = $key;
return $this;
}
/**
* @param object|array $payload
* @param int|null $exp
* @param int|null $nbf
* @return string
* @throws Exception
*/
public function encode($payload, $exp = null, $nbf = null): string
{
if (!$this->key) {
throw new Exception('Invalid JWT key');
}
$builder = new Builder();
foreach ($payload as $key => $value) {
$builder->set($key, $value);
}
if ($exp !== null) {
$builder->setExpiration($exp);
}
if ($nbf !== null) {
$builder->setNotBefore($nbf);
}
$builder->sign(new Sha256(), $this->key);
return (string) $builder->getToken();
}
/**
* @param string $jwt
* @return array
* @throws Exception
*/
public function decode($jwt): array
{
if (!$this->key) {
throw new Exception('Invalid JWT key');
}
$token = (new Parser())->parse($jwt);
if (!$token->verify(new Sha256(), $this->key)) {
throw new Exception('Invalid JWT');
}
return array_map(function (Claim $claim) {
return $claim->getValue();
}, $token->getClaims());
}
/**
* @return string
*/
public function randomString(): string
{
$bytes = openssl_random_pseudo_bytes(128);
return hash('sha512', $bytes);
}
}
......@@ -328,4 +328,13 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
{
return array_reduce($this->data, $callback, $initialValue);
}
/**
* Returns the first element of the Response, or null if empty
* @return mixed|null
*/
public function first()
{
return $this->data[0] ?? null;
}
}
......@@ -16,6 +16,8 @@ use Minds\Entities;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Exceptions\TwoFactorRequired;
use Minds\Core\Queue;
use Minds\Core\Subscriptions;
class authenticate implements Interfaces\Api, Interfaces\ApiIgnorePam
{
......@@ -44,6 +46,7 @@ class authenticate implements Interfaces\Api, Interfaces\ApiIgnorePam
}
$user = new Entities\User(strtolower($_POST['username']));
/** @var Core\Security\LoginAttempts $attempts */
$attempts = Core\Di\Di::_()->get('Security\LoginAttempts');
......@@ -117,7 +120,7 @@ class authenticate implements Interfaces\Api, Interfaces\ApiIgnorePam
public function delete($pages)
{
$sessions = Di::_()->get('Sessions\Manager');
if (isset($pages[0]) && $pages[0] === 'all') {
$sessions->destroy(true);
} else {
......
......@@ -13,6 +13,7 @@ use Minds\Interfaces;
use Minds\Entities;
use Minds\Api\Factory;
use Minds\Common\ChannelMode;
use Minds\Core\Di\Di;
use ElggFile;
class channel implements Interfaces\Api
......@@ -46,6 +47,10 @@ class channel implements Interfaces\Api
return Factory::response(['status'=>'error', 'message'=>'The user is banned']);
}
Di::_()->get('Referrals\Cookie')
->setEntity($user)
->create();
$user->fullExport = true; //get counts
$user->exportCounts = true;
$return = Factory::exportable([$user]);
......@@ -87,6 +92,19 @@ class channel implements Interfaces\Api
$block = Core\Security\ACL\Block::_();
$response['channel']['blocked'] = $block->isBlocked($user);
if ($user->isPro()) {
/** @var Core\Pro\Manager $manager */
$manager = Core\Di\Di::_()->get('Pro\Manager');
$manager
->setUser($user);
$proSettings = $manager->get();
if ($proSettings) {
$response['channel']['pro_settings'] = $proSettings;
}
}
return Factory::response($response);
}
......
......@@ -5,14 +5,14 @@
* @version 1
* @author Mark Harding
*/
namespace Minds\Controllers\api\v1;
use Minds\Api\Factory;
use Minds\Core;
use Minds\Core\Di\Di;
use Minds\Entities;
use Minds\Entities\User;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Helpers;
class register implements Interfaces\Api, Interfaces\ApiIgnorePam
{
......@@ -21,7 +21,7 @@ class register implements Interfaces\Api, Interfaces\ApiIgnorePam
*/
public function get($pages)
{
return Factory::response(['status'=>'error', 'message'=>'GET is not supported for this endpoint']);
return Factory::response(['status' => 'error', 'message' => 'GET is not supported for this endpoint']);
}
/**
......@@ -37,11 +37,11 @@ class register implements Interfaces\Api, Interfaces\ApiIgnorePam
public function post($pages)
{
if (!isset($_POST['username']) || !isset($_POST['password']) || !isset($_POST['username']) || !isset($_POST['email'])) {
return Factory::response(['status'=>'error']);
return Factory::response(['status' => 'error']);
}
if (!$_POST['username'] || !$_POST['password'] || !$_POST['username'] || !$_POST['email']) {
return Factory::response(['status'=>'error', 'message' => "Please fill out all the fields"]);
return Factory::response(['status' => 'error', 'message' => "Please fill out all the fields"]);
}
try {
......@@ -119,11 +119,11 @@ class register implements Interfaces\Api, Interfaces\ApiIgnorePam
$sessions->save(); // Save to db and cookie
$response = [
'guid' => $guid,
'user' => $user->export()
'guid' => $guid,
'user' => $user->export(),
];
} catch (\Exception $e) {
$response = ['status'=>'error', 'message'=>$e->getMessage()];
$response = ['status' => 'error', 'message' => $e->getMessage()];
}
return Factory::response($response);
}
......
......@@ -69,10 +69,6 @@ class settings implements Interfaces\Api
{
Factory::isLoggedIn();
if (!Core\Security\XSRF::validateRequest()) {
//return false;
}
if (Core\Session::getLoggedInUser()->isAdmin() && isset($pages[0])) {
$user = new entities\User($pages[0]);
} else {
......
<?php
/**
* pro
*
* @author edgebal
*/
namespace Minds\Controllers\api\v2\admin;
use Minds\Api\Factory;
use Minds\Core\Pro\Manager;
use Minds\Entities\User as UserEntity;
use Minds\Interfaces;
use Minds\Core\Di\Di;
class pro implements Interfaces\Api, Interfaces\ApiAdminPam
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
*/
public function get($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP POST method
* @param array $pages
* @return mixed|null
*/
public function post($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP PUT method
* @param array $pages
* @return mixed|null
*/
public function put($pages)
{
if (!($pages[0] ?? null)) {
return Factory::response([
'status' => 'error',
'message' => 'Emtpy target',
]);
}
$target = new UserEntity($pages[0]);
if (!$target || !$target->guid) {
return Factory::response([
'status' => 'error',
'message' => 'Invalid target',
]);
}
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager
->setUser($target);
$success = $manager->enable(time() + (365 * 86400));
if (!$success) {
return Factory::response([
'status' => 'error',
'message' => 'Error disabling Pro',
]);
}
return Factory::response([]);
}
/**
* Equivalent to HTTP DELETE method
* @param array $pages
* @return mixed|null
*/
public function delete($pages)
{
if (!($pages[0] ?? null)) {
return Factory::response([
'status' => 'error',
'message' => 'Emtpy target',
]);
}
$target = new UserEntity($pages[0]);
if (!$target || !$target->guid) {
return Factory::response([
'status' => 'error',
'message' => 'Invalid target',
]);
}
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager
->setUser($target);
$success = $manager->disable();
if (!$success) {
return Factory::response([
'status' => 'error',
'message' => 'Error disabling Pro',
]);
}
return Factory::response([]);
}
}
......@@ -113,6 +113,10 @@ class views implements Interfaces\Api
error_log($e);
}
Di::_()->get('Referrals\Cookie')
->setEntity($activity)
->create();
break;
}
......
<?php
/**
* pro
* @author edgebal
*/
namespace Minds\Controllers\api\v2;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Manager;
use Minds\Core\Session;
use Minds\Interfaces;
use Minds\Api\Factory;
class pro implements Interfaces\Api
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
* @throws Exception
*/
public function get($pages)
{
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager
->setUser(Session::getLoggedinUser());
return Factory::response([
'isActive' => $manager->isActive(),
]);
}
/**
* Equivalent to HTTP POST method
* @param array $pages
* @return mixed
*/
public function post($pages)
{
return Factory::response([
'status' => 'error',
'message' => 'Minds Pro is not yet publicly available.',
]);
// /** @var Manager $manager */
// $manager = Di::_()->get('Pro\Manager');
// $manager
// ->setUser(Session::getLoggedinUser());
//
// // TODO: Send and process payment data
// $success = $manager->enable(time() + (365 * 86400));
//
// if (!$success) {
// return Factory::response([
// 'status' => 'error',
// 'message' => 'Error activating Pro',
// ]);
// }
//
// return Factory::response([
// 'isActive' => $manager->isActive(),
// 'settings' => $manager->get(),
// ]);
}
/**
* Equivalent to HTTP PUT method
* @param array $pages
* @return mixed|null
*/
public function put($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP DELETE method
* @param array $pages
* @return mixed
*/
public function delete($pages)
{
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager
->setUser(Session::getLoggedinUser());
$success = $manager->disable();
if (!$success) {
return Factory::response([
'status' => 'error',
'message' => 'Error disabling Pro',
]);
}
return Factory::response([]);
}
}
<?php
/**
* channel
* @author edgebal
*/
namespace Minds\Controllers\api\v2\pro;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Channel\Manager as ChannelManager;
use Minds\Core\Pro\Manager;
use Minds\Core\Session;
use Minds\Entities\User;
use Minds\Helpers\Campaigns\Referrals;
use Minds\Interfaces;
use Minds\Api\Factory;
class channel implements Interfaces\Api
{
public $request;
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
* @throws Exception
*/
public function get($pages)
{
$currentUser = Session::getLoggedinUser();
$channel = new User($pages[0]);
$channel->fullExport = true; //get counts
$channel->exportCounts = true;
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager->setUser($channel);
/** @var ChannelManager $manager */
$channelManager = Di::_()->get('Pro\Channel\Manager');
$channelManager->setUser($channel);
switch ($pages[1] ?? '') {
case 'content':
return Factory::response([
'content' => $channelManager->getAllCategoriesContent(),
]);
default:
$proSettings = $manager->get();
$exportedChannel = $channel->export();
$exportedChannel['pro_settings'] = $proSettings;
$origin = strtolower($this->request->getServerParams()['HTTP_X_MINDS_ORIGIN'] ?? '');
$domain = strtolower($proSettings->getDomain());
if ($domain === $origin) {
Referrals::register($channel->username);
}
return Factory::response([
'channel' => $exportedChannel,
'me' => $currentUser ? $currentUser->export() : null,
]);
}
}
/**
* Equivalent to HTTP POST method
* @param array $pages
* @return mixed|null
*/
public function post($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP PUT method
* @param array $pages
* @return mixed|null
*/
public function put($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP DELETE method
* @param array $pages
* @return mixed|null
*/
public function delete($pages)
{
return Factory::response([]);
}
}
<?php
/**
* @author: eiennohi.
*/
namespace Minds\Controllers\api\v2\pro;
use Minds\Api\Exportable;
use Minds\Api\Factory;
use Minds\Common\Repository\Response;
use Minds\Core;
use Minds\Core\Di\Di;
use Minds\Entities\Factory as EntitiesFactory;
use Minds\Entities\User;
use Minds\Interfaces;
class content implements Interfaces\Api
{
const MIN_COUNT = 6;
public function get($pages)
{
/** @var User $currentUser */
$currentUser = Core\Session::getLoggedinUser();
$container_guid = $pages[0] ?? null;
$owner_guid = null;
if (!$container_guid) {
return Factory::response([
'status' => 'error',
'message' => 'Invalid container',
]);
}
$container = EntitiesFactory::build($container_guid);
if (!$container || !Core\Security\ACL::_()->read($container, $currentUser)) {
return Factory::response([
'status' => 'error',
'message' => 'Forbidden',
]);
}
$type = '';
switch ($pages[1]) {
case 'activities':
$type = 'activity';
break;
case 'images':
$type = 'object:image';
break;
case 'videos':
$type = 'object:video';
break;
case 'blogs':
$type = 'object:blog';
break;
case 'groups':
$type = 'group';
$container_guid = null;
$owner_guid = $pages[0];
break;
case 'all':
$type = 'all';
break;
}
$hardLimit = 5000;
$offset = 0;
if (isset($_GET['offset'])) {
$offset = intval($_GET['offset']);
}
$limit = 12;
if (isset($_GET['limit'])) {
$limit = abs(intval($_GET['limit']));
}
if (($offset + $limit) > $hardLimit) {
$limit = $hardLimit - $offset;
}
if ($limit <= 0) {
return Factory::response([
'status' => 'success',
'entities' => [],
'load-next' => $hardLimit,
'overflow' => true,
]);
}
$sync = (bool) ($_GET['sync'] ?? false);
$fromTimestamp = $_GET['from_timestamp'] ?? 0;
$exclude = explode(',', $_GET['exclude'] ?? '');
$forcePublic = (bool) ($_GET['force_public'] ?? false);
$asActivities = (bool) ($_GET['as_activities'] ?? true);
$query = null;
if (isset($_GET['query'])) {
$query = $_GET['query'];
}
/** @var Core\Feeds\Top\Entities $entities */
$entities = new Core\Feeds\Top\Entities();
$entities->setActor($currentUser);
$isOwner = false;
if ($currentUser) {
$entities->setActor($currentUser);
$isOwner = $currentUser->guid == $container_guid;
}
$opts = [
'cache_key' => $currentUser ? $currentUser->guid : null,
'container_guid' => $container_guid,
'owner_guid' => $owner_guid,
'access_id' => $isOwner && !$forcePublic ? [0, 1, 2, $container_guid] : [2, $container_guid],
'custom_type' => null,
'limit' => $limit,
'type' => $type,
'algorithm' => 'top',
'period' => '7d',
'sync' => $sync,
'from_timestamp' => $fromTimestamp,
'query' => $query,
'single_owner_threshold' => 0,
'pinned_guids' => $type === 'activity' ? array_reverse($container->getPinnedPosts()) : null,
'exclude' => $exclude,
];
if (isset($_GET['nsfw'])) {
$nsfw = $_GET['nsfw'] ?? '';
$opts['nsfw'] = explode(',', $nsfw);
}
$hashtag = null;
if (isset($_GET['hashtag'])) {
$hashtag = strtolower($_GET['hashtag']);
}
if ($hashtag) {
$opts['hashtags'] = [$hashtag];
$opts['filter_hashtags'] = true;
} elseif (isset($_GET['hashtags']) && $_GET['hashtags']) {
$opts['hashtags'] = explode(',', $_GET['hashtags']);
$opts['filter_hashtags'] = true;
}
try {
$result = $this->getData($entities, $opts, $asActivities, $sync);
if ($result->count() <= static::MIN_COUNT) {
$opts['algorithm'] = 'latest';
$result = $this->getData($entities, $opts, $asActivities, $sync);
}
$response = [
'status' => 'success',
'entities' => Exportable::_($result),
'load-next' => $result->getPagingToken(),
];
return Factory::response($response);
} catch (\Exception $e) {
error_log($e);
return Factory::response(['status' => 'error', 'message' => $e->getMessage()]);
}
}
/**
* @param Core\Feeds\Top\Entities $entities
* @param array $opts
* @param bool $asActivities
* @param bool $sync
* @return Response
* @throws \Exception
*/
private function getData($entities, $opts, $asActivities, $sync)
{
/** @var Core\Feeds\Top\Manager $manager */
$manager = Di::_()->get('Feeds\Top\Manager');
$result = $manager->getList($opts);
if (!$sync) {
// Remove all unlisted content, if ES document is not in sync, it'll
// also remove pending activities
$result = $result->filter([$entities, 'filter']);
if ($asActivities) {
// Cast to ephemeral Activity entities, if another type
$result = $result->map([$entities, 'cast']);
}
}
return $result;
}
public function post($pages)
{
return Factory::response([]);
}
public function put($pages)
{
return Factory::response([]);
}
public function delete($pages)
{
return Factory::response([]);
}
}
<?php
/**
* settings
* @author edgebal
*/
namespace Minds\Controllers\api\v2\pro;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Manager;
use Minds\Core\Session;
use Minds\Entities\User;
use Minds\Interfaces;
use Minds\Api\Factory;
class settings implements Interfaces\Api
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
*/
public function get($pages)
{
$user = Session::getLoggedinUser();
if (isset($pages[0]) && $pages[0]) {
if (!Session::isAdmin()) {
return Factory::response([
'status' => 'error',
'message' => 'You are not authorized',
]);
}
$user = new User($pages[0]);
}
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager
->setUser($user)
->setActor(Session::getLoggedinUser());
return Factory::response([
'isActive' => $manager->isActive(),
'settings' => $manager->get(),
]);
}
/**
* Equivalent to HTTP POST method
* @param array $pages
* @return mixed|null
*/
public function post($pages)
{
$user = Session::getLoggedinUser();
if (isset($pages[0]) && $pages[0]) {
if (!Session::isAdmin()) {
return Factory::response([
'status' => 'error',
'message' => 'You are not authorized',
]);
}
$user = new User($pages[0]);
}
/** @var Manager $manager */
$manager = Di::_()->get('Pro\Manager');
$manager
->setUser($user)
->setActor(Session::getLoggedinUser());
if (!$manager->isActive()) {
return Factory::response([
'status' => 'error',
'message' => 'You are not Pro',
]);
}
try {
$success = $manager->set($_POST);
if (!$success) {
throw new Exception('Cannot save Pro settings');
}
} catch (\Exception $e) {
return Factory::response([
'status' => 'error',
'message' => $e->getMessage(),
]);
}
return Factory::response([]);
}
/**
* Equivalent to HTTP PUT method
* @param array $pages
* @return mixed|null
*/
public function put($pages)
{
return Factory::response([]);
}
/**
* Equivalent to HTTP DELETE method
* @param array $pages
* @return mixed|null
*/
public function delete($pages)
{
return Factory::response([]);
}
}
......@@ -46,7 +46,15 @@ class thumbnail extends Core\page implements Interfaces\page
$contents = $thumbnail->read();
}
header('Content-type: image/jpeg');
try {
$finfo = new \finfo(FILEINFO_MIME);
$contentType = $finfo->buffer($contents) ?: 'image/jpeg';
} catch (\Exception $e) {
error_log($e);
$contentType = 'image/jpeg';
}
header('Content-type: ' . $contentType);
header('Expires: ' . date('r', strtotime('today + 6 months')), true);
header('Pragma: public');
header('Cache-Control: public');
......
......@@ -6,6 +6,7 @@ use Minds\Core;
use Minds\Entities;
use Minds\Helpers;
use Minds\Helpers\Counters;
use Zend\Diactoros\ServerRequestFactory;
class SEO
{
......@@ -76,18 +77,30 @@ class SEO
$params = $event->getParameters();
$slugs = $params['slugs'];
if ((count($slugs) < 3) || ($slugs[1] != 'blog')) {
/** @var Core\Pro\Domain $proDomain */
$proDomain = Core\Di\Di::_()->get('Pro\Domain');
$request = ServerRequestFactory::fromGlobals();
$serverParams = $request->getServerParams() ?? [];
$host = parse_url($serverParams['HTTP_ORIGIN'] ?? '', PHP_URL_HOST) ?: $serverParams['HTTP_HOST'];
$proSettings = $proDomain->lookup($host);
if ($proSettings && (count($slugs) < 2 || $slugs[0] === 'blog')) {
$slugParts = explode('-', $slugs[1]);
} elseif (!$proSettings && count($slugs) >= 3 && $slugs[1] === 'blog') {
$slugParts = explode('-', $slugs[2]);
} else {
return;
}
$slugParts = explode('-', $slugs[2]);
$guid = $slugParts[count($slugParts) - 1];
if (!is_numeric($guid)) {
return;
}
$event->setResponse($this->viewHandler([ $guid ]));
$event->setResponse($this->viewHandler([$guid]));
});
}
......
......@@ -7,6 +7,7 @@
namespace Minds\Core\Feeds;
use JsonSerializable;
use Minds\Traits\Exportable;
use Minds\Traits\MagicAttributes;
......@@ -22,7 +23,7 @@ use Minds\Traits\MagicAttributes;
* @method string getUrn()
* @method FeedSyncEntity setUrn(string $urn)
*/
class FeedSyncEntity
class FeedSyncEntity implements JsonSerializable
{
use MagicAttributes;
......@@ -55,4 +56,16 @@ class FeedSyncEntity
'entity' => $this->entity ? $this->entity->export() : null,
];
}
/**
* Specify data which should be serialized to JSON
* @link https://php.net/manual/en/jsonserializable.jsonserialize.php
* @return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* @since 5.4.0
*/
public function jsonSerialize(): array
{
return $this->export();
}
}
......@@ -73,6 +73,7 @@ class Minds extends base
(new \Minds\Entities\EntitiesProvider())->register();
(new Config\ConfigProvider())->register();
(new Router\RouterProvider())->register();
(new OAuth\OAuthProvider())->register();
(new Sessions\SessionsProvider())->register();
(new Boost\BoostProvider())->register();
......@@ -105,6 +106,7 @@ class Minds extends base
(new Faq\FaqProvider())->register();
(new Rewards\RewardsProvider())->register();
(new Plus\PlusProvider())->register();
(new Pro\ProProvider())->register();
(new Hashtags\HashtagsProvider())->register();
(new Feeds\FeedsProvider())->register();
(new Analytics\AnalyticsProvider())->register();
......
<?php
/**
* Manager
* @author edgebal
*/
namespace Minds\Core\Pro\Channel;
use Exception;
use Minds\Core\Data\cache\abstractCacher;
use Minds\Core\Di\Di;
use Minds\Core\Feeds\Top\Manager as TopManager;
use Minds\Core\Pro\Repository;
use Minds\Core\Pro\Settings;
use Minds\Entities\User;
class Manager
{
const CACHE_TTL = 300; // Cache homepage content for 5 minutes
/** @var Repository */
protected $repository;
/** @var TopManager */
protected $top;
/** @var abstractCacher */
protected $cache;
/** @var User */
protected $user;
/**
* Manager constructor.
* @param Repository $repository
* @param TopManager $top
* @param abstractCacher $cache
*/
public function __construct(
$repository = null,
$top = null,
$cache = null
) {
$this->repository = $repository ?: new Repository();
$this->top = $top ?: new TopManager();
$this->cache = $cache ?: Di::_()->get('Cache');
}
/**
* @param User $user
* @return Manager
*/
public function setUser(User $user): Manager
{
$this->user = $user;
return $this;
}
/**
* @return array
* @throws Exception
*/
public function getAllCategoriesContent(): array
{
if (!$this->user) {
throw new Exception('No user set');
}
/** @var Settings $settings */
$settings = $this->repository->getList([
'user_guid' => $this->user->guid
])->first();
if (!$settings) {
throw new Exception('Invalid Pro user');
}
$cacheKey = sprintf("pro::v1::getAllCategoriesContent::%s", $this->user->guid);
$cachedContent = $this->cache->get($cacheKey);
if ($cachedContent) {
return $cachedContent;
}
$tags = $settings->getTagList() ?: [];
$output = [];
$container = (string) $this->user->guid;
foreach ($tags as $tag) {
$opts = [
'container_guid' => $container,
'access_id' => [2, $container],
'hashtags' => [strtolower($tag['tag'])],
'filter_hashtags' => true,
'limit' => 4,
'type' => 'all',
'algorithm' => 'top',
'period' => '7d',
'sync' => true,
'single_owner_threshold' => 0,
];
$content = $this->top->getList($opts)->toArray();
if (count($content) < 2) {
$opts['algorithm'] = 'latest';
$content = $this->top->getList($opts)->toArray();
}
$output[] = [
'tag' => $tag,
'content' => $content,
];
}
$this->cache->set($cacheKey, $output, static::CACHE_TTL);
return $output;
}
}
<?php
/**
* HydrateSettingsDelegate
* @author edgebal
*/
namespace Minds\Core\Pro\Delegates;
use Minds\Core\Config;
use Minds\Core\Di\Di;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Pro\Settings;
use Minds\Entities\Object\Carousel;
use Minds\Entities\User;
use Minds\Helpers\Text;
class HydrateSettingsDelegate
{
/** @var EntitiesBuilder */
protected $entitiesBuilder;
/** @var Config */
protected $config;
/**
* HydrateSettingsDelegate constructor.
* @param EntitiesBuilder $entitiesBuilder
* @param Config $config
*/
public function __construct(
$entitiesBuilder = null,
$config = null
) {
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->config = $config ?: Di::_()->get('Config');
}
/**
* @param User $user
* @param Settings $settings
* @return Settings
*/
public function onGet(User $user, Settings $settings): Settings
{
try {
$logoImage = $settings->getLogoGuid() ? sprintf(
'%sfs/v1/thumbnail/%s/master',
$this->config->get('cdn_url'),
$settings->getLogoGuid()
) : $user->getIconURL('large');
if ($logoImage) {
$settings
->setLogoImage($logoImage);
}
} catch (\Exception $e) {
error_log($e);
}
try {
$carousels = $this->entitiesBuilder->get(['subtype' => 'carousel', 'owner_guid' => (string) $user->guid]);
$carousel = $carousels[0] ?? null;
if ($carousel) {
$settings
->setBackgroundImage(sprintf(
'%sfs/v1/banners/%s/fat/%s',
$this->config->get('cdn_url'),
$carousel->guid,
$carousel->last_updated
));
}
} catch (\Exception $e) {
error_log($e);
}
try {
if ($user->getPinnedPosts()) {
$pinnedPosts = $this->entitiesBuilder->get(['guids' => Text::buildArray($user->getPinnedPosts())]);
uasort($pinnedPosts, function ($a, $b) {
if (!$a || !$b) {
return 0;
}
return ($a->time_created < $b->time_created) ? 1 : -1;
});
$featuredContent = Text::buildArray(array_values(array_filter(array_map(function ($pinnedPost) {
return $pinnedPost->entity_guid ?: $pinnedPost->guid ?: null;
}, $pinnedPosts))));
$settings->setFeaturedContent($featuredContent);
}
} catch (\Exception $e) {
error_log($e);
}
return $settings;
}
}
<?php
/**
* InitializeSettingsDelegate
* @author edgebal
*/
namespace Minds\Core\Pro\Delegates;
use Exception;
use Minds\Core\Pro\Repository;
use Minds\Core\Pro\Settings;
use Minds\Entities\User;
class InitializeSettingsDelegate
{
/** @var Repository */
protected $repository;
/** @var SetupRoutingDelegate */
protected $setupRoutingDelegate;
/**
* InitializeSettingsDelegate constructor.
* @param Repository $repository
* @param SetupRoutingDelegate $setupRoutingDelegate
*/
public function __construct(
$repository = null,
$setupRoutingDelegate = null
) {
$this->repository = $repository ?: new Repository();
$this->setupRoutingDelegate = $setupRoutingDelegate ?: new SetupRoutingDelegate();
}
/**
* @param User $user
* @throws Exception
*/
public function onEnable(User $user): void
{
/** @var Settings|null $settings */
$settings = $this->repository
->getList(['user_guid' => $user->guid])
->first();
if (!$settings) {
$settings = new Settings();
$settings
->setUserGuid($user->guid);
}
if (!$settings->getTitle()) {
$settings->setTitle($user->name ?: $user->username);
}
$this->setupRoutingDelegate
->onUpdate($settings);
$this->repository
->add($settings);
}
}