Skip to content
Commits on Source (12)
......@@ -259,6 +259,16 @@ class Response implements \Iterator, \ArrayAccess, \Countable, \JsonSerializable
return count($this->data);
}
/**
* @param array $data
* @return Response
*/
public function pushArray(array $data)
{
array_push($this->data, ...$data);
return $this;
}
/**
* Exports the data array
* @return array
......
......@@ -2,13 +2,22 @@
/**
* Converts a static class to use instances
*/
namespace Minds\Common;
use ReflectionClass;
use ReflectionException;
class StaticToInstance
{
/** @var $class */
/** @var ReflectionClass */
private $class;
/**
* StaticToInstance constructor.
* @param $class
* @throws ReflectionException
*/
public function __construct($class)
{
$this->setClass($class);
......@@ -16,11 +25,13 @@ class StaticToInstance
/**
* Set the class in question
* @return StripeStaticToOO
* @param $class
* @return static
* @throws ReflectionException
*/
public function setClass($class)
{
$this->class = new \ReflectionClass($class);
$this->class = new ReflectionClass($class);
return clone $this;
}
......@@ -28,7 +39,7 @@ class StaticToInstance
* Call the static functions as OO style
* @param string $method
* @param array $arguments
* @return midex
* @return mixed
*/
public function __call($method, $arguments)
{
......
......@@ -4,7 +4,7 @@ namespace Minds\Controllers\Cli;
use Minds\Core\Minds;
use Minds\Cli;
use Minds\Core\Feeds\Top\Manager;
use Minds\Core\Feeds\Elastic\Manager;
use Minds\Exceptions\CliException;
use Minds\Interfaces;
......
<?php
namespace Minds\Controllers\Cli\Top;
use Exception;
use Minds\Core\Feeds\Elastic\Sync;
use Minds\Core\Minds;
use Minds\Cli;
use Minds\Exceptions\CliException;
use Minds\Interfaces;
class All extends Cli\Controller implements Interfaces\CliControllerInterface
{
/** @var Sync */
private $sync;
/**
* Top constructor.
*/
public function __construct()
{
$minds = new Minds();
$minds->start();
$this->sync = new Sync();
}
/**
* @param null $command
* @return void
*/
public function help($command = null)
{
$this->out('Syntax usage: cli top all sync_<type> --metric=? --from=? --to=?');
}
/**
* @return void
*/
public function exec()
{
$this->help();
}
/**
* @throws CliException
*/
public function sync_activity(): void
{
list($from, $to) = $this->getTimeRangeFromArgs();
$this->syncBy('activity', null, $this->getOpt('metric'), $from, $to);
}
/**
* @throws CliException
*/
public function sync_images(): void
{
list($from, $to) = $this->getTimeRangeFromArgs();
$this->syncBy('object', 'image', $this->getOpt('metric'), $from, $to);
}
/**
* @throws CliException
*/
public function sync_videos(): void
{
list($from, $to) = $this->getTimeRangeFromArgs();
$this->syncBy('object', 'video', $this->getOpt('metric'), $from, $to);
}
/**
* @throws CliException
*/
public function sync_blogs(): void
{
list($from, $to) = $this->getTimeRangeFromArgs();
$this->syncBy('object', 'blog', $this->getOpt('metric'), $from, $to);
}
/**
* @throws CliException
*/
public function sync_groups(): void
{
list($from, $to) = $this->getTimeRangeFromArgs();
$this->syncBy('group', null, $this->getOpt('metric'), $from, $to);
}
/**
* @throws CliException
*/
public function sync_channels(): void
{
list($from, $to) = $this->getTimeRangeFromArgs();
$this->syncBy('user', null, $this->getOpt('metric'), $from, $to);
}
/**
* @return int[]
* @throws CliException
*/
protected function getTimeRangeFromArgs(): array
{
$to = $this->getOpt('to') ?: time();
if ($this->getOpt('from') && $this->getOpt('secsAgo')) {
throw new CliException('Cannot specify both `from` and `secsAgo`');
} elseif (!$this->getOpt('from') && !$this->getOpt('secsAgo')) {
throw new CliException('You should specify either `from` or `secsAgo`');
}
if ($this->getOpt('secsAgo')) {
$from = time() - $this->getOpt('secsAgo');
} else {
$from = $this->getOpt('from');
}
return [$from, $to];
}
/**
* @param $type
* @param $subtype
* @param $metric
* @param $from
* @param $to
* @throws CliException
* @throws Exception
*/
protected function syncBy($type, $subtype, $metric, $from, $to): void
{
if (!$metric) {
throw new CliException('Missing `metric`');
}
if (!$from || !is_numeric($from)) {
throw new CliException('Missing or invalid `from` value');
}
if (!$to || !is_numeric($to)) {
throw new CliException('Invalid `to` value');
}
if ($from > $to) {
throw new CliException('`from` must be lesser than `to`');
}
error_reporting(E_ALL);
ini_set('display_errors', 1);
$displayType = trim(implode(':', [$type, $subtype]), ':');
$this->out(sprintf(
"%s -> %s",
date('r', $from),
date('r', $to)
));
$this->out("Syncing {$displayType} / {$metric}");
$this->sync
->setType($type ?: '')
->setSubtype($subtype ?: '')
->setMetric($metric)
->setFrom($from * 1000)
->setTo($to * 1000)
->run();
$this->out("\nCompleted syncing '{$displayType}'.");
}
}
......@@ -220,6 +220,11 @@ class blog implements Interfaces\Api
$blog->setMature(!!$_POST['mature']);
}
if (isset($_POST['nsfw'])) {
$nsfw = !is_array($_POST['nsfw']) ? json_decode($_POST['nsfw']) : $_POST['nsfw'];
$blog->setNsfw($nsfw);
}
if (isset($_POST['wire_threshold'])) {
$threshold = is_string($_POST['wire_threshold']) ? json_decode($_POST['wire_threshold']) : $_POST['wire_threshold'];
$blog->setWireThreshold($threshold);
......@@ -266,7 +271,7 @@ class blog implements Interfaces\Api
}
if ($blog->isMonetized()) {
if ($blog->isMature()) {
if ($blog->getNsfw() || $blog->isMature()) {
return Factory::response([
'status' => 'error',
'message' => 'Cannot monetize an explicit blog'
......@@ -283,7 +288,8 @@ class blog implements Interfaces\Api
}
}
if (isset($_POST['mature']) && $_POST['mature']) {
if ((isset($_POST['nsfw']) && $_POST['nsfw'])
|| (isset($_POST['mature']) && $_POST['mature'])) {
$user = Core\Session::getLoggedInUser();
if (!$user->getMatureContent()) {
......
......@@ -263,10 +263,10 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
$entity = Core\Media\Factory::build($clientType);
$container_guid = isset($data['container_guid']) && is_numeric($data['container_guid']) ? $data['container_guid'] : null;
$entity->patch([
'title' => isset($data['name']) ? $data['name'] : '',
'mature' => isset($data['mature']) && !!$data['mature'],
'nsfw' => !is_array($_POST['nsfw']) ? json_decode($_POST['nsfw']) : $_POST['nsfw'],
'batch_guid' => 0,
'access_id' => 0,
'owner_guid' => $user->guid,
......
......@@ -539,6 +539,7 @@ class newsfeed implements Interfaces\Api
$activity = new Activity();
$activity->setMature(isset($_POST['mature']) && !!$_POST['mature']);
$activity->setNsfw($_POST['nsfw'] ?? []);
$user = Core\Session::getLoggedInUser();
......
......@@ -112,8 +112,8 @@ class firehose implements Interfaces\Api, Interfaces\ApiAdminPam
}
if ($type !== 'activity') {
/** @var Core\Feeds\Top\Entities $entities */
$entities = new Core\Feeds\Top\Entities();
/** @var Core\Feeds\Elastic\Entities $entities */
$entities = new Core\Feeds\Elastic\Entities();
$entities->setActor($currentUser);
$activities = $activities->map([$entities, 'cast']);
}
......
......@@ -4,6 +4,7 @@ namespace Minds\Controllers\api\v2;
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;
......@@ -12,6 +13,13 @@ use Minds\Interfaces;
class feeds implements Interfaces\Api
{
const PERIOD_FALLBACK = [
'12h' => '7d',
'24h' => '7d',
'7d' => '30d',
'30d' => '1y'
];
/**
* Gets a list of suggested hashtags, including the ones the user has opted in
* @param array $pages
......@@ -21,6 +29,9 @@ class feeds implements Interfaces\Api
{
Factory::isLoggedIn();
$now = time();
$periodsInSecs = Core\Feeds\Elastic\Repository::PERIODS;
/** @var User $currentUser */
$currentUser = Core\Session::getLoggedinUser();
......@@ -119,6 +130,8 @@ class feeds implements Interfaces\Api
$sync = (bool) ($_GET['sync'] ?? false);
$periodFallback = (bool) ($_GET['period_fallback'] ?? false);
$asActivities = (bool) ($_GET['as_activities'] ?? true);
$query = isset($_GET['query']) ? urldecode($_GET['query']) : null;
......@@ -137,12 +150,13 @@ class feeds implements Interfaces\Api
}
}
/** @var Core\Feeds\Top\Manager $manager */
$manager = Di::_()->get('Feeds\Top\Manager');
/** @var Core\Feeds\Elastic\Manager $manager */
$manager = Di::_()->get('Feeds\Elastic\Manager');
/** @var Core\Feeds\Top\Entities $entities */
$entities = new Core\Feeds\Top\Entities();
$entities->setActor($currentUser);
/** @var Core\Feeds\Elastic\Entities $elasticEntities */
$elasticEntities = new Core\Feeds\Elastic\Entities();
$elasticEntities
->setActor($currentUser);
$opts = [
'cache_key' => Core\Session::getLoggedInUserGuid(),
......@@ -185,22 +199,49 @@ class feeds implements Interfaces\Api
}
try {
$result = $manager->getList($opts);
$entities = new Response();
$fallbackAt = null;
$i = 0;
while ($entities->count() < $limit) {
$rows = $manager->getList($opts);
$entities = $entities->pushArray($rows->toArray());
if (
!$periodFallback ||
$opts['algorithm'] !== 'top' ||
!isset(static::PERIOD_FALLBACK[$opts['period']]) ||
++$i > 2 // Stop at 2nd fallback (i.e. 12h > 7d > 30d)
) {
break;
}
$period = $opts['period'];
$from = $now - $periodsInSecs[$period];
$opts['from_timestamp'] = $from * 1000;
$opts['period'] = static::PERIOD_FALLBACK[$period];
if (!$fallbackAt) {
$fallbackAt = $from;
}
}
if (!$sync) {
// Remove all unlisted content, if ES document is not in sync, it'll
// also remove pending activities
$result = $result->filter([$entities, 'filter']);
$entities = $entities->filter([$elasticEntities, 'filter']);
if ($asActivities) {
// Cast to ephemeral Activity entities, if another type
$result = $result->map([$entities, 'cast']);
$entities = $entities->map([$elasticEntities, 'cast']);
}
}
return Factory::response([
'status' => 'success',
'entities' => Exportable::_($result),
'entities' => Exportable::_($entities),
'fallback_at' => $fallbackAt,
'load-next' => $limit + $offset,
]);
} catch (\Exception $e) {
......
......@@ -108,11 +108,11 @@ class container implements Interfaces\Api
$custom_type = isset($_GET['custom_type']) && $_GET['custom_type'] ? [$_GET['custom_type']] : null;
/** @var Core\Feeds\Top\Manager $manager */
$manager = Di::_()->get('Feeds\Top\Manager');
/** @var Core\Feeds\Elastic\Manager $manager */
$manager = Di::_()->get('Feeds\Elastic\Manager');
/** @var Core\Feeds\Top\Entities $entities */
$entities = new Core\Feeds\Top\Entities();
/** @var Core\Feeds\Elastic\Entities $entities */
$entities = new Core\Feeds\Elastic\Entities();
$entities->setActor($currentUser);
$isOwner = false;
......
......@@ -123,11 +123,11 @@ class scheduled implements Interfaces\Api
$custom_type = isset($_GET['custom_type']) && $_GET['custom_type'] ? [$_GET['custom_type']] : null;
/** @var Core\Feeds\Top\Manager $manager */
$manager = Di::_()->get('Feeds\Top\Manager');
/** @var Core\Feeds\Elastic\Manager $manager */
$manager = Di::_()->get('Feeds\Elastic\Manager');
/** @var Core\Feeds\Top\Entities $entities */
$entities = new Core\Feeds\Top\Entities();
/** @var Core\Feeds\Elastic\Entities $entities */
$entities = new Core\Feeds\Elastic\Entities();
$entities->setActor($currentUser);
$isOwner = false;
......@@ -151,7 +151,7 @@ class scheduled implements Interfaces\Api
'query' => $query,
'single_owner_threshold' => 0,
'pinned_guids' => $type === 'activity' ? array_reverse($container->getPinnedPosts()) : null,
'time_created_upper' => false,
'future' => true,
'owner_guid' => $currentUser->guid,
];
......
......@@ -85,11 +85,11 @@ class subscribed implements Interfaces\Api
$custom_type = isset($_GET['custom_type']) && $_GET['custom_type'] ? [$_GET['custom_type']] : null;
/** @var Core\Feeds\Top\Manager $manager */
$manager = Di::_()->get('Feeds\Top\Manager');
/** @var Core\Feeds\Elastic\Manager $manager */
$manager = Di::_()->get('Feeds\Elastic\Manager');
/** @var Core\Feeds\Top\Entities $entities */
$entities = new Core\Feeds\Top\Entities();
/** @var Core\Feeds\Elastic\Entities $entities */
$entities = new Core\Feeds\Elastic\Entities();
$entities->setActor($currentUser);
$opts = [
......
......@@ -111,8 +111,8 @@ class content implements Interfaces\Api
$query = $_GET['query'];
}
/** @var Core\Feeds\Top\Entities $entities */
$entities = new Core\Feeds\Top\Entities();
/** @var Core\Feeds\Elastic\Entities $entities */
$entities = new Core\Feeds\Elastic\Entities();
$entities->setActor($currentUser);
$isOwner = false;
......@@ -181,7 +181,7 @@ class content implements Interfaces\Api
}
/**
* @param Core\Feeds\Top\Entities $entities
* @param Core\Feeds\Elastic\Entities $entities
* @param array $opts
* @param bool $asActivities
* @param bool $sync
......@@ -191,8 +191,8 @@ class content implements Interfaces\Api
private function getData($entities, $opts, $asActivities, $sync)
{
/** @var Core\Feeds\Top\Manager $manager */
$manager = Di::_()->get('Feeds\Top\Manager');
/** @var Core\Feeds\Elastic\Manager $manager */
$manager = Di::_()->get('Feeds\Elastic\Manager');
$result = $manager->getList($opts);
if (!$sync) {
......
......@@ -502,6 +502,7 @@ class Blog extends RepositoryEntity
}
}
$this->nsfw = $array;
$this->markAsDirty('nsfw');
return $this;
}
......
......@@ -60,6 +60,7 @@ class CreateActivity
->setThumbnail($blog->getIconUrl())
->setFromEntity($blog)
->setMature($blog->isMature())
->setNsfw($blog->getNsfw())
->setOwner($owner->export())
->setWireThreshold($blog->getWireThreshold())
->setPaywall($blog->isPaywall());
......
......@@ -138,6 +138,7 @@ class Manager
}
$this->paywallReview->queue($blog);
$this->propagateProperties->from($blog);
}
return $saved;
......
<?php
/**
* @author edgebal
*/
namespace Minds\Core;
use Minds\Common\StaticToInstance;
use Minds\Helpers\Counters as CountersHelper;
use ReflectionException;
/**
* Class Counters
* @package Minds\Core
* @method increment($entity, $metric, $value = 1, $client = null)
* @method decrement($entity, $metric, $value = 1, $client = null)
* @method incrementBatch($entities, $metric, $value = 1, $client = null)
* @method get($entity, $metric, $cache = true, $client = null)
* @method clear($entity, $metric, $value = 0, $client = null)
*/
class Counters extends StaticToInstance
{
/**
* Counters constructor.
* @throws ReflectionException
*/
public function __construct()
{
parent::__construct(new CountersHelper());
}
}
......@@ -10,7 +10,7 @@ namespace Minds\Core\Entities\Delegates;
use Minds\Common\Urn;
use Minds\Core\Di\Di;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Feeds\Top\Entities as TopEntities;
use Minds\Core\Feeds\Elastic\Entities as TopEntities;
class EntityGuidResolverDelegate implements ResolverDelegate
{
......
......@@ -23,10 +23,11 @@ class Manager
/** @var Cookie $cookie */
private $cookie;
public function __construct($config = null, $cookie = null)
public function __construct($config = null, $cookie = null, $user = null)
{
$this->config = $config ?: Di::_()->get('Config');
$this->cookie = $cookie ?: new Cookie;
$this->user = $user ?? Session::getLoggedInUser();
}
/**
......@@ -59,6 +60,10 @@ class Manager
return true;
}
if ($features[$feature] === 'canary' && $this->user && $this->user->get('canary')) {
return true;
}
return $features[$feature] === true;
}
......
......@@ -5,7 +5,7 @@
* @author: Emiliano Balbuena <edgebal>
*/
namespace Minds\Core\Feeds\Top;
namespace Minds\Core\Feeds\Elastic;
use Minds\Core\Blogs\Blog;
use Minds\Core\Di\Di;
......