...
 
Commits (61)
image: minds/php:latest
services:
- docker:dind
image: minds/php:7.3
stages:
- build
......@@ -16,7 +13,7 @@ stages:
build:
stage: build
script:
- apk update && apk add --no-cache git
- apk add --no-cache git
- sh tools/setup.sh production
artifacts:
name: '$CI_COMMIT_REF_SLUG'
......@@ -26,19 +23,21 @@ build:
test:
stage: test
image: minds/php-tests:latest
image: minds/php:7.3
script:
- bin/phpspec run
- php -n -c Spec/php-test.ini bin/phpspec run
lint:
stage: test
image: minds/php-tests:latest
image: minds/php:7.3
script:
- bin/php-cs-fixer fix --allow-risky=yes --verbose --dry-run
prepare:fpm:
stage: prepare
image: minds/ci:latest
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- |
......@@ -53,6 +52,8 @@ prepare:fpm:
prepare:runners:
stage: prepare
image: minds/ci:latest
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- |
......@@ -136,6 +137,8 @@ qa:manual:
staging:fpm:
stage: deploy:staging
image: minds/ci:latest
services:
- docker:dind
script:
- IMAGE_LABEL="staging"
- $(aws ecr get-login --no-include-email --region us-east-1)
......@@ -162,6 +165,8 @@ staging:fpm:
canary:fpm:
stage: deploy:canary
image: minds/ci:latest
services:
- docker:dind
script:
- IMAGE_LABEL="canary"
- $(aws ecr get-login --no-include-email --region us-east-1)
......@@ -190,6 +195,8 @@ canary:fpm:
production:fpm:
stage: deploy:production
image: minds/ci:latest
services:
- docker:dind
script:
- IMAGE_LABEL="production"
- $(aws ecr get-login --no-include-email --region us-east-1)
......@@ -218,6 +225,8 @@ production:fpm:
production:runners:
stage: deploy:production
image: minds/ci:latest
services:
- docker:dind
script:
- IMAGE_LABEL="production"
- $(aws ecr get-login --no-include-email --region us-east-1)
......
......@@ -40,12 +40,14 @@ class CustomerSync extends Cli\Controller implements Interfaces\CliControllerInt
$guid = $data[29];
try {
$insert = new Core\Data\Cassandra\Prepared\Custom();
$insert->query("INSERT INTO user_index_to_guid (key, column1, value) VALUES (?, ?, ?)",
[
$insert->query(
"INSERT INTO user_index_to_guid (key, column1, value) VALUES (?, ?, ?)",
[
"$guid:payments",
"customer_id",
$id
]);
]
);
$this->db->request($insert);
$this->out("$guid with customer id $id done");
} catch (\Exception $e) {
......
......@@ -56,18 +56,21 @@ class SubscriptionSync extends Cli\Controller implements Interfaces\CliControlle
$amount = ($result->quantity * $result->plan->amount) / 100;
$insert = new Core\Data\Cassandra\Prepared\Custom();
$insert->query("INSERT INTO plans (user_guid, plan, entity_guid, amount) VALUES (?, ?, ?, ?)",
[
$insert->query(
"INSERT INTO plans (user_guid, plan, entity_guid, amount) VALUES (?, ?, ?, ?)",
[
$plan['user_guid'],
$plan['plan'],
$plan['entity_guid'],
(int) $amount
]);
]
);
$this->db->request($insert);
if ($plan['plan'] == 'exclusive') {
$query = new Core\Data\Cassandra\Prepared\Custom();
$query->query("INSERT INTO wire
$query->query(
"INSERT INTO wire
(receiver_guid, sender_guid, method, timestamp, entity_guid, wire_guid, amount, recurring, status)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
[
......@@ -80,7 +83,8 @@ class SubscriptionSync extends Cli\Controller implements Interfaces\CliControlle
new \Cassandra\Decimal($amount),
true,
'success'
]);
]
);
$this->db->request($query);
}
......
......@@ -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,8 +36,12 @@ class report implements Interfaces\Api
/** @var Core\Reports\Repository $repository */
$repository = Di::_()->get('Reports\Repository');
$done = $repository->create($pages[0], Core\Session::getLoggedinUser(),
$reason, $reason_note);
$done = $repository->create(
$pages[0],
Core\Session::getLoggedinUser(),
$reason,
$reason_note
);
return Factory::response([
'done' => $done
......
......@@ -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')) {
......@@ -73,7 +85,7 @@ class media implements Interfaces\Api, Interfaces\ApiIgnorePam
Security\ACL::$ignore = $ignore;
}
/* No break */
/* no break */
default:
$entity->fullExport = true;
$response['entity'] = $entity->export();
......@@ -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;
}
}
......@@ -7,13 +7,13 @@
*/
namespace Minds\Controllers\api\v1\media;
use Minds\Api\Factory;
use Minds\Common;
use Minds\Core;
use Minds\Core\Di\Di;
use Minds\Helpers;
use Minds\Core\Features\Manager as FeaturesManager;
use Minds\Entities;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Core\Features\Manager as FeaturesManager;
class thumbnails implements Interfaces\Api, Interfaces\ApiIgnorePam
{
......@@ -29,24 +29,33 @@ class thumbnails implements Interfaces\Api, Interfaces\ApiIgnorePam
exit;
}
$featuresManager = new FeaturesManager();
$guid = $pages[0];
if ($featuresManager->has('cdn-jwt')) {
error_log("{$_SERVER['REQUEST_URI']} was hit, and should not have been");
Core\Security\ACL::$ignore = true;
$size = isset($pages[1]) ? $pages[1] : null;
$last_cache = isset($pages[2]) ? $pages[2] : time();
$entity = Entities\Factory::build($guid);
if (!$entity) {
return Factory::response([
'status' => 'error',
'message' => 'This endpoint has been deprecated. Please use fs/v1/thumbnail',
'message' => 'Entity not found'
]);
}
$guid = $pages[0];
Core\Security\ACL::$ignore = true;
$featuresManager = new FeaturesManager();
$size = isset($pages[1]) ? $pages[1] : null;
if ($entity->access_id !== Common\Access::PUBLIC && $featuresManager->has('cdn-jwt')) {
error_log("{$_SERVER['REQUEST_URI']} was hit, and should not have been");
$last_cache = isset($pages[2]) ? $pages[2] : time();
return Factory::response([
'status' => 'error',
'message' => 'This endpoint has been deprecated. Please use fs/v1/thumbnail',
]);
}
$etag = $last_cache . $guid;
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) {
......@@ -54,7 +63,7 @@ class thumbnails implements Interfaces\Api, Interfaces\ApiIgnorePam
exit;
}
$thumbnail = Di::_()->get('Media\Thumbnails')->get($guid, $size);
$thumbnail = Di::_()->get('Media\Thumbnails')->get($entity, $size);
if ($thumbnail instanceof \ElggFile) {
$thumbnail->open('read');
......
......@@ -299,28 +299,6 @@ class merchant implements Interfaces\Api
$response['uploaded'] = true;
}
break;
case "charge":
$sale = (new Payments\Sale)
->setId($pages[1]);
try {
Payments\Factory::build('braintree', ['gateway'=>'merchants'])->chargeSale($sale);
} catch (\Exception $e) {
var_dump($e);
exit;
}
exit;
break;
case "void":
$sale = (new Payments\Sale)
->setId($pages[1]);
Payments\Factory::build('braintree', ['gateway'=>'merchants'])->voidSale($sale);
break;
case "refund":
$sale = (new Payments\Sale)
->setId($pages[1]);
Payments\Factory::build('braintree', ['gateway'=>'merchants'])->refundSale($sale);
break;
}
return Factory::response($response);
......
......@@ -7,7 +7,6 @@
namespace Minds\Controllers\api\v1\minds;
use Minds;
use Minds\Core;
use Minds\Interfaces;
use Minds\Api\Factory;
......@@ -21,32 +20,10 @@ class config implements Interfaces\Api, Interfaces\ApiIgnorePam
*/
public function get($pages)
{
$minds = [
"cdn_url" => Minds\Core\Config::_()->get('cdn_url') ?: Minds\Core\Config::_()->cdn_url,
"cinemr_url" => Minds\Core\Config::_()->get('cinemr_url') ?: Minds\Core\Config::_()->cinemr_url,
"site_url" => Minds\Core\Config::_()->get('site_url') ?: Minds\Core\Config::_()->site_url,
"socket_server" => Minds\Core\Config::_()->get('sockets-server-uri') ?: 'ha-socket-io-us-east-1.minds.com:3030',
"thirdpartynetworks" => Minds\Core\Di\Di::_()->get('ThirdPartyNetworks\Manager')->availableNetworks(),
"categories" => Minds\Core\Config::_()->get('categories') ?: [],
"stripe_key" => Minds\Core\Config::_()->get('payments')['stripe']['public_key'],
"recaptchaKey" => Minds\Core\Config::_()->get('google')['recaptcha']['site_key'],
"max_video_length" => (Core\Session::getLoggedInUser() && Core\Session::getLoggedInUser()->isPlus())
? Minds\Core\Config::_()->get('max_video_length_plus')
: Minds\Core\Config::_()->get('max_video_length'),
"max_video_file_size" => Minds\Core\Config::_()->get('max_video_file_size') ?: 0,
"features" => (object) (Minds\Core\Config::_()->get('features') ?: []),
"blockchain" => (object) Minds\Core\Di\Di::_()->get('Blockchain\Manager')->getPublicSettings(),
"plus" => Minds\Core\Config::_()->get('plus'),
"report_reasons" => Minds\Core\Config::_()->get('report_reasons'),
"last_tos_update" => (Minds\Core\Config::_()->get('last_tos_update') ?: time()),
'handlers' => [
'plus' => Minds\Core\Di\Di::_()->get('Config')->get('plus')['handler'] ?? null,
'pro' => Minds\Core\Di\Di::_()->get('Config')->get('pro')['handler'] ?? null,
],
'upgrades' => Minds\Core\Di\Di::_()->get('Config')->get('upgrades'),
];
return Factory::response($minds);
return Factory::response(
Core\Di\Di::_()->get('Config\Exported')
->export()
);
}
/**
......
......@@ -360,7 +360,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)
->setNsfw($embeded instanceof Flaggable ? $embeded->getFlag('nsfw') : false)
......@@ -382,7 +383,7 @@ class newsfeed implements Interfaces\Api
->setTitle($embeded->title)
->setBlurb($embeded->description)
->export()
)
)
->setMessage($message);
}
$save->setEntity($activity)
......@@ -421,7 +422,7 @@ class newsfeed implements Interfaces\Api
->setTitle($embeded->title)
->setBlurb($embeded->description)
->export()
)
)
->setMessage($message);
}
$save->setEntity($activity)
......@@ -530,7 +531,7 @@ class newsfeed implements Interfaces\Api
]);
}
}
$save->setEntity($activity)
->save();
......
<?php
/**
* Minds Payments API:: braintree
*
* @version 1
* @author Mark Harding
*/
namespace Minds\Controllers\api\v1\payments;
use Minds\Core;
use Minds\Helpers;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Core\Payments;
class braintree implements Interfaces\Api
{
/**
* Returns merchant information
* @param array $pages
*
* API:: /v1/merchant/:slug
*/
public function get($pages)
{
$response = [];
switch ($pages[0]) {
case "token":
$gateway = isset($pages[1]) ? $pages[1] : 'default';
$response['token'] = Payments\Factory::build('braintree', ['gateway'=>$gateway])->getToken();
break;
}
return Factory::response($response);
}
public function post($pages)
{
$response = [];
switch ($pages[0]) {
case "charge":
$amount = $_POST['amount'];
$fee = $amount * 0.05 + 0.30; //5% + $.30
if (!isset($_POST['merchant'])) {
$merchant = Core\Session::getLoggedInUser();
}
$sale = (new Payments\Sale())
->setAmount($amount)
->setMerchant($merchant)
->setFee($fee)
->setCustomerId(Core\Session::getLoggedInUser()->guid)
->setNonce($_POST['nonce']);
try {
$result = Payments\Factory::build('braintree', ['gateway'=>'merchants'])->setSale($sale);
} catch (\Exception $e) {
$response['status'] = "error";
$response['message'] = $e->getMessage();
}
break;
case "charge-master":
$amount = $_POST['amount'];
$sale = (new Payments\Sale())
->setAmount($amount)
->setCustomerId(Core\Session::getLoggedInUser()->guid)
->setSettle(true)
->setFee(0)
->setNonce($_POST['nonce']);
try {
$result = Payments\Factory::build('braintree', ['gateway'=>'merchants'])->setSale($sale);
} catch (\Exception $e) {
$response['status'] = "error";
$response['message'] = $e->getMessage();
}
break;
}
return Factory::response($response);
}
public function put($pages)
{
return Factory::response([]);
}
public function delete($pages)
{
return Factory::response([]);
}
}
......@@ -97,9 +97,6 @@ class subscriptions implements Interfaces\Api
}
$stripe = Core\Di\Di::_()->get('StripePayments');
$stripe->cancelSubscription($subscription);
} else {
$braintree = Payments\Factory::build("Braintree", ['gateway'=>'default']);
$braintree->cancelSubscription($subscription);
}
}
......
<?php
/**
* Minds Webhook: Braintree
*
* @version 1
* @author Mark Harding
*/
namespace Minds\Controllers\api\v1\webhooks;
use Minds\Core;
use Minds\Entities;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Helpers;
use Minds\Core\Payments;
use Braintree_WebhookNotification;
class braintree implements Interfaces\Api, Interfaces\ApiIgnorePam
{
/**
* NOT AVAILABLE
*/
public function get($pages)
{
return Factory::response(['status'=>'error', 'message'=>'GET is not supported for this endpoint']);
}
/**
*/
public function post($pages)
{
error_log("[webhooks]:: hit first entrace point");
/*$gateway = isset($pages[0]) ? $pages[0] : 'default';
$bt = Payments\Factory::build('braintree', ['gateway'=>$gateway]);
$hooks = new Payments\Hooks();
$hooks->loadDefaults();
$webhooks = new Payments\Braintree\Webhooks($hooks, $bt);
$webhooks->setSignature($_POST['bt_signature'])
->setPayload($_POST['bt_payload'])
->run();*/
}
public function put($pages)
{
}
public function delete($pages)
{
}
}
......@@ -48,24 +48,9 @@ class stripe implements Interfaces\Api, Interfaces\ApiIgnorePam
$hooks->run();
// Do something with $event_json
http_response_code(200); // PHP 5.4 or greater
exit;
/*$gateway = isset($pages[0]) ? $pages[0] : 'default';
$bt = Payments\Factory::build('braintree', ['gateway'=>$gateway]);
$hooks = new Payments\Hooks();
$hooks->loadDefaults();
$webhooks = new Payments\Braintree\Webhooks($hooks, $bt);
$webhooks->setSignature($_POST['bt_signature'])
->setPayload($_POST['bt_payload'])
->run();*/
return Factory::response([]);
}
......
......@@ -50,7 +50,10 @@ class wallet implements Interfaces\Api
$offChainBalanceVal = BigNumber::_($offChainBalance->get());
$offchainAvailableVal = BigNumber::_($offChainBalance->getAvailable());
$balance = $onChainBalanceVal->add($offChainBalanceVal);
$balance = $onChainBalanceVal
? $onChainBalanceVal->add($offChainBalanceVal)
: $offChainBalanceVal;
$wireCap = Di::_()->get('Blockchain\Wallets\OffChain\Cap')
->setUser(Session::getLoggedinUser())
......
......@@ -48,8 +48,10 @@ class peer implements Interfaces\Api
case 'inbox':
default:
$review->setType(Core\Session::getLoggedinUser()->guid);
$boosts = $review->getReviewQueue(isset($_GET['limit']) ? $_GET['limit'] : 12,
isset($_GET['offset']) ? $_GET['offset'] : "");
$boosts = $review->getReviewQueue(
isset($_GET['limit']) ? $_GET['limit'] : 12,
isset($_GET['offset']) ? $_GET['offset'] : ""
);
$response['boosts'] = Factory::exportable($boosts['data']);
......
......@@ -65,7 +65,11 @@ class scheduled implements Interfaces\Api
return Factory::response([
'status' => 'success',
'count' => $manager->getScheduledCount(['container_guid' => $container_guid, 'type' => $type])
'count' => $manager->getScheduledCount([
'container_guid' => $container_guid,
'type' => $type,
'owner_guid' => $currentUser->guid,
])
]);
default:
return Factory::response([
......@@ -148,6 +152,7 @@ class scheduled implements Interfaces\Api
'single_owner_threshold' => 0,
'pinned_guids' => $type === 'activity' ? array_reverse($container->getPinnedPosts()) : null,
'time_created_upper' => false,
'owner_guid' => $currentUser->guid,
];
if (isset($_GET['nsfw'])) {
......
......@@ -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([]);
......
......@@ -39,9 +39,14 @@ class keys implements Interfaces\Api
// $priv = helpers\openssl::temporaryPrivateKey($priv, $unlock_password, $unlock_password);
// \elgg_set_plugin_user_setting('privatekey', $priv, elgg_get_logged_in_user_guid(), 'gatherings');
//}
$keystore->unlockPrivateKey($unlock_password, null);
$tmp = $keystore->getUnlockedPrivateKey();
try {
$keystore->unlockPrivateKey($unlock_password, null);
$tmp = $keystore->getUnlockedPrivateKey();
} catch (\Exception $e) {
$response['status'] = 'error';
$response['message'] = $e->getMessage();
return Factory::response($response);
}
if (!$tmp || !$unlock_password) {
$response['status'] = 'error';
......
......@@ -30,7 +30,7 @@ class channel implements Interfaces\Api
{
$currentUser = Session::getLoggedinUser();
$channel = new User($pages[0]);
$channel = new User(strtolower($pages[0]));
$channel->fullExport = true; //get counts
$channel->exportCounts = true;
......
......@@ -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;
......@@ -33,7 +34,7 @@ class settings implements Interfaces\Api
]);
}
$user = new User($pages[0]);
$user = new User(strtolower($pages[0]));
}
/** @var Manager $manager */
......@@ -65,7 +66,7 @@ class settings implements Interfaces\Api
]);
}
$user = new User($pages[0]);
$user = new User(strtolower($pages[0]));
}
/** @var Manager $manager */
......@@ -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(strtolower($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(strtolower($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;
......
......@@ -10,7 +10,7 @@ namespace Minds\Controllers\fs\v1;
use Minds\Core;
use Minds\Entities;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Helpers\File;
class avatars implements Interfaces\FS
{
......@@ -59,10 +59,8 @@ class avatars implements Interfaces\FS
$contents = file_get_contents($filepath);
}
if ($filepath) {
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $filepath);
finfo_close($finfo);
if (!empty($contents)) {
$mimetype = File::getMime($contents);
} else {
$mimetype = 'image/jpeg';
}
......
......@@ -10,7 +10,7 @@ namespace Minds\Controllers\fs\v1;
use Minds\Core;
use Minds\Entities;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Helpers\File;
class banners implements Interfaces\FS
{
......@@ -56,6 +56,7 @@ class banners implements Interfaces\FS
$f->owner_guid = $entity->owner_guid ?: $entity->getOwnerObj()->guid;
$f->setFilename("group/{$entity->getGuid()}.jpg");
$f->open('read');
// no break
case "object":
break;
}
......@@ -101,9 +102,7 @@ class banners implements Interfaces\FS
}
}
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_buffer($finfo, $content);
finfo_close($finfo);
$mimetype = File::getMime($content);
header('Content-Type: '.$mimetype);
header('Expires: ' . date('r', time() + 864000));
......
......@@ -10,7 +10,7 @@ namespace Minds\Controllers\fs\v1;
use Minds\Core;
use Minds\Entities;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Helpers\File;
class paywall implements Interfaces\FS
{
......@@ -31,9 +31,7 @@ class paywall implements Interfaces\FS
$contents = file_get_contents(Core\Di\Di::_()->get('Config')->get('path') . 'engine/Assets/photos/andromeda-galaxy.jpg');
}
$finfo = finfo_open(FILEINFO_MIME);
$mimetype = finfo_file($finfo, $filepath);
finfo_close($finfo);
$mimetype = File::getMime($contents);
header('Content-Type: '.$mimetype);
header('Expires: ' . date('r', time() + 864000));
header("Pragma: public");
......
<?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;
}
}
......@@ -4,11 +4,14 @@
*/
namespace Minds\Controllers\fs\v1;
use Minds\Api\Factory;
use Minds\Common;
use Minds\Core;
use Minds\Core\Di\Di;
use Minds\Core\Features\Manager as FeaturesManager;
use Minds\Entities;
use Minds\Interfaces;
use Minds\Core\Features\Manager as FeaturesManager;
use Minds\Helpers\File;
class thumbnail extends Core\page implements Interfaces\page
{
......@@ -18,9 +21,30 @@ class thumbnail extends Core\page implements Interfaces\page
exit;
}
Core\Security\ACL::$ignore = true;
$guid = $pages[0] ?? null;
if (!$guid) {
return Factory::response([
'status' => 'error',
'message' => 'guid must be provided'
]);
}
$size = isset($pages[1]) ? $pages[1] : null;
$entity = Entities\Factory::build($guid);
if (!$entity) {
return Factory::response([
'status' => 'error',
'message' => 'Entity not found'
]);
}
$featuresManager = new FeaturesManager;
if ($featuresManager->has('cdn-jwt')) {
if ($entity->access_id !== Common\Access::PUBLIC && $featuresManager->has('cdn-jwt')) {
$signedUri = new Core\Security\SignedUri();
$uri = (string) \Zend\Diactoros\ServerRequestFactory::fromGlobals()->getUri();
if (!$signedUri->confirm($uri)) {
......@@ -31,9 +55,8 @@ class thumbnail extends Core\page implements Interfaces\page
/** @var Core\Media\Thumbnails $mediaThumbnails */
$mediaThumbnails = Di::_()->get('Media\Thumbnails');
Core\Security\ACL::$ignore = true;
$size = isset($pages[1]) ? $pages[1] : null;
$thumbnail = $mediaThumbnails->get($pages[0], $size);
$thumbnail = $mediaThumbnails->get($entity, $size);
if ($thumbnail instanceof \ElggFile) {
$thumbnail->open('read');
......@@ -47,8 +70,7 @@ class thumbnail extends Core\page implements Interfaces\page
}
try {
$finfo = new \finfo(FILEINFO_MIME);
$contentType = $finfo->buffer($contents) ?: 'image/jpeg';
$contentType = File::getMime($contents);
} catch (\Exception $e) {
error_log($e);
$contentType = 'image/jpeg';
......
<?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(),
];
}
}
......@@ -4,9 +4,11 @@ namespace Minds\Core\Analytics\Dashboards;
class Manager
{
const DASHBOARDS = [
'summary' => SummaryDashboard::class,
'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-*',
......
......@@ -14,7 +14,7 @@ class ReferralsEarningsMetric extends AbstractEarningsMetric
protected $id = 'earnings_referrals';
/** @var string */
protected $label = 'Referrals USD';
protected $label = 'Referrals';
/** @var string */
protected $description = "Total earnings for your active referrals. You earn $0.10 for every active referral. A referral must log in at least 3 of 7 days after registration to be credited.";
......
......@@ -14,7 +14,7 @@ class SalesEarningsMetric extends AbstractEarningsMetric
protected $id = 'earnings_sales';
/** @var string */
protected $label = 'Sales USD';
protected $label = 'Sales';
/** @var string */
protected $description = "Total earnings for the sales you have referred. You earn a 25% commission when your referrals purchase Plus, Pro or Minds Tokens.";
......
......@@ -14,7 +14,7 @@ class ViewsEarningsMetric extends AbstractEarningsMetric
protected $id = 'earnings_views';
/** @var string */
protected $label = 'Pageviews USD';
protected $label = 'Pageviews';
/** @var string */
protected $description = "Total earnings for the pageviews on your channel's assets. You earn $1 for every 1,000 pageviews.";
......
<?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 = 'Subscribers';
/** @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,
],
[
......
<?php
/**
* Summary Dashboard
*/
namespace Minds\Core\Analytics\Dashboards;
use Minds\Entities\User;
use Minds\Traits\MagicAttributes;
/**
* @method TrafficDashboard setTimespanId(string $timespanId)
* @method TrafficDashboard setFilterIds(array $filtersIds)
* @method TrafficDashboard setUser(User $user)
*/
class SummaryDashboard implements DashboardInterface
{
use MagicAttributes;
/** @var string */
private $timespanId = '30d';
/** @var string[] */
private $filterIds = [ 'platform::browser' ];
/** @var string */
private $metricId = 'active_users';
/** @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\PlatformFilter(),
new Filters\ViewTypeFilter(),
new Filters\ChannelFilter()
);
$this->metricsCollection
->setTimespansCollection($this->timespansCollection)
->setFiltersCollection($this->filtersCollection)
->setSelectedId($this->metricId)
->setUser($this->user)
->addMetrics(
new Metrics\ActiveUsersMetric()
)
->build();
return $this;
}
/**
* Export
* @param array $extras
* @return array
*/
public function export(array $extras = []): array
{
$this->build();
return [
'category' => 'summary',
'label' => 'Summary',
'description' => null,
'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(),
];
}
}
......@@ -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,
......
......@@ -74,11 +74,11 @@ class TokenSaleEvent implements BlockchainEventInterface
$purchase = $this->manager->getPurchase($transaction->getData()['phone_number_hash'], $transaction->getTx());
if (!$purchase) {
echo "purchase not found";
error_log("purchase not found");
return; //purchase not found
}
var_dump($log);
error_log(print_r($log, true));
//is the requested amount below what has already been recorded
if ($transaction->getAmount() > $purchase->getUnissuedAmount()) {
return; //requested more than can issue
......
......@@ -96,7 +96,6 @@ class WithdrawEvent implements BlockchainEventInterface
try {
$this->manager->complete($request, $transaction);
} catch (\Exception $e) {
var_dump($e);
error_log(print_r($e, true));
}
}
......
......@@ -85,11 +85,13 @@ class Pending
public function delete($type, $tx_id)
{
$query = new Core\Data\Cassandra\Prepared\Custom();
$query->query("DELETE FROM blockchain_pending WHERE type = ? AND tx_id = ?",
$query->query(
"DELETE FROM blockchain_pending WHERE type = ? AND tx_id = ?",
[
(string) $type,
(string) $tx_id
]);
]
);
try {
return (bool) $this->db->request($query);
......
......@@ -46,14 +46,16 @@ class Sums
$query = new Custom();
if ($this->user) {
$query->query("SELECT
$query->query(
"SELECT
SUM(amount) as balance
FROM blockchain_transactions_mainnet_by_address
WHERE user_guid = ?
AND wallet_address = 'offchain'",
[
new Varint((int) $this->user->guid)
]);
]
);
$query->setOpts([
'consistency' => \Cassandra::CONSISTENCY_ALL
]);
......
......@@ -49,14 +49,16 @@ class TestnetSums
throw new \Exception('User is not set');
}
$query->query("SELECT
$query->query(
"SELECT
SUM(amount) as balance
FROM blockchain_transactions_by_address
WHERE user_guid = ?
AND wallet_address = 'offchain'",
[
new Varint((int) $this->user->guid)
]);
]
);
$query->setOpts([
'consistency' => \Cassandra::CONSISTENCY_ALL
]);
......
......@@ -60,6 +60,11 @@ class Balance
}
$balance = $this->token->balanceOf($address);
if ($balance === null) {
return null;
}
$this->cache->set($cacheKey, serialize($balance), 60);
return $balance;
......
......@@ -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;
}
});
}
}
......@@ -47,7 +47,8 @@ class Navigation
->setVisibility(0); //only show for loggedin
$link = new Item();
NavigationManager::add($link
NavigationManager::add(
$link
->setPriority(4)
->setIcon('subject')
->setName('Blogs')
......
......@@ -244,8 +244,10 @@ class Iterator implements \Iterator
foreach ($boosts as $boost) {
$owner_guids[] = $boost->owner_guid;
}
$blocked = array_flip(Core\Security\ACL\Block::_()->isBlocked($owner_guids,
Core\Session::getLoggedInUserGuid()));
$blocked = array_flip(Core\Security\ACL\Block::_()->isBlocked(
$owner_guids,
Core\Session::getLoggedInUserGuid()
));
foreach ($boosts as $i => $boost) {
if (isset($blocked[$boost->owner_guid])) {
......
......@@ -97,10 +97,12 @@ class Repository
return $this;
}
foreach ($this->categories as $category) {
$query->query("INSERT INTO categories
$query->query(
"INSERT INTO categories
(type, category, filter, guid)
VALUES (?, ?, ?, ?)",
[ $this->type, $category, $this->filter, (string) $guid ]);
[ $this->type, $category, $this->filter, (string) $guid ]
);
try {
$result = $this->db->request($query);
} catch (\Exception $e) {
......@@ -116,9 +118,11 @@ class Repository
return $this;
}
foreach ($this->categories as $category) {
$query->query("DELETE FROM categories
$query->query(
"DELETE FROM categories
WHERE type = ? AND category = ? AND filter = ? AND guid = ?",
[ $this->type, $category, $this->filter, (string) $guid ]);
[ $this->type, $category, $this->filter, (string) $guid ]
);
try {
$result = $this->db->request($query);
} catch (\Exception $e) {
......
<?php
/**
* @author: eiennohi.
*/
namespace Minds\Core\Channels\Delegates;
use Minds\Core\Analytics\Metrics\Event;
use Minds\Entities\User;
class MetricsDelegate
{
public function onDelete(User $user)
{
$event = new Event();
$event->setType('action')
->setAction('delete')
->setProduct('platform')
->setUserGuid((string) $user->guid)
->setUserPhoneNumberHash($user->getPhoneNumberHash())
->push();
}
}
......@@ -5,6 +5,7 @@
namespace Minds\Core\Channels;
use Minds\Core\Channels\Delegates\MetricsDelegate;
use Minds\Core\Di\Di;
use Minds\Core\Queue\Interfaces\QueueClient;
use Minds\Entities\User;
......@@ -30,6 +31,9 @@ class Manager
/** @var Delegates\Artifacts\Factory */
protected $artifactsDelegatesFactory;
/** @var MetricsDelegate */
protected $metricsDelegate;
/** @var Delegates\Logout */
protected $logoutDelegate;
......@@ -44,10 +48,12 @@ class Manager
*/
public function __construct(
$artifactsDelegatesFactory = null,
$metricsDelegate = null,
$logoutDelegate = null,
$queueClient = null
) {
$this->artifactsDelegatesFactory = $artifactsDelegatesFactory ?: new Delegates\Artifacts\Factory();
$this->metricsDelegate = $metricsDelegate ?: new MetricsDelegate();
$this->logoutDelegate = $logoutDelegate ?: new Delegates\Logout();
$this->queueClient = $queueClient ?: Di::_()->get('Queue');
}
......@@ -139,6 +145,7 @@ class Manager
}
}
$this->metricsDelegate->onDelete($this->user);
$this->logoutDelegate->logout($this->user);
return true;
......
......@@ -9,6 +9,7 @@ use Minds\Entities\RepositoryEntity;
use Minds\Entities\User;
use Minds\Helpers\Flags;
use Minds\Helpers\Unknown;
use Minds\Helpers\Export;
use Minds\Core\Di\Di;
/**
......@@ -406,6 +407,8 @@ class Comment extends RepositoryEntity
//$output['parent_guid'] = (string) $this->entityGuid;
$output = Export::sanitize($output);
return $output;
}
}
......@@ -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']);
}
......
......@@ -17,5 +17,9 @@ class ConfigProvider extends Provider
$this->di->bind('Config', function ($di) {
return new Config();
}, ['useFactory'=>true]);
$this->di->bind('Config\Exported', function ($di) {
return new Exported();
}, ['useFactory' => true]);
}
}
<?php
/**
* Exported
* @author edgebal
*/
namespace Minds\Core\Config;
use Minds\Core\Blockchain\Manager as BlockchainManager;
use Minds\Core\Di\Di;
use Minds\Core\I18n\I18n;
use Minds\Core\Navigation\Manager as NavigationManager;
use Minds\Core\Rewards\Contributions\ContributionValues;
use Minds\Core\Session;
use Minds\Core\ThirdPartyNetworks\Manager as ThirdPartyNetworksManager;
use Minds\Entities\User;
use Minds\Helpers\Counters;
class Exported
{
/** @var Config */
protected $config;
/** @var ThirdPartyNetworksManager */
protected $thirdPartyNetworks;
/** @var I18n */
protected $i18n;
/**
* Exported constructor.
* @param Config $config
* @param ThirdPartyNetworksManager $thirdPartyNetworks
* @param I18n $i18n
* @param BlockchainManager $blockchain
*/
public function __construct(
$config = null,
$thirdPartyNetworks = null,
$i18n = null,
$blockchain = null
) {
$this->config = $config ?: Di::_()->get('Config');
$this->thirdPartyNetworks = $thirdPartyNetworks ?: Di::_()->get('ThirdPartyNetworks\Manager');
$this->i18n = $i18n ?: Di::_()->get('I18n');
$this->blockchain = $blockchain ?: Di::_()->get('Blockchain\Manager');
}
/**
* @return array
*/
public function export(): array
{
$context = defined('__MINDS_CONTEXT__') ? __MINDS_CONTEXT__ : 'app';
$exported = [
'MindsContext' => $context,
'LoggedIn' => Session::isLoggedIn() ? true : false,
'Admin' => Session::isAdmin() ? true : false,
'cdn_url' => $this->config->get('cdn_url'),
'cdn_assets_url' => $this->config->get('cdn_assets_url'),
'site_url' => $this->config->get('site_url'),
'cinemr_url' => $this->config->get('cinemr_url'),
'socket_server' => $this->config->get('sockets-server-uri') ?: 'ha-socket-io-us-east-1.minds.com:3030',
'navigation' => NavigationManager::export(),
'thirdpartynetworks' => $this->thirdPartyNetworks->availableNetworks(),
'language' => $this->i18n->getLanguage(),
'languages' => $this->i18n->getLanguages(),
'categories' => $this->config->get('categories') ?: [],
'stripe_key' => $this->config->get('payments')['stripe']['public_key'],
'recaptchaKey' => $this->config->get('google')['recaptcha']['site_key'],
'max_video_length' => $this->config->get('max_video_length'),
'max_video_file_size' => $this->config->get('max_video_file_size'),
'features' => (object) ($this->config->get('features') ?: []),
'blockchain' => (object) $this->blockchain->getPublicSettings(),
'sale' => $this->config->get('blockchain')['sale'],
'last_tos_update' => $this->config->get('last_tos_update') ?: time(),
'tags' => $this->config->get('tags') ?: [],
'plus' => $this->config->get('plus'),
'report_reasons' => $this->config->get('report_reasons'),
'handlers' => [
'plus' => $this->config->get('plus')['handler'] ?? null,
'pro' => $this->config->get('pro')['handler'] ?? null,
],
'upgrades' => $this->config->get('upgrades'),
'contribution_values' => ContributionValues::export(),
'environment' => getenv('MINDS_ENV') ?: 'development',
];
if (Session::isLoggedIn()) {
/** @var User $user */
$user = Session::getLoggedinUser();
$exported['user'] = $user->export();
$exported['user']['rewards'] = (bool) $user->getPhoneNumberHash();
$exported['wallet'] = [
'balance' => Counters::get($user->guid, 'points', false),
];
if ($user->isPlus()) {
$exported['max_video_length'] = $this->config->get('max_video_length_plus');
}
}
if ($context === 'embed') {
$exported['MindsEmbed'] = $embedded_entity ?? null;
}
return $exported;
}
}
......@@ -161,13 +161,15 @@ class Call
array_push(self::$keys, $key);
$options = array_merge(
[
[
'multi' => false,
'offset' => "",
'finish' => "",
'limit' => 500,
'reversed' => true
], $options);
],
$options
);
$query = new Cassandra\Prepared\Custom();
......
......@@ -41,13 +41,13 @@ class Client implements Interfaces\ClientInterface
try {
$statement = $this->session->prepare($cql['string']);
$future = $this->session->executeAsync(
$statement,
@new Driver\ExecutionOptions(array_merge(
[
$statement,
@new Driver\ExecutionOptions(array_merge(
[
'arguments' => $cql['values']
],
$request->getOpts()
))
$request->getOpts()
))
);
if ($silent) {
return $future;
......
......@@ -75,14 +75,16 @@ class DataProvider extends Provider
$sslmode = isset($config['sslmode']) ? $config['sslmode'] : 'disable';
$username = isset($config['username']) ? $config['username'] : 'php';
// This is a generic data object using the postgres driver to connect to cockroachdb.
return new PDO("pgsql:host=$host;port=$port;dbname=$name;sslmode=$sslmode",
return new PDO(
"pgsql:host=$host;port=$port;dbname=$name;sslmode=$sslmode",
$username,
null,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_PERSISTENT => isset($config['persistent']) ? $config['persistent'] : false,
]);
]
);
}, ['useFactory'=>true]);
/**
* Locks
......
......@@ -11,13 +11,15 @@ class Client
public function __construct($dbh = null)
{
$this->dbh = $dbh ?: new PDO('pgsql:host=cockroachdb;port=26257;dbname=minds;sslmode=disable',
$this->dbh = $dbh ?: new PDO(
'pgsql:host=cockroachdb;port=26257;dbname=minds;sslmode=disable',
'maxroach',
null,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => true,
]);
]
);
}
/**
......
......@@ -72,8 +72,10 @@ class EndFacebookSupport
foreach ($this->getUsers() as $user) {
if (!$user instanceof \Minds\Entities\User || !$user->guid || $user->disabled_emails || $user->enabled != "yes") {
$skipped++;
echo "\r [emails]: $queued queued | $skipped skipped | " . date('d-m-Y',
$user->time_created) . " | $user->guid ";
echo "\r [emails]: $queued queued | $skipped skipped | " . date(
'd-m-Y',
$user->time_created
) . " | $user->guid ";
continue;
}
$queued++;
......@@ -95,8 +97,10 @@ class EndFacebookSupport
if (!$this->dryRun) {
$this->mailer->queue($message);
}
echo "\r [emails]: $queued queued | $skipped skipped | " . date('d-m-Y',
$user->time_created) . " | $user->guid ";
echo "\r [emails]: $queued queued | $skipped skipped | " . date(
'd-m-Y',
$user->time_created
) . " | $user->guid ";
}
echo "[emails]: Completed ($queued queued | $skipped skipped) \n";
}
......
......@@ -59,8 +59,10 @@ class GlobalTips extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -70,8 +70,10 @@ class InactiveUsers extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($this->subject)
->setHtml($this->template);
......
......@@ -65,8 +65,10 @@ class Invoice
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1('invoice' . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1('invoice' . time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -76,8 +76,10 @@ class MissedSinceLogin extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($this->subject)
->setHtml($this->template);
......
......@@ -73,8 +73,10 @@ class News extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($this->subject)
->setHtml($this->template);
......
......@@ -70,8 +70,10 @@ class Promotion extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($this->subject)
->setHtml($this->template);
......
You've received a gift of 1 Minds token! You can spend this token to earn 1,000 extra views on your content with [Boost](https://www.minds.com/boost?__e_ct_guid=<?= $vars['guid']?>&campaign=<?= $vars['campaign']?>&topic=<?= $vars['topic'] ?>&validator=<?= $vars['validator'] ?>) or to tip your favorite content creators with [Wire](https://www.minds.com/wire?__e_ct_guid=<?= $vars['guid']?>&campaign=<?= $vars['campaign']?>&topic=<?= $vars['topic'] ?>&validator=<?= $vars['validator'] ?>).
Please use the button below to claim your gift (note: you will need to open the link in a web browser, the mobile app is not yet supported):
| |
|:--:|
| [![Claim Gift](https://cdn-assets.minds.com/emails/claim-gift.png){=150x}](https://www.minds.com/wallet/tokens/transactions?__e_ct_guid=<?= $vars['guid']?>&campaign=<?= $vars['campaign']?>&topic=<?= $vars['topic'] ?>&validator=<?= $vars['validator'] ?>) |
| |
......@@ -62,8 +62,10 @@ class GoneCold extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -62,8 +62,10 @@ class WelcomeComplete extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -62,8 +62,10 @@ class WelcomeIncomplete extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -60,8 +60,10 @@ class WhenBoost extends EmailCampaign
$subject = 'Your boost is complete';
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -49,8 +49,10 @@ class WhenNotifications extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign.$this->topic.time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -75,8 +75,10 @@ class WhenWire extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($this->subject)
->setHtml($this->template);
......
......@@ -118,8 +118,10 @@ class WirePayment
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->template . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->template . time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -75,8 +75,10 @@ class WirePromotions extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($this->subject)
->setHtml($this->template);
......
......@@ -58,8 +58,10 @@ class WithActivity extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -72,8 +72,10 @@ class WithBlogs extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -50,8 +50,10 @@ class WithChannelTips extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -39,8 +39,10 @@ class WithImprovementTips extends EmailCampaign
$message = new Message();
$message->setTo($this->user)
->setMessageId(implode('-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]))
->setMessageId(implode(
'-',
[$this->user->guid, sha1($this->user->getEmail()), sha1($this->campaign . $this->topic . time())]
))
->setSubject($subject)
->setHtml($this->template);
......
......@@ -3,6 +3,12 @@ namespace Minds\Core\Email;
use Minds\Traits\MagicAttributes;
/**
* Class EmailSubscription
* @package Minds\Core\Email
*
* @method string getUserGuid()
*/
class EmailSubscription
{
use MagicAttributes;
......
......@@ -38,10 +38,14 @@ class Manager
$result = $this->repository->getList($options);
if (!$result || count($result['data'] === 0)) {
if (!$result || count($result['data']) === 0) {
return [];
}
/*$guids = [];
foreach($result['data'] as $subscription) {
$guids[] = $subscription->getUserGuid();
}*/
$guids = array_map(function ($item) {
return $item->getUserGuid();
}, $result['data']);
......
......@@ -79,8 +79,8 @@ class Manager
public function save(
$entity,
User $moderator,
int $time = null)
{
int $time = null
) {
if (!$time) {
$time = time();
}
......@@ -92,8 +92,8 @@ class Manager
private function saveEntity(
$entity,
User $moderator,
int $time = null)
{
int $time = null
) {
$entity->setModeratorGuid($moderator->getGUID());
$entity->setTimeModerated($time);
......
......@@ -28,6 +28,7 @@ class Repository
$opts = array_merge([
'container_guid' => null,
'type' => null,
'owner_guid' => null,
], $opts);
if (!$opts['type']) {
......@@ -64,6 +65,16 @@ class Repository
]
];
if ($opts['owner_guid']) {
$ownerGuids = Text::buildArray($opts['owner_guid']);
$query['body']['query']['bool']['must'][] = [
'terms' => [
'owner_guid' => $ownerGuids,
],
];
}
$prepared = new Prepared\Count();
$prepared->query($query);
......
......@@ -34,7 +34,8 @@ class Entities
* @param ACL $acl
*/
public function __construct(
$entitiesBuilder = null, $acl = null
$entitiesBuilder = null,
$acl = null
) {
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->acl = $acl ?: ACL::_();
......
<?php
/**
* Index
* @author edgebal
*/
namespace Minds\Core\Front;
use Minds\Core\Config\Exported;
use Minds\Core\Di\Di;
use Minds\Core\Pro\Domain as ProDomain;
use Minds\Core\Pro\Settings;
use Minds\Core\SEO\Manager;
class Index
{
/** @var Exported */
protected $configExported;
/** @var ProDomain */
protected $proDomain;
/** @var string[] */
protected $meta = [];
/** @var string[] */
protected $head = [];
/** @var string[] */
protected $tail = [];
/** @var string */
protected $context = 'app';
/** @var string */
protected $title = '';
/**
* Index constructor.
* @param Exported $configExported
* @param ProDomain $proDomain
*/
public function __construct(
$configExported = null,
$proDomain = null
) {
$this->configExported = $configExported ?: Di::_()->get('Config\Exported');
$this->proDomain = $proDomain ?: Di::_()->get('Pro\Domain');
$this->build();
}
public function build(): void
{
$this->meta = [];
$this->head = [];
$this->tail = [];
//
/** @var Settings|null $pro */
$pro = $this->proDomain->lookup($_SERVER['HTTP_HOST'] ?? null);
// Config variable
$minds = $this->configExported->export();
if ($pro) {
$minds['pro'] = $pro;
}
// Title
$this->title = 'Minds';
if ($pro) {
$this->title = $pro->getTitle() ?: $pro->getDomain();
}
// Favicons
$icons = [
[
'type' => 'image/svg',
'href' => "{$minds['cdn_assets_url']}assets/logos/bulb.svg",
],
[
'rel' => 'apple-touch-icon',
'type' => 'image/png',
'href' => "{$minds['cdn_assets_url']}assets/logos/bulb-apple-touch-icon.png",
'sizes' => '180x180',
],
[
'type' => 'image/png',
'href' => "{$minds['cdn_assets_url']}assets/logos/bulb-32x32.png",
'sizes' => '32x32',
],
[
'type' => 'image/png',
'href' => "{$minds['cdn_assets_url']}assets/logos/bulb-16x16.png",
'sizes' => '16x16',
],
];
if ($pro) {
$icons = [
[
'type' => 'image/jpeg',
'href' => $this->proDomain->getIcon($pro),
],
];
}
foreach ($icons as $icon) {
$attrs = [];
foreach (array_merge(['rel' => 'icon'], $icon) as $key => $value) {
$attrs[] = sprintf("%s=%s", $key, htmlspecialchars($value));
}
$this->meta[] = sprintf("<link %s />", implode(' ', $attrs));
}
// SEO Meta + Title Override
$meta = Manager::get();
foreach ($meta as $name => $content) {
$name = htmlspecialchars($name);
$content = htmlspecialchars($content);
if ($name === 'title') {
$this->title = $content;
continue;
}
$nameAttr = 'name';
if (
strpos($name, ":") !== false &&
strpos($name, "smartbanner") === false
) {
// Attributes with a colon that are not smartbanner
// should use property="<name>"
$nameAttr = 'property';
}
$this->meta[] = sprintf(
"<meta %s=\"%s\" content=\"%s\" />",
$nameAttr,
$name,
$content
);
}
// Head
if ($pro) {
$this->head[] = sprintf(
"<!-- Minds Pro: %s -->\n%s\n<!-- End -->",
$pro->getUserGuid(),
$pro->getCustomHead() ?: '<!-- (no custom head) -->'
);
}
// Tail
$this->tail[] = sprintf("<script>window.Minds = %s;</script>", json_encode($minds));
}
/**
* @return string
*/
public function getMetaHtml(): string
{
return PHP_EOL . implode(PHP_EOL, $this->meta) . PHP_EOL;
}
/**
* @return string
*/
public function getHeadHtml(): string
{
return PHP_EOL . implode(PHP_EOL, $this->head) . PHP_EOL;
}
/**
* @return string
*/
public function getTailHtml(): string
{
return PHP_EOL . implode(PHP_EOL, $this->tail) . PHP_EOL;
}
/**
* @return string
*/
public function getContext(): string
{
return $this->context;
}
/**
* @return string
*/
public function getTitle(): string
{
return $this->title;
}
}
<?php
/**
* Module
* @author edgebal
*/
namespace Minds\Core\Front;
use Minds\Interfaces\ModuleInterface;
class Module implements ModuleInterface
{
/**
* Executed onInit
* @return void
*/
public function onInit(): void
{
(new Provider())->register();
}
}
<?php
/**
* Provider
* @author edgebal
*/
namespace Minds\Core\Front;
use Minds\Core\Di\Provider as DiProvider;
class Provider extends DiProvider
{
public function register()
{
$this->di->bind('Front\Index', function () {
return new Index();
}, [ 'useFactory' => true ]);
}
}
......@@ -42,7 +42,6 @@ class Notifications
$cql = null,
$notifications = null,
$notificationBatches = null
) {
$this->relDB = $relDb ?: Di::_()->get('Database\Cassandra\Relationships');
$this->indexDb = $indexDb ?: Di::_()->get('Database\Cassandra\Indexes');
......
......@@ -27,7 +27,10 @@ class CurlWrapper
curl_setopt($this->handle, CURLOPT_BUFFERSIZE, 128);
curl_setopt($this->handle, CURLOPT_NOPROGRESS, false);
curl_setopt($this->handle, CURLOPT_PROGRESSFUNCTION, function (
$downloadSize, $downloaded, $uploadSize, $uploaded
$downloadSize,
$downloaded,
$uploadSize,
$uploaded
) use ($limitKb) {
error_log($downloaded);
if ($downloaded) {
......
......@@ -20,7 +20,7 @@ class Image implements AssetsInterface
public function upload(array $media, array $data)
{
$filename = "/image/{$this->entity->batch_guid}/{$this->entity->guid}/master.jpg";
$filename = "image/{$this->entity->batch_guid}/{$this->entity->guid}/master.jpg";
// @note: legacy file handling
$file = new \ElggFile();
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.