Skip to content
Commits on Source (23)
......@@ -8,6 +8,9 @@
namespace Minds\Api;
use Minds\Core\Di\Di;
use Minds\Core\Session;
class Exportable implements \JsonSerializable
{
/** @var array */
......@@ -100,6 +103,14 @@ class Exportable implements \JsonSerializable
$exported = $item->export(...$this->exportArgs);
if ($item && Di::_()->get('Features\Manager')->has('permissions')) {
$userGuid = $user ? $user->getGuid() : null;
$permissionsManager = Di::_()->get('Permissions\Manager');
$permissions = $permissionsManager->getList(['user_guid' => Session::getLoggedinUser(),
'entities' => [$item]]);
$exported['permissions'] = $permissions->export();
}
// Shims
// TODO: Maybe allow customization via classes? i.e. JavascriptGuidShim, ExceptionShim, etc
......@@ -126,8 +137,6 @@ class Exportable implements \JsonSerializable
$exported[$exception] = $item->{$exception};
}
//
$output[$key] = $exported;
}
......
......@@ -208,15 +208,25 @@ class Factory
*/
public static function exportable($entities, $exceptions = [], $exportContext = false)
{
$permissionsManager = Di::_()->get('Permissions\Manager');
if (!$entities) {
return [];
}
foreach ($entities as $k => $entity) {
if ($exportContext && method_exists($entity, 'setExportContext')) {
$entity->setExportContext($exportContext);
}
$entities[$k] = $entity->export();
//Calculate new permissions object with the entities
if ($entity && Di::_()->get('Features\Manager')->has('permissions')) {
$userGuid = $user ? $user->getGuid() : null;
$permissions = $permissionsManager->getList(['user_guid' => Session::getLoggedinUser(),
'entities' => [$entity]]);
$entities[$k]['permissions'] = $permissions->export();
}
$entities[$k]['guid'] = (string) $entities[$k]['guid']; //javascript doesn't like long numbers..
if (isset($entities[$k]['ownerObj']['guid'])) {
$entities[$k]['ownerObj']['guid'] = (string) $entity->ownerObj['guid'];
......
......@@ -119,6 +119,7 @@ class authenticate implements Interfaces\Api, Interfaces\ApiIgnorePam
public function delete($pages)
{
/** @var Core\Sessions\Manager $sessions */
$sessions = Di::_()->get('Sessions\Manager');
if (isset($pages[0]) && $pages[0] === 'all') {
......
......@@ -362,7 +362,7 @@ class blog implements Interfaces\Api
}
if ($saved) {
if ($blog->isPublished() && $blog->getAccessId() == Access::PUBLIC) {
if ($blog->isPublished() && in_array($blog->getAccessId(), [Access::PUBLIC, Access::LOGGED_IN], false)) {
if (!$editing || ($editing && !$alreadyPublished) || ($editing && $oldAccessId == Access::UNLISTED)) {
(new CreateActivity())->save($blog);
}
......
......@@ -526,9 +526,6 @@ class newsfeed implements Interfaces\Api
$activity->indexes = ["activity:$activity->owner_guid:edits"]; //don't re-index on edit
(new Core\Translation\Storage())->purge($activity->guid);
$attachmentPaywallDelegate = new Core\Feeds\Activity\Delegates\AttachmentPaywallDelegate();
$attachmentPaywallDelegate->onUpdate($activity);
if (isset($_POST['time_created']) && ($_POST['time_created'] != $activity->getTimeCreated())) {
try {
$timeCreatedDelegate = new Core\Feeds\Activity\Delegates\TimeCreatedDelegate();
......@@ -544,6 +541,8 @@ class newsfeed implements Interfaces\Api
$save->setEntity($activity)
->save();
(new Core\Entities\PropagateProperties())->from($activity);
$activity->setExportContext(true);
return Factory::response(['guid' => $activity->guid, 'activity' => $activity->export(), 'edited' => true]);
}
......
......@@ -67,46 +67,49 @@ class views implements Interfaces\Api
]);
break;
case 'activity':
$activity = new Entities\Activity($pages[1]);
case 'entity':
$entity = Entities\Factory::build($pages[1]);
if (!$activity->guid) {
if (!$entity) {
return Factory::response([
'status' => 'error',
'message' => 'Could not find activity post'
'message' => 'Could not the entity'
]);
}
try {
Core\Analytics\App::_()
if ($entity->type === 'activity') {
try {
Core\Analytics\App::_()
->setMetric('impression')
->setKey($activity->guid)
->setKey($entity->guid)
->increment();
if ($activity->remind_object) {
Core\Analytics\App::_()
if ($entity->remind_object) {
Core\Analytics\App::_()
->setMetric('impression')
->setKey($activity->remind_object['guid'])
->setKey($entity->remind_object['guid'])
->increment();
Core\Analytics\App::_()
Core\Analytics\App::_()
->setMetric('impression')
->setKey($activity->remind_object['owner_guid'])
->setKey($entity->remind_object['owner_guid'])
->increment();
}
}
Core\Analytics\User::_()
Core\Analytics\User::_()
->setMetric('impression')
->setKey($activity->owner_guid)
->setKey($entity->owner_guid)
->increment();
} catch (\Exception $e) {
error_log($e->getMessage());
} catch (\Exception $e) {
error_log($e->getMessage());
}
}
try {
$viewsManager->record(
(new Core\Analytics\Views\View())
->setEntityUrn($activity->getUrn())
->setOwnerGuid((string) $activity->getOwnerGuid())
->setEntityUrn($entity->getUrn())
->setOwnerGuid((string) $entity->getOwnerGuid())
->setClientMeta($_POST['client_meta'] ?? [])
);
} catch (\Exception $e) {
......@@ -114,7 +117,7 @@ class views implements Interfaces\Api
}
Di::_()->get('Referrals\Cookie')
->setEntity($activity)
->setEntity($entity)
->create();
break;
......
......@@ -124,10 +124,14 @@ class feed implements Interfaces\Api
// $next = 0;
// }
$len = count($boosts);
$next = $boosts[$len -1]->getTimestamp();
if ($boosts[$len -1]) {
$next = $boosts[$len -1]->getTimestamp();
}
} elseif ($isBoostFeed) {
$len = count($boosts);
$next = $boosts[$len -1]->getTimestamp();
if ($boosts[$len -1]) {
$next = $boosts[$len -1]->getTimestamp();
}
}
// $ttl = 1800; // 30 minutes
......
......@@ -13,6 +13,12 @@ class emails implements Interfaces\Api
public function get($pages)
{
$user = Core\Session::getLoggedInUser();
if (!$user) {
return Factory::response([
'status' => 'error',
'message' => 'User must be logged in.'
]);
}
$campaigns = [ 'when', 'with', 'global' ];
......
......@@ -78,6 +78,9 @@ class Sums
public function getContractBalance($contract = '', $onlySpend = false)
{
if (!$this->user) {
return 0;
}
$cql = "SELECT SUM(amount) as balance from blockchain_transactions_mainnet WHERE user_guid = ? AND wallet_address = ?";
$values = [
new Varint($this->user->guid),
......
<?php
namespace Minds\Core\Blogs\Delegates;
use Minds\Core\Entities\Propagator\Properties;
use Minds\Entities\Activity;
/**
* Class PropagateProperties
* @package Minds\Core\Blogs\Delegates
*/
class PropagateProperties extends Properties
{
protected $actsOnSubtype = ['blog'];
/**
* Propagate Entity properties to activity
* @param $from
* @param Activity $to
* @return Activity
*/
public function toActivity($from, Activity $to): Activity
{
if ($this->valueHasChanged($from->getTitle(), $to->get('title'))) {
$to->set('title', $from->getTitle());
}
$blurb = strip_tags($from->getBody());
if ($this->valueHasChanged($blurb, $to->get('blurb'))) {
$to->set('blurb', $blurb);
}
if ($this->valueHasChanged($from->getUrl(), $to->getURL())) {
$to->setURL($from->getUrl());
}
if ($this->valueHasChanged($from->getIconUrl(), $to->get('thumbnail_src'))) {
$to->set('thumbnail_src', $from->getIconUrl());
}
return $to;
}
/**
* Propagate activity properties to entity
* @param Activity $from
* @param $to
* @return mixed
*/
public function fromActivity(Activity $from, $to)
{
return $to;
}
}
......@@ -9,6 +9,7 @@
namespace Minds\Core\Blogs;
use Minds\Core\Di\Di;
use Minds\Core\Entities\PropagateProperties;
use Minds\Core\Security\Spam;
class Manager
......@@ -31,6 +32,9 @@ class Manager
/** @var Delegates\Search */
protected $search;
/** @var PropagateProperties */
protected $propagateProperties;
/**
* Manager constructor.
* @param null $repository
......@@ -39,6 +43,7 @@ class Manager
* @param null $feeds
* @param null $spam
* @param null $search
* @param PropagateProperties $propagateProperties
* @throws \Exception
*/
public function __construct(
......@@ -47,7 +52,8 @@ class Manager
$slug = null,
$feeds = null,
$spam = null,
$search = null
$search = null,
PropagateProperties $propagateProperties = null
) {
$this->repository = $repository ?: new Repository();
$this->paywallReview = $paywallReview ?: new Delegates\PaywallReview();
......@@ -55,6 +61,7 @@ class Manager
$this->feeds = $feeds ?: new Delegates\Feeds();
$this->spam = $spam ?: Di::_()->get('Security\Spam');
$this->search = $search ?: new Delegates\Search();
$this->propagateProperties = $propagateProperties ?? Di::_()->get('PropagateProperties');
}
/**
......@@ -170,6 +177,7 @@ class Manager
}
$this->paywallReview->queue($blog);
$this->propagateProperties->from($blog);
}
return $saved;
......
You've received a gift of 2 Minds tokens! You can spend these tokens to earn 2,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'] ?>) |
| |
......@@ -14,8 +14,6 @@ use Minds\Helpers\MagicAttributes;
/**
* Save Action
* @method Save setEntity($entity)
* @method bool save(...$args)
*/
class Save
{
......
<?php
namespace Minds\Core\Entities\Delegates;
use Minds\Core\Entities\Propagator\Properties;
use Minds\Entities\Activity;
/**
* Class PropagateProperties
* @package Minds\Core\Entities\Delegates
*/
class PropagateProperties extends Properties
{
/**
* Propagate Entity properties to activity
* @param $from
* @param Activity $to
* @return Activity
*/
public function toActivity($from, Activity $to): Activity
{
if ($this->valueHasChanged($from->getNsfw(), $to->getNsfw())) {
$to->setNsfw($from->getNsfw());
}
if ($this->valueHasChanged($from->getNsfwLock(), $to->getNsfwLock())) {
$to->setNsfwLock($from->getNsfwLock());
}
return $to;
}
/**
* Propagate activity properties to entity
* @param Activity $from
* @param $to
* @return mixed
*/
public function fromActivity(Activity $from, $to)
{
if ($this->valueHasChanged($from->getNsfw(), $to->getNsfw())) {
$to->setNsfw($from->getNsfw());
}
if ($this->valueHasChanged($from->getNsfwLock(), $to->getNsfwLock())) {
$to->setNsfwLock($from->getNsfwLock());
}
return $to;
}
}
<?php
namespace Minds\Core\Entities;
use Minds\Core\Data\Call;
use Minds\Core\Di\Di;
use Minds\Core\Entities\Actions\Save;
use Minds\Core\Entities\Propagator\Properties;
use Minds\Core\EntitiesBuilder;
use Minds\Entities\Activity;
use Minds\Core;
/**
* Class PropagateProperties
* @package Minds\Core\Entities
*/
class PropagateProperties
{
/** @var Properties[] */
protected $propagators;
/** @var Call */
private $db;
/** @var Save */
private $save;
/** @var EntitiesBuilder */
private $entitiesBuilder;
/** @var bool */
private $changed = false;
/**
* PropagateProperties constructor.
* @param Call|null $db
* @param Save|null $save
* @param EntitiesBuilder|null $entitiesBuilder
*/
public function __construct(Call $db = null, Save $save = null, EntitiesBuilder $entitiesBuilder = null)
{
$this->db = $db ?? new Call('entities_by_time');
$this->save = $save ?? new Save();
$this->entitiesBuilder = $entitiesBuilder ?? Di::_()->get('EntitiesBuilder');
$this->registerPropagators();
}
/**
* Register PropagateProperties classes
* @throws \Exception
*/
protected function registerPropagators(): void
{
$this->addPropagator(Core\Blogs\Delegates\PropagateProperties::class);
$this->addPropagator(Core\Feeds\Delegates\PropagateProperties::class);
$this->addPropagator(Core\Media\Delegates\PropagateProperties::class);
$this->addPropagator(Core\Entities\Delegates\PropagateProperties::class);
$this->addPropagator(Core\Permissions\Delegates\PropagateProperties::class);
}
public function clearPropogators(): void
{
$this->propagators = [];
}
/**
* Add a propagator to be called in the chain
* @param string $class
* @throws \Exception
*/
protected function addPropagator(string $class): void
{
$obj = new $class();
if (!$obj instanceof Properties) {
throw new \Exception('Propagator class is not a Property Propagator');
}
$this->propagators[] = $obj;
}
/**
* Propagate the properties from the passed entity
* @param $entity
*/
public function from($entity): void
{
if ($entity instanceof Activity) {
$this->fromActivity($entity);
} else {
$this->toActivities($entity);
}
}
/**
* Propagate properties from an Activity to to it's attachment
* @param Activity $activity
* @throws \Minds\Exceptions\StopEventException
* @throws \Exception
*/
protected function fromActivity(Activity $activity): void
{
$this->changed = false;
$attachment = $this->entitiesBuilder->single($activity->get('entity_guid'));
if ($attachment === false) {
return;
}
foreach ($this->propagators as $propagator) {
if ($propagator->willActOnEntity($attachment)) {
$attachment = $propagator->fromActivity($activity, $attachment);
$this->changed |= $propagator->changed();
if (!is_object($attachment)) {
throw new \Exception(get_class($propagator) . ' fromActivity method did not return a modified object');
}
}
}
if ($this->changed) {
$this->save->setEntity($attachment)->save();
}
}
/**
* Propagate properties from an Entity to it's activities
* @param $entity
* @throws \Minds\Exceptions\StopEventException
*/
protected function toActivities($entity): void
{
$activities = $this->getActivitiesForEntity($entity->getGuid());
foreach ($activities as $activity) {
$this->propagateToActivity($entity, $activity);
if ($this->changed) {
$this->save->setEntity($activity)->save();
}
}
}
/**
* Get activities for an entity
* @param string $entityGuid
* @return Activity[]
*/
private function getActivitiesForEntity(string $entityGuid): array
{
$activities = [];
foreach ($this->db->getRow("activity:entitylink:{$entityGuid}") as $activityGuid => $ts) {
$activities[] = $this->entitiesBuilder->single($activityGuid);
}
return $activities;
}
/**
* Propagate properties from and entity to an activity
* @param $entity
* @param Activity $activity
*/
public function propagateToActivity($entity, Activity &$activity): void
{
$this->changed = false;
foreach ($this->propagators as $propagator) {
if ($propagator->willActOnEntity($entity)) {
$activity = $propagator->toActivity($entity, $activity);
$this->changed |= $propagator->changed();
}
}
}
}
<?php
namespace Minds\Core\Entities\Propagator;
use Minds\Entities\Activity;
/**
* Properties class that all PropagateProperties delegates should inherit
* @package Minds\Core\Entities\Propagator
*/
abstract class Properties
{
/**
* @var array
*/
protected $actsOnType = [];
/**
* @var array
*/
protected $actsOnSubtype = [];
/**
* @var bool
*/
protected $changed = false;
/**
* @return array
*/
public function actsOnType(): array
{
return $this->actsOnType;
}
/**
* @return array
*/
public function actsOnSubType(): array
{
return $this->actsOnSubtype;
}
/**
* @param $entity
* @return bool
* @throws \Exception
*/
public function willActOnEntity($entity): bool
{
if (!is_array($this->actsOnType)) {
throw new \Exception('actsOnType must be an array');
}
if (!is_array($this->actsOnSubtype)) {
throw new \Exception('actsOnSubType must be an array');
}
if ($this->actsOnType === [] || in_array($entity->getType(), $this->actsOnType, true)) {
return $this->actsOnSubtype === [] || in_array($entity->getSubtype(), $this->actsOnSubtype, true);
}
return false;
}
/**
* @param $from
* @param $to
* @return bool
*/
protected function valueHasChanged($from, $to): bool
{
$changed = $from !== $to;
$this->changed |= $changed;
return $changed;
}
/**
* @return bool
*/
public function changed(): bool
{
return $this->changed;
}
/**
* @param $from
* @param Activity $to
* @return Activity
*/
abstract public function toActivity($from, Activity $to): Activity;
/**
* @param Activity $from
* @param $to
* @return mixed
*/
abstract public function fromActivity(Activity $from, $to);
}
<?php
/**
* ActivityDelegateInterface
* @author edgebal
*/
namespace Minds\Core\Feeds\Activity\Delegates;
use Minds\Entities\Activity;
interface ActivityDelegateInterface
{
public function onAdd();
public function onUpdate(Activity $activity);
}
<?php
/**
* AttachmentPaywallDelegate
* @author edgebal
*/
namespace Minds\Core\Feeds\Activity\Delegates;
use Minds\Core\Di\Di;
use Minds\Core\Entities\Actions\Save;
use Minds\Core\EntitiesBuilder;
use Minds\Entities\Activity;
class AttachmentPaywallDelegate implements ActivityDelegateInterface
{
/** @var EntitiesBuilder */
protected $entitiesBuilder;
/** @var Save */
protected $save;
/**
* AttachmentPaywallDelegate constructor.
* @param EntitiesBuilder $entitiesBuilder
* @param Save $save
*/
public function __construct(
$entitiesBuilder = null,
$save = null
) {
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->save = $save ?: new Save();
}
/**
* @throws \NotImplementedException
*/
public function onAdd()
{
throw new \NotImplementedException();
}
/**
* @param Activity $activity
* @return bool
*/
public function onUpdate(Activity $activity)
{
if ($activity->entity_guid) {
$attachment = $this->entitiesBuilder->single($activity->entity_guid);
if ($attachment->owner_guid == $activity->owner_guid) {
$attachment->access_id = $activity->isPaywall() ? 0 : 2;
if ($attachment->getSubtype() === 'blog') {
$attachment->setHidden($activity->isPaywall());
} else {
$attachment->hidden = $activity->isPaywall();
}
if (method_exists($attachment, 'setFlag')) {
$attachment->setFlag('paywall', (bool) $activity->isPaywall());
}
if (method_exists($attachment, 'setWireThreshold')) {
$attachment->setWireThreshold($activity->getWireThreshold() ?: false);
}
$this->save
->setEntity($attachment)
->save();
}
}
return true;
}
}
<?php
namespace Minds\Core\Feeds\Delegates;
use Minds\Core\Blogs\Blog;
use Minds\Core\Entities\Propagator\Properties;
use Minds\Entities\Activity;
use Minds\Entities\Entity;
/**
* Class PropagateProperties
* @package Minds\Core\Feeds\Delegates
*/
class PropagateProperties extends Properties
{
/**
* Propagate Entity properties to activity
* @param $from
* @param Activity $to
* @return Activity
*/
public function toActivity($from, Activity $to): Activity
{
if ($this->valueHasChanged((int)$from->getModeratorGuid(), (int)$to->getModeratorGuid())) {
$to->setModeratorGuid((int)$from->getModeratorGuid());
}
if ($this->valueHasChanged((int)$from->getTimeModerated(), (int)$to->getTimeModerated())) {
$to->setTimeModerated((int)$from->getTimeModerated());
}
return $to;
}
/**
* Propagate activity properties to entity
* @param Activity $from
* @param Entity|Blog $to
* @return mixed
*/
public function fromActivity(Activity $from, $to)
{
if ($this->valueHasChanged((int)$from->getModeratorGuid(), (int)$to->getModeratorGuid())) {
$to->setModeratorGuid((int)$from->getModeratorGuid());
}
if ($this->valueHasChanged((int)$from->getTimeModerated(), (int)$to->getTimeModerated())) {
$to->setTimeModerated((int)$from->getTimeModerated());
}
$to = $this->propagateAttachmentPaywallProperties($from, $to);
return $to;
}
/**
* @param Activity $from
* @param Entity $to
* @return mixed
*/
private function propagateAttachmentPaywallProperties(Activity $from, $to)
{
if ($to->owner_guid == $from->owner_guid) {
$newAccessId = $from->isPaywall() ? 0 : 2;
if ($this->valueHasChanged($to->access_id, $from->access_id)) {
$to->access_id = $newAccessId;
}
$newHidden = $from->isPayWall();
if ($to->getSubtype() === 'blog') {
/** @var $to Blog */
if ($this->valueHasChanged($to->getHidden(), $newHidden)) {
$to->setHidden($newHidden);
}
} else {
if ($this->valueHasChanged($to->hidden, $newHidden)) {
$to->hidden = $newHidden;
}
}
if (method_exists($to, 'setFlag')) {
if ($this->valueHasChanged($to->getFlag('paywall'), (bool)$from->isPaywall())) {
$to->setFlag('paywall', (bool)$from->isPaywall());
}
}
if (method_exists($to, 'setWireThreshold')) {
if ($this->valueHasChanged($to->getWireThreshold(), $from->getWireThreshold())) {
$to->setWireThreshold($from->getWireThreshold() ?: false);
}
}
}
return $to;
}
}
......@@ -3,12 +3,10 @@
namespace Minds\Core\Feeds\Firehose;
use Minds\Entities\User;
use Minds\Entities\Entity;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Data\Call;
use Minds\Core\Entities\Actions\Save;
use Minds\Core\Di\Di;
use Minds\Core\Feeds\Top\Manager as TopFeedsManager;
use Minds\Core\Entities\PropagateProperties;
class Manager
{
......@@ -16,25 +14,21 @@ class Manager
protected $topFeedsManager;
/** @var ModerationCache */
protected $moderationCache;
/** @var EntitiesBuilder $entitiesBuilder */
protected $entitiesBuilder;
/** @var Call */
protected $db;
/** @var Save */
protected $save;
/** @var PropagateProperties */
protected $propagateProperties;
public function __construct(
TopFeedsManager $topFeedsManager = null,
ModerationCache $moderationCache = null,
EntitiesBuilder $entitiesBuilder = null,
Call $db = null,
Save $save = null
Save $save = null,
PropagateProperties $propagateProperties = null
) {
$this->topFeedsManager = $topFeedsManager ?: Di::_()->get('Feeds\Top\Manager');
$this->moderationCache = $moderationCache ?: new ModerationCache();
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->db = $db ?: new Call('entities_by_time');
$this->save = $save ?: new Save(); //Mockable, else instantiate a new one on save.
$this->save = $save ?: new Save();
$this->propagateProperties = $propagateProperties ?? Di::_()->get('PropagateProperties');
}
/**
......@@ -79,7 +73,7 @@ class Manager
* Marks an entity as moderated.
*
* @param $entity the entity to mark as moderated, typeless because images do not inherit entity
* @param User $user the moderator
* @param User $moderator the moderator
* @param int $time
*/
public function save(
......@@ -91,22 +85,8 @@ class Manager
$time = time();
}
//Save the entity
$this->saveEntity($entity, $moderator, $time);
if (method_exists($entity, 'getType')
&& $entity->getType() == 'activity'
&& $entity->get('entity_guid')
) {
$attachment = $this->entitiesBuilder->single($entity->get('entity_guid'));
$this->saveEntity($attachment, $moderator, $time);
}
//Moderate parents
foreach ($this->db->getRow('activity:entitylink:'.$entity->getGUID()) as $parentGuid => $ts) {
$activity = $this->entitiesBuilder->single($parentGuid);
$this->saveEntity($activity, $moderator, $time);
}
$this->propagateProperties->from($entity);
}
private function saveEntity(
......