...
 
Commits (19)
......@@ -18,11 +18,12 @@ class Transcode extends Cli\Controller implements Interfaces\CliControllerInterf
{
$this->out('TBD');
}
public function exec()
{
$transcoder = new Core\Media\Services\FFMpeg;
$transcoder->setKey($this->getOpt('guid'));
$transcoder->transcode();
$transcoder->setFullHD($this->getOpt('full_hd') ?? false);
$transcoder->onQueue();
}
}
......@@ -36,6 +36,13 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
return Factory::response(['status' => 'error']);
}
if (!in_array($this->getType($entity), ['object:video', 'object:image'], true)) {
return Factory::response([
'status' => 'error',
'message' => 'Entity is not a media entity',
]);
}
switch ($entity->subtype) {
case "video":
// Helpers\Counters::increment($pages[0], 'plays');
......@@ -43,10 +50,12 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
if (isset($pages[1]) && $pages[1] == 'play') {
http_response_code(302);
$res = !empty($_GET['res']) && in_array($_GET['res'], ['360', '720', '1080'], true) ?$_GET['res'] : '360';
if ($entity->subtype == 'audio') {
\forward($entity->getSourceUrl('128.mp3'));
} else {
\forward($entity->getSourceUrl('360.mp4'));
\forward($entity->getSourceUrl("{$res}.mp4"));
}
exit;
......@@ -58,8 +67,11 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
$response = $entities[0];
$response['transcodes'] = [
'360.mp4' => $entity->getSourceUrl('360.mp4'),
'720.mp4' => $entity->getSourceUrl('720.mp4')
'720.mp4' => $entity->getSourceUrl('720.mp4'),
];
if ($entity->getFlag('full_hd')) {
$response['transcodes']['1080.mp4'] = $entity->getSourceUrl('1080.mp4');
}
}
if (method_exists($entity, 'getWireThreshold')) {
......@@ -158,6 +170,7 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
$body = $req['body'];
fwrite($fp, $body);
$video->access_id = 0;
$video->patch(['full_hd', Core\Session::getLoggedinUser()->isPro()]);
$video->upload($tmpFilename);
$guid = $video->save();
fclose($fp);
......@@ -258,7 +271,8 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
'access_id' => 0,
'owner_guid' => $user->guid,
'hidden' => $container_guid !== null,
'container_guid' => $container_guid
'container_guid' => $container_guid,
'full_hd' => $user->isPro(),
]);
$assets = Core\Media\AssetsFactory::build($entity);
......@@ -267,7 +281,7 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
$entity->setAssets($assets->upload($media, $data));
// Save initial entity
$success = $save
->setEntity($entity)
->save(true);
......@@ -356,4 +370,9 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
return $response;
}
private function getType($entity): string
{
return $entity->subtype ? "{$entity->type}:{$entity->subtype}" : $entity->type;
}
}
......@@ -358,7 +358,8 @@ class newsfeed implements Interfaces\Api
->setCustom('video', [
'thumbnail_src' => $embeded->getIconUrl(),
'guid' => $embeded->guid,
'mature' => $embeded instanceof Flaggable ? $embeded->getFlag('mature') : false
'mature' => $embeded instanceof Flaggable ? $embeded->getFlag('mature') : false,
'full_hd' => $embeded->getFlag('full_hd') ?? false,
])
->setTitle($embeded->title)
->setBlurb($embeded->description)
......@@ -525,7 +526,7 @@ class newsfeed implements Interfaces\Api
]);
}
}
$save->setEntity($activity)
->save();
......
......@@ -10,6 +10,8 @@ namespace Minds\Controllers\api\v2\media;
use Minds\Api\Factory;
use Minds\Core\Di\Di;
use Minds\Core\Media\ClientUpload\Manager;
use Minds\Core\Session;
use Minds\Interfaces;
use Minds\Core\Media\ClientUpload\ClientUploadLease;
......@@ -38,6 +40,7 @@ class upload implements Interfaces\Api
*/
public function put($pages)
{
/** @var Manager $manager */
$manager = Di::_()->get("Media\ClientUpload\Manager");
switch ($pages[0]) {
case 'prepare':
......@@ -55,7 +58,9 @@ class upload implements Interfaces\Api
$lease->setGuid($guid)
->setMediaType($mediaType);
$manager->complete($lease);
$manager
->setFullHD(Session::getLoggedinUser()->isPro())
->complete($lease);
break;
}
return Factory::response([]);
......
......@@ -8,6 +8,7 @@ namespace Minds\Controllers\api\v2\pro;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Domain as ProDomain;
use Minds\Core\Pro\Manager;
use Minds\Core\Session;
use Minds\Entities\User;
......@@ -81,6 +82,18 @@ class settings implements Interfaces\Api
]);
}
if (isset($_POST['domain'])) {
/** @var ProDomain $proDomain */
$proDomain = Di::_()->get('Pro\Domain');
if (!$proDomain->isAvailable($_POST['domain'], (string) $user->guid)) {
return Factory::response([
'status' => 'error',
'message' => 'This domain is taken',
]);
}
}
try {
$success = $manager->set($_POST);
......
<?php
/**
* settings assets
* @author edgebal
*/
namespace Minds\Controllers\api\v2\pro\settings;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Manager;
use Minds\Core\Pro\Assets\Manager as AssetsManager;
use Minds\Core\Session;
use Minds\Entities\User;
use Minds\Interfaces;
use Minds\Api\Factory;
use Zend\Diactoros\ServerRequest;
class assets implements Interfaces\Api
{
/** @var ServerRequest */
public $request;
/**
* 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
* @throws Exception
*/
public function post($pages)
{
$type = $pages[0] ?? null;
// Check and validate user
$user = Session::getLoggedinUser();
if (isset($pages[1]) && $pages[1]) {
if (!Session::isAdmin()) {
return Factory::response([
'status' => 'error',
'message' => 'You are not authorized',
]);
}
$user = new User($pages[1]);
}
// Check uploaded file
/** @var \Zend\Diactoros\UploadedFile[] $files */
$files = $this->request->getUploadedFiles();
if (!$files || !isset($files['file'])) {
return Factory::response([
'status' => 'error',
'message' => 'Missing file',
]);
}
$file = $files['file'];
if ($file->getError()) {
return Factory::response([
'status' => 'error',
'message' => sprintf('Error %s when uploading file', $files['file']->getError()),
]);
}
// Get Pro managers
/** @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',
]);
}
/** @var AssetsManager $assetsManager */
$assetsManager = Di::_()->get('Pro\Assets\Manager');
$assetsManager
->setType($type)
->setUser($user)
->setActor(Session::getLoggedinUser());
try {
$success = $assetsManager
->set($file);
if (!$success) {
throw new Exception(sprintf("Cannot save Pro %s asset", $type));
}
} 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([]);
}
}
<?php
/**
* domain
* @author edgebal
*/
namespace Minds\Controllers\api\v2\pro\settings;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Domain as ProDomain;
use Minds\Core\Session;
use Minds\Entities\User;
use Minds\Interfaces;
use Minds\Api\Factory;
class domain implements Interfaces\Api
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
* @throws Exception
*/
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 ProDomain $proDomain */
$proDomain = Di::_()->get('Pro\Domain');
return Factory::response([
'isValid' => $proDomain->isAvailable($_GET['domain'], (string) $user->guid)
]);
}
/**
* 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
namespace Minds\Controllers\Api\v2\settings;
namespace Minds\Controllers\api\v2\settings;
use Minds\Api\Factory;
use Minds\Core;
......
<?php
namespace Minds\Controllers\Api\v2\settings;
namespace Minds\Controllers\api\v2\settings;
use Minds\Api\Factory;
use Minds\Core;
......
<?php
/**
* pro
* @author edgebal
*/
namespace Minds\Controllers\fs\v1;
use Minds\Core\Pro\Assets\Asset;
use Minds\Interfaces;
class pro implements Interfaces\FS
{
/**
* Equivalent to HTTP GET method
* @param array $pages
* @return mixed|null
* @throws \IOException
* @throws \InvalidParameterException
* @throws \Exception
*/
public function get($pages)
{
$asset = new Asset();
$asset
->setType($pages[1] ?? null)
->setUserGuid($pages[0] ?? null);
$file = $asset->getFile();
$file->open('read');
$contents = $file->read();
header(sprintf("Content-Type: %s", $asset->getMimeType()));
header(sprintf("Expires: %s", date('r', time() + 864000)));
header('Pragma: public');
header('Cache-Control: public');
echo $contents;
exit;
}
}
<?php
/**
* Engagement Dashboard
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Entities\User;
use Minds\Traits\MagicAttributes;
/**
* @method EngagementDashboard setTimespanId(string $timespanId)
* @method EngagementDashboard setFilterIds(array $filtersIds)
* @method EngagementDashboard setUser(User $user)
*/
class EngagementDashboard implements DashboardInterface
{
use MagicAttributes;
/** @var string */
private $timespanId = '30d';
/** @var string[] */
private $filterIds = [ 'platform::browser' ];
/** @var string */
private $metricId = 'votes_up';
/** @var Timespans\TimespansCollection */
private $timespansCollection;
/** @var Metrics\MetricsCollection */
private $metricsCollection;
/** @var Filters\FiltersCollection */
private $filtersCollection;
/** @var User */
private $user;
public function __construct(
$timespansCollection = null,
$metricsCollection = null,
$filtersCollection = null
) {
$this->timespansCollection = $timespansCollection ?? new Timespans\TimespansCollection();
$this->metricsCollection = $metricsCollection ?? new Metrics\MetricsCollection();
$this->filtersCollection = $filtersCollection ?? new Filters\FiltersCollection();
}
/**
* Build the dashboard
* @return self
*/
public function build(): self
{
$this->timespansCollection
->setSelectedId($this->timespanId)
->addTimespans(
new Timespans\TodayTimespan(),
new Timespans\_30dTimespan(),
new Timespans\_1yTimespan(),
new Timespans\MtdTimespan(),
new Timespans\YtdTimespan()
);
$this->filtersCollection
->setSelectedIds($this->filterIds)
->setUser($this->user)
->addFilters(
new Filters\ChannelFilter()
);
$this->metricsCollection
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->setUser($this->user)
->addMetrics(
new Metrics\Engagement\VotesUpMetric(),
new Metrics\Engagement\CommentsMetric(),
new Metrics\Engagement\RemindsMetric(),
new Metrics\Engagement\SubscribersMetric()
)
->build();
return $this;
}
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
$this->build();
return [
'category' => 'engagement',
'label' => 'Engagement',
'description' => '',
'timespan' => $this->timespansCollection->getSelected()->getId(),
'timespans' => $this->timespansCollection->export(),
'metric' => $this->metricsCollection->getSelected()->getId(),
'metrics' => $this->metricsCollection->export(),
'filter' => $this->filtersCollection->getSelectedIds(),
'filters' => $this->filtersCollection->export(),
];
}
}
......@@ -7,6 +7,7 @@ class Manager
'traffic' => TrafficDashboard::class,
'trending' => TrendingDashboard::class,
'earnings' => EarningsDashboard::class,
'engagement' => EngagementDashboard::class,
];
/**
......
......@@ -39,7 +39,7 @@ class ActiveUsersMetric extends AbstractMetric
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$comparisonTsMs = strtotime("-{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$comparisonTsMs = strtotime("midnight -{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$currentTsMs = $timespan->getFromTsMs();
// Field name to use for the aggregation
......@@ -76,10 +76,14 @@ class ActiveUsersMetric extends AbstractMetric
],
];
$must[]['exists'] = [
'field' => 'active::total',
];
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lte' => strtotime("midnight +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
'lt' => strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
],
];
......@@ -145,6 +149,10 @@ class ActiveUsersMetric extends AbstractMetric
'entity_urn' => 'urn:metric:global'
];
$must[]['exists'] = [
'field' => 'active::total',
];
// Specify the resolution to avoid duplicates
$must[] = [
'term' => [
......
......@@ -54,7 +54,7 @@ abstract class AbstractEarningsMetric extends AbstractMetric
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lte' => strtotime("midnight +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
'lt' => strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
],
];
......@@ -66,6 +66,12 @@ abstract class AbstractEarningsMetric extends AbstractMetric
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
......@@ -127,6 +133,12 @@ abstract class AbstractEarningsMetric extends AbstractMetric
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
......
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
abstract class AbstractEngagementMetric extends AbstractMetric
{
/** @var Elasticsearch\Client */
private $es;
/** @var string */
protected $id = '';
/** @var string */
protected $label = '';
/** @var string */
protected $description = '';
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $unit = 'number';
/** @var string */
protected $aggField = '';
public function __construct($es = null)
{
$this->es = $es ?? Di::_()->get('Database\ElasticSearch');
}
/**
* Build the metrics
* @return self
*/
public function buildSummary(): self
{
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$comparisonTsMs = strtotime("midnight -{$timespan->getComparisonInterval()} days", $timespan->getFromTsMs() / 1000) * 1000;
$currentTsMs = $timespan->getFromTsMs();
$values = [];
foreach ([ 'value' => $currentTsMs, 'comparison' => $comparisonTsMs ] as $key => $tsMs) {
$must = [];
$maxTs = strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000);
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lt' => $maxTs * 1000,
],
];
if ($userGuid = $this->getUserGuid()) {
$must[] = [
'term' => [
'owner_guid' => $userGuid,
],
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
$indexes = implode(',', [
'minds-entitycentric-' . date('m-Y', $tsMs / 1000),
'minds-entitycentric-' . date('m-Y', $maxTs),
]);
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'sum' => [
'field' => $this->aggField,
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
$values[$key] = $response['aggregations']['1']['value'];
}
$this->summary = new MetricSummary();
$this->summary
->setValue($values['value'])
->setComparisonValue($values['comparison'])
->setComparisonInterval($timespan->getComparisonInterval())
->setComparisonPositivity(true);
return $this;
}
/**
* Build a visualisation for the metric
* @return self
*/
public function buildVisualisation(): self
{
$timespan = $this->timespansCollection->getSelected();
$filters = $this->filtersCollection->getSelected();
$must = [];
// Range must be from previous period
$must[]['range'] = [
'@timestamp' => [
'gte' => $timespan->getFromTsMs(),
],
];
if ($userGuid = $this->getUserGuid()) {
$must[] = [
'term' => [
'owner_guid' => $userGuid,
],
];
}
$must[] = [
'exists' => [
'field' => $this->aggField,
],
];
// Do the query
$query = [
'index' => 'minds-entitycentric-*',
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'date_histogram' => [
'field' => '@timestamp',
'interval' => $timespan->getInterval(),
'min_doc_count' => 0,
'extended_bounds' => [
'min' => $timespan->getFromTsMs(),
'max' => time() * 1000,
],
],
'aggs' => [
'2' => [
'sum' => [
'field' => $this->aggField,
],
],
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
$buckets = [];
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
$date = date(Visualisations\ChartVisualisation::DATE_FORMAT, $bucket['key'] / 1000);
$buckets[] = [
'key' => $bucket['key'],
'date' => date('c', $bucket['key'] / 1000),
'value' => $bucket['2']['value']
];
}
$this->visualisation = (new Visualisations\ChartVisualisation())
->setXLabel('Date')
->setYLabel('Count')
->setBuckets($buckets);
return $this;
}
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class CommentsMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'comments';
/** @var string */
protected $label = 'Comments';
/** @var string */
protected $description = "Number of comments you have received on your content";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'comment::total';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class RemindsMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'reminds';
/** @var string */
protected $label = 'Reminds';
/** @var string */
protected $description = "Number of reminds you have received on your content";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'remind::total';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class SubscribersMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'subscribers';
/** @var string */
protected $label = 'Subscribes';
/** @var string */
protected $description = "Number of subscribers your channel has gained";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'subscribe::total';
}
<?php
namespace Minds\Core\Analytics\Dashboards\Metrics\Engagement;
use Minds\Core\Di\Di;
use Minds\Core\Session;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Analytics\Dashboards\Metrics\AbstractMetric;
use Minds\Core\Analytics\Dashboards\Metrics\MetricSummary;
use Minds\Core\Analytics\Dashboards\Metrics\Visualisations;
class VotesUpMetric extends AbstractEngagementMetric
{
/** @var string */
protected $id = 'votes_up';
/** @var string */
protected $label = 'Votes up';
/** @var string */
protected $description = "Number of votes up you have received on your content";
/** @var array */
protected $permissions = [ 'user', 'admin' ];
/** @var string */
protected $aggField = 'vote:up::total';
}
......@@ -54,7 +54,7 @@ class SignupsMetric extends AbstractMetric
$must[]['range'] = [
'@timestamp' => [
'gte' => $tsMs,
'lte' => strtotime("midnight +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
'lt' => strtotime("midnight tomorrow +{$timespan->getComparisonInterval()} days", $tsMs / 1000) * 1000,
],
];
......
......@@ -151,7 +151,7 @@ class ViewsTableMetric extends AbstractMetric
->setColumns([
[
'id' => 'entity',
'label' => '',
'label' => 'Content',
'order' => 0,
],
[
......
......@@ -16,7 +16,7 @@ class MtdTimespan extends AbstractTimespan
protected $fromTsMs;
/** @var int */
protected $comparisonInterval = 28;
protected $comparisonInterval = 30;
public function __construct()
{
......
......@@ -16,7 +16,7 @@ class _30dTimespan extends AbstractTimespan
protected $fromTsMs;
/** @var int */
protected $comparisonInterval = 28;
protected $comparisonInterval = 30;
public function __construct()
{
......
<?php
namespace Minds\Core\Analytics\EntityCentric;
use Minds\Core\Data\ElasticSearch;
use Minds\Core\Di\Di;
use DateTime;
use Exception;
class EngagementSynchroniser
{
/** @var array */
private $records = [];
/** @var ElasticSearch\Client */
private $es;
public function __construct($es = null)
{
$this->es = $es ?? Di::_()->get('Database\ElasticSearch');
}
/**
* @param int $from
* @return self
*/
public function setFrom($from): self
{
$this->from = $from;
return $this;
}
/**
* Convert to records
* @return iterable
*/
public function toRecords(): iterable
{
$date = (new DateTime())->setTimestamp($this->from);
$now = new DateTime();
$days = (int) $date->diff($now)->format('%a');
$months = round($days / 28);
$i = 0;
foreach ($this->getEntitiesMetrics() as $buckets) {
$urn = null;
$ownerGuid = null;
if (!$buckets['type']['buckets'][0]['key'] && $buckets['metrics']['buckets'][0]['key'] === 'subscribe') {
$urn = "urn:user:{$buckets['key']}";
$ownerGuid = (string) $buckets['key'];
} elseif (!$buckets['type']['buckets'][0]['key']) {
echo "\nEngagement: skipping as no type";
continue;
} else {
$urn = "urn:{$buckets['type']['buckets'][0]['key']}:{$buckets['key']}";
$ownerGuid = (string) $buckets['owner']['buckets'][0]['key'];
if ($buckets['type']['buckets'][0]['key'] === 'object') {
$urn = "urn:{$buckets['subtype']['buckets'][0]['key']}:{$buckets['key']}";
}
}
$record = new EntityCentricRecord();
$record->setEntityUrn($urn)
->setOwnerGuid($ownerGuid)
->setTimestamp($this->from)
->setResolution('day');
foreach ($buckets['metrics']['buckets'] as $metrics) {
$record->incrementSum($metrics['key'] . '::total', (int) $metrics['doc_count']);
}
$this->records[] = $record;
++$i;
error_log("Engagement: $i");
}
foreach ($this->records as $record) {
yield $record;
}
}
private function getEntitiesMetrics()
{
$opts = array_merge([
'fields' => [],
'from' => time(),
], []);
$must = [];
// $must[] = [
// 'term' => [
// 'action.keyword' => 'subscribe',
// ],
//];
$must[] = [
'range' => [
'@timestamp' => [
'gte' => $this->from * 1000,
'lt' => strtotime('+1 day', $this->from) * 1000,
],
],
];
$partition = 0;
$partitions = 50;
$partitionSize = 5000; // Allows for 250,000 entities
$index = 'minds-metrics-' . date('m-Y', $this->from);
while (++$partition < $partitions) {
// Do the query
$query = [
'index' => $index,
'size' => 0,
'body' => [
'query' => [
'bool' => [
'must' => $must,
],
],
'aggs' => [
'1' => [
'terms' => [
'field' => 'entity_guid.keyword',
'min_doc_count' => 1,
'size' => $partitionSize,
'include' => [
'partition' => $partition,
'num_partitions' => $partitions,
],
],
'aggs' => [
'metrics' => [
'terms' => [
'field' => 'action.keyword',
'min_doc_count' => 1,
],
],
'owner' => [
'terms' => [
'field' => 'entity_owner_guid.keyword',
'min_doc_count' => 1,
],
],
'type' => [
'terms' => [
'field' => 'entity_type.keyword',
],
],
'subtype' => [
'terms' => [
'field' => 'entity_subtype.keyword',
]
],
],
],
],
],
];
// Query elasticsearch
$prepared = new ElasticSearch\Prepared\Search();
$prepared->query($query);
$response = $this->es->request($prepared);
foreach ($response['aggregations']['1']['buckets'] as $bucket) {
yield $bucket;
}
}
}
}
......@@ -13,6 +13,7 @@ class Manager
{
/** @var array */
const SYNCHRONISERS = [
EngagementSynchroniser::class,
PartnerEarningsSynchroniser::class,
SignupsSynchroniser::class,
ActiveUsersSynchroniser::class,
......
......@@ -5,6 +5,7 @@ namespace Minds\Core\Blogs;
use Minds\Core\Di\Di;
use Minds\Core\Events\Event;
use Minds\Core\Events\EventsDispatcher;
use Minds\Core\Session;
class Events
{
......@@ -37,5 +38,29 @@ class Events
$manager = Di::_()->get('Blogs\Manager');
$event->setResponse($manager->update($blog));
});
$this->eventsDispatcher->register('export:extender', 'blog', function (Event $event) {
$params = $event->getParameters();
/** @var Core\Blogs\Blog $blog */
$blog = $params['entity'];
$export = $event->response() ?: [];
$currentUser = Session::getLoggedInUserGuid();
$dirty = false;
if ($blog->isPaywall() && $blog->owner_guid != $currentUser) {
$export['description'] = '';
$export['body'] = '';
$dirty = true;
}
if ($dirty) {
return $event->setResponse($export);
}
if (!$currentUser) {
return;
}
});
}
}
......@@ -86,7 +86,7 @@ class Repository
$opts['parent_guid_l1'] = $parent_guids[0] ?? 0;
$opts['parent_guid_l2'] = $parent_guids[1] ?? 0;
$opts['parent_guid_l3'] = 0; //do not support l3 yet
$cql = "SELECT * from comments";
$values = [];
$cqlOpts = [];
......@@ -94,16 +94,25 @@ class Repository
$where = [];
if ($opts['entity_guid']) {
if (!is_numeric($opts['entity_guid'])) {
return new Response();
}
$where[] = 'entity_guid = ?';
$values[] = new Varint($opts['entity_guid']);
}
if ($opts['parent_guid_l1'] !== null) {
if (!is_numeric($opts['parent_guid_l1'])) {
return new Response();
}
$where[] = 'parent_guid_l1 = ?';
$values[] = new Varint((int) $opts['parent_guid_l1']);
}
if ($opts['parent_guid_l2'] !== null) {
if (!is_numeric($opts['parent_guid_l2'])) {
return new Response();
}
$where[] = 'parent_guid_l2 = ?';
$values[] = new Varint((int) $opts['parent_guid_l2']);
}
......
......@@ -12,15 +12,29 @@ use Minds\Entities\Video;
class Manager
{
/** @var FFMepg */
/** @var FFMpeg */
private $ffmpeg;
/** @var Guid $guid */
private $guid;
/** @var bool */
private $full_hd;
/** @var Save $save */
private $save;
/**
* @param bool $value
* @return Manager
*/
public function setFullHD(bool $value): Manager
{
$this->full_hd = $value;
return $this;
}
public function __construct(
FFMpeg $FFMpeg = null,
GuidBuilder $guid = null,
......@@ -70,12 +84,16 @@ class Manager
$video->set('guid', $lease->getGuid());
$video->set('cinemr_guid', $lease->getGuid());
$video->set('access_id', 0); // Hide until published
$video->setFlag('full_hd', $this->full_hd);
// Save the video
$this->save->setEntity($video)->save();
$this->ffmpeg->setKey($lease->getGuid());
// Set the full hd flag
$this->ffmpeg->setFullHD($this->full_hd);
// Start the transcoding process
$this->ffmpeg->transcode();
......
......@@ -49,6 +49,17 @@ class Manager
return $this->image->getImageBlob();
}
public function getPng()
{
if (!$this->image) {
throw new \Exception('Output was not generated');
}
$this->image->setImageFormat('png');
return $this->image->getImageBlob();
}
/**
* @param $value
* @return $this
......@@ -61,6 +72,14 @@ class Manager
return $this;
}
public function setImageFromBlob($blob, $fileName = null)
{
$this->image = new \Imagick();
$this->image->readImageBlob($blob, $fileName);
return $this;
}
/**
* @return $this
*/
......
......@@ -38,6 +38,9 @@ class FFMpeg implements ServiceInterface
/** @var string $dir */
private $dir = 'cinemr_data';
/** @var bool $full_hd */
private $full_hd = false;
public function __construct(
$queue = null,
$ffmpeg = null,
......@@ -71,6 +74,10 @@ class FFMpeg implements ServiceInterface
$this->dir = $this->config->get('transcoder')['dir'];
}
/**
* @param $key
* @return FFMpeg
*/
public function setKey($key)
{
$this->key = $key;
......@@ -78,6 +85,16 @@ class FFMpeg implements ServiceInterface
return $this;
}
/**
* @param bool $value
* @return FFMpeg
*/
public function setFullHD(bool $value)
{
$this->full_hd = $value;
return $this;
}
/**
* Create a PresignedUr for client based uploads
* @return string
......@@ -136,6 +153,7 @@ class FFMpeg implements ServiceInterface
->setQueue('Transcode')
->send([
'key' => $this->key,
'full_hd' => $this->full_hd,
]);
return $this;
......@@ -207,6 +225,10 @@ class FFMpeg implements ServiceInterface
'formats' => ['mp4', 'webm'],
], $opts);
if ($opts['pro'] && !$this->full_hd) {
continue;
}
if ($rotated) {
$ratio = $videostream->get('width') / $videostream->get('height');
$width = round($opts['height'] * $ratio);
......
<?php
namespace Minds\Core\Media;
use Minds\Core;
......@@ -23,8 +24,13 @@ class Thumbnails
$loggedInUser = Core\Session::getLoggedinUser();
if (!Di::_()->get('Wire\Thresholds')->isAllowed($loggedInUser, $entity)) {
return false;
try {
if (!Di::_()->get('Wire\Thresholds')->isAllowed($loggedInUser, $entity)) {
return false;
}
} catch (\Exception $e) {
error_log('[Core/Media/Thumbnails::get] ' . $e->getMessage());
// don't do anything if the entity cannot be paywalled
}
$user = $entity->getOwnerEntity(false);
......
<?php
/**
* Info
* @author edgebal
*/
namespace Minds\Core\Pro\Assets;
use ElggFile;
use Exception;
use Minds\Traits\MagicAttributes;
/**
* Class Asset
* @package Minds\Core\Pro\Assets
* @method string getType()
* @method int|string getUserGuid()
* @method Asset setUserGuid(int|string $userGuid)
*/
class Asset
{
use MagicAttributes;
/** @var string */
protected $type;
/** @var int|string */
protected $userGuid;
/** @var string[] */
const TYPES = ['logo', 'background'];
/**
* @param string $type
* @return Asset
* @throws Exception
*/
public function setType(string $type): Asset
{
if (!in_array($type, static::TYPES, true)) {
throw new Exception('Invalid Asset type');
}
$this->type = $type;
return $this;
}
/**
* @return string
* @throws Exception
*/
public function getExt(): string
{
switch ($this->type) {
case 'logo':
return 'png';
case 'background':
return 'jpg';
}
throw new Exception('Invalid Asset');
}
/**
* @return string
* @throws Exception
*/
public function getMimeType(): string
{
switch ($this->type) {
case 'logo':
return 'image/png';
case 'background':
return 'image/jpg';
}
throw new Exception('Invalid Asset');
}
/**
* @return ElggFile
* @throws Exception
*/
public function getFile(): ElggFile
{
$file = new ElggFile();
$file->owner_guid = $this->userGuid;
$file->setFilename(sprintf("pro/%s.%s", $this->type, $this->getExt()));
return $file;
}
}
<?php
/**
* Manager
* @author edgebal
*/
namespace Minds\Core\Pro\Assets;
use ElggFile;
use Exception;
use Minds\Core\Di\Di;
use Minds\Core\Media\Imagick\Manager as ImageManager;
use Minds\Entities\User;
use Zend\Diactoros\UploadedFile;
class Manager
{
/** @var ImageManager */
protected $imageManager;
/** @var string */
protected $type;
/** @var User */
protected $user;
/** @var User */
protected $actor;
/**
* Manager constructor.
* @param ImageManager $imageManager
*/
public function __construct(
$imageManager = null
) {
$this->imageManager = $imageManager ?: Di::_()->get('Media\Imagick\Manager');
}
/**
* @param string $type
* @return Manager
*/
public function setType(string $type): Manager
{
$this->type = $type;
return $this;
}
/**
* @param User $user
* @return Manager
*/
public function setUser(User $user): Manager
{
$this->user = $user;
return $this;
}
/**
* @param User $actor
* @return Manager
*/
public function setActor(User $actor): Manager
{
$this->actor = $actor;
return $this;
}
/**
* @param UploadedFile $file
* @param Asset|null $asset
* @return bool
* @throws Exception
*/
public function set(UploadedFile $file, Asset $asset = null)
{
if (!$this->user) {
throw new Exception('Invalid user');
} elseif (!$this->type || !in_array($this->type, Asset::TYPES, true)) {
throw new Exception('Invalid asset type');
}
// Load image
$this->imageManager
->setImageFromBlob(
$file->getStream()->getContents(),
$file->getClientFilename()
);
// Setup asset
if (!$asset) {
$asset = new Asset();
}
$asset
->setType($this->type)
->setUserGuid($this->user->guid);
// Handle asset type
switch ($this->type) {
case 'logo':
$blob = $this->imageManager
->resize(1920, 1080, false, false) // Max: 2K
->getPng();
break;
case 'background':
$blob = $this->imageManager
->autorotate()
->resize(3840, 2160, false, false) // Max: 4K
->getJpeg(85);
break;
default:
throw new Exception('Invalid asset type handler');
}
$file = $asset->getFile();
$file->open('write');
$file->write($blob);
$file->close();
return true;
}
}
......@@ -43,10 +43,11 @@ class HydrateSettingsDelegate
public function onGet(User $user, Settings $settings): Settings
{
try {
$logoImage = $settings->getLogoGuid() ? sprintf(
'%sfs/v1/thumbnail/%s/master',
$logoImage = $settings->hasCustomLogo() ? sprintf(
'%sfs/v1/pro/%s/logo/%s',
$this->config->get('cdn_url'),
$settings->getLogoGuid()
$settings->getUserGuid(),
$settings->getTimeUpdated()
) : $user->getIconURL('large');
if ($logoImage) {
......@@ -58,17 +59,32 @@ class HydrateSettingsDelegate
}
try {
$carousels = $this->entitiesBuilder->get(['subtype' => 'carousel', 'owner_guid' => (string) $user->guid]);
$carousel = $carousels[0] ?? null;
$backgroundImage = null;
if ($carousel) {
$settings
->setBackgroundImage(sprintf(
if ($settings->hasCustomBackground()) {
$backgroundImage = sprintf(
'%sfs/v1/pro/%s/background/%s',
$this->config->get('cdn_url'),
$settings->getUserGuid(),
$settings->getTimeUpdated()
);
} else {
$carousels = $this->entitiesBuilder->get(['subtype' => 'carousel', 'owner_guid' => (string) $user->guid]);
$carousel = $carousels[0] ?? null;
if ($carousel) {
$backgroundImage = sprintf(
'%sfs/v1/banners/%s/fat/%s',
$this->config->get('cdn_url'),
$carousel->guid,
$carousel->last_updated
));
);
}
}
if ($backgroundImage) {
$settings
->setBackgroundImage($backgroundImage);
}
} catch (\Exception $e) {
error_log($e);
......
......@@ -9,6 +9,7 @@ namespace Minds\Core\Pro;
use Exception;
use Minds\Core\Config;
use Minds\Core\Di\Di;
use Minds\Core\Util\StringValidator;
use Minds\Entities\User;
class Domain
......@@ -49,6 +50,27 @@ class Domain
])->first();
}
/**
* @param string $domain
* @param string $userGuid
* @return bool|null
*/
public function isAvailable(string $domain, string $userGuid): ?bool
{
$rootDomains = $this->config->get('pro')['root_domains'] ?? [];
if (in_array(strtolower($domain), $rootDomains, true)) {
return false;
}
if (!StringValidator::isDomain($domain)) {
return null;
}
$settings = $this->lookup($domain);
return !$settings || ((string) $settings->getUserGuid() === $userGuid);
}
/**
* @param Settings $settings
* @param User|null $owner
......
......@@ -258,18 +258,6 @@ class Manager
->setTileRatio($values['tile_ratio']);
}
if (isset($values['logo_guid']) && $values['logo_guid'] !== '') {
$image = $this->entitiesBuilder->single($values['logo_guid']);
// if the image doesn't exist or the guid doesn't correspond to an image
if (!$image || ($image->type !== 'object' || $image->subtype !== 'image')) {
throw new \Exception('logo_guid must be a valid image guid');
}
$settings
->setLogoGuid(trim($values['logo_guid']));
}
if (isset($values['footer_text'])) {
$footer_text = trim($values['footer_text']);
......@@ -322,6 +310,16 @@ class Manager
->setCustomHead($values['custom_head']);
}
if (isset($values['has_custom_logo'])) {
$settings
->setHasCustomLogo((bool) $values['has_custom_logo']);
}
if (isset($values['has_custom_background'])) {
$settings
->setHasCustomBackground((bool) $values['has_custom_background']);
}
if (isset($values['published'])) {
$this->user->setProPublished($values['published']);
$this->saveAction
......@@ -329,6 +327,8 @@ class Manager
->save();
}
$settings->setTimeUpdated(time());
$this->setupRoutingDelegate
->onUpdate($settings);
......
......@@ -39,5 +39,9 @@ class ProProvider extends Provider
$this->di->bind('Pro\Channel\Manager', function ($di) {
return new Channel\Manager();
}, ['useFactory' => true]);
$this->di->bind('Pro\Assets\Manager', function ($di) {
return new Assets\Manager();
}, ['useFactory' => true]);
}
}
......@@ -92,13 +92,15 @@ class Repository
->setTextColor($data['text_color'] ?? '')
->setPrimaryColor($data['primary_color'] ?? '')
->setPlainBackgroundColor($data['plain_background_color'] ?? '')
->setLogoGuid($data['logo_guid'] ?? '')
->setTileRatio($data['tile_ratio'] ?? '')
->setFooterText($data['footer_text'] ?? '')
->setFooterLinks($data['footer_links'] ?? [])
->setTagList($data['tag_list'] ?? [])
->setScheme($data['scheme'] ?? '')
->setCustomHead($data['custom_head'] ?? '')
->setHasCustomLogo($data['has_custom_logo'] ?? false)
->setHasCustomBackground($data['has_custom_background'] ?? false)
->setTimeUpdated($data['time_updated'] ?? 0)
;
$response[] = $settings;
......@@ -140,12 +142,14 @@ class Repository
'primary_color' => $settings->getPrimaryColor(),
'plain_background_color' => $settings->getPlainBackgroundColor(),
'tile_ratio' => $settings->getTileRatio(),
'logo_guid' => $settings->getLogoGuid(),
'footer_text' => $settings->getFooterText(),
'footer_links' => $settings->getFooterLinks(),
'tag_list' => $settings->getTagList(),
'scheme' => $settings->getScheme(),
'custom_head' => $settings->getCustomHead(),
'has_custom_logo' => $settings->hasCustomLogo(),
'has_custom_background' => $settings->hasCustomBackground(),
'time_updated' => $settings->getTimeUpdated(),
]),
];
......
......@@ -28,8 +28,6 @@ use Minds\Traits\MagicAttributes;
* @method Settings setPlainBackgroundColor(string $plainBackgroundColor)
* @method string getTileRatio()
* @method Settings setTileRatio(string $tileRatio)
* @method int|string getLogoGuid()
* @method Settings setLogoGuid(int|string $logoGuid)
* @method string getFooterText()
* @method Settings setFooterText(string $footerText)
* @method array getFooterLinks()
......@@ -48,6 +46,12 @@ use Minds\Traits\MagicAttributes;
* @method Settings setCustomHead(string $customHead)
* @method bool isPublished()
* @method Settings setPublished(bool $published)
* @method bool hasCustomLogo()
* @method Settings setHasCustomLogo(bool $customLogo)
* @method bool hasCustomBackground()
* @method Settings setHasCustomBackground(bool $customBackground)
* @method int getTimeUpdated()
* @method Settings setTimeUpdated(int $timeUpdated)
*/
class Settings implements JsonSerializable
{
......@@ -92,14 +96,17 @@ class Settings implements JsonSerializable
/** @var string */
protected $plainBackgroundColor;
/** @var int */
protected $logoGuid;
/** @var string */
protected $tileRatio = '16:9';
/** @var bool */
protected $hasCustomBackground;
/** @var string */
protected $backgroundImage;
/** @var string */
protected $tileRatio = '16:9';
/** @var bool */
protected $hasCustomLogo;
/** @var string */
protected $logoImage;
......@@ -125,6 +132,9 @@ class Settings implements JsonSerializable
/** @var bool */
protected $published;
/** @var int */
protected $timeUpdated;
/**
* @return string
*/
......@@ -155,15 +165,17 @@ class Settings implements JsonSerializable
'footer_text' => $this->footerText,
'footer_links' => $this->footerLinks,
'tag_list' => $this->tagList,
'logo_guid' => (string) $this->logoGuid,
'background_image' => $this->backgroundImage,
'has_custom_logo' => $this->hasCustomLogo,
'logo_image' => $this->logoImage,
'has_custom_background' => $this->hasCustomBackground,
'background_image' => $this->backgroundImage,
'featured_content' => $this->featuredContent,
'scheme' => $this->scheme,
'custom_head' => $this->customHead,
'one_line_headline' => $this->getOneLineHeadline(),
'styles' => $this->buildStyles(),
'published' => $this->published,
'time_updated' => $this->timeUpdated,
];
}
......
......@@ -13,9 +13,11 @@ class Transcode implements Interfaces\QueueRunner
$client = Core\Queue\Client::Build();
$client->setQueue("Transcode")
->receive(function ($data) {
$d = $data->getData();
echo "Received a transcode request \n";
$transcoder = new Core\Media\Services\FFMpeg();
$transcoder->setKey($data->getData()['key']);
$transcoder->setKey($d['key']);
$transcoder->setFullHD($d['full_hd']);
$transcoder->onQueue();
}, [ 'max_messages' => 1 ]);
}
......
......@@ -64,33 +64,6 @@ class Events
}
});
/**
* Blogs need more exportable fields for paywall
*/
Dispatcher::register('export:extender', 'blog', function (Event $event) {
$params = $event->getParameters();
/** @var Core\Blogs\Blog $blog */
$blog = $params['entity'];
$export = $event->response() ?: [];
$currentUser = Session::getLoggedInUserGuid();
$dirty = false;
if ($blog->isPaywall() && $blog->owner_guid != $currentUser) {
$export['description'] = '';
$export['body'] = '';
$dirty = true;
}
if ($dirty) {
return $event->setResponse($export);
}
if (!$currentUser) {
return;
}
});
/**
* Wire paywall hooks. Allows access if sent wire or is plus
*/
......
......@@ -55,8 +55,9 @@ class Video extends Object
$transcoder = ServiceFactory::build('FFMpeg');
$transcoder->setKey($this->getGuid())
->saveToFilestore($filepath)
->transcode();
->setFullHD($this->getFlag('full_hd'))
->saveToFilestore($filepath)
->transcode();
$this->cinemr_guid = $this->getGuid();
}
......@@ -193,6 +194,7 @@ class Video extends Object
'container_guid' => null,
'rating' => 2, //open by default
'time_sent' => time(),
'full_hd' => false,
], $data);
$allowed = [
......@@ -205,7 +207,8 @@ class Video extends Object
'mature',
'boost_rejection_reason',
'rating',
'time_sent'
'time_sent',
'full_hd',
];
foreach ($allowed as $field) {
......@@ -215,8 +218,8 @@ class Video extends Object
if ($field == 'access_id') {
$data[$field] = (int) $data[$field];
} elseif ($field == 'mature') {
$this->setFlag('mature', !!$data['mature']);
} elseif (in_array($field, ['mature', 'full_hd'], true)) {
$this->setFlag($field, !!$data[$field]);
continue;
}
......@@ -253,6 +256,7 @@ class Video extends Object
'thumbnail_src' => $this->getIconUrl(),
'guid' => $this->guid,
'mature' => $this->getFlag('mature'),
'full_hd' => $this->getFlag('full_hd'),
'license' => $this->license ?? '',
]
];
......
......@@ -58,7 +58,7 @@ class ManagerSpec extends ObjectBehavior
$lease->getGuid()
->willReturn(456);
$this->save->setEntity(Argument::that(function ($video) {
return $video->guid == 456
&& $video->access_id == 0;
......@@ -72,10 +72,14 @@ class ManagerSpec extends ObjectBehavior
$this->ffmpeg->setKey(456)
->shouldBeCalled();
$this->ffmpeg->setFullHD(false)
->shouldBeCalled();
$this->ffmpeg->transcode()
->shouldBeCalled();
$this->complete($lease)
$this->setFullHD(false)
->complete($lease)
->shouldReturn(true);
}
}
<?php
namespace Spec\Minds\Core\Pro\Assets;
use Exception;
use Minds\Core\Pro\Assets\Asset;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class AssetSpec extends ObjectBehavior
{
public function it_is_initializable()
{
$this->shouldHaveType(Asset::class);
}
public function it_should_set_type()
{
$this
->setType(Asset::TYPES[0])
->getType()
->shouldReturn(Asset::TYPES[0]);
}
public function it_should_throw_if_type_is_invalid()
{
$this
->shouldThrow(Exception::class)
->duringSetType('-!__!@_#)!@#_)!@#@!_#)INVALID');
}
}
<?php
namespace Spec\Minds\Core\Pro\Assets;
use ElggFile;
use Minds\Core\Media\Imagick\Manager as ImageManager;
use Minds\Core\Pro\Assets\Asset;
use Minds\Core\Pro\Assets\Manager;
use Minds\Entities\User;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Psr\Http\Message\StreamInterface;
use Zend\Diactoros\UploadedFile;
class ManagerSpec extends ObjectBehavior
{
/** @var ImageManager */
protected $imageManager;
public function let(
ImageManager $imageManager
) {
$this->imageManager = $imageManager;
$this->beConstructedWith($imageManager);
}
public function it_is_initializable()
{
$this->shouldHaveType(Manager::class);
}
public function it_should_set_logo(
User $user,
UploadedFile $file,
StreamInterface $fileStream,
Asset $asset,
ElggFile $assetFile
) {
$file->getStream()
->shouldBeCalled()
->willReturn($fileStream);
$fileStream->getContents()
->shouldBeCalled()
->willReturn('~image file~');
$file->getClientFilename()
->shouldBeCalled()
->willReturn('asset.img');
$this->imageManager->setImageFromBlob('~image file~', 'asset.img')
->shouldBeCalled()
->willReturn();
$asset->setType('logo')
->shouldBeCalled()
->willReturn($asset);
$user->get('guid')
->shouldBeCalled()
->willReturn(1000);
$asset->setUserGuid(1000)
->shouldBeCalled()
->willReturn($asset);
$this->imageManager->resize(Argument::cetera())
->shouldBeCalled()
->willReturn($this->imageManager);
$this->imageManager->getPng()
->shouldBeCalled()
->willReturn('~png file~');
$asset->getFile()
->shouldBeCalled()
->willReturn($assetFile);
$assetFile->open('write')
->shouldBeCalled()
->willReturn(true);
$assetFile->write('~png file~')
->shouldBeCalled()
->willReturn(true);
$assetFile->close()
->shouldBeCalled()
->willReturn(true);
$this
->setType('logo')
->setUser($user)
->setActor($user)
->set($file, $asset)
->shouldReturn(true);
}
public function it_should_set_background(
User $user,
UploadedFile $file,
StreamInterface $fileStream,
Asset $asset,
ElggFile $assetFile
) {
$file->getStream()
->shouldBeCalled()
->willReturn($fileStream);
$fileStream->getContents()
->shouldBeCalled()
->willReturn('~image file~');
$file->getClientFilename()
->shouldBeCalled()
->willReturn('asset.img');
$this->imageManager->setImageFromBlob('~image file~', 'asset.img')
->shouldBeCalled()
->willReturn();
$asset->setType('background')
->shouldBeCalled()
->willReturn($asset);
$user->get('guid')
->shouldBeCalled()
->willReturn(1000);
$asset->setUserGuid(1000)
->shouldBeCalled()
->willReturn($asset);
$this->imageManager->autorotate()
->shouldBeCalled()
->willReturn($this->imageManager);
$this->imageManager->resize(Argument::cetera())
->shouldBeCalled()
->willReturn($this->imageManager);
$this->imageManager->getJpeg(Argument::type('int'))
->shouldBeCalled()
->willReturn('~jpg file~');
$asset->getFile()
->shouldBeCalled()
->willReturn($assetFile);
$assetFile->open('write')
->shouldBeCalled()
->willReturn(true);
$assetFile->write('~jpg file~')
->shouldBeCalled()
->willReturn(true);
$assetFile->close()
->shouldBeCalled()
->willReturn(true);
$this
->setType('background')
->setUser($user)
->setActor($user)
->set($file, $asset)
->shouldReturn(true);
}
}
......@@ -43,18 +43,105 @@ class HydrateSettingsDelegateSpec extends ObjectBehavior
Activity $activity1,
Activity $activity2
) {
$settings->getLogoGuid()
$this->config->get('cdn_url')
->shouldBeCalled()
->willReturn('http://phpspec.test/');
$settings->hasCustomLogo()
->shouldBeCalled()
->willReturn(true);
$settings->getUserGuid()
->shouldBeCalled()
->willReturn(7500);
->willReturn(1000);
$settings->getTimeUpdated()
->shouldBeCalled()
->willReturn(999999);
$settings->setLogoImage('http://phpspec.test/fs/v1/pro/1000/logo/999999')
->shouldBeCalled()
->willReturn($settings);
$settings->hasCustomBackground()
->shouldBeCalled()
->willReturn(true);
$settings->setBackgroundImage('http://phpspec.test/fs/v1/pro/1000/background/999999')
->shouldBeCalled()
->willReturn($settings);
$user->getPinnedPosts()
->shouldBeCalled()
->willReturn([5000, 5001]);
$this->entitiesBuilder->get(['guids' => ['5000', '5001']])
->shouldBeCalled()
->willReturn([ $activity1, $activity2 ]);
$activity1->get('time_created')
->shouldBeCalled()
->willReturn(10000010);
$activity1->get('entity_guid')
->shouldBeCalled()
->willReturn(7400);
$activity2->get('time_created')
->shouldBeCalled()
->willReturn(10000090);
$activity2->get('guid')
->shouldBeCalled()
->willReturn(5001);
$activity2->get('entity_guid')
->shouldBeCalled()
->willReturn(null);
$settings->setFeaturedContent([5001, 7400])
->shouldBeCalled()
->willReturn($settings);
$user->isProPublished()
->willReturn(false);
$settings->setPublished(false)
->shouldBeCalled();
$this
->shouldNotThrow(Exception::class)
->duringOnGet($user, $settings);
}
public function it_should_hydrate_settings_with_default_assets_on_get(
User $user,
Settings $settings,
Carousel $carousel,
Activity $activity1,
Activity $activity2
) {
$this->config->get('cdn_url')
->shouldBeCalled()
->willReturn('http://phpspec.test/');
$settings->setLogoImage('http://phpspec.test/fs/v1/thumbnail/7500/master')
$settings->hasCustomLogo()
->shouldBeCalled()
->willReturn(false);
$user->getIconURL('large')
->shouldBeCalled()
->willReturn('http://phpspec.test/fs/v1/avatar/1000');
$settings->setLogoImage('http://phpspec.test/fs/v1/avatar/1000')
->shouldBeCalled()
->willReturn($settings);
$settings->hasCustomBackground()
->shouldBeCalled()
->willReturn(false);
$user->get('guid')
->shouldBeCalled()
->willReturn(1000);
......
......@@ -75,6 +75,104 @@ class DomainSpec extends ObjectBehavior
->shouldReturn(null);
}
public function it_should_check_if_domain_is_unavailable(
Response $getListResponse,
Settings $settings
) {
$this->config->get('pro')
->shouldBeCalled()
->willReturn([
'root_domains' => ['phpspec.test']
]);
$this->repository->getList([
'domain' => 'phpspec-test.com'
])
->shouldBeCalled()
->willReturn($getListResponse);
$getListResponse->first()
->shouldBeCalled()
->willReturn($settings);
$settings->getUserGuid()
->shouldBeCalled()
->willReturn(1001);
$this
->isAvailable('phpspec-test.com', 1000)
->shouldReturn(false);
}
public function it_should_check_if_domain_is_available_if_same_owner(
Response $getListResponse,
Settings $settings
) {
$this->config->get('pro')
->shouldBeCalled()
->willReturn([
'root_domains' => ['phpspec.test']
]);
$this->repository->getList([
'domain' => 'phpspec-test.com'
])
->shouldBeCalled()
->willReturn($getListResponse);
$getListResponse->first()
->shouldBeCalled()
->willReturn($settings);
$settings->getUserGuid()
->shouldBeCalled()
->willReturn(1000);
$this
->isAvailable('phpspec-test.com', 1000)
->shouldReturn(true);
}
public function it_should_check_if_domain_is_available(
Response $getListResponse
) {
$this->config->get('pro')
->shouldBeCalled()
->willReturn([
'root_domains' => ['phpspec.test']
]);
$this->repository->getList([
'domain' => 'phpspec-test.com'
])
->shouldBeCalled()
->willReturn($getListResponse);
$getListResponse->first()
->shouldBeCalled()
->willReturn(null);
$this
->isAvailable('phpspec-test.com', 1000)
->shouldReturn(true);
}
public function it_should_return_as_unavailable_if_root_domain()
{
$this->config->get('pro')
->shouldBeCalled()
->willReturn([
'root_domains' => ['phpspec.test']
]);
$this->repository->getList(Argument::cetera())
->shouldNotBeCalled();
$this
->isAvailable('phpspec.test', 1000)
->shouldReturn(false);
}
public function it_should_get_icon(
Settings $settings,
User $owner
......
......@@ -540,7 +540,7 @@ $CONFIG->set('transcoder', [
'bitrate' => 500,
'audio_bitrate' => 80,
'formats' => [ 'mp4', 'webm' ],
'charge' => false,
'pro' => false,
],
[
'width' => 1280,
......@@ -548,7 +548,7 @@ $CONFIG->set('transcoder', [
'bitrate' => 2000,
'audio_bitrate' => 128,
'formats' => [ 'mp4', 'webm' ],
'charge' => false,
'pro' => false,
],
[
'width' => 1920,
......@@ -556,7 +556,7 @@ $CONFIG->set('transcoder', [
'bitrate' => 2000,
'audio_bitrate' => 128,
'formats' => [ 'mp4', 'webm' ],
'charge' => true,
'pro' => true,
],
]
]);
......