...
 
Commits (11)
......@@ -190,7 +190,7 @@ class channel implements Interfaces\Api
'src'=> Core\Config::build()->cdn_url . "fs/v1/banners/$item->guid/fat/$item->last_updated"
);
if (is_uploaded_file($_FILES['file']['tmp_name'])) {
if ($item->canEdit() && is_uploaded_file($_FILES['file']['tmp_name'])) {
$manager->setImage($_FILES['file']['tmp_name'])
->autorotate()
->resize(2000, 10000);
......
......@@ -34,6 +34,7 @@ class config implements Interfaces\Api, Interfaces\ApiIgnorePam
"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'),
......
<?php
/**
* Referrals API
*
* @version 1
* @author Olivia Madrid
*/
namespace Minds\Controllers\api\v2;
use Minds\Core;
use Minds\Core\Referrals\Referral;
use Minds\Interfaces;
use Minds\Api\Factory;
use Minds\Core\Di\Di;
class referrals implements Interfaces\Api
{
/**
* Returns a list of referrals
* @param array $pages
*/
public function get($pages)
{
$response = [];
$referrer_guid = isset($pages[0]) ? $pages[0] : Core\Session::getLoggedInUser()->guid;
$limit = isset($_GET['limit']) ? $_GET['limit'] : 12;
$offset = isset($_GET['offset']) ? $_GET['offset'] : "";
$manager = Di::_()->get('Referrals\Manager');
$opts = [
'referrer_guid'=>$referrer_guid,
'limit'=>$limit,
'offset'=>$offset
];
$referrals = $manager->getList($opts);
if (!$referrals) {
return Factory::response(array(
'status' => 'error',
'message' => 'You have no referrals'
));
}
$response['referrals'] = Factory::exportable(array_values($referrals->toArray()));
$response['load-next'] = (string) $referrals->getPagingToken();
return Factory::response($response);
}
// Not implemented
// Note: New referrals are added when prospect registers for Minds (in `Core/Events/Hooks/Register.php`)
public function post($pages)
{
}
// Notify a prospect to urge them to join the rewards program
// Note: referrals are updated when prospect joins rewards (in `Core/Rewards/Delegates/ReferralDelegate.php`)
public function put($pages)
{
$referrer_guid = Core\Session::getLoggedInUser()->guid;
if (!$referrer_guid) {
return Factory::response([
'status' => 'error',
'message' => 'You must be logged in to trigger a notification',
]);
}
if (!isset($pages[0])) {
return Factory::response([
'status' => 'error',
'message' => 'Prospect guid is required to trigger a notification',
]);
}
$prospect_guid = $pages[0];
$referral = new Referral;
$referral->setReferrerGuid($referrer_guid)
->setProspectGuid($prospect_guid)
->setPingTimestamp(time());
$manager = Di::_()->get('Referrals\Manager');
if(!$manager->ping($referral)){
return Factory::response([
'status' => 'error',
'done' => false,
]);
}
return Factory::response([
'status' => 'success',
'done' => true,
]);
}
// Not implemented
public function delete($pages)
{
}
}
......@@ -3,8 +3,10 @@
namespace Minds\Core\Events\Hooks;
use Minds\Core;
use Minds\Core\Referrals\Referral;
use Minds\Entities;
use Minds\Core\Events\Dispatcher;
use Minds\Core\Di\Di;
class Register
{
......@@ -36,6 +38,15 @@ class Register
$params['user']->save();
$params['user']->subscribe($user->guid);
}
$referral = new Referral();
$referral->setProspectGuid($params['user']->getGuid())
->setReferrerGuid((string) $user->guid)
->setRegisterTimestamp(time());
$manager = Di::_()->get('Referrals\Manager');
$manager->add($referral);
}
});
......
This diff is collapsed.
......@@ -23,6 +23,7 @@ class Minds extends base
Subscriptions\Module::class,
SendWyre\Module::class,
Suggestions\Module::class,
Referrals\Module::class,
Reports\Module::class,
VideoChat\Module::class,
];
......
......@@ -100,6 +100,9 @@ class Manager
'friends',
'welcome_chat',
'welcome_discover',
'referral_ping',
'referral_pending',
'referral_complete',
];
break;
case "groups":
......@@ -187,6 +190,9 @@ class Manager
case 'friends':
case 'welcome_chat':
case 'welcome_discover':
case 'referral_ping':
case 'referral_pending':
case 'referral_complete':
return 'subscriptions';
break;
case 'group_invite':
......
......@@ -20,9 +20,6 @@ class PushSettings
'friends' => true,
'remind' => true,
'boost_gift' => true,
'friends' => true,
'remind' => true,
'boost_gift' => true,
'boost_request' => true,
'boost_accepted' => true,
'boost_rejected' => true,
......@@ -30,6 +27,9 @@ class PushSettings
'boost_completed' => true,
'group_invite' => true,
'messenger_invite' => true,
'referral_ping' => true,
'referral_pending' => true,
'referral_complete' => true,
];
protected $userGuid;
protected $toBeSaved = [];
......
......@@ -37,9 +37,9 @@ class UpdateMarker
public function export()
{
return [
'user_guid' => $this->userGuid,
'user_guid' => (string) $this->userGuid,
'entity_type' => $this->entityType,
'entity_guid' => $this->entityGuid,
'entity_guid' => (string) $this->entityGuid,
'marker' => $this->marker,
'updated_timestamp' => $this->updatedTimestamp,
'disabled' => (bool) $this->disabled,
......
......@@ -1415,6 +1415,15 @@ CREATE TABLE minds.hidden_hashtags (
PRIMARY KEY (hashtag)
);
CREATE TABLE minds.referrals (
referrer_guid bigint,
prospect_guid bigint,
register_timestamp timestamp,
join_timestamp timestamp,
ping_timestamp timestamp,
PRIMARY KEY (referrer_guid, prospect_guid)
) WITH CLUSTERING ORDER BY (prospect_guid DESC);
CREATE TABLE minds.search_dispatcher_queue (
entity_urn text,
last_retry timestamp,
......
<?php
/**
* Notification delegate for referrals
*/
namespace Minds\Core\Referrals\Delegates;
use Minds\Core\Di\Di;
use Minds\Core\Referrals\Referral;
use Minds\Core\Events\EventsDispatcher;
class NotificationDelegate
{
/** @var EventsDispatcher */
protected $dispatcher;
/** @var EntitiesBuilder $entitiesBuilder */
protected $entitiesBuilder;
public function __construct($dispatcher = null, $entitiesBuilder = null, $urn = null)
{
$this->dispatcher = $dispatcher ?: Di::_()->get('EventsDispatcher');
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
}
/**
* Sends a notification to referrer on prospect state change
* @param Referral $referral
* @return void
*/
public function notifyReferrer(Referral $referral)
{
$entityGuid = $referral->getProspectGuid();
$entity = $this->entitiesBuilder->single($entityGuid);
if (!$referral->getJoinTimestamp()) {
$notification_view = 'referral_pending';
} else {
$notification_view = 'referral_complete';
}
$this->dispatcher->trigger('notification', 'all', [
'to' => [$referral->getReferrerGuid()],
'entity' => $entity,
'from' => $referral->getProspectGuid(),
'notification_view' => $notification_view,
'params' => [],
]);
}
/**
* Sends a notification to pending referral prospect to suggest they join rewards program
* @param Referral $referral
* @return void
*/
public function notifyProspect(Referral $referral)
{
$entityGuid = $referral->getReferrerGuid();
$entity = $this->entitiesBuilder->single($entityGuid);
if ($referral->getJoinTimestamp()) {
return;
}
$this->dispatcher->trigger('notification', 'all', [
'to' => [$referral->getProspectGuid()],
'entity' => $entity,
'from' => $referral->getReferrerGuid(),
'notification_view' => 'referral_ping',
'params' => [],
]);
}
}
\ No newline at end of file
<?php
/**
* Referrals Manager
*/
namespace Minds\Core\Referrals;
use Minds\Core\Referrals\Referral;
use Minds\Core\Referrals\Repository;
use Minds\Core\Referrals\Delegates\NotificationDelegate;
use Minds\Core\EntitiesBuilder;
use Minds\Common\Repository\Response;
use Minds\Core\Di\Di;
class Manager
{
/** @var Repository $repository */
private $repository;
/** @var Delegates\NotificationDelegate $notificationDelegate */
private $notificationDelegate;
public function __construct(
$repository = null,
$notificationDelegate = null,
$entitiesBuilder = null
)
{
$this->repository = $repository ?: new Repository;
$this->notificationDelegate = $notificationDelegate ?: new Delegates\NotificationDelegate;
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
}
/**
* Return a list of referrals
* @param array $opts
* @return Response
*/
public function getList($opts = [])
{
$opts = array_merge([
'limit' => 12,
'offset' => '',
'referrer_guid' => null,
'hydrate' => true,
], $opts);
$response = $this->repository->getList($opts);
if ($opts['hydrate']) {
foreach ($response as $referral) {
$prospect = $this->entitiesBuilder->single($referral->getProspectGuid());
$referral->setProspect($prospect);
}
}
return $response;
}
/**
* Create referral for pending prospect who registered for Minds
* @param Referral $referral
* @return bool
*/
public function add($referral)
{
$this->repository->add($referral);
// Send a notification to the referrer
$this->notificationDelegate->notifyReferrer($referral);
return true;
}
/**
* Update referral for completed prospect who has joined rewards program
* @param Referral $referral
* @return bool
*/
public function update($referral)
{
// Update join_timestamp
$this->repository->update($referral);
// Send a notification to the referrer
$this->notificationDelegate->notifyReferrer($referral);
return true;
}
/**
* Send a notification to pending prospect to suggest they join rewards program
* @param Referral $referral
* @return bool
*/
public function ping($referral)
{
// Update ping_timestamp
$this->repository->ping($referral);
// Send a ping notification to the prospect
$this->notificationDelegate->notifyProspect($referral);
return true;
}
}
<?php
/**
* Referrals module
*/
namespace Minds\Core\Referrals;
use Minds\Interfaces\ModuleInterface;
class Module implements ModuleInterface
{
/**
* OnInit.
*/
public function onInit()
{
$provider = new Provider();
$provider->register();
}
}
<?php
/**
* Minds Referrals Provider
*/
namespace Minds\Core\Referrals;
use Minds\Core\Di\Provider as DiProvider;
class Provider extends DiProvider
{
public function register()
{
$this->di->bind('Referrals\Manager', function ($di) {
return new Manager();
}, [ 'useFactory'=>false ]);
}
}
<?php
/**
* Referral Model
*/
namespace Minds\Core\Referrals;
use Minds\Traits\MagicAttributes;
/**
* Referral
* @package Minds\Core\Referrals
* @method Referral setReferrerGuid()
* @method long getReferrerGuid()
* @method Referral setProspectGuid()
* @method long getProspectGuid()
* @method Referral setProspect()
* @method User getProspect()
* @method Referral setRegisterTimestamp(int $ts)
* @method int getRegisterTimestamp
* @method Referral setJoinTimestamp(int $ts)
* @method int getJoinTimestamp
* @method Referral setPingTimestamp(int $ts)
* @method int getPingTimestamp
*/
class Referral
{
use MagicAttributes;
/** @var long $referrerGuid */
private $referrerGuid;
/** @var long $prospectGuid */
private $prospectGuid;
/** @var User $prospect */
private $prospect;
/** @var int $registerTimestamp */
private $registerTimestamp;
/** @var int $joinTimestamp */
private $joinTimestamp;
/** @var int $pingTimestamp */
private $pingTimestamp;
/**
* Return the state
* @return string
*/
public function getState()
{
// Referral goes from pending to complete when the prospect joins rewards
if ($this->joinTimestamp) {
return 'complete';
}
return 'pending';
}
/**
* Return whether 7 days has passed since last ping
* @return bool
*/
public function getPingable()
{
// Duration referrer must wait before re-pinging (in seconds)
$waitTime= 60*60*24*7; // 7 days
$now = time();
$elapsedTime = $now - $this->pingTimestamp;
if ($this->pingTimestamp && $elapsedTime < $waitTime) {
return false;
}
// Also disable ping if they've already joined rewards
if ($this->joinTimestamp) {
return false;
}
return true;
}
/**
* Export
* @return array
*/
public function export()
{
return [
'referrer_guid' => $this->referrerGuid,
'prospect' => $this->prospect ? $this->prospect->export() : null,
'state' => $this->getState(),
'pingable' => $this->getPingable(),
'register_timestamp' => $this->registerTimestamp * 1000,
'join_timestamp' => $this->joinTimestamp * 1000,
'ping_timestamp' => $this->pingTimestamp * 1000
];
}
}
<?php
/**
* Cassandra Repository for Referrals
*/
namespace Minds\Core\Referrals;
use Minds\Common\Repository\Response;
use Minds\Core\Di\Di;
use Minds\Core\Data\Cassandra\Prepared;
use Cassandra;
use Cassandra\Bigint;
class Repository
{
/** @var Client $client */
private $client;
public function __construct($client = null)
{
$this->client = $client ?: Di::_()->get('Database\Cassandra\Cql');
}
/**
* Return a list of referrals
* @param array $opts
* @return Response
*/
public function getList($opts = [])
{
$opts = array_merge([
'limit' => 12,
'offset' => '',
'referrer_guid' => null,
], $opts);
if (!$opts['referrer_guid']) {
throw new \Exception('Referrer GUID is required');
}
$cqlOpts = [];
if ($opts['limit']) {
$cqlOpts['page_size'] = (int) $opts['limit'];
}
if ($opts['offset']) {
$cqlOpts['paging_state_token'] = base64_decode($opts['offset']);
}
$template = "SELECT * FROM referrals WHERE referrer_guid = ?";
$values = [ new Bigint($opts['referrer_guid']) ];
$query = new Prepared\Custom();
$query->query($template, $values);
$query->setOpts($cqlOpts);
$response = new Response();
try {
$rows = $this->client->request($query);
foreach ($rows as $row) {
$referral = new Referral();
$referral->setProspectGuid((string) $row['prospect_guid'])
->setReferrerGuid((string) $row['referrer_guid'])
->setRegisterTimestamp(isset($row['register_timestamp']) ? (int) $row['register_timestamp']->time() : null)
->setJoinTimestamp(isset($row['join_timestamp']) ? (int) $row['join_timestamp']->time() : null)
->setPingTimestamp(isset($row['ping_timestamp']) ? (int) $row['ping_timestamp']->time() : null);
$response[] = $referral;
}
$response->setPagingToken(base64_encode($rows->pagingStateToken()));
$response->setLastPage($rows->isLastPage());
} catch (\Exception $e) {
// $response = $e;
return $response;
}
return $response;
}
/**
* Add a referral
* @param Referral $referral
* @return bool
*/
public function add(Referral $referral)
{
if (!$referral->getReferrerGuid()) {
throw new \Exception('Referrer GUID is required');
}
if (!$referral->getProspectGuid()) {
throw new \Exception('Prospect GUID is required');
}
if (!$referral->getRegisterTimestamp()) {
throw new \Exception('Register timestamp is required');
}
$template = "INSERT INTO referrals
(referrer_guid, prospect_guid, register_timestamp)
VALUES
(?, ?, ?)
";
$values = [
new Cassandra\Bigint($referral->getReferrerGuid()),
new Cassandra\Bigint($referral->getProspectGuid()),
new Cassandra\Timestamp($referral->getRegisterTimestamp()),
];
$query = new Prepared\Custom();
$query->query($template, $values);
try {
$success = $this->client->request($query);
} catch (\Exception $e) {
return false;
}
return $success;
}
/**
* Update a referral when the prospect joins rewards program
* @param Referral $referral
* @return bool
*/
public function update(Referral $referral)
{
if (!$referral->getReferrerGuid()) {
throw new \Exception('Referrer GUID is required');
}
if (!$referral->getProspectGuid()) {
throw new \Exception('Prospect GUID is required');
}
if (!$referral->getJoinTimestamp()) {
throw new \Exception('Join timestamp is required');
}
$template = "UPDATE referrals SET join_timestamp = ? WHERE referrer_guid = ? AND prospect_guid = ?";
$values = [
new Cassandra\Timestamp($referral->getJoinTimestamp()),
new Cassandra\Bigint($referral->getReferrerGuid()),
new Cassandra\Bigint($referral->getProspectGuid()),
];
$query = new Prepared\Custom();
$query->query($template, $values);
try {
$success = $this->client->request($query);
} catch (\Exception $e) {
return false;
}
return true;
}
/**
* Update referral when prospect is notified by the referrer to urge them to join rewards
* @param Referral $referral
* @return bool
*/
public function ping(Referral $referral)
{
if (!$referral->getReferrerGuid()) {
throw new \Exception('Referrer GUID is required');
}
if (!$referral->getProspectGuid()) {
throw new \Exception('Prospect GUID is required');
}
if (!$referral->getPingTimestamp()) {
throw new \Exception('Ping timestamp is required');
}
$template = "UPDATE referrals SET ping_timestamp = ? WHERE referrer_guid = ? AND prospect_guid = ?";
$values = [
new Cassandra\Timestamp($referral->getPingTimestamp()),
new Cassandra\Bigint($referral->getReferrerGuid()),
new Cassandra\Bigint($referral->getProspectGuid()),
];
$query = new Prepared\Custom();
$query->query($template, $values);
try {
$success = $this->client->request($query);
} catch (\Exception $e) {
return false;
}
return true;
}
/**
* void
*/
public function delete($referral)
{
}
}
......@@ -9,7 +9,8 @@ class ContributionValues
'reminds' => 4,
'votes' => 1,
'subscribers' => 4,
'referrals' => 10,
'referrals' => 50,
'referrals_welcome' => 50,
'checkin' => 2,
'jury_duty' => 25,
];
......
......@@ -96,6 +96,15 @@ class Manager
return $contributions;
}
/**
* Add a contibution score row manually
* @param Contribution $contribution
* @return bool
*/
public function add(Contribution $contribution) : bool
{
return (bool) $this->repository->add($contribution);
}
public function issueCheckins($count)
{
......
<?php
/**
* Trigger referral update
*/
namespace Minds\Core\Rewards\Delegates;
use Minds\Core\Di\Di;
use Minds\Entities\User;
use Minds\Core\Referrals\Referral;
use Minds\Core\Rewards\Contributions\Contribution;
use Minds\Core\Rewards\Contributions\ContributionValues;
class ReferralDelegate
{
/** @var Manager $manager */
private $manager;
public function __construct($manager = null, $contributionsManager = null)
{
$this->manager = $manager ?: Di::_()->get('Referrals\Manager');
$this->contributionsManager = $contributionsManager ?? Di::_()->get('Rewards\Contributions\Manager');
}
/**
* Update a referral via Referral Manager
* @param Referral $referral
* @return void
*/
public function onReferral(User $user)
{
$referral = new Referral();
$referral->setReferrerGuid((string) $user->referrer)
->setProspectGuid($user->guid)
->setJoinTimestamp(time());
$this->manager->update($referral);
// TODO: This should be in its own delegate?
$this->issueContributionScore($user);
}
/**
* Issue contribution score when referred
* TODO: Move to own delegate?
* @param User $user
* @return void
*/
private function issueContributionScore(User $user) : void
{
$ts = strtotime('midnight') * 1000;
$contribution = new Contribution();
$contribution
->setMetric('referrals_welcome')
->setTimestamp($ts)
->setUser($user)
->setScore(ContributionValues::$multipliers['referrals_welcome'])
->setAmount(1);
$this->contributionsManager->add($contribution);
}
}
\ No newline at end of file
......@@ -6,6 +6,7 @@ namespace Minds\Core\Rewards;
use Minds\Core\Di\Di;
use Minds\Core;
use Minds\Core\Referrals\Referral;
use Minds\Entities\User;
use Minds\Core\Util\BigNumber;
......@@ -41,13 +42,16 @@ class Join
/** @var OfacBlacklist */
private $ofacBlacklist;
/** @var TestnetBalance */
private $testnetBalance;
/** @var Call */
private $db;
/** @var ReferralDelegate $eventsDelegate */
private $referralDelegate;
public function __construct(
$twofactor = null,
$sms = null,
......@@ -57,7 +61,8 @@ class Join
$db = null,
$joinedValidator = null,
$ofacBlacklist = null,
$testnetBalance = null
$testnetBalance = null,
$referralDelegate = null
)
{
$this->twofactor = $twofactor ?: Di::_()->get('Security\TwoFactor');
......@@ -69,6 +74,7 @@ class Join
$this->joinedValidator = $joinedValidator ?: Di::_()->get('Rewards\JoinedValidator');
$this->ofacBlacklist = $ofacBlacklist ?: Di::_()->get('Rewards\OfacBlacklist');
$this->testnetBalance = $testnetBalance ?: Di::_()->get('Blockchain\Wallets\OffChain\TestnetBalance');
$this->referralDelegate = $referralDelegate ?: new Delegates\ReferralDelegate;
}
public function setUser(&$user)
......@@ -132,11 +138,10 @@ class Join
public function confirm()
{
if ($this->user->getPhoneNumberHash()) {
return false; //already joined
}
if ($this->twofactor->verifyCode($this->secret, $this->code, 8)) {
$hash = hash('sha256', $this->number . $this->config->get('phone_number_hash_salt'));
$this->user->setPhoneNumberHash($hash);
......@@ -152,22 +157,17 @@ class Join
->setAction('joined')
->push();
$this->testnetBalance->setUser($this->user);
$testnetBalanceVal = BigNumber::_($this->testnetBalance->get());
if ($testnetBalanceVal->lt(0)) {
return false; //balance negative
}
// User receives one free token automatically when they join rewards
$transactions = Di::_()->get('Blockchain\Wallets\OffChain\Transactions');
$transactions
->setUser($this->user)
->setType('joined')
->setAmount((string) $testnetBalanceVal);
->setAmount(pow(10,18));
$transaction = $transactions->create();
}
// Validate referral and give both prospect and referrer +50 contribution score
if ($this->user->referrer && $this->user->guid != $this->user->referrer) {
$this->validator->setHash($hash);
......@@ -181,6 +181,10 @@ class Join
->setEntityType('user')
->setAction('referral')
->push();
// TODO: give prospect +50 contribution score as well
$this->referralDelegate->onReferral($this->user);
}
}
} else {
......
......@@ -7,6 +7,10 @@ class RewardsProvider extends Provider
{
public function register()
{
$this->di->bind('Rewards\Contributions\Manager', function ($di) {
return new Contributions\Manager();
}, [ 'useFactory'=> true ]);
$this->di->bind('Rewards\Contributions\Repository', function ($di) {
return new Contributions\Repository();
}, [ 'useFactory'=> true ]);
......
......@@ -337,6 +337,23 @@ class Defaults
return $meta;
});
Manager::add('/wallet/tokens/referrals', function ($slugs = []) {
$meta = [
'title' => 'Referrals',
'description' => 'Share links and track your referrals',
'og:title' => 'Referrals',
'og:description' => 'Share links and track your referrals',
'og:url' => $this->config->site_url . 'wallet/tokens/referrals',
'og:image' => $this->config->cdn_assets_url . 'assets/photos/graph.jpg',
'og:image:width' => 2000,
'og:image:height' => 1000,
'twitter:site' => '@minds',
'twitter:card' => 'summary',
];
return $meta;
});
$marketing = [
'plus' => [
'title' => 'Minds Plus',
......@@ -429,7 +446,7 @@ class Defaults
}
}
public function channelHandler($slugs = [])
public function channelHandler($slugs = [])
{
$username = ($slugs[0] == 'blog') ? $slugs[1]: $slugs[0];
if (isset($username) && is_string($username)) {
......
<?php
namespace Spec\Minds\Core\Referrals\Delegates;
use Minds\Core\Referrals\Referral;
use Minds\Core\Referrals\Manager;
use Minds\Core\Referrals\Repository;
use Minds\Core\Referrals\Delegates\NotificationDelegate;
use Minds\Core\Di\Di;
use Minds\Core\Events\EventsDispatcher;
use Minds\Core\EntitiesBuilder;
use Minds\Entities\Entity;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class NotificationDelegateSpec extends ObjectBehavior
{
/** @var EventsDispatcher $dispatcher */
private $dispatcher;
/** @var EntitiesBuilder $entitiesBuilder */
private $entitiesBuilder;
function let(
EventsDispatcher $dispatcher,
EntitiesBuilder $entitiesBuilder
)
{
$this->beConstructedWith($dispatcher, $entitiesBuilder);
$this->dispatcher=$dispatcher;
$this->entitiesBuilder = $entitiesBuilder;
}
function it_is_initializable()
{
$this->shouldHaveType(NotificationDelegate::class);
}
function it_should_send_a_pending_referral_notification_to_referrer(Referral $referral, Entity $entity)
{
$referral->getReferrerGuid()
->shouldBeCalled()
->willReturn(456);
$referral->getProspectGuid()
->shouldBeCalled()
->willReturn(123);
$this->entitiesBuilder->single(123)
->willReturn($entity);
$referral->getJoinTimestamp()
->shouldBeCalled()
->willReturn(null); // Referral is pending bc hasn't joined rewards yet
$this->dispatcher->trigger('notification', 'all', Argument::that(function ($opts) {
return $opts['notification_view'] === 'referral_pending';
}))
->shouldBeCalled();
$this->notifyReferrer($referral);
}
function it_should_send_a_completed_referral_notification_to_referrer(Referral $referral, Entity $entity )
{
$referral->getReferrerGuid()
->shouldBeCalled()
->willReturn(456);
$referral->getProspectGuid()
->shouldBeCalled()
->willReturn(123);
$this->entitiesBuilder->single(123)
->willReturn($entity);
$referral->getJoinTimestamp()
->shouldBeCalled()
->willReturn(111); // Referral is complete bc prospect has joined rewards
$this->dispatcher->trigger('notification', 'all', Argument::that(function ($opts) {
return $opts['notification_view'] === 'referral_complete';
}))
->shouldBeCalled();
$this->notifyReferrer($referral);
}
function it_should_send_a_ping_notification_to_pending_prospect(Referral $referral, Entity $entity )
{
$referral->getProspectGuid()
->shouldBeCalled()
->willReturn(123);
$referral->getReferrerGuid()
->shouldBeCalled()
->willReturn(456);
$this->entitiesBuilder->single(456)
->willReturn($entity);
$referral->getJoinTimestamp()
->shouldBeCalled()
->willReturn(); // Referral is pending bc hasn't joined rewards yet
$this->dispatcher->trigger('notification', 'all', Argument::that(function ($opts) {
return $opts['notification_view'] === 'referral_ping';
}))
->shouldBeCalled();
$this->notifyProspect($referral);
}
function it_should_not_send_a_ping_notification_to_completed_prospect(Referral $referral, Entity $entity )
{
$referral->getReferrerGuid()
->shouldBeCalled()
->willReturn(456);
$this->entitiesBuilder->single(456)
->willReturn($entity);
$referral->getJoinTimestamp()
->shouldBeCalled()
->willReturn(111); // Referral is complete bc joined rewards
$this->dispatcher->trigger('notification', 'all', Argument::that(function ($opts) {
return $opts['notification_view'] === 'referral_ping';
}))
->shouldNotBeCalled();
$this->notifyProspect($referral);
}
}
<?php
namespace Spec\Minds\Core\Referrals;
use Minds\Core\Referrals\Manager;
use Minds\Core\Referrals\Referral;
use Minds\Core\Referrals\Repository;
use Minds\Core\Referrals\Delegates\NotificationDelegate;
use Minds\Core\EntitiesBuilder;
use Minds\Entities\User;
use Minds\Common\Repository\Response;
use Minds\Core\Di\Di;
use PhpSpec\ObjectBehavior;
class ManagerSpec extends ObjectBehavior
{
private $repository;
private $notificationDelegate;
private $entitiesBuilder;
function let(
Repository $repository,
NotificationDelegate $notificationDelegate,
EntitiesBuilder $entitiesBuilder
)
{
$this->beConstructedWith($repository, $notificationDelegate, $entitiesBuilder);
$this->repository = $repository;
$this->notificationDelegate = $notificationDelegate;
$this->entitiesBuilder = $entitiesBuilder;
}
function it_is_initializable()
{
$this->shouldHaveType(Manager::class);
}
function it_should_add_a_referral()
{
$referral = new Referral();
$referral->setProspectGuid(444)
->setReferrerGuid(456)
->setRegisterTimestamp(21);
$this->repository->add($referral)
->shouldBeCalled();
$this->notificationDelegate->notifyReferrer($referral)
->shouldBeCalled();
$this->add($referral)
->shouldReturn(true);
}
function it_should_update_a_referral()
{
$referral = new Referral();
$referral->setProspectGuid(555)
->setReferrerGuid(456)
->setJoinTimestamp(22);
$this->repository->update($referral)
->shouldBeCalled();
$this->notificationDelegate->notifyReferrer($referral)
->shouldBeCalled();
$this->update($referral)
->shouldReturn(true);
}
function it_should_update_ping_timestamp_and_trigger_ping_notification()
{
$referral = new Referral();
$referral->setProspectGuid(123)
->setReferrerGuid(456)
->setPingTimestamp(111);
$this->repository->ping($referral)
->shouldBeCalled();
$this->notificationDelegate->notifyProspect($referral)
->shouldBeCalled();
$this->ping($referral)
->shouldReturn(true);
}
function it_should_get_a_list_of_referrals()
{
$response = new Response();
$response[] = (new Referral)
->setReferrerGuid(123)
->setProspectGuid(456)
->setRegisterTimestamp(11)
->setJoinTimestamp(22)
->setPingTimestamp(null);
$this->repository->getList([
'limit' => 12,
'offset' => '',
'referrer_guid' => 123,
'hydrate' => true,
])
->shouldBeCalled()
->willReturn($response);
$this->entitiesBuilder->single(456)
->shouldBeCalled()
->willReturn((new User)->set('guid', 456));
$newResponse = $this->getList([
'limit' => 12,
'offset' => '',
'referrer_guid' => 123,
'hydrate' => true
]);
$newResponse[0]->getReferrerGuid()
->shouldBe(123);
$newResponse[0]->getProspectGuid()
->shouldBe(456);
$newResponse[0]->getProspect()->getGuid()
->shouldBe(456);
$newResponse[0]->getRegisterTimestamp()
->shouldBe(11);
$newResponse[0]->getJoinTimestamp()
->shouldBe(22);
$newResponse[0]->getPingTimestamp()
->shouldBe(null);
}
}
<?php
namespace Spec\Minds\Core\Referrals;
use Minds\Core\Referrals\Referral;
use PhpSpec\ObjectBehavior;
class ReferralSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(Referral::class);
}
}
<?php
namespace Spec\Minds\Core\Referrals;
use Minds\Common\Repository\Response;
use Minds\Core\Referrals\Referral;
use Minds\Core\Referrals\Repository;
use Minds\Core\Data\Cassandra\Client;
use Cassandra\Bigint;
use Cassandra\Timestamp;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Spec\Minds\Mocks\Cassandra\Rows;
class RepositorySpec extends ObjectBehavior
{
private $client;
function let(Client $client)
{
$this->beConstructedWith($client);
$this->client = $client;
}
function it_is_initializable()
{
$this->shouldHaveType(Repository::class);
}
function it_should_add_a_referral(Referral $referral)
{
$referral->getReferrerGuid()
->shouldBeCalled()
->willReturn(123);
$referral->getProspectGuid()
->shouldBeCalled()
->willReturn(456);
$referral->getRegisterTimestamp()
->shouldBeCalled()
->willReturn(78);
$this->client->request(Argument::that(function($prepared) {
$values = $prepared->build()['values'];
$template = $prepared->build()['string'];
return strpos($template, 'INSERT INTO referrals') !== FALSE
&& $values[0]->value() == 123
&& $values[1]->value() == 456
&& $values[2]->value() == 78;
}))
->shouldBeCalled()
->willReturn(true);
$this->add($referral)
->shouldBe(true);
}
function it_should_update_a_referral()
{
$referral = new Referral();
$referral->setReferrerGuid(123)
->setProspectGuid(456)
->setJoinTimestamp(789);
$this
->update($referral)
->shouldReturn(true);
}
function it_should_return_a_list_of_referrals()
{
$this->client->request(Argument::that(function($prepared) {
return true;
}))
->shouldBeCalled()
->willReturn(new Rows([
[
'referrer_guid' => new Bigint(123),
'prospect_guid' => new Bigint(456),
'register_timestamp' => new Timestamp(1545451597777),
'join_timestamp' => new Timestamp(1545451597778),
'ping_timestamp' => null,
],
[
'referrer_guid' => new Bigint(123),
'prospect_guid' => new Bigint(567),
'register_timestamp' => new Timestamp(1545451598888),
'join_timestamp' => new Timestamp(1545451598889),
'ping_timestamp' => null,
],
], 'my-cool-paging-token'));
$response = $this->getList([
'referrer_guid' => 123,
]);
$response->shouldHaveCount(2);
$response[0]->getProspectGuid()
->shouldBe('456');
$response[0]->getRegisterTimestamp()
->shouldBe(1545451597777);
$response[0]->getJoinTimestamp()
->shouldBe(1545451597778);
}
function it_should_throw_if_no_referrer_guid_during_get_list()
{
$opts = [
'limit' => 1000,
'offset' => 2000,
];
$this->shouldThrow(new \Exception('Referrer GUID is required'))
->duringGetList($opts);
}
function it_should_throw_if_no_prospect_guid_during_add()
{
$referral = new Referral();
$referral->setReferrerGuid(123);
$referral->setRegisterTimestamp(456);
$this->shouldThrow(new \Exception('Prospect GUID is required'))
->duringAdd($referral);
}
function it_should_throw_if_no_referrer_guid_during_add()
{
$referral = new Referral();
$referral->setProspectGuid(123);
$referral->setRegisterTimestamp(456);
$this->shouldThrow(new \Exception('Referrer GUID is required'))
->duringAdd($referral);
}
function it_should_throw_if_no_register_timestamp_during_add()
{
$referral = new Referral();
$referral->setReferrerGuid(123);
$referral->setProspectGuid(456);
$this->shouldThrow(new \Exception('Register timestamp is required'))
->duringAdd($referral);
}
function it_should_throw_if_no_join_timestamp_during_update()
{
$referral = new Referral();
$referral->setReferrerGuid(123);
$referral->setProspectGuid(456);
$this->shouldThrow(new \Exception('Join timestamp is required'))
->duringUpdate($referral);
}
}
......@@ -2,20 +2,34 @@
namespace Spec\Minds\Core\Reports;
use Minds\Core\Entities\Resolver;
use Minds\Core\Reports\Manager;
use Minds\Core\Reports\PreFeb2019Repository;
use Minds\Core\Reports\Repository;
use Minds\Core\Reports\Report;
use Minds\Core\Security\ACL;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ManagerSpec extends ObjectBehavior
{
private $repository;
function let(Repository $repo)
private $preFeb2019Repository;
private $entitiesResolver;
private $acl;
function let(
Repository $repo,
PreFeb2019Repository $preFeb2019Repository,
Resolver $entitiesResolver,
ACL $acl
)
{
$this->beConstructedWith($repo);
$this->beConstructedWith($repo, $preFeb2019Repository, $entitiesResolver, $acl);
$this->repository = $repo;
$this->preFeb2019Repository = $preFeb2019Repository;
$this->entitiesResolver = $entitiesResolver;
$this->acl = $acl;
}
function it_is_initializable()
......
<?php
namespace Spec\Minds\Core\Rewards\Delegates;
use Minds\Core\Rewards\Delegates\ReferralDelegate;
use Minds\Core\Referrals\Referral;
use Minds\Core\Referrals\Manager;
use Minds\Entities\User;
use Minds\Core\Di\Di;
use Minds\Core\Rewards\Contributions;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ReferralDelegateSpec extends ObjectBehavior
{
/** @var Manager $manager */
private $manager;
/** @var $contributionsManager */
private $contributionsManager;
/** @var User $user */
private $user;
function let(
Manager $manager,
User $user,
Contributions\Manager $contributionsManager
)
{
$this->beConstructedWith($manager, $contributionsManager);
$this->manager = $manager;
$this->contributionsManager = $contributionsManager;
$this->user = $user;
}
function it_is_initializable()
{
$this->shouldHaveType(ReferralDelegate::class);
}
function it_should_tell_manager_to_update_referral()
{
$user = new User();
$user->referrer = 123;
$user->guid = 456;
$referral = new Referral;
$referral->setReferrerGuid($user->referrer)
->shouldBeCalled();
$referral->setProspectGuid($user->guid)
->shouldBeCalled();
$referral->setJoinTimestamp(time())
->shouldBeCalled();
$this->manager->update($referral)
->shouldBeCalled();
$this->contributionsManager->add(Argument::that(function($contribution) {
return $contribution->getMetric() === 'referrals_welcome'
&& $contribution->getScore() === 50
&& $contribution->getAmount() === 1
&& $contribution->getUser()->guid === 456
&& $contribution->getTimestamp() === strtotime('midnight') * 1000;
}))
->willReturn(true);
$this->onReferral($user);
}
}
......@@ -487,10 +487,15 @@ $CONFIG->set('email', [
]
]);
/* Maximum video length for non-plus users */
$CONFIG->set('max_video_length', 900);
/* Maximum video length for plus */
$CONFIG->set('max_video_length_plus', 1860);
/* Maximum video file size, in bytes */
$CONFIG->set('max_video_file_size', 3900000000);
$CONFIG->set('aws', [
'key' => '',
'secret' => '',
......