at the end of the day, it was inevitable

This commit is contained in:
Mo Elzubeir
2022-12-09 08:36:26 -06:00
commit 1218570914
1768 changed files with 887087 additions and 0 deletions
@@ -0,0 +1,103 @@
<?php
namespace UserBundle\Command;
use AppBundle\Command\AbstractSingleCopyCommand;
use Symfony\Component\Console\Style\SymfonyStyle;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use UserBundle\Entity\User;
use UserBundle\Repository\UserRepository;
use UserBundle\Entity\Plan;
/**
* Class CancelSubscriptionCommand
*
* @package UserBundle\Command
*/
class CancelSubscriptionCommand extends AbstractSingleCopyCommand
{
const NAME = 'socialhose:cancel-subscription';
/**
* @var EntityManagerInterface
*/
private $em;
/**
* CancelSubscriptionCommand constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface instance.
* @param LoggerInterface $logger A LoggerInterface instance.
*/
public function __construct(EntityManagerInterface $em, LoggerInterface $logger)
{
parent::__construct(self::NAME, $logger);
$this->em = $em;
}
/**
* Configures the current command.
*
* @return void
*/
protected function configure()
{
$this->setDescription('Cancel subscription');
}
/**
* @param InputInterface $input An InputInterface instance.
* @param OutputInterface $output An OutputInterface instance.
*
* @return null|integer
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function doExecute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$repository = $this->em->getRepository(User::class);
$currentDate = date('Y-m-d');
$users = $repository->getAllUserBillingSubscription($currentDate);
foreach ($users as $user) {
$repository = $this->em->getRepository(Plan::class);
$planObj = $repository->findOneBy([ 'title' => 'Free' ]);
$plan = $user->getBillingSubscription()->getPlan();
$plan->setTitle($user->getCompanyName());
$plan->setInnerName('Starter');
$plan->setPrice(0);
$plan->setNews($planObj->isNews());
$plan->setBlog($planObj->isBlog());
$plan->setReddit($planObj->isReddit());
$plan->setInstagram($planObj->isInstagram());
$plan->setTwitter($planObj->isTwitter());
$plan->setAnalytics($planObj->isAnalytics());
$plan->setSearchesPerDay($planObj->getSearchesPerDay());
$plan->setSavedFeeds($planObj->getSavedFeeds());
$plan->setMasterAccounts($planObj->getMasterAccounts());
$plan->setSubscriberAccounts($planObj->getSubscriberAccounts());
$plan->setAlerts($planObj->getAlerts());
$plan->setNewsLetters($planObj->getNewsLetters());
$plan->setWebFeeds($planObj->getWebFeeds());
$plan->setAlerts($planObj->getAlerts());
$plan->setIsPlanDowngrade(false);
$this->em->persist($plan);
$this->em->flush();
$subscription = $user->getBillingSubscription();
$subscription->setIsSubscriptionCancelled(false);
$this->em->persist($subscription);
$this->em->flush();
}
$io->success('Cancel Subscription Successfully.');
return 0;
}
}
@@ -0,0 +1,175 @@
<?php
namespace UserBundle\Command;
use AppBundle\Command\AbstractSingleCopyCommand;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use UserBundle\Entity\Subscription\AbstractSubscription;
use UserBundle\Repository\SubscriptionRepository;
use Symfony\Component\Console\Style\SymfonyStyle;
use UserBundle\Entity\User;
use UserBundle\Repository\UserRepository;
use UserBundle\Entity\Plan;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Class DowngradeSubscriptionPlanCommand
*
* @package UserBundle\Command
*/
class DowngradeSubscriptionPlanCommand extends AbstractSingleCopyCommand
{
const NAME = 'socialhose:downgrade-subscription-plan';
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var ContainerInterface
*/
private $container;
/**
* DowngradeSubscriptionPlanCommand constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface instance.
* @param LoggerInterface $logger A LoggerInterface instance.
*/
public function __construct(EntityManagerInterface $em, LoggerInterface $logger,ContainerInterface $container)
{
parent::__construct(self::NAME, $logger);
$this->em = $em;
$this->container = $container;
}
/**
* Configures the current command.
*
* @return void
*/
protected function configure()
{
$this->setDescription('Downgrade subscription plan');
}
/**
* @param InputInterface $input An InputInterface instance.
* @param OutputInterface $output An OutputInterface instance.
*
* @return null|integer
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function doExecute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$repository = $this->em->getRepository(User::class);
$currentDate = date('Y-m-d');
$users = $repository->getAllUserBillingSubscriptionPlanDowngrade($currentDate);
foreach ($users as $user) {
$repository = $this->em->getRepository(Plan::class);
$planObj = $repository->findOneBy(['isPlanDowngrade' => true, 'user' => $user->getId()], ['id'=>'desc']);
$subscription = $user->getBillingSubscription();
$subscription->setIsPlanDowngrade(false);
$subscription->setPlan($planObj);
$this->em->persist($subscription);
$this->em->flush();
$this->downgradePlanInStripe($user,$planObj);
}
$io->success('Downgrade Subscription Plan Successfully.');
return 0;
}
protected function downgradePlanInStripe($user, $planObj)
{
$stripe = $this->container->get('stripe.service');
$stripe->setApiKey();
$customer = $stripe->getCustomer(
$user->getStripeUserId()
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer not found'];
$this->logger->info(sprintf(
'Error cron job downgrade \'%s\'',
json_encode($customerArray)
));
}
if (isset($customer['id'])) {
$price = $stripe->addPrice(
[
'unit_amount' => !empty($planObj->getPrice()) ? $planObj->getPrice() * 100 : 0,
'currency' => 'usd',
'recurring' => ['interval' => 'month'],
'product' => $customer['metadata']['productId']
]
);
if ($price instanceof ApiErrorException) {
$priceArray = ['paymentError' => 1,'data'=>$price,'message'=>'Price not found'];
$this->logger->info(sprintf(
'Error cron job downgrade \'%s\'',
json_encode($priceArray)
));
return $this->generateResponse($priceArray, 400);
}
if (isset($price['id'])) {
//Add subscription
$subscription = $stripe->getSubscription(
$customer['metadata']['subscriptionId']
);
if ($subscription instanceof ApiErrorException) {
$subscriptionArray = ['paymentError' => 1,'data'=>$subscription,'message'=>'Subscribtion get failed'];
$this->logger->info(sprintf(
'Error cron job downgrade \'%s\'',
json_encode($subscriptionArray)
));
return $this->generateResponse($subscriptionArray, 400);
}
if (isset($subscription['id'])) {
//Add subscription item
$subscriptionItem = $stripe->updateSubscriptionItem($subscription['items']['data'][0]['id'],
[
'price' => $price['id'],
]
);
if ($subscriptionItem instanceof ApiErrorException) {
$subscriptionItemArray = ['paymentError' => 1,'data'=>$subscriptionItem,'message'=>'Subscribtion Item failed'];
$this->logger->info(sprintf(
'Error cron job downgrade \'%s\'',
json_encode($subscriptionItemArray)
));
return $this->generateResponse($subscriptionItemArray, 400);
}
//update customer metadata
$customer = $stripe->updateCustomer($customer['id'],
[
'metadata' => [
'priceId' => $price['id'],
'subscriptionId' => $subscription['id'],
'subStartDate' => $subscription['current_period_start'],
'subEndDate' => $subscription['current_period_end'],
]
]
);
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer update meta data failed'];
$this->logger->info(sprintf(
'Error cron job downgrade \'%s\'',
json_encode($customerArray)
));
}
}
}
}
}
}
@@ -0,0 +1,67 @@
<?php
namespace UserBundle\Command;
use AppBundle\Command\AbstractSingleCopyCommand;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use UserBundle\Entity\Subscription\AbstractSubscription;
use UserBundle\Repository\SubscriptionRepository;
/**
* Class RenewSearchLimitsCommand
*
* @package UserBundle\Command
*/
class RenewSearchLimitsCommand extends AbstractSingleCopyCommand
{
const NAME = 'socialhose:renew-search-limits';
/**
* @var EntityManagerInterface
*/
private $em;
/**
* RenewSearchLimitsCommand constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface instance.
* @param LoggerInterface $logger A LoggerInterface instance.
*/
public function __construct(EntityManagerInterface $em, LoggerInterface $logger)
{
parent::__construct(self::NAME, $logger);
$this->em = $em;
}
/**
* Configures the current command.
*
* @return void
*/
protected function configure()
{
$this->setDescription('Set all searchPerDay limits to zero');
}
/**
* @param InputInterface $input An InputInterface instance.
* @param OutputInterface $output An OutputInterface instance.
*
* @return null|integer
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function doExecute(InputInterface $input, OutputInterface $output)
{
/** @var SubscriptionRepository $repository */
$repository = $this->em->getRepository(AbstractSubscription::class);
$repository->renewSearchLimits();
return 0;
}
}
@@ -0,0 +1,353 @@
<?php
namespace UserBundle\Controller\Developing;
use AppBundle\AppBundleServices;
use AppBundle\Configuration\ConfigurationInterface;
use CacheBundle\Entity\Comment;
use CacheBundle\Entity\Document;
use Faker\Factory;
use IndexBundle\Index\Strategy\IndexStrategyInterface;
use IndexBundle\Index\Strategy\HoseIndexStrategy;
use IndexBundle\Model\ArticleDocument;
use IndexBundle\Model\ArticleDocumentInterface;
use IndexBundle\Model\Generator\ExternalDocumentGenerator;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Notification\NotificationTheme;
use UserBundle\Entity\Notification\NotificationThemeOptions;
use UserBundle\Entity\User;
use UserBundle\Enum\ThemeOptionsUserCommentsEnum;
use UserBundle\Enum\ThemeTypeEnum;
use UserBundle\Manager\Notification\Model\FeedData;
use UserBundle\Manager\Notification\SendableNotification;
use UserBundle\Manager\Notification\SendableNotificationConfig;
/**
* Class EmailController
*
* For developing ant testing email's only.
*
* @package UserBundle\Controller\Developing
*
* @Route("/emails")
*/
class EmailController extends Controller
{
/**
* @var \Faker\Generator
*/
private $faker;
/**
* @var IndexStrategyInterface
*/
private $strategy;
/**
* EmailController constructor.
*/
public function __construct()
{
$this->strategy = new HoseIndexStrategy();
}
/**
* @Route("/plain", methods={ "GET" })
*
* @return Response
*/
public function plainAction()
{
$notification = $this->generateNotification(Notification::create()->setThemeType(ThemeTypeEnum::plain()));
$notification->setPlainThemeOptionsDiff([
// 'summary' => '<p>Summary</p>',
// 'conclusion' => '<p>Conclusion</p>',
//
// 'header.imageUrl' => ThemeOptionHeader::DEFAULT_IMAGE,
// 'header.logoLink' => 'http://ya.ru',
//
// 'content.showInfo.images' => true,
// 'content.showInfo.sectionDivider' => true,
// 'content.showInfo.sourceCountry' => true,
// 'content.showInfo.articleCount' => true,
// 'content.showInfo.tableOfContents' => ThemeOptionsTableOfContentsEnum::SOURCE_HEADLINE_DATE,
'content.showInfo.userComments' => ThemeOptionsUserCommentsEnum::WITHOUT_AUTHOR_DATE,
//
// 'colors.text.articleHeadline' => 'yellow',
// 'colors.text.source' => 'green',
// 'colors.text.articleContent' => 'red',
// 'colors.text.author' => '#fdfdfd',
// 'colors.text.publishDate' => '#23fa1f',
//
// 'fonts.tableOfContents.size' => 18,
// 'fonts.tableOfContents.family' => 'Courier New',
// 'fonts.tableOfContents.style.bold' => true,
// 'fonts.tableOfContents.style.italic' => true,
// 'fonts.tableOfContents.style.underline' => true,
//
// 'fonts.feedTitle.size' => 18,
// 'fonts.feedTitle.family' => 'Courier New',
// 'fonts.feedTitle.style.bold' => true,
// 'fonts.feedTitle.style.italic' => true,
// 'fonts.feedTitle.style.underline' => true,
//
// 'fonts.articleHeadline.size' => 10,
// 'fonts.articleHeadline.family' => 'Courier New',
// 'fonts.articleHeadline.style.bold' => true,
// 'fonts.articleHeadline.style.italic' => true,
// 'fonts.articleHeadline.style.underline' => true,
//
// 'fonts.source.size' => 10,
// 'fonts.source.family' => 'Courier New',
// 'fonts.source.style.bold' => true,
// 'fonts.source.style.italic' => true,
// 'fonts.source.style.underline' => true,
//
// 'fonts.author.size' => 6,
// 'fonts.author.family' => 'Courier New',
// 'fonts.author.style.bold' => true,
// 'fonts.author.style.italic' => true,
// 'fonts.author.style.underline' => true,
//
// 'fonts.date.size' => 14,
// 'fonts.date.family' => 'Courier New',
// 'fonts.date.style.bold' => true,
// 'fonts.date.style.italic' => true,
// 'fonts.date.style.underline' => true,
//
// 'fonts.articleContent.size' => 6,
// 'fonts.articleContent.family' => 'Courier New',
// 'fonts.articleContent.style.bold' => true,
// 'fonts.articleContent.style.italic' => true,
// 'fonts.articleContent.style.underline' => true,
]);
/** @var ConfigurationInterface $configuration */
$configuration = $this->get(AppBundleServices::CONFIGURATION);
$sendableNotification = new SendableNotification(
SendableNotificationConfig::fromConfiguration($configuration),
$notification,
$this->generateFeeds(3)
);
return new Response($sendableNotification->render($this->get('templating')));
}
/**
* @Route("/enhanced", methods={ "GET" })
*
* @return Response
*/
public function enhancedAction()
{
$notification = $this->generateNotification(Notification::create()->setThemeType(ThemeTypeEnum::enhanced()));
$notification->setEnhancedThemeOptionsDiff([
// 'summary' => '<p>Summary</p>',
// 'conclusion' => '<p>Conclusion</p>',
//
// 'header.imageUrl' => ThemeOptionHeader::DEFAULT_IMAGE,
// 'header.logoLink' => 'http://ya.ru',
//
// 'content.showInfo.sectionDivider' => true,
// 'content.showInfo.sourceCountry' => true,
// 'content.showInfo.articleCount' => true,
// 'content.showInfo.tableOfContents' => ThemeOptionsTableOfContentsEnum::HEADLINE_SOURCE_DATE,
'content.showInfo.userComments' => ThemeOptionsUserCommentsEnum::WITH_AUTHOR_DATE,
//
// 'colors.background.header' => '#541dab',
// 'colors.background.accent' => '#df10bc',
// 'colors.background.emailBody' => '#eeeeee',
//
// 'colors.text.header' => '#12fdfd',
// 'colors.text.articleHeadline' => 'yellow',
// 'colors.text.source' => 'green',
// 'colors.text.articleContent' => 'red',
// 'colors.text.author' => '#fdfdfd',
// 'colors.text.publishDate' => '#23fa1f',
//
// 'fonts.header.size' => 18,
// 'fonts.header.family' => 'Courier New',
// 'fonts.header.style.bold' => true,
// 'fonts.header.style.italic' => true,
// 'fonts.header.style.underline' => true,
//
// 'fonts.tableOfContents.size' => 32,
// 'fonts.tableOfContents.family' => 'Courier New',
// 'fonts.tableOfContents.style.bold' => true,
// 'fonts.tableOfContents.style.italic' => true,
// 'fonts.tableOfContents.style.underline' => true,
//
// 'fonts.feedTitle.size' => 18,
// 'fonts.feedTitle.family' => 'Courier New',
// 'fonts.feedTitle.style.bold' => true,
// 'fonts.feedTitle.style.italic' => true,
// 'fonts.feedTitle.style.underline' => true,
//
// 'fonts.articleHeadline.size' => 10,
// 'fonts.articleHeadline.family' => 'Courier New',
// 'fonts.articleHeadline.style.bold' => true,
// 'fonts.articleHeadline.style.italic' => true,
// 'fonts.articleHeadline.style.underline' => true,
//
// 'fonts.source.size' => 10,
// 'fonts.source.family' => 'Courier New',
// 'fonts.source.style.bold' => true,
// 'fonts.source.style.italic' => true,
// 'fonts.source.style.underline' => true,
//
// 'fonts.author.size' => 6,
// 'fonts.author.family' => 'Courier New',
// 'fonts.author.style.bold' => true,
// 'fonts.author.style.italic' => true,
// 'fonts.author.style.underline' => true,
//
// 'fonts.date.size' => 14,
// 'fonts.date.family' => 'Courier New',
// 'fonts.date.style.bold' => true,
// 'fonts.date.style.italic' => true,
// 'fonts.date.style.underline' => true,
//
// 'fonts.articleContent.size' => 12,
// 'fonts.articleContent.family' => 'Courier New',
// 'fonts.articleContent.style.bold' => true,
// 'fonts.articleContent.style.italic' => true,
// 'fonts.articleContent.style.underline' => true,
]);
/** @var ConfigurationInterface $configuration */
$configuration = $this->get(AppBundleServices::CONFIGURATION);
$feeds = $this->generateFeeds(3);
$sendableNotification = new SendableNotification(
SendableNotificationConfig::fromConfiguration($configuration),
$notification,
$feeds
);
return new Response($sendableNotification->render($this->get('templating')));
}
/**
* Generate notification.
*
* @param Notification $notification A Notification instance.
*
* @return Notification
*/
private function generateNotification(Notification $notification)
{
$faker = $this->getFaker();
$user = User::create('some@email.com')
->setFirstName('John')
->setLastName('Due');
$defaultOptions = NotificationThemeOptions::createDefault();
return $notification
->setSubject(ucfirst($faker->word))
->setName($faker->word)
->setOwner($user)
->setTheme(NotificationTheme::create()
->setName('Some')
->setEnhanced($defaultOptions)
->setPlain($defaultOptions)
->setDefault(true));
}
/**
* @param integer $count Number of generated feeds.
*
* @return array[]
*/
private function generateFeeds($count = 2)
{
$feeds = [];
$faker = $this->getFaker();
for ($i = 0; $i < $count; ++$i) {
$documentCount = random_int(1, 3);
$feeds[] = new FeedData(
$faker->word,
$this->generateDocuments($documentCount)
);
}
return $feeds;
}
/**
* Generate documents for notification rendering.
*
* @param integer $count Number of documents.
*
* @return ArticleDocumentInterface[]
*/
private function generateDocuments($count)
{
$documents = [];
$generator = new ExternalDocumentGenerator();
for ($i = 0; $i < $count; ++$i) {
$documentEntity = $this->generateComments($generator->generate()->toDocumentEntity());
$data = $documentEntity->getData();
$data['comments'] = $documentEntity->getComments()->toArray();
$data['commentsCount'] = count($data['comments']);
$documents[] = new ArticleDocument(
$this->strategy,
$this->strategy->createDocument($data)->getNormalizedData()
);
}
return $documents;
}
/**
* Generate comments for documents.
*
* @param Document $document A Document entity instance.
*
* @return Document
*/
private function generateComments(Document $document)
{
$faker = $this->getFaker();
$commentsCount = random_int(0, 3);
for ($i = 0; $i < $commentsCount; ++$i) {
$user = User::create($faker->email, '')
->setFirstName($faker->firstName)
->setLastName($faker->lastName);
$document->addComment(new Comment(
$user,
$faker->text,
$faker->optional(0.4, '')->word
));
}
return $document;
}
/**
* @return \Faker\Generator
*/
private function getFaker()
{
if ($this->faker === null) {
$this->faker = Factory::create();
}
return $this->faker;
}
}
@@ -0,0 +1,84 @@
<?php
namespace UserBundle\Controller\Security;
use ApiBundle\Controller\AbstractApiController;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
/**
* Class CostCalculationController
* @package UserBundle\Controller\Security
*
* @Route("/cost_calculation", service="user.controller.cost_calculation")
*/
class CostCalculationController extends AbstractApiController
{
/**
* @Route("", methods={ "POST" })
*
* @return array
*/
public function costCalculationAction(Request $request, $isCallContoller = false)
{
$data = $request->request->all();
$mtPrice = 0;
$totalPrice = 0;
$responseData = [];
//echo '<pre>'; print_r($data); die;
if(!empty($data)){
if(isset(
$data['news'],
$data['blog'],
$data['reddit'],
$data['instagram'],
$data['twitter'],
$data['searchesPerDay'],
$data['savedFeeds'],
$data['subscriberAccounts'],
$data['webFeeds'],
$data['alerts'],
$data['analytics']
)
){
//die('hello');
$mtPrice += ($data['news'] == true) ? 20 : 0;
$mtPrice += ($data['blog'] == true) ? 15 : 0;
$mtPrice += ($data['reddit'] == true) ? 1 : 0;
$mtPrice += ($data['instagram'] == true) ? 3 : 0;
$mtPrice += ($data['twitter'] == true) ? 3 : 0;
$responseData['selectedMediaTypeCost'] = $mtPrice;
$searchPerDayPrice = ($data['searchesPerDay'] > 10) ? ($data['searchesPerDay'] - 10)/10 : 0;
$searchPerDayPrice = $searchPerDayPrice ? ($searchPerDayPrice * $mtPrice) : 0;
$responseData['searchPerDayPrice'] = $searchPerDayPrice;
$savedFeedsPrice = $data['savedFeeds'] ? ($data['savedFeeds'] * $mtPrice) : 0;
$responseData['savedFeedsPrice'] = $savedFeedsPrice;
$subscriberAccountsPrice = $data['subscriberAccounts'] > 1 ? (($data['subscriberAccounts'] - 1) * 15) : 0; // Fixed price $15 per account
$responseData['subscriberAccountsPrice'] = $subscriberAccountsPrice;
$webFeedsPrice = $data['webFeeds'] > 0 ? ($data['webFeeds'] * 5) : 0; // Fixed price $5 per export/webFeeds
$responseData['webFeedsPrice'] = $webFeedsPrice;
$alertsPrice = $data['alerts'] ? ($data['alerts'] * 5) : 0; // Fixed price $5 per alerts
$responseData['alertsPrice'] = $alertsPrice;
$analyticsPrice = $data['analytics'] ? ($data['savedFeeds'] * 15) : 0; // Fixed price $15 if analytics field comes true in request
$responseData['analyticsPrice'] = $analyticsPrice;
$totalPrice = $searchPerDayPrice + $savedFeedsPrice + $subscriberAccountsPrice + $webFeedsPrice + $alertsPrice + $analyticsPrice;
$responseData['totalPrice'] = $totalPrice;
if ($isCallContoller) {
return ['price' => $totalPrice];
}
return $this->generateResponse($responseData, 200);
} else {
return $this->generateResponse("Invalid request", 400);
}
} else {
return $this->generateResponse("Something went wrong in the request.", 400);
}
}
}
@@ -0,0 +1,60 @@
<?php
namespace UserBundle\Controller\Security;
use ApiBundle\Controller\AbstractApiController;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Form\HubSpotRegistrationType;
use UserBundle\Manager\User\UserManagerInterface;
/**
* Class HubSpotRegistrationController
* @package UserBundle\Controller\Security
*
* @Route("/hubspot-registration", service="user.controller.hubspot_registration")
*/
class HubSpotRegistrationController extends AbstractApiController
{
/**
* Register new user.
* Return empty response.
*
* @Route("", methods={ "POST" })
*
*
* @param Request $request A Request instance.
*
* @return array|\ApiBundle\Response\ViewInterface
*/
public function registerAction(Request $request)
{
/** @var $userManager UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** @var \UserBundle\Entity\User $user */
$user = $userManager->createUser();
$user->setEnabled(true);
$form = $this->createForm(HubSpotRegistrationType::class, $user);
$form->submit($request->request->all());
if ($form->isSubmitted() && $form->isValid()) {
$user->setPassword('');
/** @var TokenGeneratorInterface $tokenGenerator */
$tokenGenerator = $this->container->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
$userManager->updateUser($user);
return $this->generateResponse([
'code' => $user->getConfirmationToken(),
]);
}
return $this->generateResponse($form, 400);
}
}
@@ -0,0 +1,57 @@
<?php
namespace UserBundle\Controller\Security;
use ApiBundle\Controller\AbstractApiController;
use PaymentBundle\Enum\PaymentGatewayEnum;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Entity\Plan;
use UserBundle\Entity\Subscription\AbstractSubscription;
use UserBundle\Form\PlanType;
use UserBundle\Repository\PlanRepository;
use UserBundle\Repository\SubscriptionRepository;
/**
* Class PlanController
* @package UserBundle\Controller\Security
*
* @Route("/plans", service="user.controller.plan")
*/
class PlanController extends AbstractApiController
{
/**
* @Route("", methods={ "GET" })
*
* @return array
*/
public function indexAction()
{
$repository = $this->getManager()->getRepository(Plan::class);
$qb = $repository->createQueryBuilder('p');
$query = $qb
->where('p.is_default = true')
->andwhere('p.title != :title')
->setParameters(array(
'title' => 'Free'
));
$query = $query->getQuery();
$plans = $query->getResult();
if (count($plans) === 0) {
return $this->generateResponse("Can't find plans.", 404);
}
return $this->generateResponse($plans, 200, [
'id',
'plan'
]);
}
}
@@ -0,0 +1,441 @@
<?php
namespace UserBundle\Controller\Security;
use ApiBundle\Controller\AbstractApiController;
use AppBundle\AppBundleServices;
use AppBundle\Configuration\ConfigurationInterface;
use AppBundle\Configuration\ParametersName;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Util\TokenGeneratorInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use PaymentBundle\Enum\PaymentGatewayEnum;
use PaymentBundle\Gateway\Factory\PaymentGatewayFactoryInterface;
use PaymentBundle\Model\BillingSubscription;
use PaymentBundle\Model\PaymentData;
use PaymentBundle\PaymentBundleServices;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Entity\Organization;
use UserBundle\Entity\Plan;
use UserBundle\Entity\User;
use UserBundle\Form\PaymentDataType;
use UserBundle\Form\RegistrationType;
use UserBundle\Manager\User\UserManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use FOS\UserBundle\Event\GetResponseUserEvent;
use UserBundle\Mailer\MailerInterface;
use Stripe\Exception\ApiErrorException;
use UserBundle\Security\CostCalculationController;
use Symfony\Component\HttpFoundation\Response;
/**
* Class RegistrationController
* @package UserBundle\Controller\Security
*
* @Route("/registration", service="user.controller.registration")
*/
class RegistrationController extends AbstractApiController
{
/**
* Max organization rep response.
*/
const DEFAULT_LIMIT = 10;
/**
* Register new user.
* Return empty response.
*
* @Route("", methods={ "POST" })
*
* @ApiDoc(
* resource="Registration",
* section="Security",
* input={
* "class"="UserBundle\Form\RegistrationType",
* "name"=false
* },
* output={
* "class"="",
* "data"={
* "message"={
* "dataType"="string",
* "description"="Registration success message"
* }
* }
* },
* statusCodes={
* 200="Register successfully."
* }
* )
*
* @param Request $request A Request instance.
* @param UserPasswordEncoderInterface $encoder
*
* @return array|\ApiBundle\Response\ViewInterface
*/
public function registerAction(Request $request)
{
/** @var $userManager UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
$dispatcher = $this->get('event_dispatcher');
/** @var \UserBundle\Entity\User $user */
$user = $userManager->createUser();
$user->setEnabled(true);
$form = $this->createForm(RegistrationType::class, $user, array(
'paymentID' => $request->request->get('paymentID'),
));
$form->submit($request->request->all());
if ($form->isSubmitted() && $form->isValid()) {
$passwordEncoder = $this->get('security.password_encoder');
$encoded = $passwordEncoder->encodePassword($user, $form['password']->getData());
$user->setPassword($encoded);
// if (!empty($user->getBillingSubscription()->getPlan()->isFree())) {
// $user->getBillingSubscription()->setPayed(true);
// $userManager->updateUser($user);
// /** @var ConfigurationInterface $configuration */
// $configuration = $this->get(AppBundleServices::CONFIGURATION);
// return $this->generateResponse([
// 'message' => $configuration->getParameter(ParametersName::REGISTRATION_PAYMENT_AWAITING),
// ]);
// }
/** @var TokenGeneratorInterface $tokenGenerator */
$tokenGenerator = $this->container->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
//stripe register user
if (isset($form['paymentID'])) {
$stripe = $this->get('stripe.service');
$stripe->setApiKey();
$customer = $stripe->createCustomer(
[
'email' => $form['email']->getData(),
'name' => $form['firstName']->getData().' '.$form['lastName']->getData(),
]
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer failed'];
return $this->generateResponse($customerArray, 400);
}
if (isset($customer['id'])) {
$user->setStripeUserId($customer['id']);
//Add card atatch to customer
$cardAttach = $stripe->paymentMethodAttachToCustomer($form['paymentID']->getData(),
['customer' => $customer['id']]
);
if ($cardAttach instanceof ApiErrorException) {
$cardAttachArray = ['paymentError' => 1,'data'=>$cardAttach,'message'=>'Card attached to customer failed'];
return $this->generateResponse($cardAttachArray, 500);
}
//Add product
$product = $stripe->addProduct(
[
'name' => 'SOCIALHOSE.IO Media Monitoring Subscription',
'metadata' => (array)$customer['id']
]
);
if ($product instanceof ApiErrorException) {
$productArray = ['paymentError' => 1,'data'=>$product,'message'=>'Product failed'];
return $this->generateResponse($productArray, 400);
}
if (isset($product['id'])) {
//Call cost calculation plan
$costCalculation = $this->get('cost.calculation');
$response = $costCalculation->costCalculationAction($request, true);
//Add plan
// $plan = $stripe->addPlan(
// [
// 'amount' => isset($response['price']) ? $response['price'] * 100 : 0,
// 'currency' => 'usd',
// 'interval' => 'month',
// 'product' => $product['id']
// ]
// );
// if ($plan instanceof ApiErrorException) {
// $planArray = ['paymentError' => 1,'data'=>[],'message'=>'Plan failed'];
// return $this->generateResponse($planArray, 500);
// }
$price = $stripe->addPrice(
[
'unit_amount' => isset($response['price']) ? $response['price'] * 100 : 0,
'currency' => 'usd',
'recurring' => ['interval' => 'month'],
'product' => $product['id']
]
);
if ($price instanceof ApiErrorException) {
$priceArray = ['paymentError' => 1,'data'=>$price,'message'=>'Price add failed'];
return $this->generateResponse($priceArray, 400);
}
//Plan subscription code
if (isset($price['id'])) {
//Add plan
$subscription = $stripe->createSubscription(
[
'customer' => $customer['id'],
'items' => [['price' => $price['id']]],
'default_payment_method' => $form['paymentID']->getData()
]
);
if ($subscription instanceof ApiErrorException) {
$subscriptionArray = ['paymentError' => 1,'data'=>$subscription,'message'=>'Subscribtion failed'];
return $this->generateResponse($subscriptionArray, 400);
}
}
}
}
}
$mailer = $this->get('user.mailer.default');
$baseurl = $request->getScheme() . '://' . $request->getHttpHost() . $request->getBasePath();
$mailer->sendEmailMessage($user, $baseurl);
$userManager->updateUser($user);
return $this->generateResponse([
'success' => true,
'isFreeUser'=> isset($form['paymentID']) ? false : true
]);
}
return $this->generateResponse($form, 400);
}
/**
* Receive the confirmation token from user email provider, login the user.
*
* @param Request $request
* @param string $token
*
* @return Response
*/
public function confirmAction(Request $request, $token)
{
/** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserByConfirmationToken($token);
if (null === $user) {
throw new NotFoundHttpException(sprintf('The user with confirmation token "%s" does not exist', $token));
}
/** @var $dispatcher EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user->setConfirmationToken(null);
$user->setEnabled(true);
$user->setVerified(true);
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_CONFIRM, $event);
$userManager->updateUser($user);
return $this->generateResponse([
'success' => true,
]);
}
/**
* Get billing plans
*
* @Route("/plans", methods={ "GET" })
*
* @ApiDoc(
* resource="Registration",
* section="Security",
* output={
* "class"="Array<UserBundle\Entity\Plan>",
* "groups"={ "id", "plan" }
* },
* statusCodes={
* 200="All available plans."
* }
* )
*
* @return array|\ApiBundle\Response\ViewInterface
*/
public function billingPlansAction()
{
$repository = $this->getManager()->getRepository(Plan::class);
$plans = $repository->findAll();
if (count($plans) === 0) {
return $this->generateResponse("Can't find plans.", 404);
}
return $this->generateResponse($plans, 200, [
'id',
'plan',
]);
}
/**
* Organization autocomplete
*
* @Route("/organizationAutocomplete", methods={ "GET" })
* @ApiDoc(
* resource="Registration",
* section="Security",
* filters={
* {
* "name"="organizationName",
* "dataType"="string",
* "description"="Part of organization name",
* "requirements"="[\w\s]+"
* }
* },
* output={
* "class"="",
* "data"={
* ""={
* "dataType"="Collection of string",
* "description"="Matched organization names.",
* "required"=true,
* "readonly"=true
* }
* }
* },
* statusCodes={
* 200="All available organization names."
* }
* )
*
* @param Request $request A Request instance.
*
* @return string
*/
public function organizationAutocompleteAction(Request $request)
{
$repository = $this->getManager()->getRepository(Organization::class);
$organizationName = trim($request->query->get('organizationName'));
$organizationName = implode(' ', \nspl\a\map(function ($name) {
return '%'. trim($name) .'%';
}, explode(' ', $organizationName)));
$organizations = $repository->createQueryBuilder('Organization')
->select('Organization.name')
->where('Organization.name LIKE :name')
->setParameter('name', $organizationName)
->getQuery()
->setMaxResults(self::DEFAULT_LIMIT)
->getResult();
return $this->generateResponse(\nspl\a\map(\nspl\op\itemGetter('name'), $organizations));
}
/**
* Get list of available gateways.
*
* @Route("/paymentGateways", methods={ "GET" })
* @ApiDoc(
* resource="Registration",
* section="Security",
* output={
* "class"="",
* "data"={
* ""={
* "dataType"="collection of string",
* "description"="Available payment gateways"
* }
* }
* },
* )
*
* @return array
*/
public function gatewaysAction()
{
return PaymentGatewayEnum::getChoices();
}
/**
* Finish registration.
*
* @Route("/finish", methods={ "POST" })
* @ApiDoc(
* resource="Registration",
* section="Security",
* input={
* "class"="UserBundle\Form\PaymentDataType",
* "name"=false
* },
* output={
* "class"="",
* "data"={
* "message"={
* "dataType"="string",
* "description"="Payment success message"
* }
* }
* },
* )
*
* @param Request $request A HTTP Request instance.
*
* @return string
*/
public function finishAction(Request $request)
{
$form = $this->createForm(PaymentDataType::class);
$form->submit($request->request->all());
if ($form->isSubmitted() && $form->isValid()) {
/** @var PaymentData $data */
$data = $form->getData();
$gatewayName = $data->getGateway();
$subscription = $data->getUser()->getBillingSubscription();
$creditCard = $data->getCreditCard();
if ($subscription === null) {
return $this->generateResponse([ 'Unknown confirmation token' ], 400);
}
/** @var PaymentGatewayFactoryInterface $gatewayFactory */
$gatewayFactory = $this->get(PaymentBundleServices::PAYMENT_GATEWAY_FACTORY);
$gateway = $gatewayFactory->getGateway($gatewayName);
$billingSubscription = new BillingSubscription($subscription, $subscription->getPlan(), $creditCard);
$gateway->executeSubscription($billingSubscription);
$user = $data->getUser();
$user->setConfirmationToken(null);
/** @var EntityManagerInterface $em */
$em = $this->get('doctrine.orm.default_entity_manager');
$em->persist($user);
$em->flush();
/** @var ConfigurationInterface $configuration */
$configuration = $this->get(AppBundleServices::CONFIGURATION);
return $this->generateResponse([
'message' => $configuration->getParameter(ParametersName::REGISTRATION_PAYMENT_AWAITING),
]);
}
return $this->generateResponse($form, 400);
}
}
@@ -0,0 +1,171 @@
<?php
namespace UserBundle\Controller\Security;
use ApiBundle\Controller\AbstractApiController;
use FOS\UserBundle\Model\UserManagerInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
use UserBundle\Entity\User;
use UserBundle\Form\ResettingConfirmType;
use UserBundle\Form\ResettingRequestType;
use UserBundle\Mailer\MailerInterface;
use UserBundle\UserBundleServices;
/**
* Class ResettingController
* @package UserBundle\Controller\Security
*
* @Route("/resetting", service="user.controller.resetting")
*/
class ResettingController extends AbstractApiController
{
/**
* Request password resetting.
* User will receive email with information about resetting account.
*
* Example
*
* Request:
* ```json
* {
* "email": "socialhose@mail.com"
* }
* ```
*
* User will receive email with link "../auth/reset-password/?resetting_token=12dasv...".
*
* @Route("/request", methods={ "POST" })
* @ApiDoc(
* resource="Resetting",
* section="Security",
* input={
* "class"="UserBundle\Form\ResettingRequestType",
* "name"=false
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function requestAction(Request $request)
{
$form = $this->createForm(ResettingRequestType::class);
$form->submit($request->request->all());
if ($form->isValid()) {
$data = $form->getData();
/** @var UserManagerInterface $manager */
$manager = $this->get('fos_user.user_manager');
$user = $manager->findUserByEmail($data['email']);
if (! $user instanceof User) {
$message = "A recovery link has been sent to {$data['email']}, if found in our system.";
return $this->generateResponse($message, 400);
}
if (! $user->isEnabled()) {
// This user id locked, so we don't send reset email to him.
return $this->generateResponse('User is locked.', 400);
}
$ttl = $this->container->getParameter('fos_user.resetting.token_ttl');
if ($user->isPasswordRequestNonExpired($ttl)) {
// This user already request password changing and reset token is not
// expired yet.
return $this->generateResponse('Already requested.', 400);
}
// Generate new confirmation token.
/** @var TokenGeneratorInterface $tokenGenerator */
$tokenGenerator = $this->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
$user->setPasswordRequestedAt(new \DateTime());
$manager->updateUser($user);
// Send confirmation email to user.
/** @var MailerInterface $mailer */
$mailer = $this->get(UserBundleServices::MAILER);
$mailer->sendPasswordResettingConfirmation($user);
return $this->generateResponse();
}
return $this->generateResponse($form, 400);
}
/**
* Confirm password resetting.
*
* Example
*
* Request:
* ```json
* {
* "confirmationToken": "12dasv ...",
* "password": "newPassword"
* }
* ```
*
* @Route("/confirm", methods={ "POST" })
* @ApiDoc(
* resource="Resetting",
* section="Security",
* input={
* "class"="UserBundle\Form\ResettingConfirmType",
* "name"=false
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function confirmAction(Request $request)
{
$form = $this->createForm(ResettingConfirmType::class);
$form->submit($request->request->all());
if ($form->isValid()) {
/** @var UserManagerInterface $userManager */
$userManager = $this->get('fos_user.user_manager');
$data = $form->getData();
$user = $userManager
->findUserByConfirmationToken($data['confirmationToken']);
if ($user === null) {
// Can't find user by provided confirmation token
return $this->generateResponse('Invalid token.', 400);
}
if (! $user->isEnabled()) {
// This user id locked, so we don't send reset email to him.
return $this->generateResponse('User is locked.', 400);
}
$ttl = $this->container->getParameter('fos_user.resetting.token_ttl');
if (! $user->isPasswordRequestNonExpired($ttl)) {
// This token is expired.
return $this->generateResponse('Confirmation token expired.', 400);
}
// All ok.
$user
->setPlainPassword($data['password'])
->setConfirmationToken(null)
->setPasswordRequestedAt(null);
$userManager->updateUser($user);
return $this->generateResponse();
}
return $this->generateResponse($form, 400);
}
}
@@ -0,0 +1,124 @@
<?php
namespace UserBundle\Controller\V1;
use ApiBundle\Controller\AbstractCRUDController;
use ApiBundle\Form\ActivatedEntitiesBatchType;
use ApiBundle\Form\EntitiesBatchType;
use ApiBundle\Form\SubscribeToNotificationsBatchType;
use ApiBundle\Security\Inspector\InspectorInterface;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Manager\Notification\NotificationManagerInterface;
use UserBundle\Security\Inspector\NotificationInspector;
use UserBundle\UserBundleServices;
/**
* Class AbstractRecipientController
*
* @package UserBundle\Controller\V1
*/
abstract class AbstractRecipientController extends AbstractCRUDController
{
/**
* Batch remove of recipients.
*
* @param Request $request A HTTP Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
protected function batchDelete(Request $request)
{
$processor = function (Collection $recipients) {
$em = $this->getManager();
foreach ($recipients as $recipient) {
$em->remove($recipient);
}
$em->flush();
};
return $this->batchProcessing(
$request,
InspectorInterface::DELETE,
EntitiesBatchType::class,
$processor
);
}
/**
* Batch activate/deactivate recipients.
*
* @param Request $request A HTTP Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
protected function batchActiveToggle(Request $request)
{
$processor = function (Collection $recipients, $active) {
$em = $this->getManager();
foreach ($recipients as $recipient) {
$recipient->setActive($active);
$em->persist($recipient);
}
$em->flush();
};
return $this->batchProcessing(
$request,
InspectorInterface::UPDATE,
ActivatedEntitiesBatchType::class,
$processor
);
}
/**
* Batch subscribe/unsubscribe specified recipient from notifications.
*
* @param Request $request A HTTP Request instance.
* @param integer $id A recipient entity instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
protected function batchSubscriptionToggle(Request $request, $id)
{
$recipient = $this->getManager()->getRepository($this->entity)->find($id);
if ($recipient === null) {
$name = \app\c\getShortName($this->entity);
// Remove 'Abstract' prefix if it exists.
if (strpos($name, 'Abstract') !== false) {
$name = substr($name, 8);
}
return $this->generateResponse("Can't find {$name} with id {$id}.", 404);
}
$reasons = $this->checkAccess(NotificationInspector::UPDATE, $recipient);
if (count($reasons) > 0) {
return $this->generateResponse($reasons, 403);
}
$processor = function (Collection $notifications, $subscribed) use ($recipient) {
/** @var NotificationManagerInterface $manager */
$manager = $this->get(UserBundleServices::NOTIFICATION_MANAGER);
$manager->subscriptionToggle(
$recipient,
$notifications->toArray(),
(bool) $subscribed
);
};
return $this->batchProcessing(
$request,
function (Collection $notifications, $subscribed) {
return $subscribed ? NotificationInspector::SUBSCRIBE : NotificationInspector::UNSUBSCRIBE;
},
SubscribeToNotificationsBatchType::class,
$processor
);
}
}
@@ -0,0 +1,272 @@
<?php
namespace UserBundle\Controller\V1;
use ApiBundle\Controller\AbstractApiController;
use ApiBundle\Controller\Annotation\Roles;
use FOS\UserBundle\Model\UserManagerInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Entity\User;
use UserBundle\Enum\UserRoleEnum;
use UserBundle\Form\SubscriberType;
use UserBundle\Mailer\MailerInterface;
use UserBundle\Repository\UserRepository;
use UserBundle\UserBundleServices;
/**
* Class CurrentSubscriberController
* @package UserBundle\Controller\V1
*
* @Route(
* "/users/current/subscribers",
* service="user.controller.current_subscriber"
* )
*/
class CurrentSubscriberController extends AbstractApiController
{
/**
* Get list of subscriber for current master.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("", methods={ "GET" })
* @ApiDoc(
* resource="Current user subscribers",
* section="User",
* filters={
* {
* "name"="page",
* "dataType"="integer",
* "description"="Requested page number, start from 1",
* "requirements"="\d+",
* "default"="1"
* },
* {
* "name"="limit",
* "dataType"="integer",
* "description"="Max entities per page, default 100",
* "requirements"="\d+",
* "default"="100"
* }
* },
* output={
* "class"="Pagination<UserBundle\Entity\User>",
* "groups"={ "subscriber", "id" }
* },
* statusCodes={
* 200="Subscribers successfully returned."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function listAction(Request $request)
{
/** @var UserRepository $repository */
$repository = $this->getManager()->getRepository('UserBundle:User');
$user = $this->getCurrentUser();
$pagination = $this->paginate(
$request,
$repository->getSubscribersQueryBuilder($user->getId())
);
return $this->generateResponse($pagination, 200, [ 'subscriber', 'id' ]);
}
/**
* Get information about subscriber.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/{id}", requirements={ "id"="\d+" }, methods={ "GET" })
* @ApiDoc(
* resource="Current user subscribers",
* section="User",
* output={
* "class"="Pagination<UserBundle\Entity\User>",
* "groups"={ "subscriber", "id" }
* },
* statusCodes={
* 200="Subscriber successfully returned."
* }
* )
*
* @param integer $id A subscriber User entity id.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function getAction($id)
{
/** @var UserManagerInterface $manager */
$manager = $this->get('fos_user.user_manager');
$current = $this->getCurrentUser();
$user = $manager->findUserBy([
'id' => $id,
'masterUser' => $current->getId(),
]);
if ($user === null) {
return $this->generateResponse("Can't find subscriber with id {$id}.", 404);
}
return $this->generateResponse($user, 200, [ 'subscriber', 'id' ]);
}
/**
* Create new subscriber for current user.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("", methods={ "POST" })
* @ApiDoc(
* resource="Current user subscribers",
* section="User",
* input={
* "class"="UserBundle\Form\SubscriberType",
* "name"=false
* },
* output={
* "class"="Pagination<UserBundle\Entity\User>",
* "groups"={ "subscriber", "id" }
* },
* statusCodes={
* 200="Subscriber successfully created."
* }
* )
*
* @param Request $request A Request instance.
*
* @return User|\ApiBundle\Response\ViewInterface
*/
public function createAction(Request $request)
{
$current = $this->getCurrentUser();
$user = new User();
$user
->generatePassword()
->setMasterUser($current)
->setRoles([ UserRoleEnum::SUBSCRIBER ]);
$form = $this->createForm(SubscriberType::class, $user);
$form->submit($request->request->all());
if ($form->isValid()) {
/** @var UserManagerInterface $manager */
$manager = $this->get('fos_user.user_manager');
$password = $user->getPlainPassword();
$manager->updateUser($user);
/** @var MailerInterface $mailer */
$mailer = $this->get(UserBundleServices::MAILER);
$mailer->sendPassword($user, $password);
return $this->generateResponse($user, 200, [ 'subscriber', 'id' ]);
}
return $this->generateResponse($form, 400);
}
/**
* Update current user subscriber.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/{id}", requirements={ "id"="\d+" }, methods={ "PUT" })
* @ApiDoc(
* resource="Current user subscribers",
* section="User",
* input={
* "class"="UserBundle\Form\SubscriberType",
* "name"=false
* },
* output={
* "class"="Pagination<UserBundle\Entity\User>",
* "groups"={ "subscriber", "id" }
* },
* statusCodes={
* 200="Subscriber successfully updated.",
* 404="Can't find category by specified id."
* }
* )
*
* @param Request $request A Request instance.
* @param integer $id A subscriber User entity id.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function putAction(Request $request, $id)
{
/** @var UserManagerInterface $manager */
$manager = $this->get('fos_user.user_manager');
$current = $this->getCurrentUser();
$user = $manager->findUserBy([
'id' => $id,
'masterUser' => $current->getId(),
]);
if ($user === null) {
return $this->generateResponse("Can't find subscriber with id {$id}.", 404);
}
$form = $this->createForm(SubscriberType::class, $user, [
'method' => 'PUT',
]);
$form->submit($request->request->all());
if ($form->isValid()) {
$manager->updateUser($user);
return $this->generateResponse($user, 200, [ 'subscriber', 'id' ]);
}
return $this->generateResponse($form, 400);
}
/**
* Update current user subscriber.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/{id}", requirements={ "id"="\d+" }, methods={ "DELETE" })
* @ApiDoc(
* resource="Current user subscribers",
* section="User",
* statusCodes={
* 200="Subscriber successfully deleted.",
* 404="Can't find category by specified id."
* }
* )
*
* @param integer $id A subscriber User entity id.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function deleteAction($id)
{
/** @var UserManagerInterface $manager */
$manager = $this->get('fos_user.user_manager');
$current = $this->getCurrentUser();
$user = $manager->findUserBy([
'id' => $id,
'masterUser' => $current->getId(),
]);
if ($user === null) {
return $this->generateResponse("Can't find subscriber with id {$id}.", 404);
}
$manager->deleteUser($user);
return $this->generateResponse();
}
}
@@ -0,0 +1,440 @@
<?php
namespace UserBundle\Controller\V1;
use ApiBundle\Controller\Annotation\Roles;
use ApiBundle\Security\Inspector\InspectorInterface;
use AppBundle\Model\SortingOptions;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Entity\Recipient\GroupRecipient;
use UserBundle\Entity\Recipient\PersonRecipient;
use UserBundle\Enum\StatusFilterEnum;
use UserBundle\Repository\GroupRecipientRepository;
use UserBundle\Repository\PersonRecipientRepository;
use UserBundle\Utils\AdditionalConditions;
/**
* Class GroupRecipientController
* @package UserBundle\Controller\V1
*
* @Route(
* "/recipients/groups",
* service="user.controller.group_recipient"
* )
*/
class GroupRecipientController extends AbstractRecipientController
{
/**
* Get list of available person recipients.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("", methods={ "GET" })
* @ApiDoc(
* resource="Group",
* section="Receivers",
* filters={
* {
* "name"="page",
* "dataType"="integer",
* "description"="Requested page number, start from 1",
* "requirements"="\d+",
* "default"="1"
* },
* {
* "name"="limit",
* "dataType"="integer",
* "description"="Max entities per page, default 100",
* "requirements"="\d+",
* "default"="100"
* },
* {
* "name"="recipientId",
* "dataType"="string",
* "description"="If set get recipients for specified recipient group",
* "requirements"="\d+",
* "required"=false
* },
* {
* "name"="sortField",
* "dataType"="string",
* "description"="Field name for sorting. Available: name, active, recipientsNumber, creationDate",
* "requirements"="\w+",
* "default"="name",
* "required"=false
* },
* {
* "name"="sortDirection",
* "dataType"="string",
* "description"="Sort direction. Available: asc, desc",
* "requirements"="(asc|desc)",
* "default"="asc",
* "required"=false
* },
* {
* "name"="filter",
* "dataType"="string",
* "description"="Keyword for searching groups by names",
* "requirements"="\w+"
* },
* {
* "name"="statusFilter",
* "dataType"="string",
* "description"="Keyword for searching groups by status",
* "requirements"="(no|yes|all)"
* },
* {
* "name"="include",
* "dataType"="string",
* "description"="Comma separated list of recipient group ids."
* },
* {
* "name"="exclude",
* "dataType"="string",
* "description"="Comma separated list of recipient group ids."
* }
* },
* output={
* "class"="",
* "data"={
* "groups"={
* "class"="UserBundle\Entity\Recipient\PersonRecipient",
* "groups"={ "id", "recipient" }
* },
* "meta"={
* "dataType"="model",
* "description"="Response meta information",
* "required"=true,
* "readonly"=true,
* "children"={
* "sort"={
* "dataType"="model",
* "requited"=true,
* "readonly"=true,
* "children"={
* "field"={
* "dataType"="string",
* "description"="Field name for sorting. Available: name, email, creationDate, active",
* "required"=true,
* "readonly"=true
* },
* "direction"={
* "dataType"="string",
* "description"="Sort direction. Available: asc, desc",
* "required"=true,
* "readonly"=true
* }
* }
* }
* }
* }
* }
* },
* statusCodes={
* 200="Recipients groups successfully funded.",
* 403="Don't has requested permissions."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function listAction(Request $request)
{
/** @var EntityManagerInterface $em */
$em = $this->get('doctrine.orm.default_entity_manager');
$recipientId = trim($request->query->get('recipientId'));
$currentUser = $this->getCurrentUser();
//
// Get sort parameters and filter.
//
$sortingOptions = SortingOptions::fromRequest($request, 'name');
$filter = trim($request->query->get('filter'));
/** @var GroupRecipientRepository $groupRepository */
$groupRepository = $em->getRepository(GroupRecipient::class);
//
// If we got group id we should try to fetch proper recipient group and
// check that user can get this group recipient.
//
if ($recipientId !== '') {
/** @var PersonRecipientRepository $personRepository */
$personRepository = $em->getRepository(PersonRecipient::class);
$person = $personRepository->getForUser($recipientId, $currentUser->getId());
if (! $person instanceof PersonRecipient) {
return $this->generateResponse("Can't find recipient with id {$recipientId}.", 404);
}
//
// Check access.
//
$reasons = $this->checkAccess(InspectorInterface::READ, $person);
if (count($reasons) > 0) {
return $this->generateResponse($reasons, 403);
}
$statusFilter = trim($request->query->get('statusFilter', StatusFilterEnum::ALL));
if ($statusFilter !== '') {
if (! StatusFilterEnum::isValid($statusFilter)) {
return $this->generateResponse("'statusFilter' should be one of ". implode(', ', StatusFilterEnum::getAvailables()));
}
$statusFilter = new StatusFilterEnum($statusFilter);
}
$additionalCond = AdditionalConditions::fromRequest($request);
$qb = $groupRepository->getQueryBuilderForPerson(
$currentUser->getId(),
$person->getId(),
$statusFilter,
$sortingOptions,
$filter,
$additionalCond
);
} else {
$qb = $groupRepository->getQueryBuilderForUser(
$currentUser->getId(),
$sortingOptions,
$filter
);
}
//
// We should get all paginated data and put 'subscribed' field value into
// Notification entity.
//
/** @var SlidingPagination $pagination */
$pagination = $this->paginate($request, $qb);
$serializationGroups = [ 'recipient', 'id' ];
if ($recipientId !== '') {
$data = array_map(function (array $element) {
/** @var AbstractRecipient $recipient */
$recipient = $element[0];
$recipient->enrolled = (bool) $element['enrolled'];
return $recipient;
}, iterator_to_array($pagination));
$serializationGroups[] = 'sublist';
$totalCount = $pagination->getTotalItemCount();
$pagination = $this->paginate($request, $data);
$pagination->setTotalItemCount($totalCount);
}
return $this->generateResponse(
[
'groups' => $pagination,
'meta' => [ 'sort' => $sortingOptions ],
],
200,
$serializationGroups
);
}
/**
* Create new recipient group.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("", methods={ "POST" })
* @ApiDoc(
* resource="Group",
* section="Receivers",
* input={
* "class"="UserBundle\Form\GroupRecipientType",
* "name"=false
* },
* output={
* "class"="UserBundle\Entity\Recipient\GroupRecipient",
* "groups"={ "id", "recipient" }
* },
* statusCodes={
* 204="New recipient group successfully created.",
* 400="Invalid parameters."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Entity\ManageableEntityInterface|\ApiBundle\Response\ViewInterface
*/
public function createAction(Request $request)
{
return parent::createEntity($request, GroupRecipient::create()->setOwner($this->getCurrentUser()));
}
/**
* Update specified recipient group.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/{id}", methods={ "PUT" }, requirements={ "id": "\d*" })
* @ApiDoc(
* resource="Group",
* section="Receivers",
* input={
* "class"="UserBundle\Form\GroupRecipientType",
* "name"=false
* },
* output={
* "class"="UserBundle\Entity\Recipient\GroupRecipient",
* "groups"={ "id", "recipient" }
* },
* statusCodes={
* 204="Recipient group successfully updated.",
* 400="Invalid parameters."
* }
* )
*
* @param Request $request A Request instance.
* @param integer $id A GroupRecipient entity id.
*
* @return \ApiBundle\Entity\ManageableEntityInterface|\ApiBundle\Response\ViewInterface
*/
public function putAction(Request $request, $id)
{
return parent::putEntity($request, $id);
}
/**
* Delete recipient groups.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/delete", methods={ "POST" })
* @ApiDoc(
* resource="Group",
* section="Receivers",
* input={
* "class"="",
* "data"={
* "ids"={
* "dataType"="Array of recipient group ids",
* "actualType"="collection",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* }
* }
* },
* output={
* "class"="UserBundle\Entity\Recipient\GroupRecipient",
* "groups"={ "id", "recipient" }
* },
* statusCodes={
* 204="Recipient groups successfully deleted.",
* 400="Invalid parameters."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface|null
*/
public function deleteAction(Request $request)
{
return $this->batchDelete($request);
}
/**
* Activate/deactivate recipient groups.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/active", methods={ "PUT" })
* @ApiDoc(
* resource="Group",
* section="Receivers",
* input={
* "class"="",
* "data"={
* "ids"={
* "dataType"="Array of recipient groups ids",
* "actualType"="collection",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* },
* "active"={
* "dataType"="Boolean flag",
* "actualType"="boolean",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* }
* }
*
* },
* statusCodes={
* 204="Recipient groups successfully activated/deactivated."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function activateAction(Request $request)
{
return $this->batchActiveToggle($request);
}
/**
* Subscribe/unsubscribe recipient from specified notifications.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/{id}/subscribe", methods={ "PUT" })
* @ApiDoc(
* resource="Group",
* section="Receivers",
* input={
* "class"="",
* "data"={
* "ids"={
* "dataType"="Array of recipients ids",
* "actualType"="collection",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* },
* "subscribe"={
* "dataType"="Boolean flag",
* "actualType"="boolean",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* }
* }
*
* },
* statusCodes={
* 204="Recipient group successfully subscribed/unsubscribed."
* }
* )
*
* @param Request $request A Request instance.
* @param integer $id A PersonRecipient entity instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function subscribeAction(Request $request, $id)
{
return $this->batchSubscriptionToggle($request, $id);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
<?php
namespace UserBundle\Controller\V1;
use ApiBundle\Controller\AbstractApiController;
use ApiBundle\Controller\Annotation\Roles;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use UserBundle\Entity\Notification\NotificationTheme;
use UserBundle\Repository\NotificationThemeRepository;
/**
* Class NotificationThemeController
* @package UserBundle\Controller\V1
*
* @Route(
* "/notifications/themes",
* service="user.controller.notification_theme"
* )
*/
class NotificationThemeController extends AbstractApiController
{
/**
* Get list of all notification's for current user.
*
* @Roles("ROLE_SUBSCRIBER")
*
* @Route("/default", methods={ "GET" })
* @ApiDoc(
* resource=true,
* section="NotificationTheme",
* output={
* "class"="UserBundle\Entity\Notification\NotificationTheme",
* "groups"={ "id", "notification_theme"}
* },
* statusCodes={
* 200="Default notification successfully returned.",
* 404="Can't find default notification theme."
* }
* )
*
* @return \ApiBundle\Response\ViewInterface
*/
public function defaultAction()
{
/** @var NotificationThemeRepository $repository */
$repository = $this->getManager()->getRepository(NotificationTheme::class);
$notification = $repository->getDefault();
if (! $notification instanceof NotificationTheme) {
return $this->generateResponse('Can\'t find default notification theme', 404);
}
return $this->generateResponse($notification, 200, [
'id',
'notification_theme',
]);
}
}
@@ -0,0 +1,441 @@
<?php
namespace UserBundle\Controller\V1;
use ApiBundle\Controller\Annotation\Roles;
use ApiBundle\Response\ViewInterface;
use ApiBundle\Security\Inspector\InspectorInterface;
use AppBundle\Model\SortingOptions;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Entity\Recipient\GroupRecipient;
use UserBundle\Entity\Recipient\PersonRecipient;
use UserBundle\Enum\StatusFilterEnum;
use UserBundle\Repository\GroupRecipientRepository;
use UserBundle\Repository\PersonRecipientRepository;
use UserBundle\Utils\AdditionalConditions;
/**
* Class PersonRecipientController
* @package UserBundle\Controller\V1
*
* @Route(
* "/recipients",
* service="user.controller.person_recipient"
* )
*/
class PersonRecipientController extends AbstractRecipientController
{
/**
* Get list of available person recipients.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("", methods={ "GET" })
* @ApiDoc(
* resource="Recipient",
* section="Receivers",
* filters={
* {
* "name"="page",
* "dataType"="integer",
* "description"="Requested page number, start from 1",
* "requirements"="\d+",
* "default"="1"
* },
* {
* "name"="limit",
* "dataType"="integer",
* "description"="Max entities per page, default 100",
* "requirements"="\d+",
* "default"="100"
* },
* {
* "name"="groupId",
* "dataType"="string",
* "description"="If set get recipients for specified recipient group",
* "requirements"="\d+",
* "required"=false
* },
* {
* "name"="sortField",
* "dataType"="string",
* "description"="Field name for sorting. Available: name, email,
* creationDate, active",
* "requirements"="\w+",
* "default"="name",
* "required"=false
* },
* {
* "name"="sortDirection",
* "dataType"="string",
* "description"="Sort direction. Available: asc, desc",
* "requirements"="(asc|desc)",
* "default"="asc",
* "required"=false
* },
* {
* "name"="filter",
* "dataType"="string",
* "description"="Keyword for searching. Part of name or email",
* "requirements"="\w+"
* },
* {
* "name"="statusFilter",
* "dataType"="string",
* "description"="Keyword for searching groups by status",
* "requirements"="(no|yes|all)"
* },
* {
* "name"="include",
* "dataType"="string",
* "description"="Comma separated list of recipient ids."
* },
* {
* "name"="exclude",
* "dataType"="string",
* "description"="Comma separated list of recipient ids."
* }
* },
* output={
* "class"="",
* "data"={
* "recipients"={
* "class"="UserBundle\Entity\Recipient\PersonRecipient",
* "groups"={ "id", "recipient" }
* },
* "meta"={
* "dataType"="model",
* "description"="Response meta information",
* "required"=true,
* "readonly"=true,
* "children"={
* "sort"={
* "dataType"="model",
* "requited"=true,
* "readonly"=true,
* "children"={
* "field"={
* "dataType"="string",
* "description"="Field name for sorting. Available:
* name, email, creationDate, active",
* "required"=true,
* "readonly"=true
* },
* "direction"={
* "dataType"="string",
* "description"="Sort direction. Available: asc,
* desc",
* "required"=true,
* "readonly"=true
* }
* }
* }
* }
* }
* }
* },
* statusCodes={
* 200="Recipients successfully funded.",
* 403="Don't has requested permissions."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function listAction(Request $request)
{
/** @var EntityManagerInterface $em */
$em = $this->get('doctrine.orm.default_entity_manager');
$groupId = trim($request->query->get('groupId'));
$currentUser = $this->getCurrentUser();
//
// Get sort parameters and filter.
//
$sortingOptions = SortingOptions::fromRequest($request, 'name');
$filter = $request->query->get('filter', '');
/** @var PersonRecipientRepository $personRepository */
$personRepository = $em->getRepository(PersonRecipient::class);
//
// If we got group id we should try to fetch proper recipient group and
// check that user can get this group recipient.
//
if ($groupId !== '') {
/** @var GroupRecipientRepository $groupRepository */
$groupRepository = $em->getRepository(GroupRecipient::class);
$group = $groupRepository->get($groupId);
if (! $group instanceof GroupRecipient) {
return $this->generateResponse("Can't find group recipient with id {$groupId}.", 404);
}
//
// Check access.
//
$reasons = $this->checkAccess(InspectorInterface::READ, $group);
if (count($reasons) > 0) {
return $this->generateResponse($reasons, 403);
}
$statusFilter = trim($request->query->get('statusFilter', StatusFilterEnum::ALL));
if ($statusFilter !== '') {
if (! StatusFilterEnum::isValid($statusFilter)) {
return $this->generateResponse("'statusFilter' should be one of ". implode(', ', StatusFilterEnum::getAvailables()));
}
$statusFilter = new StatusFilterEnum($statusFilter);
}
$additionalCond = AdditionalConditions::fromRequest($request);
$qb = $personRepository->getQueryBuilderForGroup(
$currentUser->getId(),
$group->getId(),
$statusFilter,
$sortingOptions,
$filter,
$additionalCond
);
} else {
$qb = $personRepository->getQueryBuilderForUser(
$currentUser->getId(),
$sortingOptions,
$filter
);
}
//
// We should get all paginated data and put 'subscribed' field value into
// Notification entity.
//
/** @var SlidingPagination $pagination */
$pagination = $this->paginate($request, $qb);
$serializationGroups = [ 'recipient', 'id' ];
if ($groupId !== '') {
$data = array_map(function (array $element) {
/** @var AbstractRecipient $recipient */
$recipient = $element[0];
$recipient->enrolled = (bool) $element['enrolled'];
return $recipient;
}, iterator_to_array($pagination));
$serializationGroups[] = 'sublist';
$totalCount = $pagination->getTotalItemCount();
$pagination = $this->paginate($request, $data);
$pagination->setTotalItemCount($totalCount);
}
return $this->generateResponse(
[
'recipients' => $pagination,
'meta' => [ 'sort' => $sortingOptions ],
],
200,
$serializationGroups
);
}
/**
* Create new recipient.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("", methods={ "POST" })
* @ApiDoc(
* resource="Recipient",
* section="Receivers",
* input={
* "class"="UserBundle\Form\PersonRecipientType",
* "name"=false
* },
* output={
* "class"="UserBundle\Entity\Recipient\PersonRecipient",
* "groups"={ "id", "recipient" }
* },
* statusCodes={
* 204="New recipient successfully created.",
* 400="Invalid parameters."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function createAction(Request $request)
{
return parent::createEntity($request, PersonRecipient::create()->setOwner($this->getCurrentUser()));
}
/**
* Update recipient.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/{id}", methods={ "PUT" }, requirements={ "id": "\d+" })
* @ApiDoc(
* resource="Recipient",
* section="Receivers",
* input={
* "class"="UserBundle\Form\PersonRecipientType",
* "name"=false
* },
* output={
* "class"="UserBundle\Entity\Recipient\PersonRecipient",
* "groups"={ "id", "recipient" }
* },
* statusCodes={
* 200="Recipient successfully updated.",
* 400="Invalid parameters."
* }
* )
*
* @param Request $request A Request instance.
* @param integer $id A PersonRecipient entity id.
*
* @return \ApiBundle\Entity\ManageableEntityInterface|\ApiBundle\Response\ViewInterface
*/
public function putAction(Request $request, $id)
{
return parent::putEntity($request, $id);
}
/**
* Delete recipient.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/delete", methods={ "POST" })
* @ApiDoc(
* resource="Recipient",
* section="Receivers",
* input={
* "class"="",
* "data"={
* "ids"={
* "dataType"="Array of recipients ids",
* "actualType"="collection",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* }
* }
* },
* statusCodes={
* 204="Recipients successfully deleted.",
* 400="Invalid parameters."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function deleteAction(Request $request)
{
return $this->batchDelete($request);
}
/**
* Activate/deactivate recipients.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/active", methods={ "PUT" })
* @ApiDoc(
* resource="Recipient",
* section="Receivers",
* input={
* "class"="",
* "data"={
* "ids"={
* "dataType"="Array of recipients ids",
* "actualType"="collection",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* },
* "active"={
* "dataType"="Boolean flag",
* "actualType"="boolean",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* }
* }
*
* },
* statusCodes={
* 204="Recipient successfully activated/deactivated."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function activateAction(Request $request)
{
return $this->batchActiveToggle($request);
}
/**
* Subscribe/unsubscribe recipient from specified notifications.
*
* @Roles("ROLE_MASTER_USER")
*
* @Route("/{id}/subscribe", methods={ "PUT" })
* @ApiDoc(
* resource="Recipient",
* section="Receivers",
* input={
* "class"="",
* "data"={
* "ids"={
* "dataType"="Array of recipients ids",
* "actualType"="collection",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* },
* "subscribe"={
* "dataType"="Boolean flag",
* "actualType"="boolean",
* "subtype"="string",
* "required"=true,
* "readonly"=true
* }
* }
*
* },
* statusCodes={
* 204="Recipient successfully subscribed/unsubscribed."
* }
* )
*
* @param Request $request A Request instance.
* @param integer $id A PersonRecipient entity instance.
*
* @return ViewInterface
*/
public function subscribeAction(Request $request, $id)
{
return $this->batchSubscriptionToggle($request, $id);
}
}
@@ -0,0 +1,198 @@
<?php
namespace UserBundle\Controller\V1;
use ApiBundle\Controller\Annotation\Roles;
use AppBundle\Controller\Traits\TokenStorageAwareTrait;
use AppBundle\Controller\V1\AbstractV1Controller;
use AppBundle\Model\SortingOptions;
use Doctrine\ORM\EntityManagerInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use UserBundle\Entity\Notification\NotificationSendHistory;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Enum\NotificationTypeEnum;
use UserBundle\Repository\NotificationSendHistoryRepository;
use UserBundle\Repository\RecipientRepository;
/**
* Class ReceiverController
* @package UserBundle\Controller\V1
*
* @Route(
* "/receivers",
* service="user.controller.receiver"
* )
*/
class ReceiverController extends AbstractV1Controller
{
const DEFAULT_LIMIT = 30;
use TokenStorageAwareTrait;
/**
* @var EntityManagerInterface
*/
private $em;
/**
* ReceiverController constructor.
*
* @param TokenStorageInterface $tokenStorage A TokenStorageInterface
* instance.
* @param EntityManagerInterface $em A EntityManagerInterface
* instance.
*/
public function __construct(
TokenStorageInterface $tokenStorage,
EntityManagerInterface $em
) {
$this->tokenStorage = $tokenStorage;
$this->em = $em;
}
/**
* Get list of available receivers.
*
* @Roles("ROLE_SUBSCRIBER")
*
* @Route("", methods={ "GET" })
* @ApiDoc(
* resource=true,
* section="Receivers",
* filters={
* {
* "name"="filter",
* "dataType"="string",
* "description"="Receivers name filter",
* "requirements"="[\w\s]+"
* },
* {
* "name"="exclude",
* "dataType"="string",
* "description"="Comma separated list of ids.",
* "requirements"="[\w,]+"
* }
* },
* output={
* "class"="Pagination<UserBundle\Entity\Recipient\AbstractRecipient>",
* "groups"={ "id", "recipient_autocompletion" }
* },
* statusCodes={
* 200="Receivers successfully funded."
* }
* )
*
* @param Request $request A Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function listAction(Request $request)
{
$keyword = trim($request->query->get('filter'));
$exclude = array_filter(array_map('trim', explode(',', $request->query->get('exclude', ''))));
/** @var RecipientRepository $repository */
$repository = $this->em->getRepository(AbstractRecipient::class);
$recipients = $repository->search(
$this->getCurrentUser()->getId(),
self::DEFAULT_LIMIT,
$keyword,
$exclude
);
return $this->generateResponse($recipients, 200, [ 'recipient_autocompletion', 'id' ]);
}
/**
* Get email history for specified receiver.
*
* @Roles("ROLE_SUBSCRIBER")
*
* @Route("/{id}/emailHistory", methods={ "GET" }, requirements={ "id": "\d+" })
* @ApiDoc(
* resource=true,
* section="Receivers",
* filters={
* {
* "name"="page",
* "dataType"="integer",
* "description"="Requested page number, start from 1",
* "requirements"="\d+",
* "default"="1"
* },
* {
* "name"="limit",
* "dataType"="integer",
* "description"="Max entities per page, default 100",
* "requirements"="\d+",
* "default"="100"
* },
* {
* "name"="sortField",
* "dataType"="string",
* "description"="Field name for sorting. Available: name, type, scheduleTime, sentTime",
* "requirements"="\w+",
* "default"="name",
* "required"=false
* },
* {
* "name"="sortDirection",
* "dataType"="string",
* "description"="Sort direction. Available: asc, desc",
* "requirements"="(asc|desc)",
* "default"="asc",
* "required"=false
* },
* {
* "name"="typeFilter",
* "dataType"="string",
* "description"="Filter receivers by notification type of specified entity id.",
* "requirements"="(alerts|newsletter|all)",
* "default"="all",
* "required"=false
* }
* },
* output={
* "class"="Pagination<UserBundle\Entity\Notification\NotificationSendHistory>",
* "groups"={ "id", "history", "schedule" }
* },
* statusCodes={
* 200="Receivers successfully funded."
* }
* )
*
* @param Request $request A Request instance.
* @param integer $id A AbstractRecipient entity id.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function historyAction(Request $request, $id)
{
$recipient = $this->em->find(AbstractRecipient::class, $id);
if (! $recipient instanceof AbstractRecipient) {
return $this->generateResponse("Can't find receiver with id {$id}.", 404);
}
$sortingOptions = SortingOptions::fromRequest($request, 'sentTime');
$typeFilter = $request->query->get('typeFilter', 'all');
if (($typeFilter !== 'all') && ! NotificationTypeEnum::isValid($typeFilter)) {
return $this->generateResponse("'typeFilter' should be one of all, ". implode(', ', NotificationTypeEnum::getAvailables()));
}
/** @var NotificationSendHistoryRepository $repository */
$repository = $this->em->getRepository(NotificationSendHistory::class);
$qb = $repository->getListForRecipient($recipient, $sortingOptions, $typeFilter);
$pagination = $this->paginate(
$qb,
$request->query->getInt('page', 1),
$request->query->getInt('limit', 10)
);
return $this->generateResponse($pagination, 200, [ 'id', 'history', 'schedule' ]);
}
}
@@ -0,0 +1,741 @@
<?php
namespace UserBundle\Controller\V1;
use ApiBundle\Controller\Annotation\Roles;
use AppBundle\Controller\Traits\FormFactoryAwareTrait;
use AppBundle\Controller\Traits\TokenStorageAwareTrait;
use AppBundle\Controller\V1\AbstractV1Controller;
use FOS\UserBundle\Model\UserManagerInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use UserBundle\Form\ChangePasswordType;
use Symfony\Component\DependencyInjection\ContainerInterface;
use PaymentBundle\Enum\PaymentGatewayEnum;
use UserBundle\Entity\Subscription\OrganizationSubscription;
use Stripe\Exception\ApiErrorException;
use UserBundle\Entity\Plan;
/**
* Class UserController
* @package UserBundle\Controller\V1
*
* @Route(
* "/users",
* service="user.controller.user"
* )
*/
class UserController extends AbstractV1Controller
{
use
TokenStorageAwareTrait,
FormFactoryAwareTrait;
/**
* @var UserManagerInterface
*/
private $userManager;
/**
* @var ContainerInterface
*/
private $container;
/**
* UserController constructor.
*
* @param TokenStorageInterface $tokenStorage A TokenStorageInterface
* instance.
* @param FormFactoryInterface $formFactory A FormFactoryInterface instance.
* @param UserManagerInterface $userManager A UserManagerInterface instance.
*/
public function __construct(
TokenStorageInterface $tokenStorage,
FormFactoryInterface $formFactory,
UserManagerInterface $userManager,
ContainerInterface $container
) {
$this->tokenStorage = $tokenStorage;
$this->formFactory = $formFactory;
$this->userManager = $userManager;
$this->container = $container;
}
/**
* Change password for current user.
*
* @Roles("ROLE_SUBSCRIBER")
*
* @Route("/change-password", methods={ "POST" })
* @ApiDoc(
* resource="Security",
* section="User",
* input={
* "class"="UserBundle\Form\ChangePasswordType",
* "name"=false
* }
* )
*
* @param Request $request A Http Request instance.
*
* @return \ApiBundle\Response\ViewInterface
*/
public function changePasswordAction(Request $request)
{
$user = $this->getCurrentUser();
$form = $this
->createForm(ChangePasswordType::class, $user)
->submit($request->request->all());
if ($form->isSubmitted() && $form->isValid()) {
$this->userManager->updateUser($user);
return $this->generateResponse();
}
return $this->generateResponse($form, 400);
}
/**
* Get list of subscriber for current master.
*
* @Roles("ROLE_SUBSCRIBER")
*
* @Route("/current/restrictions", methods={ "GET" })
* @ApiDoc(
* resource="Current",
* section="User",
* output={
* "class"="",
* "data"={
* "limits"={
* "dataType"="object",
* "required"=true,
* "readonly"=true,
* "children"={
* "searchesPerDay"={
* "dataType"="object",
* "description"="Searches per day limits",
* "required"=true,
* "readonly"=true,
* "children"={
* "limit"={
* "dataType"="integer",
* "description"="Allowed searches count",
* "required"=true,
* "readonly"=true
* },
* "current"={
* "dataType"="integer",
* "description"="Used searches count",
* "required"=true,
* "readonly"=true
* }
* }
* },
* "savedFeeds"={
* "dataType"="object",
* "description"="Feeds limits",
* "required"=true,
* "readonly"=true,
* "children"={
* "limit"={
* "dataType"="integer",
* "description"="Allowed feeds count",
* "required"=true,
* "readonly"=true
* },
* "current"={
* "dataType"="integer",
* "description"="Used feeds count",
* "required"=true,
* "readonly"=true
* }
* }
* },
* "masterAccounts"={
* "dataType"="object",
* "description"="Master accounts limits",
* "required"=true,
* "readonly"=true,
* "children"={
* "limit"={
* "dataType"="integer",
* "description"="Allowed master accounts count",
* "required"=true,
* "readonly"=true
* },
* "current"={
* "dataType"="integer",
* "description"="Used master accounts count",
* "required"=true,
* "readonly"=true
* }
* }
* },
* "subscriberAccounts"={
* "dataType"="object",
* "description"="Subscriber accounts limits",
* "required"=true,
* "readonly"=true,
* "children"={
* "limit"={
* "dataType"="integer",
* "description"="Allowed subscriber accounts count",
* "required"=true,
* "readonly"=true
* },
* "current"={
* "dataType"="integer",
* "description"="Used subscriber accounts count",
* "required"=true,
* "readonly"=true
* }
* }
* },
* "alerts"={
* "dataType"="object",
* "description"="Alerts limits",
* "required"=true,
* "readonly"=true,
* "children"={
* "limit"={
* "dataType"="integer",
* "description"="Allowed alerts count",
* "required"=true,
* "readonly"=true
* },
* "current"={
* "dataType"="integer",
* "description"="Used alerts count",
* "required"=true,
* "readonly"=true
* }
* }
* },
* "newsletters"={
* "dataType"="object",
* "description"="Newsletters limits",
* "required"=true,
* "readonly"=true,
* "children"={
* "limit"={
* "dataType"="integer",
* "description"="Allowed newsletters count",
* "required"=true,
* "readonly"=true
* },
* "current"={
* "dataType"="integer",
* "description"="Used newsletters count",
* "required"=true,
* "readonly"=true
* }
* }
* }
*
* }
* },
* "permissions"={
* "dataType"="object",
* "required"=true,
* "readonly"=true,
* "children"={
* "analytics"={
* "dataType"="boolean",
* "description"="Can user use analytics or not",
* "required"=true,
* "readonly"=true
* }
* }
* }
* }
* },
* statusCodes={
* 200="List of restrictions successfully returned."
* }
* )
*
* @return \ApiBundle\Response\ViewInterface
*/
public function restrictionsAction()
{
$user = $this->getCurrentUser();
if ($user === null) {
return $this->generateResponse([], 403);
}
return $this->generateResponse($user->getRestrictions());
}
/**
*
* @Route("/update/plan", methods={ "POST" })
*
* @param Request $request A Http Request instance.
*/
public function updatePlanAction(Request $request)
{
$user = $this->getCurrentUser();
$data = $request->request->all();
$gateway = PaymentGatewayEnum::paypal();
$em = $this->container->get('doctrine.orm.default_entity_manager');
if (isset(
$data['news'],
$data['blog'],
$data['reddit'],
$data['instagram'],
$data['twitter'],
$data['analytics'],
$data['searchesPerDay'],
$data['savedFeeds'],
$data['masterAccounts'],
$data['subscriberAccounts'],
$data['webFeeds'],
$data['alerts']
) &&
($data['searchesPerDay'] >= 0) &&
($data['savedFeeds'] >= 0) &&
($data['masterAccounts'] >= 0) &&
($data['subscriberAccounts'] >= 0) &&
($data['webFeeds'] >= 0) &&
($data['alerts'] >= 0)
) {
//Call cost calculation plan
$costCalculation = $this->container->get('cost.calculation');
$response = $costCalculation->costCalculationAction($request, true);
$oldPrice = $user->getBillingSubscription()->getPlan()->getPrice();
//Stripe process
$stripe = $this->container->get('stripe.service');
$stripe->setApiKey();
if (empty($user->getStripeUserId())) {
$customer = $stripe->createCustomer(
[
'email' => $user->getEmail(),
'name' => $user->getFirstName().' '.$user->getLastName(),
'metadata' => ['paymentMethod' => $data['paymentID']]
]
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer failed'];
return $this->generateResponse($customerArray, 400);
}
if (isset($customer['id'])) {
$user->setStripeUserId($customer['id']);
$em->persist($user);
$em->flush();
//Add card atatch to customer
if (isset($data['paymentID']) && !empty($data['paymentID'])) {
$cardAttach = $stripe->paymentMethodAttachToCustomer($data['paymentID'],
['customer' => $customer['id']]
);
if ($cardAttach instanceof ApiErrorException) {
$cardAttachArray = ['paymentError' => 1,'data'=>$cardAttach,'message'=>'Card attached to customer failed'];
return $this->generateResponse($cardAttachArray, 500);
}
}
//Add product
$product = $stripe->addProduct(
[
'name' => $user->getCompanyName(),
'metadata' => (array)$customer['id']
]
);
if ($product instanceof ApiErrorException) {
$productArray = ['paymentError' => 1,'data'=>$product,'message'=>'Product failed'];
return $this->generateResponse($productArray, 400);
}
if (isset($product['id'])) {
//Call cost calculation plan
$costCalculation = $this->container->get('cost.calculation');
$response = $costCalculation->costCalculationAction($request, true);
$price = $stripe->addPrice(
[
'unit_amount' => isset($response['price']) ? $response['price'] * 100 : 0,
'currency' => 'usd',
'recurring' => ['interval' => 'month'],
'product' => $product['id']
]
);
if ($price instanceof ApiErrorException) {
$priceArray = ['paymentError' => 1,'data'=>$price,'message'=>'Price add failed'];
return $this->generateResponse($priceArray, 400);
}
//Plan subscription code
if (isset($price['id'])) {
//Add plan
$subscription = $stripe->createSubscription(
[
'customer' => $customer['id'],
'items' => [['price' => $price['id']]],
'default_payment_method' => $data['paymentID']
]
);
if ($subscription instanceof ApiErrorException) {
$subscriptionArray = ['paymentError' => 1,'data'=>$subscription,'message'=>'Subscribtion failed'];
return $this->generateResponse($subscriptionArray, 400);
}
//update customer metadata
$customer = $stripe->updateCustomer($customer['id'],
[
'metadata' => [
'paymentMethod' => $data['paymentID'],
'productId' => $product['id'],
'priceId' => $price['id'],
'subscriptionId' => $subscription['id'],
'subStartDate' => $subscription['current_period_start'],
'subEndDate' => $subscription['current_period_end'],
]
]
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer add meta data failed'];
return $this->generateResponse($customerArray, 400);
}
$plan = $user->getBillingSubscription()->getPlan();
$plan->setTitle($user->getCompanyName())
->setInnerName('Starter')
->setPrice(isset($response['price']) ? $response['price'] : 0)
->setNews($data['news'])
->setBlog($data['blog'])
->setReddit($data['reddit'])
->setInstagram($data['instagram'])
->setTwitter($data['twitter'])
->setAnalytics($data['analytics'])
->setSearchesPerDay($data['searchesPerDay'])
->setSavedFeeds($data['savedFeeds'])
->setMasterAccounts($data['masterAccounts'])
->setSubscriberAccounts($data['subscriberAccounts'])
->setWebFeeds($data['webFeeds'])
->setUser($user)
->setAlerts($data['alerts']);
$em->persist($plan);
$em->flush();
$subscriptionObj = $user->getBillingSubscription();
$subscriptionObj->setGateway($gateway);
$subscriptionObj->setStartDate(new \DateTime('@' . $subscription['current_period_start']));
$subscriptionObj->setEndDate(new \DateTime('@' . $subscription['current_period_end']));
$em->persist($subscriptionObj);
$em->flush();
}
}
}
} else {
if ($response['price'] < $oldPrice) {
$planNew = new Plan();
$planNew->setTitle($user->getCompanyName());
$planNew->setInnerName('Starter');
$planNew->setPrice(isset($response['price']) ? $response['price'] : 0);
$planNew->setNews($data['news']);
$planNew->setBlog($data['blog']);
$planNew->setReddit($data['reddit']);
$planNew->setInstagram($data['instagram']);
$planNew->setTwitter($data['twitter']);
$planNew->setAnalytics($data['analytics']);
$planNew->setSearchesPerDay($data['searchesPerDay']);
$planNew->setSavedFeeds($data['savedFeeds']);
$planNew->setMasterAccounts($data['masterAccounts']);
$planNew->setSubscriberAccounts($data['subscriberAccounts']);
$planNew->setWebFeeds($data['webFeeds']);
$planNew->setAlerts($data['alerts']);
$planNew->setUser($user);
$planNew->setIsPlanDowngrade(true);
$em->persist($planNew);
$em->flush();
$subscription = $user->getBillingSubscription();
$subscription->setIsPlanDowngrade(true);
$em->persist($subscription);
$em->flush();
$customer = $stripe->getCustomer(
$user->getStripeUserId()
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer not found'];
return $this->generateResponse($customerArray, 400);
}
if (isset($customer['id']) && isset($data['paymentID']) && !empty($data['paymentID'])) {
//Add card atatch to customer
$cardAttach = $stripe->paymentMethodAttachToCustomer($data['paymentID'],
['customer' => $customer['id']]
);
if ($cardAttach instanceof ApiErrorException) {
$cardAttachArray = ['paymentError' => 1,'data'=>$cardAttach,'message'=>'Card update attached to customer failed'];
return $this->generateResponse($cardAttachArray, 500);
}
//Card detach to customer
$cardDetachPaymentMethod = $stripe->paymentMethodDetachToCustomer($customer['metadata']['paymentMethod']
);
if ($cardDetachPaymentMethod instanceof ApiErrorException) {
$cardDetachArray = ['paymentError' => 1,'data'=>$cardDetachPaymentMethod,'message'=>'Card detach to customer failed'];
return $this->generateResponse($cardDetachArray, 500);
}
//update customer metadata
$customer = $stripe->updateCustomer($customer['id'],
[
'metadata' => [
'paymentMethod' => isset($data['paymentID']) ? $data['paymentID'] : $customer['metadata']['paymentMethod'],
]
]
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer update meta data failed'];
return $this->generateResponse($customerArray, 400);
}
}
} else {
$customer = $stripe->getCustomer(
$user->getStripeUserId()
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer not found'];
return $this->generateResponse($customerArray, 400);
}
if (isset($customer['id'])) {
//Add card atatch to customer
if (isset($data['paymentID']) && !empty($data['paymentID'])) {
$cardAttach = $stripe->paymentMethodAttachToCustomer($data['paymentID'],
['customer' => $customer['id']]
);
if ($cardAttach instanceof ApiErrorException) {
$cardAttachArray = ['paymentError' => 1,'data'=>$cardAttach,'message'=>'Card update attached to customer failed'];
return $this->generateResponse($cardAttachArray, 500);
}
//Card detach to customer
$cardDetachPaymentMethod = $stripe->paymentMethodDetachToCustomer($customer['metadata']['paymentMethod']
);
if ($cardDetachPaymentMethod instanceof ApiErrorException) {
$cardDetachArray = ['paymentError' => 1,'data'=>$cardDetachPaymentMethod,'message'=>'Card detach to customer failed'];
return $this->generateResponse($cardDetachArray, 500);
}
}
if (isset($customer['metadata']['productId'])) {
$price = $stripe->addPrice(
[
'unit_amount' => isset($response['price']) ? $response['price'] * 100 : 0,
'currency' => 'usd',
'recurring' => ['interval' => 'month'],
'product' => $customer['metadata']['productId']
]
);
if ($price instanceof ApiErrorException) {
$priceArray = ['paymentError' => 1,'data'=>$price,'message'=>'Price not found'];
return $this->generateResponse($priceArray, 400);
}
//Plan subscription code
if (isset($price['id'])) {
//Add subscription
$subscription = $stripe->getSubscription(
$customer['metadata']['subscriptionId']
);
if ($subscription instanceof ApiErrorException) {
$subscriptionArray = ['paymentError' => 1,'data'=>$subscription,'message'=>'Subscribtion get failed'];
return $this->generateResponse($subscriptionArray, 400);
}
if (isset($subscription['id'])) {
//Add subscription item
$subscriptionItem = $stripe->updateSubscriptionItem($subscription['items']['data'][0]['id'],
[
'price' => $price['id'],
]
);
if ($subscriptionItem instanceof ApiErrorException) {
$subscriptionItemArray = ['paymentError' => 1,'data'=>$subscriptionItem,'message'=>'Subscribtion Item failed'];
return $this->generateResponse($subscriptionItemArray, 400);
}
}
//update customer metadata
$customer = $stripe->updateCustomer($customer['id'],
[
'metadata' => [
'paymentMethod' => isset($data['paymentID']) ? $data['paymentID'] : $customer['metadata']['paymentMethod'],
'priceId' => $price['id'],
'subscriptionId' => $subscription['id'],
'subStartDate' => $subscription['current_period_start'],
'subEndDate' => $subscription['current_period_end'],
]
]
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer update meta data failed'];
return $this->generateResponse($customerArray, 400);
}
$plan = $user->getBillingSubscription()->getPlan();
$plan->setTitle($user->getCompanyName())
->setInnerName('Starter')
->setPrice(isset($response['price']) ? $response['price'] : 0)
->setNews($data['news'])
->setBlog($data['blog'])
->setReddit($data['reddit'])
->setInstagram($data['instagram'])
->setTwitter($data['twitter'])
->setAnalytics($data['analytics'])
->setSearchesPerDay($data['searchesPerDay'])
->setSavedFeeds($data['savedFeeds'])
->setMasterAccounts($data['masterAccounts'])
->setSubscriberAccounts($data['subscriberAccounts'])
->setWebFeeds($data['webFeeds'])
->setUser($user)
->setAlerts($data['alerts']);
$em->persist($plan);
$em->flush();
$user->getBillingSubscription()->setStartDate(new \DateTime('@' . $subscription['current_period_start']));
$user->getBillingSubscription()->setEndDate(new \DateTime('@' . $subscription['current_period_end']));
$em->persist($user);
$em->flush();
}
}
}
}
}
}
return $this->generateResponse([
'success' => true,
]);
}
/**
*
* @Route("/cancel/plan", methods={ "POST" })
*
* @param Request $request A Http Request instance.
*/
public function cancelSubscriptionAction(Request $request)
{
$user = $this->getCurrentUser();
//Stripe process
$stripe = $this->container->get('stripe.service');
$stripe->setApiKey();
$em = $this->container->get('doctrine.orm.default_entity_manager');
$customer = $stripe->getCustomer(
$user->getStripeUserId()
);
$customerArray = [];
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer not found'];
return $this->generateResponse($customerArray, 400);
}
if (isset($customer['id'])) {
$updateSubscription = $stripe->updateSubscription($customer['metadata']['subscriptionId'],
[
'cancel_at_period_end' => true,
]
);
if ($updateSubscription instanceof ApiErrorException) {
$updateSubscriptionArray = ['paymentError' => 1,'data'=>$updateSubscription,'message'=>'Cancel subscription'];
return $this->generateResponse($updateSubscriptionArray, 500);
}
$user->getBillingSubscription()->setIsSubscriptionCancelled(true);
$em->persist($user);
$em->flush();
}
return $this->generateResponse([
'success' => true,
]);
}
/**
*
* @Route("/card/change", methods={ "POST" })
*
* @param Request $request A Http Request instance.
*/
public function changeCardAction(Request $request)
{
$user = $this->getCurrentUser();
if (!empty($user->getStripeUserId())) {
$customerStripeId = $user->getStripeUserId();
$stripe = $this->container->get('stripe.service');
$stripe->setApiKey();
$customer = $stripe->getCustomer(
$user->getStripeUserId()
);
//Card detach to customer
$cardDetachPaymentMethod = $stripe->paymentMethodDetachToCustomer($customer['metadata']['paymentMethod']
);
if ($cardDetachPaymentMethod instanceof ApiErrorException) {
$cardDetachArray = ['paymentError' => 1,'data'=>$cardDetachPaymentMethod,'message'=>'Card detach to customer failed'];
return $this->generateResponse($cardDetachArray, 500);
}
//Card attach to customer
$cardAttachPaymentMethod = $stripe->paymentMethodAttachToCustomer($request->request->get('paymentID'),
['customer' => $customerStripeId]
);
if ($cardAttachPaymentMethod instanceof ApiErrorException) {
$cardAttachArray = ['paymentError' => 1,'data'=>$cardAttachPaymentMethod,'message'=>'Updated card attached to customer failed'];
return $this->generateResponse($cardAttachArray, 500);
}
//update customer metadata
$customer = $stripe->updateCustomer($customerStripeId,
[
'metadata' => ['paymentMethod' => $request->request->get('paymentID')]
]
);
if ($customer instanceof ApiErrorException) {
$customerArray = ['paymentError' => 1,'data'=>$customer,'message'=>'Customer update meta data failed'];
return $this->generateResponse($customerArray, 400);
}
$data = ['success' => 1,'message'=>'Card updated successfully to this customer..'];
return $this->generateResponse($data,200);
}
$data = ['error' => 1,'message'=>'Customer not registered in Stripe'];
return $this->generateResponse($data, 400);
}
/**
*
* @Route("/invoices", methods={ "GET" })
*
* @param Request $request A Http Request instance.
*/
public function getInvoiceAction(Request $request)
{
$user = $this->getCurrentUser();
$invoices = [];
if (!empty($user->getStripeUserId())) {
$stripe = $this->container->get('stripe.service');
$stripe->setApiKey();
$invoices = $stripe->getAllInvoice(['customer' => $user->getStripeUserId()]);
if ($invoices instanceof ApiErrorException) {
$invoicesArray = ['paymentError' => 1,'data'=>$invoices,'message'=>'List all invoice of customer faild'];
return $this->generateResponse($invoicesArray, 400);
}
}
$data = ['success' => 1,'data' => $invoices];
return $this->generateResponse($data, 200);
}
}
@@ -0,0 +1,35 @@
<?php
namespace UserBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Class RemoveLastLoginListenerPass
*
* Remove standard LastLoginListener.
* We use stateless authentication so last login will be update on each request
* to api and this is not right.
*
* @package UserBundle\DependencyInjection\Compiler
*/
class RemoveLastLoginListenerPass implements CompilerPassInterface
{
const ID = 'fos_user.security.interactive_login_listener';
/**
* You can modify the container here before it is dumped to PHP code.
*
* @param ContainerBuilder $container A ContainerBuilder instance.
*
* @return void
*/
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition(self::ID)) {
$container->removeDefinition(self::ID);
}
}
}
@@ -0,0 +1,34 @@
<?php
namespace UserBundle\Doctrine\DBAL\Types;
use AppBundle\Doctrine\DBAL\Types\AbstractEnumType;
use UserBundle\Enum\NotificationTypeEnum;
/**
* Class NotificationTypeEnumType
* @package UserBundle\Doctrine\DBAL\Types
*/
class NotificationTypeEnumType extends AbstractEnumType
{
/**
* Return concrete enum class
*
* @return string
*/
protected function getClass()
{
return NotificationTypeEnum::class;
}
/**
* Gets the name of this type.
*
* @return string
*/
public function getName()
{
return 'notification_type';
}
}
@@ -0,0 +1,34 @@
<?php
namespace UserBundle\Doctrine\DBAL\Types;
use AppBundle\Doctrine\DBAL\Types\AbstractEnumType;
use UserBundle\Enum\ThemeTypeEnum;
/**
* Class ThemeTypeEnumType
* @package UserBundle\Doctrine\DBAL\Types
*/
class ThemeTypeEnumType extends AbstractEnumType
{
/**
* Return concrete enum class
*
* @return string
*/
protected function getClass()
{
return ThemeTypeEnum::class;
}
/**
* Gets the name of this type.
*
* @return string
*/
public function getName()
{
return 'theme_type';
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,199 @@
<?php
namespace UserBundle\Entity\Notification;
use ApiBundle\Entity\NormalizableEntityInterface;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use UserBundle\Entity\Notification\Schedule\AbstractNotificationSchedule;
/**
* Class NotificationSendHistory
*
* @ORM\Table(name="notifications_history")
* @ORM\Entity(repositoryClass="UserBundle\Repository\NotificationSendHistoryRepository")
*/
class NotificationSendHistory implements EntityInterface, NormalizableEntityInterface
{
use BaseEntityTrait;
/**
* @var Notification
*
* @ORM\ManyToOne(
* targetEntity="UserBundle\Entity\Notification\Notification",
* inversedBy="history"
* )
*/
private $notification;
/**
* Schedules which trigger notification sending.
*
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="UserBundle\Entity\Notification\Schedule\AbstractNotificationSchedule",
* mappedBy="history",
* cascade={ "persist", "remove" }
* )
*/
private $schedules;
/**
* @var \DateTime
*
* @ORM\Column(type="datetime")
*/
private $date;
/**
* NotificationSendHistory constructor.
*
* @param Notification $notification A Notification entity
* instance.
* @param AbstractNotificationSchedule[]|array $schedule Array of schedules which
* trigger notification
* sending.
*/
public function __construct(Notification $notification, array $schedule)
{
$this->notification = $notification;
foreach ($schedule as $item) {
$this->addSchedule($item);
}
$this->date = $notification->getLastSentAt();
}
/**
* Set notification
*
* @param Notification $notification A Notification entity instance.
*
* @return NotificationSendHistory
*/
public function setNotification(Notification $notification = null)
{
$this->notification = $notification;
return $this;
}
/**
* Get notification
*
* @return Notification
*/
public function getNotification()
{
return $this->notification;
}
/**
* Add schedule
*
* @param AbstractNotificationSchedule $schedule A AbstractNotificationSchedule
* instance.
*
* @return NotificationSendHistory
*/
public function addSchedule(AbstractNotificationSchedule $schedule)
{
$this->schedules[] = $schedule;
$schedule->setHistory($this);
return $this;
}
/**
* Remove schedule
*
* @param AbstractNotificationSchedule $schedule A AbstractNotificationSchedule
* instance.
*
* @return NotificationSendHistory
*/
public function removeSchedule(AbstractNotificationSchedule $schedule)
{
$this->schedules->removeElement($schedule);
$schedule->setHistory(null);
return $this;
}
/**
* Get schedules
*
* @return Collection
*/
public function getSchedules()
{
return $this->schedules;
}
/**
* Set date
*
* @param \DateTime $date Sent date.
*
* @return NotificationSendHistory
*/
public function setDate(\DateTime $date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* @return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Return metadata for current entity.
*
* @return \ApiBundle\Serializer\Metadata\Metadata
*/
public function getMetadata()
{
return new Metadata(static::class, [
PropertyMetadata::createInteger('notification', [ 'history' ])
->setField(function () {
return $this->notification->getId();
}),
PropertyMetadata::createString('name', [ 'history' ])
->setField(function () {
return $this->notification->getName();
}),
PropertyMetadata::createString('type', [ 'history' ])
->setField(function () {
return $this->notification->getNotificationType()->getValue();
}),
PropertyMetadata::createCollection('schedule', AbstractNotificationSchedule::class, [ 'history' ])
->setField('schedules'),
PropertyMetadata::createDate('date', [ 'history' ]),
]);
}
/**
* Return default normalization groups.
*
* @return array
*/
public function defaultGroups()
{
return [ 'id', 'history' ];
}
}
@@ -0,0 +1,211 @@
<?php
namespace UserBundle\Entity\Notification;
use ApiBundle\Entity\NormalizableEntityInterface;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* NotificationTheme
*
* @ORM\Table(name="notification_themes")
* @ORM\Entity(repositoryClass="UserBundle\Repository\NotificationThemeRepository")
*/
class NotificationTheme implements EntityInterface, NormalizableEntityInterface
{
use BaseEntityTrait;
/**
* Theme name.
*
* @var string
*
* @ORM\Column
*/
private $name;
/**
* @var NotificationThemeOptions
*
* @ORM\Embedded(
* class="UserBundle\Entity\Notification\NotificationThemeOptions",
* columnPrefix="enhanced_"
* )
*/
private $enhanced;
/**
* @var NotificationThemeOptions
*
* @ORM\Embedded(
* class="UserBundle\Entity\Notification\NotificationThemeOptions",
* columnPrefix="plain_"
* )
*/
private $plain;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $published = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean", name="`default`")
*/
private $default = false;
/**
* Set name
*
* @param string $name Theme name.
*
* @return NotificationTheme
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set enhanced
*
* @param NotificationThemeOptions $enhanced A NotificationThemeOptions instance.
*
* @return NotificationTheme
*/
public function setEnhanced(NotificationThemeOptions $enhanced)
{
$this->enhanced = $enhanced;
return $this;
}
/**
* Get enhanced
*
* @return NotificationThemeOptions
*/
public function getEnhanced()
{
return $this->enhanced;
}
/**
* Set plain
*
* @param NotificationThemeOptions $plain A NotificationThemeOptions instance.
*
* @return NotificationTheme
*/
public function setPlain(NotificationThemeOptions $plain)
{
$this->plain = $plain;
return $this;
}
/**
* Get plainOptions
*
* @return NotificationThemeOptions
*/
public function getPlain()
{
return $this->plain;
}
/**
* @param boolean $published Should this notification be published or not.
*
* @return NotificationTheme
*/
public function setPublished($published = true)
{
$this->published = $published;
return $this;
}
/**
* @return boolean
*/
public function isPublished()
{
return $this->published;
}
/**
* Set default
*
* @param boolean $default Set theme as default.
*
* @return NotificationTheme
*/
public function setDefault($default = true)
{
$this->default = $default;
return $this;
}
/**
* Get default
*
* @return boolean
*/
public function isDefault()
{
return $this->default;
}
/**
* Return metadata for current entity.
*
* @return \ApiBundle\Serializer\Metadata\Metadata
*/
public function getMetadata()
{
return new Metadata(static::class, [
PropertyMetadata::createInteger('id', [ 'id' ]),
PropertyMetadata::createString('name', [ 'notification_theme', 'notification_theme_list' ]),
PropertyMetadata::createBoolean('published', [ 'notification_theme' ]),
PropertyMetadata::createObject('enhanced', [ 'notification_theme' ])
->setField('enhanced')
->setActualType(NotificationThemeOptions::class),
PropertyMetadata::createObject('plain', [ 'notification_theme' ])
->setField('plain')
->setActualType(NotificationThemeOptions::class),
]);
}
/**
* Return default normalization groups.
*
* @return array
*/
public function defaultGroups()
{
return [ 'id', 'notification_theme' ];
}
}
@@ -0,0 +1,300 @@
<?php
namespace UserBundle\Entity\Notification;
use Doctrine\ORM\Mapping as ORM;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionColors;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionColorsBackground;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionColorsText;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionContent;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionFont;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionFonts;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionHeader;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionHighlightKeywords;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionShowInfo;
use UserBundle\Enum\FontFamilyEnum;
use UserBundle\Enum\ThemeOptionExtractEnum;
use UserBundle\Enum\ThemeOptionsTableOfContentsEnum;
use UserBundle\Enum\ThemeOptionsUserCommentsEnum;
/**
* NotificationThemeOptions
*
* @ORM\Embeddable
*/
class NotificationThemeOptions
{
const DEFAULT_HEADER_SIZE = 18;
const DEFAULT_TABLE_OF_CONTENTS_SIZE = 12;
const DEFAULT_FEED_TITLE_SIZE = 12;
const DEFAULT_ARTICLE_HEADLINE_SIZE = 16;
const DEFAULT_SOURCE_SIZE = 12;
const DEFAULT_AUTHOR_SIZE = 12;
const DEFAULT_DATE_SIZE = 12;
const DEFAULT_ARTICLE_CONTENT_SIZE = 12;
/**
* Text which locate just after header but before content.
* Contains HTML.
*
* @var string
*
* @ORM\Column(type="text")
*/
private $summary;
/**
* Text which locate before footer just after content.
* Contains HTML.
*
* @var string
*
* @ORM\Column(type="text")
*/
private $conclusion;
/**
* @var ThemeOptionHeader
*
* @ORM\Column(type="object")
*/
private $header;
/**
* @var ThemeOptionFonts
*
* @ORM\Column(type="object")
*/
private $fonts;
/**
* @var ThemeOptionContent
*
* @ORM\Column(type="object")
*/
private $content;
/**
* @var ThemeOptionColors
*
* @ORM\Column(type="object")
*/
private $colors;
/**
* NotificationThemeOptions constructor.
*
* @param string $summary Summary text.
* @param string $conclusion Conclusion text.
* @param ThemeOptionHeader $header A ThemeOptionHeader instance.
* @param ThemeOptionFonts $fonts A ThemeOptionFonts instance.
* @param ThemeOptionContent $content A ThemeOptionContent instance.
* @param ThemeOptionColors $colors A ThemeOptionColors instance.
*/
public function __construct(
$summary,
$conclusion,
ThemeOptionHeader $header,
ThemeOptionFonts $fonts,
ThemeOptionContent $content,
ThemeOptionColors $colors
) {
$this->summary = trim($summary);
$this->conclusion = trim($conclusion);
$this->header = $header;
$this->fonts = $fonts;
$this->content = $content;
$this->colors = $colors;
}
/**
* @return static
*/
public static function createDefault()
{
return new static(
'',
'',
new ThemeOptionHeader(
ThemeOptionHeader::DEFAULT_IMAGE,
'',
'Newsletter'
),
new ThemeOptionFonts(
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_HEADER_SIZE),
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_TABLE_OF_CONTENTS_SIZE),
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_FEED_TITLE_SIZE),
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_ARTICLE_HEADLINE_SIZE),
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_SOURCE_SIZE),
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_AUTHOR_SIZE),
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_DATE_SIZE),
new ThemeOptionFont(FontFamilyEnum::arial(), self::DEFAULT_ARTICLE_CONTENT_SIZE)
),
new ThemeOptionContent(
new ThemeOptionHighlightKeywords(),
new ThemeOptionShowInfo(
ThemeOptionsUserCommentsEnum::no(),
ThemeOptionsTableOfContentsEnum::simple()
),
'en',
ThemeOptionExtractEnum::context()
),
new ThemeOptionColors(
new ThemeOptionColorsBackground(),
new ThemeOptionColorsText()
)
);
}
/**
* @return string
*/
public function getSummary()
{
return $this->summary;
}
/**
* @return boolean
*/
public function hasSummary()
{
return $this->summary !== '';
}
/**
* @param string $summary Summary text.
*
* @return NotificationThemeOptions
*/
public function setSummary($summary)
{
$this->summary = $summary;
return $this;
}
/**
* @return string
*/
public function getConclusion()
{
return $this->conclusion;
}
/**
* @return boolean
*/
public function hasConclusion()
{
return $this->conclusion !== '';
}
/**
* @param string $conclusion Conclusion text.
*
* @return NotificationThemeOptions
*/
public function setConclusion($conclusion)
{
$this->conclusion = $conclusion;
return $this;
}
/**
* @return ThemeOptionHeader
*/
public function getHeader()
{
return $this->header;
}
/**
* @param ThemeOptionHeader $header A ThemeOptionHeader instance.
*
* @return NotificationThemeOptions
*/
public function setHeader(ThemeOptionHeader $header)
{
$this->header = $header;
return $this;
}
/**
* @return ThemeOptionFonts
*/
public function getFonts()
{
return $this->fonts;
}
/**
* @param ThemeOptionFonts $fonts A ThemeOptionFonts instance.
*
* @return NotificationThemeOptions
*/
public function setFonts(ThemeOptionFonts $fonts)
{
$this->fonts = $fonts;
return $this;
}
/**
* @return ThemeOptionContent
*/
public function getContent()
{
return $this->content;
}
/**
* @param ThemeOptionContent $content A ThemeOptionContent instance.
*
* @return NotificationThemeOptions
*/
public function setContent(ThemeOptionContent $content)
{
$this->content = $content;
return $this;
}
/**
* @return ThemeOptionColors
*/
public function getColors()
{
return $this->colors;
}
/**
* @param ThemeOptionColors $colors A ThemeOptionColors instace.
*
* @return NotificationThemeOptions
*/
public function setColors(ThemeOptionColors $colors)
{
$this->colors = $colors;
return $this;
}
/**
* @return array
*/
public function toArray()
{
return [
'summary' => $this->summary,
'conclusion' => $this->conclusion,
'header' => $this->header->toArray(),
'fonts' => $this->fonts->toArray(),
'content' => $this->content->toArray(),
'colors' => $this->colors->toArray(),
];
}
}
@@ -0,0 +1,140 @@
<?php
namespace UserBundle\Entity\Notification\Schedule;
use ApiBundle\Entity\NormalizableEntityInterface;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Notification\NotificationSendHistory;
/**
* Class AbstractNotificationSchedule
*
* @ORM\Table(name="notification_schedule")
* @ORM\Entity
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({
* "daily"="DailyNotificationSchedule",
* "weekly"="WeeklyNotificationSchedule",
* "monthly"="MonthlyNotificationSchedule"
* })
*/
abstract class AbstractNotificationSchedule implements
EntityInterface,
NormalizableEntityInterface
{
use BaseEntityTrait;
/**
* @var Notification
*
* @ORM\ManyToOne(
* targetEntity="UserBundle\Entity\Notification\Notification",
* inversedBy="schedules"
* )
*/
private $notification;
/**
* @var NotificationSendHistory
*
* @ORM\ManyToOne(
* targetEntity="UserBundle\Entity\Notification\NotificationSendHistory",
* inversedBy="schedules"
* )
*/
private $history;
/**
* Set notification
*
* @param Notification $notification A Notification instance.
*
* @return static
*/
public function setNotification(Notification $notification = null)
{
$this->notification = $notification;
return $this;
}
/**
* Get notification
*
* @return Notification
*/
public function getNotification()
{
return $this->notification;
}
/**
* Set history
*
* @param NotificationSendHistory $history A NotificationSendHistory instance.
*
* @return AbstractNotificationSchedule
*/
public function setHistory(NotificationSendHistory $history = null)
{
$this->history = $history;
return $this;
}
/**
* Get history
*
* @return NotificationSendHistory
*/
public function getHistory()
{
return $this->history;
}
/**
* Compute all date's for this schedule in specified period.
*
* @param \DateTime $start Start of computing period.
* @param \DateTime $end End of computing period.
*
* @return \DateTime[]
*/
public function computeDates(\DateTime $start, \DateTime $end)
{
$modifiedStart = clone $start;
$modifiedEnd = clone $end;
$modifiedStart
->setTime($modifiedStart->format('H'), $modifiedStart->format('i'), 0);
// Add 1 seconds in order to catch end date if it should exists in result's.
$modifiedEnd
->setTime($modifiedEnd->format('H'), $modifiedEnd->format('i'), 1);
return $this->doComputeDates($modifiedStart, $modifiedEnd);
}
/**
* Return key identifier for current schedule.
*
* @return string
*/
abstract public function getKey();
/**
* Compute all date's for this schedule in specified period.
*
* @param \DateTime $start Start of computing period.
* @param \DateTime $end End of computing period.
*
* @return \DateTime[]
*/
abstract protected function doComputeDates(\DateTime $start, \DateTime $end);
}
@@ -0,0 +1,251 @@
<?php
namespace UserBundle\Entity\Notification\Schedule;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class DailyNotificationSchedule
*
* @ORM\Entity
*/
class DailyNotificationSchedule extends AbstractNotificationSchedule
{
const TIME_15_M = '15m';
const TIME_30_M = '30m';
const TIME_1_H = '1h';
const TIME_2_H = '2h';
const TIME_3_H = '3h';
const TIME_4_H = '4h';
const TIME_6_H = '6h';
const TIME_12_H = '12h';
const TIME_ONCE = 'once';
const DAYS_ALL = 'all';
const DAYS_WEEKDAYS = 'weekdays';
const DAYS_WEEKENDS = 'weekends';
/**
* @var string
*
* @ORM\Column(length=5)
* @Assert\Choice(callback="getAvailableTime")
*/
private $time;
/**
* @var string
*
* @ORM\Column(length=9)
* @Assert\Choice(callback="getAvailableDays")
*/
private $days;
/**
* Map between available time values and crontab schedule string.
*
* @var string[]
*/
private static $timeMap = [
self::TIME_15_M => 'T15M',
self::TIME_30_M => 'T30M',
self::TIME_1_H => 'T1H',
self::TIME_2_H => 'T2H',
self::TIME_3_H => 'T3H',
self::TIME_4_H => 'T4H',
self::TIME_6_H => 'T6H',
self::TIME_12_H => 'T12H',
self::TIME_ONCE => '1D',
];
/**
* Get available time values.
*
* @return string[]
*/
public static function getAvailableTime()
{
return [
self::TIME_15_M,
self::TIME_30_M,
self::TIME_1_H,
self::TIME_2_H,
self::TIME_3_H,
self::TIME_4_H,
self::TIME_6_H,
self::TIME_12_H,
self::TIME_ONCE,
];
}
/**
* Get available days values.
*
* @return string[]
*/
public static function getAvailableDays()
{
return [
self::DAYS_ALL,
self::DAYS_WEEKDAYS,
self::DAYS_WEEKENDS,
];
}
/**
* Set time
*
* @param string $time One of TIME_ constant's.
*
* @return DailyNotificationSchedule
*/
public function setTime($time)
{
$this->time = $time;
return $this;
}
/**
* Get time
*
* @return string
*/
public function getTime()
{
return $this->time;
}
/**
* Set days
*
* @param string $days One of DAYS_ constant's.
*
* @return DailyNotificationSchedule
*/
public function setDays($days)
{
$this->days = $days;
return $this;
}
/**
* Get days
*
* @return string
*/
public function getDays()
{
return $this->days;
}
/**
* Get entity type
*
* @return string
*/
public function getEntityType()
{
return 'daily';
}
/**
* Return metadata for current entity.
*
* @return \ApiBundle\Serializer\Metadata\Metadata
*/
public function getMetadata()
{
return new Metadata(static::class, [
PropertyMetadata::createString('time', [ 'schedule' ]),
PropertyMetadata::createString('days', [ 'schedule' ]),
]);
}
/**
* Return default normalization groups.
*
* @return array
*/
public function defaultGroups()
{
return [ 'schedule' ];
}
/**
* @param array $data Normalized DailyNotificationSchedule.
*
* @return DailyNotificationSchedule
*/
public static function denormalize(array $data)
{
if (! isset($data['time'], $data['days'])) {
throw new \LogicException('Normalized DailyNotificationSchedule data must have \'time\' and \'days\' fields.');
}
return DailyNotificationSchedule::create()
->setTime($data['time'])
->setDays($data['days']);
}
/**
* Return key identifier for current schedule.
*
* @return string
*/
public function getKey()
{
return sprintf('daily_%s_%s', $this->time, $this->days);
}
/**
* Compute all date's for this schedule in specified period.
*
* @param \DateTime $start Start of computing period.
* @param \DateTime $end End of computing period.
*
* @return \DateTime[]
*/
protected function doComputeDates(\DateTime $start, \DateTime $end)
{
$period = new \DatePeriod(
$start,
new \DateInterval('P'. self::$timeMap[$this->time]),
$end,
\DatePeriod::EXCLUDE_START_DATE
);
$results = [];
switch ($this->days) {
case self::DAYS_ALL:
$results = iterator_to_array($period);
break;
case self::DAYS_WEEKDAYS:
/** @var \DateTime $date */
foreach ($period as $date) {
if ($date->format('N') <= 5) {
$results[] = $date;
}
}
break;
case self::DAYS_WEEKENDS:
/** @var \DateTime $date */
foreach ($period as $date) {
if ($date->format('N') > 5) {
$results[] = $date;
}
}
break;
}
return $results;
}
}
@@ -0,0 +1,235 @@
<?php
namespace UserBundle\Entity\Notification\Schedule;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class MonthlyNotificationSchedule
*
* @ORM\Entity
*/
class MonthlyNotificationSchedule extends AbstractNotificationSchedule
{
const DAY_LAST = 'last';
/**
* @var string
*
* @ORM\Column(length=10)
* @Assert\Choice(callback="getAvailableDay")
*/
private $day;
/**
* @var integer
*
* @ORM\Column(type="smallint")
*/
private $hour;
/**
* @var integer
*
* @ORM\Column(type="smallint")
*/
private $minute;
/**
* Get available day's.
*
* @return string[]
*/
public static function getAvailableDay()
{
$days = range(1, 31);
$days[] = self::DAY_LAST;
return $days;
}
/**
* Set day
*
* @param string $day One of DAY_ const's.
*
* @return MonthlyNotificationSchedule
*/
public function setDay($day)
{
$this->day = $day;
return $this;
}
/**
* Get day
*
* @return string
*/
public function getDay()
{
return $this->day;
}
/**
* Set hour
*
* @param integer $hour Schedule hours.
*
* @return MonthlyNotificationSchedule
*/
public function setHour($hour)
{
$this->hour = $hour;
return $this;
}
/**
* Get hour
*
* @return integer
*/
public function getHour()
{
return $this->hour;
}
/**
* Set minutes
*
* @param integer $minute Schedule minutes.
*
* @return MonthlyNotificationSchedule
*/
public function setMinute($minute)
{
$this->minute = $minute;
return $this;
}
/**
* Get minute
*
* @return integer
*/
public function getMinute()
{
return $this->minute;
}
/**
* Get entity type
*
* @return string
*/
public function getEntityType()
{
return 'monthly';
}
/**
* Return metadata for current entity.
*
* @return \ApiBundle\Serializer\Metadata\Metadata
*/
public function getMetadata()
{
return new Metadata(static::class, [
PropertyMetadata::createString('day', [ 'schedule' ]),
PropertyMetadata::createString('hour', [ 'schedule' ]),
PropertyMetadata::createString('minute', [ 'schedule' ]),
]);
}
/**
* Return default normalization groups.
*
* @return array
*/
public function defaultGroups()
{
return [ 'schedule' ];
}
/**
* @param array $data Normalized MonthlyNotificationSchedule.
*
* @return MonthlyNotificationSchedule
*/
public static function denormalize(array $data)
{
if (! isset($data['day'], $data['hour'], $data['minute'])) {
throw new \LogicException('Normalized MonthlyNotificationSchedule data must have \'day\', \'hour\' and \'minute\' fields.');
}
return MonthlyNotificationSchedule::create()
->setDay($data['day'])
->setHour($data['hour'])
->setMinute($data['minute']);
}
/**
* Return key identifier for current schedule.
*
* @return string
*/
public function getKey()
{
return sprintf(
'monthly_%s_%s_%s',
$this->day,
$this->hour,
$this->minute
);
}
/**
* Compute all date's for this schedule in specified period.
*
* @param \DateTime $start Start of computing period.
* @param \DateTime $end End of computing period.
*
* @return \DateTime[]
*/
protected function doComputeDates(\DateTime $start, \DateTime $end)
{
$dates = [];
$date = clone $start;
if ($this->day === self::DAY_LAST) {
$date->modify('last day of this month');
while ($date <= $end) {
$tmp = clone $date;
$dates[] = $tmp->setTime($this->hour, $this->minute);
$date->modify('last day of next month');
}
} else {
$currentNum = $date->format('j');
if ($currentNum > $this->day) {
$date
->modify('first day of next month')
->modify(sprintf('%d day', $this->day - 1));
} elseif ($currentNum < $this->day) {
$date->modify(sprintf('%d day', $this->day - $currentNum));
}
while ($date <= $end) {
$tmp = clone $date;
$dates[] = $tmp->setTime($this->hour, $this->minute);
$date
->modify('first day of next month')
->modify(sprintf('%d day', $this->day - 1));
}
}
return $dates;
}
}
@@ -0,0 +1,331 @@
<?php
namespace UserBundle\Entity\Notification\Schedule;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class WeeklyNotificationSchedule
*
* @ORM\Entity
*/
class WeeklyNotificationSchedule extends AbstractNotificationSchedule
{
const PERIOD_EVERY = 'every';
const PERIOD_FIRST = 'first';
const PERIOD_SECOND = 'second';
const PERIOD_THIRD = 'third';
const PERIOD_FOURTH = 'fourth';
const PERIOD_LAST = 'last';
const DAY_MONDAY = 'monday';
const DAY_TUESDAY = 'tuesday';
const DAY_WEDNESDAY = 'wednesday';
const DAY_THURSDAY = 'thursday';
const DAY_FRIDAY = 'friday';
const DAY_SATURDAY = 'saturday';
const DAY_SUNDAY = 'sunday';
/**
* @var string
*
* @ORM\Column(length=7)
* @Assert\Choice(callback="getAvailablePeriod")
*/
private $period;
/**
* @var string
*
* @ORM\Column(length=10)
* @Assert\Choice(callback="getAvailableDay")
*/
private $day;
/**
* @var integer
*
* @ORM\Column(type="smallint")
*/
private $hour;
/**
* @var integer
*
* @ORM\Column(type="smallint")
*/
private $minute;
/**
* Map between day and corntab schedule. Make sense only for PERIOD_EVERY.
*
* @var array[]
*/
private static $dayMap = [
self::DAY_MONDAY => 1,
self::DAY_TUESDAY => 2,
self::DAY_WEDNESDAY => 3,
self::DAY_TUESDAY => 4,
self::DAY_FRIDAY => 5,
self::DAY_SATURDAY => 6,
self::DAY_SUNDAY => 7,
];
/**
* Get available period's.
*
* @return string[]
*/
public static function getAvailablePeriod()
{
return [
self::PERIOD_EVERY,
self::PERIOD_FIRST,
self::PERIOD_SECOND,
self::PERIOD_THIRD,
self::PERIOD_FOURTH,
self::PERIOD_LAST,
];
}
/**
* Get available day's.
*
* @return string[]
*/
public static function getAvailableDay()
{
return [
self::DAY_MONDAY,
self::DAY_TUESDAY,
self::DAY_WEDNESDAY,
self::DAY_TUESDAY,
self::DAY_FRIDAY,
self::DAY_SATURDAY,
self::DAY_SUNDAY,
];
}
/**
* Set period
*
* @param string $period One of PERIOD_ const's.
*
* @return WeeklyNotificationSchedule
*/
public function setPeriod($period)
{
$this->period = $period;
return $this;
}
/**
* Get period
*
* @return string
*/
public function getPeriod()
{
return $this->period;
}
/**
* Set day
*
* @param string $day One of DAY_ const's.
*
* @return WeeklyNotificationSchedule
*/
public function setDay($day)
{
$this->day = $day;
return $this;
}
/**
* Get day
*
* @return string
*/
public function getDay()
{
return $this->day;
}
/**
* Set hour
*
* @param integer $hour Schedule hours.
*
* @return WeeklyNotificationSchedule
*/
public function setHour($hour)
{
$this->hour = $hour;
return $this;
}
/**
* Get hour
*
* @return integer
*/
public function getHour()
{
return $this->hour;
}
/**
* Set minute
*
* @param integer $minute Schedule minute.
*
* @return WeeklyNotificationSchedule
*/
public function setMinute($minute)
{
$this->minute = $minute;
return $this;
}
/**
* Get minute
*
* @return integer
*/
public function getMinute()
{
return $this->minute;
}
/**
* Get entity type
*
* @return string
*/
public function getEntityType()
{
return 'weekly';
}
/**
* Return metadata for current entity.
*
* @return \ApiBundle\Serializer\Metadata\Metadata
*/
public function getMetadata()
{
return new Metadata(static::class, [
PropertyMetadata::createString('period', [ 'schedule' ]),
PropertyMetadata::createString('day', [ 'schedule' ]),
PropertyMetadata::createString('hour', [ 'schedule' ]),
PropertyMetadata::createString('minute', [ 'schedule' ]),
]);
}
/**
* Return default normalization groups.
*
* @return array
*/
public function defaultGroups()
{
return [ 'schedule' ];
}
/**
* @param array $data Normalized WeeklyNotificationSchedule.
*
* @return WeeklyNotificationSchedule
*/
public static function denormalize(array $data)
{
if (! isset($data['period'], $data['day'], $data['hour'], $data['minute'])) {
throw new \LogicException('Normalized WeeklyNotificationSchedule data must have \'period\', \'day\', \'hour\' and \'minute\' fields.');
}
return WeeklyNotificationSchedule::create()
->setPeriod($data['period'])
->setDay($data['day'])
->setHour($data['hour'])
->setMinute($data['minute']);
}
/**
* Return key identifier for current schedule.
*
* @return string
*/
public function getKey()
{
return sprintf(
'weekly_%s_%s_%s_%s',
$this->period,
$this->day,
$this->hour,
$this->minute
);
}
/**
* Compute all date's for this schedule in specified period.
*
* @param \DateTime $start Start of computing period.
* @param \DateTime $end End of computing period.
*
* @return \DateTime[]
*/
protected function doComputeDates(\DateTime $start, \DateTime $end)
{
$dates = [];
$date = clone $start;
if ($this->period === self::PERIOD_EVERY) {
//
// Process every weekdays days.
//
if ($date->format('N') !== self::$dayMap[$this->day]) {
//
// If start date is not required weekday we should proceed to next
// required weekday.
//
$date->modify('next '. $this->day);
}
while ($date <= $end) {
$tmp = clone $date;
$dates[] = $tmp->setTime($this->hour, $this->minute);
$date->modify('next '. $this->day);
}
} else {
//
// Process specified weekday like second monday, third friday and etc.
//
$date->modify(sprintf(
'%s %s of this month',
$this->period,
$this->day
));
while ($date <= $end) {
$tmp = clone $date;
$dates[] = $tmp->setTime($this->hour, $this->minute);
$date->modify(sprintf(
'%s %s of next month',
$this->period,
$this->day
));
}
}
return $dates;
}
}
@@ -0,0 +1,117 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
/**
* Class ThemeOptionColors
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionColors implements \Serializable
{
/**
* @var ThemeOptionColorsBackground
*/
private $background;
/**
* @var ThemeOptionColorsText
*/
private $text;
/**
* ThemeOptionColors constructor.
*
* @param ThemeOptionColorsBackground $background A ThemeOptionColorsBackground
* instance.
* @param ThemeOptionColorsText $text A ThemeOptionColorsText
* instance.
*/
public function __construct(
ThemeOptionColorsBackground $background,
ThemeOptionColorsText $text
) {
$this->background = $background;
$this->text = $text;
}
/**
* @return ThemeOptionColorsBackground
*/
public function getBackground()
{
return $this->background;
}
/**
* @param ThemeOptionColorsBackground $background A ThemeOptionColorsBackground
* instance.
*
* @return ThemeOptionColors
*/
public function setBackground(ThemeOptionColorsBackground $background)
{
$this->background = $background;
return $this;
}
/**
* @return ThemeOptionColorsText
*/
public function getText()
{
return $this->text;
}
/**
* @param ThemeOptionColorsText $text A ThemeOptionColorsText instance.
*
* @return ThemeOptionColors
*/
public function setText(ThemeOptionColorsText $text)
{
$this->text = $text;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->background,
$this->text,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->background = $data[0];
$this->text = $data[1];
}
/**
* @return array
*/
public function toArray()
{
return [
'background' => $this->background->toArray(),
'text' => $this->text->toArray(),
];
}
}
@@ -0,0 +1,167 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
/**
* Class ThemeOptionColorsBackground
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionColorsBackground implements \Serializable
{
const DEFAULT_HEADER = 'rgba(36, 37, 37, 1)';
const DEFAULT_EMAIL_BODY = 'rgba(244, 244, 245, 1)';
const DEFAULT_ACCENT = 'rgba(109, 110, 113, 1)';
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $header;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $emailBody;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $accent;
/**
* ThemeOptionColorsBackground constructor.
*
* @param string $header Header background color.
* @param string $emailBody Email body color.
* @param string $accent Accent color.
*/
public function __construct(
$header = self::DEFAULT_HEADER,
$emailBody = self::DEFAULT_EMAIL_BODY,
$accent = self::DEFAULT_ACCENT
) {
$this->header = trim($header);
$this->emailBody = trim($emailBody);
$this->accent = trim($accent);
}
/**
* @return string
*/
public function getHeader()
{
return $this->header;
}
/**
* @param string $header Header background color.
*
* @return ThemeOptionColorsBackground
*/
public function setHeader($header)
{
$this->header = $header;
return $this;
}
/**
* @return string
*/
public function getEmailBody()
{
return $this->emailBody;
}
/**
* @param string $emailBody Email body color.
*
* @return ThemeOptionColorsBackground
*/
public function setEmailBody($emailBody)
{
$this->emailBody = $emailBody;
return $this;
}
/**
* @return string
*/
public function getAccent()
{
return $this->accent;
}
/**
* @param string $accent Accent background color.
*
* @return ThemeOptionColorsBackground
*/
public function setAccent($accent)
{
$this->accent = $accent;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->header,
$this->emailBody,
$this->accent,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->header = $data[0];
$this->emailBody = $data[1];
$this->accent = $data[2];
}
/**
* @return array
*/
public function toArray()
{
return [
'header' => $this->header,
'emailBody' => $this->emailBody,
'accent' => $this->accent,
];
}
}
@@ -0,0 +1,281 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
/**
* Class ThemeOptionColorsText
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionColorsText implements \Serializable
{
const DEFAULT_HEADER = 'rgba(255, 255, 255, 1)';
const DEFAULT_ARTICLE_HEADLINE = 'rgba(0, 147, 176, 1)';
const DEFAULT_ARTICLE_CONTENT = 'rgba(102, 102, 102, 1)';
const DEFAULT_AUTHOR = 'rgba(143, 43, 140, 1)';
const DEFAULT_PUBLISH_DATE = 'rgba(109, 110, 113, 1)';
const DEFAULT_SOURCE = 'rgba(82, 83, 85, 1)';
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $header;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $articleHeadline;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $articleContent;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $author;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $publishDate;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $source;
/**
* ThemeOptionColorsText constructor.
*
* @param string $header Header text color.
* @param string $articleHeadline Article headline text color.
* @param string $articleContent Article content text color.
* @param string $author Author text color.
* @param string $publishDate Publish date text color.
* @param string $source Source text color.
*/
public function __construct(
$header = self::DEFAULT_HEADER,
$articleHeadline = self::DEFAULT_ARTICLE_HEADLINE,
$articleContent = self::DEFAULT_ARTICLE_CONTENT,
$author = self::DEFAULT_AUTHOR,
$publishDate = self::DEFAULT_PUBLISH_DATE,
$source = self::DEFAULT_SOURCE
) {
$this->header = trim($header);
$this->articleHeadline = trim($articleHeadline);
$this->articleContent = trim($articleContent);
$this->author = trim($author);
$this->publishDate = trim($publishDate);
$this->source = trim($source);
}
/**
* @return string
*/
public function getHeader()
{
return $this->header;
}
/**
* @param string $header Header text color.
*
* @return ThemeOptionColorsText
*/
public function setHeader($header)
{
$this->header = $header;
return $this;
}
/**
* @return string
*/
public function getArticleHeadline()
{
return $this->articleHeadline;
}
/**
* @param string $articleHeadline Article headline text color.
*
* @return ThemeOptionColorsText
*/
public function setArticleHeadline($articleHeadline)
{
$this->articleHeadline = $articleHeadline;
return $this;
}
/**
* @return string
*/
public function getArticleContent()
{
return $this->articleContent;
}
/**
* @param string $articleContent Article content text color.
*
* @return ThemeOptionColorsText
*/
public function setArticleContent($articleContent)
{
$this->articleContent = $articleContent;
return $this;
}
/**
* @return string
*/
public function getAuthor()
{
return $this->author;
}
/**
* @param string $author Author text color.
*
* @return ThemeOptionColorsText
*/
public function setAuthor($author)
{
$this->author = $author;
return $this;
}
/**
* @return string
*/
public function getPublishDate()
{
return $this->publishDate;
}
/**
* @param string $publishDate Publish date text color.
*
* @return ThemeOptionColorsText
*/
public function setPublishDate($publishDate)
{
$this->publishDate = $publishDate;
return $this;
}
/**
* @return string
*/
public function getSource()
{
return $this->source;
}
/**
* @param string $source Source text color.
*
* @return ThemeOptionColorsText
*/
public function setSource($source)
{
$this->source = $source;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->header,
$this->articleHeadline,
$this->articleContent,
$this->author,
$this->publishDate,
$this->source,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->header = $data[0];
$this->articleHeadline = $data[1];
$this->articleContent = $data[2];
$this->author = $data[3];
$this->publishDate = $data[4];
$this->source = $data[5];
}
/**
* @return array
*/
public function toArray()
{
return [
'header' => $this->header,
'articleHeadline' => $this->articleHeadline,
'articleContent' => $this->articleContent,
'author' => $this->author,
'publishDate' => $this->publishDate,
'source' => $this->source,
];
}
}
@@ -0,0 +1,184 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
use UserBundle\Enum\ThemeOptionExtractEnum;
/**
* Class ThemeOptionContent
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionContent implements \Serializable
{
/**
* @var ThemeOptionHighlightKeywords
*/
private $highlightKeywords;
/**
* @var ThemeOptionShowInfo
*/
private $showInfo;
/**
* @var string
*/
private $language;
/**
* @var ThemeOptionExtractEnum
*/
private $extract;
/**
* ThemeOptionContent constructor.
*
* @param ThemeOptionHighlightKeywords $highlightKeywords A ThemeOptionHighlightKeywords
* instance.
* @param ThemeOptionShowInfo $showInfo A ThemeOptionShowInfo
* instance.
* @param string $language Selected notification
* language.
* @param ThemeOptionExtractEnum $extract A ThemeOptionExtractEnum
* instance.
*/
public function __construct(
ThemeOptionHighlightKeywords $highlightKeywords,
ThemeOptionShowInfo $showInfo,
$language,
ThemeOptionExtractEnum $extract
) {
$this->highlightKeywords = $highlightKeywords;
$this->showInfo = $showInfo;
$this->language = trim($language);
$this->extract = $extract;
}
/**
* @return ThemeOptionHighlightKeywords
*/
public function getHighlightKeywords()
{
return $this->highlightKeywords;
}
/**
* @param ThemeOptionHighlightKeywords $highlightKeywords A ThemeOptionHighlightKeywords
* instance.
*
* @return ThemeOptionContent
*/
public function setHighlightKeywords(ThemeOptionHighlightKeywords $highlightKeywords)
{
$this->highlightKeywords = $highlightKeywords;
return $this;
}
/**
* @return ThemeOptionShowInfo
*/
public function getShowInfo()
{
return $this->showInfo;
}
/**
* @param ThemeOptionShowInfo $showInfo A ThemeOptionShowInfo instance.
*
* @return ThemeOptionContent
*/
public function setShowInfo(ThemeOptionShowInfo $showInfo)
{
$this->showInfo = $showInfo;
return $this;
}
/**
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* @param string $language Selected theme language.
*
* @return ThemeOptionContent
*/
public function setLanguage($language)
{
$this->language = $language;
return $this;
}
/**
* @return ThemeOptionExtractEnum
*/
public function getExtract()
{
return $this->extract;
}
/**
* @param ThemeOptionExtractEnum $extract A ThemeOptionExtractEnum
* instance.
*
* @return ThemeOptionContent
*/
public function setExtract(ThemeOptionExtractEnum $extract)
{
$this->extract = $extract;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->highlightKeywords,
$this->showInfo,
$this->language,
$this->extract,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->highlightKeywords = $data[0];
$this->showInfo = $data[1];
$this->language = $data[2];
$this->extract = $data[3];
}
/**
* @return array
*/
public function toArray()
{
return [
'highlightKeywords' => $this->highlightKeywords->toArray(),
'showInfo' => $this->showInfo->toArray(),
'language' => $this->language,
'extract' => $this->extract->getValue(),
];
}
}
@@ -0,0 +1,144 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
use UserBundle\Enum\FontFamilyEnum;
/**
* Class ThemeOptionFont
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionFont implements \Serializable
{
/**
* @var FontFamilyEnum
*/
private $family;
/**
* @var integer
*/
private $size;
/**
* @var ThemeOptionFontStyle
*/
private $style;
/**
* ThemeOptionFont constructor.
*
* @param FontFamilyEnum $family Font family name.
* @param integer $size Font size.
* @param ThemeOptionFontStyle $style A ThemeOptionFontStyle instance.
*/
public function __construct(FontFamilyEnum $family, $size, ThemeOptionFontStyle $style = null)
{
$this->family = $family;
$this->size = (int) trim($size);
$this->style = $style === null ? new ThemeOptionFontStyle() : $style;
}
/**
* @return FontFamilyEnum
*/
public function getFamily()
{
return $this->family;
}
/**
* @param FontFamilyEnum $family Font family name.
*
* @return ThemeOptionFont
*/
public function setFamily(FontFamilyEnum $family)
{
$this->family = $family;
return $this;
}
/**
* @return integer
*/
public function getSize()
{
return $this->size;
}
/**
* @param integer $size Font size.
*
* @return ThemeOptionFont
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
/**
* @return ThemeOptionFontStyle
*/
public function getStyle()
{
return $this->style;
}
/**
* @param ThemeOptionFontStyle $style A ThemeOptionFontStyle instance.
*
* @return ThemeOptionFont
*/
public function setStyle(ThemeOptionFontStyle $style)
{
$this->style = $style;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->family,
$this->size,
$this->style,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->family = $data[0];
$this->size = $data[1];
$this->style = $data[2];
}
/**
* @return array
*/
public function toArray()
{
return [
'family' => $this->family->getCss(),
'size' => $this->size,
'style' => $this->style->toArray(),
];
}
}
@@ -0,0 +1,142 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
/**
* Class ThemeOptionFontStyle
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionFontStyle implements \Serializable
{
/**
* @var boolean
*/
private $bold;
/**
* @var boolean
*/
private $italic;
/**
* @var boolean
*/
private $underline;
/**
* ThemeOptionFontStyle constructor.
*
* @param boolean $bold Should be text bold or not.
* @param boolean $italic Should be text italic or not.
* @param boolean $underline Should be text underlined or not.
*/
public function __construct($bold = false, $italic = false, $underline = false)
{
$this->bold = (bool) $bold;
$this->italic = (bool) $italic;
$this->underline = (bool) $underline;
}
/**
* @return boolean
*/
public function isBold()
{
return $this->bold;
}
/**
* @param boolean $bold Should be text bold or not.
*
* @return static
*/
public function setBold($bold = true)
{
$this->bold = $bold;
return $this;
}
/**
* @return boolean
*/
public function isItalic()
{
return $this->italic;
}
/**
* @param boolean $italic Should be text italic or not.
*
* @return static
*/
public function setItalic($italic = true)
{
$this->italic = $italic;
return $this;
}
/**
* @return boolean
*/
public function isUnderline()
{
return $this->underline;
}
/**
* @param boolean $underline Should be text underlined or not.
*
* @return static
*/
public function setUnderline($underline = true)
{
$this->underline = $underline;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->bold,
$this->italic,
$this->underline,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->bold = $data[0];
$this->italic = $data[1];
$this->underline = $data[2];
}
/**
* @return array
*/
public function toArray()
{
return [
'bold' => $this->bold,
'italic' => $this->italic,
'underline' => $this->underline,
];
}
}
@@ -0,0 +1,300 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
/**
* Class ThemeOptionFonts
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionFonts implements \Serializable
{
/**
* @var ThemeOptionFont
*/
private $header;
/**
* @var ThemeOptionFont
*/
private $tableOfContents;
/**
* @var ThemeOptionFont
*/
private $feedTitle;
/**
* @var ThemeOptionFont
*/
private $articleHeadline;
/**
* @var ThemeOptionFont
*/
private $source;
/**
* @var ThemeOptionFont
*/
private $author;
/**
* @var ThemeOptionFont
*/
private $date;
/**
* @var ThemeOptionFont
*/
private $articleContent;
/**
* ThemeOptionFonts constructor.
*
* @param ThemeOptionFont $header A ThemeOptionFont instance.
* @param ThemeOptionFont $tableOfContents A ThemeOptionFont instance.
* @param ThemeOptionFont $feedTitle A ThemeOptionFont instance.
* @param ThemeOptionFont $articleHeadline A ThemeOptionFont instance.
* @param ThemeOptionFont $source A ThemeOptionFont instance.
* @param ThemeOptionFont $author A ThemeOptionFont instance.
* @param ThemeOptionFont $date A ThemeOptionFont instance.
* @param ThemeOptionFont $articleContent A ThemeOptionFont instance.
*/
public function __construct(
ThemeOptionFont $header,
ThemeOptionFont $tableOfContents,
ThemeOptionFont $feedTitle,
ThemeOptionFont $articleHeadline,
ThemeOptionFont $source,
ThemeOptionFont $author,
ThemeOptionFont $date,
ThemeOptionFont $articleContent
) {
$this->header = $header;
$this->tableOfContents = $tableOfContents;
$this->feedTitle = $feedTitle;
$this->articleHeadline = $articleHeadline;
$this->source = $source;
$this->author = $author;
$this->date = $date;
$this->articleContent = $articleContent;
}
/**
* @return ThemeOptionFont
*/
public function getHeader()
{
return $this->header;
}
/**
* @param ThemeOptionFont $header A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setHeader(ThemeOptionFont $header)
{
$this->header = $header;
return $this;
}
/**
* @return ThemeOptionFont
*/
public function getTableOfContents()
{
return $this->tableOfContents;
}
/**
* @param ThemeOptionFont $tableOfContents A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setTableOfContents(ThemeOptionFont $tableOfContents)
{
$this->tableOfContents = $tableOfContents;
return $this;
}
/**
* @return ThemeOptionFont
*/
public function getFeedTitle()
{
return $this->feedTitle;
}
/**
* @param ThemeOptionFont $feedTitle A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setFeedTitle(ThemeOptionFont $feedTitle)
{
$this->feedTitle = $feedTitle;
return $this;
}
/**
* @return ThemeOptionFont
*/
public function getArticleHeadline()
{
return $this->articleHeadline;
}
/**
* @param ThemeOptionFont $articleHeadline A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setArticleHeadline(ThemeOptionFont $articleHeadline)
{
$this->articleHeadline = $articleHeadline;
return $this;
}
/**
* @return ThemeOptionFont A ThemeOptionFont instance.
*/
public function getSource()
{
return $this->source;
}
/**
* @param ThemeOptionFont $source A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setSource(ThemeOptionFont $source)
{
$this->source = $source;
return $this;
}
/**
* @return ThemeOptionFont
*/
public function getAuthor()
{
return $this->author;
}
/**
* @param ThemeOptionFont $author A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setAuthor(ThemeOptionFont $author)
{
$this->author = $author;
return $this;
}
/**
* @return ThemeOptionFont
*/
public function getDate()
{
return $this->date;
}
/**
* @param ThemeOptionFont $date A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setDate(ThemeOptionFont $date)
{
$this->date = $date;
return $this;
}
/**
* @return ThemeOptionFont
*/
public function getArticleContent()
{
return $this->articleContent;
}
/**
* @param ThemeOptionFont $articleContent A ThemeOptionFont instance.
*
* @return ThemeOptionFonts
*/
public function setArticleContent(ThemeOptionFont $articleContent)
{
$this->articleContent = $articleContent;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->header,
$this->tableOfContents,
$this->feedTitle,
$this->articleHeadline,
$this->source,
$this->author,
$this->date,
$this->articleContent,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->header = $data[0];
$this->tableOfContents = $data[1];
$this->feedTitle = $data[2];
$this->articleHeadline = $data[3];
$this->source = $data[4];
$this->author = $data[5];
$this->date = $data[6];
$this->articleContent = $data[7];
}
/**
* @return array
*/
public function toArray()
{
return [
'header' => $this->header->toArray(),
'tableOfContents' => $this->tableOfContents->toArray(),
'feedTitle' => $this->feedTitle->toArray(),
'articleHeadline' => $this->articleHeadline->toArray(),
'source' => $this->source->toArray(),
'author' => $this->author->toArray(),
'date' => $this->date->toArray(),
'articleContent' => $this->articleContent->toArray(),
];
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,148 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
/**
* Class ThemeOptionHighlightKeywords
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionHighlightKeywords implements \Serializable
{
/**
* @var boolean
*/
private $highlight;
/**
* @var boolean
*/
private $bold;
/**
* CSS RGBA.
*
* ```
* rgba(125, 125, 125, 0.5);
* ```
*
* @var string
*/
private $color;
/**
* ThemeOptionHighlightKeywords constructor.
*
* @param boolean $highlight Should system highlight keywords or not.
* @param boolean $bold Should highlight keyword be bold or not.
* @param string $color Highlight color.
*/
public function __construct($highlight = true, $bold = true, $color = 'rgba(255, 255, 0, 1)')
{
$this->highlight = (bool) $highlight;
$this->bold = (bool) $bold;
$this->color = trim($color);
}
/**
* @return boolean
*/
public function isHighlight()
{
return $this->highlight;
}
/**
* @param boolean $highlight Should system highlight keywords or not.
*
* @return ThemeOptionHighlightKeywords
*/
public function setHighlight($highlight = true)
{
$this->highlight = $highlight;
return $this;
}
/**
* @return boolean
*/
public function isBold()
{
return $this->bold;
}
/**
* @param boolean $bold Should highlight keyword be bold or not.
*
* @return ThemeOptionHighlightKeywords
*/
public function setBold($bold = true)
{
$this->bold = $bold;
return $this;
}
/**
* @return string
*/
public function getColor()
{
return $this->color;
}
/**
* @param string $color Highlight color.
*
* @return ThemeOptionHighlightKeywords
*/
public function setColor($color)
{
$this->color = $color;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->highlight,
$this->bold,
$this->color,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->highlight = $data[0];
$this->bold = $data[1];
$this->color = $data[2];
}
/**
* @return array
*/
public function toArray()
{
return [
'highlight' => $this->highlight,
'bold' => $this->bold,
'color' => $this->color,
];
}
}
@@ -0,0 +1,313 @@
<?php
namespace UserBundle\Entity\Notification\ThemeOption;
use UserBundle\Enum\ThemeOptionsTableOfContentsEnum;
use UserBundle\Enum\ThemeOptionsUserCommentsEnum;
/**
* Class ThemeOptionShowInfo
* @package UserBundle\Entity\Notification\ThemeOption
*/
class ThemeOptionShowInfo implements \Serializable
{
/**
* @var boolean
*/
private $sourceCountry;
/**
* @var boolean
*/
private $articleSentiment;
/**
* @var boolean
*/
private $articleCount;
/**
* @var boolean
*/
private $images;
/**
* @var boolean
*/
private $sharingOptions;
/**
* @var boolean
*/
private $sectionDivider;
/**
* @var ThemeOptionsUserCommentsEnum
*/
private $userComments;
/**
* @var ThemeOptionsTableOfContentsEnum
*/
private $tableOfContents;
/**
* ThemeOptionShowInfo constructor.
*
* @param ThemeOptionsUserCommentsEnum|string $userComments A ThemeOptionsUserCommentsEnum
* instance.
* @param ThemeOptionsTableOfContentsEnum|string $tableOfContents A ThemeOptionsTableOfContentsEnum
* instance.
* @param boolean $sourceCountry Show or not source country.
* @param boolean $articleSentiment Show or not article sentiment.
* @param boolean $articleCount Show or not article count.
* @param boolean $images Show or not article images.
* @param boolean $sharingOptions Show or not sharing options.
* @param boolean $sectionDivider Show or not section divider.
*/
public function __construct(
$userComments,
$tableOfContents,
$sourceCountry = false,
$articleSentiment = true,
$articleCount = true,
$images = true,
$sharingOptions = true,
$sectionDivider = false
) {
$this->sourceCountry = (bool) $sourceCountry;
$this->articleSentiment = (bool) $articleSentiment;
$this->articleCount = (bool) $articleCount;
$this->images = (bool) $images;
$this->sharingOptions = (bool) $sharingOptions;
$this->sectionDivider = (bool) $sectionDivider;
$this->setUserComments($userComments);
$this->setTableOfContents($tableOfContents);
}
/**
* @return boolean
*/
public function isSourceCountry()
{
return $this->sourceCountry;
}
/**
* @param boolean $sourceCountry Show source country.
*
* @return ThemeOptionShowInfo
*/
public function setSourceCountry($sourceCountry = true)
{
$this->sourceCountry = $sourceCountry;
return $this;
}
/**
* @return boolean
*/
public function isArticleSentiment()
{
return $this->articleSentiment;
}
/**
* @param boolean $articleSentiment Show article sentiment.
*
* @return ThemeOptionShowInfo
*/
public function setArticleSentiment($articleSentiment = true)
{
$this->articleSentiment = $articleSentiment;
return $this;
}
/**
* @return boolean
*/
public function isArticleCount()
{
return $this->articleCount;
}
/**
* @param boolean $articleCount Show article count.
*
* @return ThemeOptionShowInfo
*/
public function setArticleCount($articleCount = true)
{
$this->articleCount = $articleCount;
return $this;
}
/**
* @return boolean
*/
public function isImages()
{
return $this->images;
}
/**
* @param boolean $images Show or not articles images.
*
* @return ThemeOptionShowInfo
*/
public function setImages($images = true)
{
$this->images = $images;
return $this;
}
/**
* @return boolean
*/
public function isSharingOptions()
{
return $this->sharingOptions;
}
/**
* @param boolean $sharingOptions Show or not sharing options.
*
* @return ThemeOptionShowInfo
*/
public function setSharingOptions($sharingOptions = true)
{
$this->sharingOptions = $sharingOptions;
return $this;
}
/**
* @return boolean
*/
public function isSectionDivider()
{
return $this->sectionDivider;
}
/**
* @param boolean $sectionDivider Show section divider or not.
*
* @return ThemeOptionShowInfo
*/
public function setSectionDivider($sectionDivider = true)
{
$this->sectionDivider = $sectionDivider;
return $this;
}
/**
* @return ThemeOptionsUserCommentsEnum
*/
public function getUserComments()
{
return $this->userComments;
}
/**
* @param ThemeOptionsUserCommentsEnum|string $userComments A ThemeOptionsUserCommentsEnum
* instance.
*
* @return ThemeOptionShowInfo
*/
public function setUserComments($userComments)
{
if (is_string($userComments)) {
$userComments = new ThemeOptionsUserCommentsEnum($userComments);
}
$this->userComments = $userComments;
return $this;
}
/**
* @return ThemeOptionsTableOfContentsEnum
*/
public function getTableOfContents()
{
return $this->tableOfContents;
}
/**
* @param ThemeOptionsTableOfContentsEnum|string $tableOfContents A ThemeOptionsTableOfContentsEnum
* instance.
*
* @return ThemeOptionShowInfo
*/
public function setTableOfContents($tableOfContents)
{
if (is_string($tableOfContents)) {
$tableOfContents = new ThemeOptionsTableOfContentsEnum($tableOfContents);
}
$this->tableOfContents = $tableOfContents;
return $this;
}
/**
* String representation of object.
*
* @return string the string representation of the object or null.
*/
public function serialize()
{
return serialize([
$this->sourceCountry,
$this->articleSentiment,
$this->articleCount,
$this->images,
$this->sharingOptions,
$this->sectionDivider,
$this->userComments,
$this->tableOfContents,
]);
}
/**
* Constructs the object
*
* @param string $serialized The string representation of the object.
*
* @return void
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->sourceCountry = $data[0];
$this->articleSentiment = $data[1];
$this->articleCount = $data[2];
$this->images = $data[3];
$this->sharingOptions = $data[4];
$this->sectionDivider = $data[5];
$this->userComments = $data[6];
$this->tableOfContents = $data[7];
}
/**
* @return array
*/
public function toArray()
{
return [
'sourceCountry' => $this->sourceCountry,
'articleSentiment' => $this->articleSentiment,
'articleCount' => $this->articleCount,
'images' => $this->images,
'sharingOptions' => $this->sharingOptions,
'sectionDivider' => $this->sectionDivider,
'userComments' => $this->userComments->getValue(),
'tableOfContents' => $this->tableOfContents->getValue(),
];
}
}
+134
View File
@@ -0,0 +1,134 @@
<?php
namespace UserBundle\Entity;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Entity\Subscription\OrganizationSubscription;
/**
* Organization
*
* @ORM\Table(name="organizations")
* @ORM\Entity(repositoryClass="UserBundle\Repository\OrganizationRepository")
*
* @UniqueEntity(fields={"name"})
*/
class Organization implements EntityInterface
{
use BaseEntityTrait;
/**
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="UserBundle\Entity\Subscription\OrganizationSubscription",
* mappedBy="organization",
* cascade={ "persist", "remove" }
* )
*/
private $subscriptions;
/**
* @var string
*
* @ORM\Column
*
* @Assert\NotBlank
*/
protected $name;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name A Organization name.
*
* @return Organization
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->name;
}
/**
* Constructor
*/
public function __construct()
{
$this->subscriptions = new ArrayCollection();
}
/**
* Add subscription
*
* @param OrganizationSubscription $subscription A new OrganizationSubscription
* entity instance.
*
* @return Organization
*/
public function addSubscription(OrganizationSubscription $subscription)
{
$this->subscriptions[] = $subscription;
$subscription->setOrganization($this);
return $this;
}
/**
* Remove subscription
*
* @param OrganizationSubscription $subscription A removed OrganizationSubscription
* entity instance.
*
* @return Organization
*/
public function removeSubscription(OrganizationSubscription $subscription)
{
$this->subscriptions->removeElement($subscription);
$subscription->setOrganization(null);
return $this;
}
/**
* Get subscriptions
*
* @return Collection
*/
public function getSubscriptions()
{
return $this->subscriptions;
}
}
+532
View File
@@ -0,0 +1,532 @@
<?php
namespace UserBundle\Entity;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use AppBundle\Entity\BaseEntityTrait;
use ApiBundle\Entity\NormalizableEntityInterface;
use AppBundle\Entity\EntityInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Entity\Subscription\AbstractSubscription;
use UserBundle\Entity\Traits\LimitAwareTrait;
use UserBundle\Enum\AppPermissionEnum;
/**
* Plan
*
* @ORM\Table(name="plan")
* @ORM\Entity(repositoryClass="UserBundle\Repository\PlanRepository")
*/
class Plan implements EntityInterface, NormalizableEntityInterface
{
use
BaseEntityTrait,
LimitAwareTrait;
/**
* @var string
*
* @ORM\Column
*
* @Assert\NotBlank
*/
private $title;
/**
* @var string
*
* @ORM\Column
*
* @Assert\NotBlank
*/
private $innerName;
/**
* One Plan has Many Subscriptions.
*
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="UserBundle\Entity\Subscription\AbstractSubscription",
* mappedBy="plan"
* )
*/
private $subscriptions;
/**
* @var float
*
* @ORM\Column(name="price", type="float")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0.0)
*/
private $price;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $analytics = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $is_default = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $news = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $blog = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $reddit = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $instagram = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $twitter = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $isPlanDowngrade = false;
/**
* @var User
*
* @ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="plan")
* @ORM\JoinColumn(nullable=true)
*/
private $user;
/**
* Constructor
*/
public function __construct()
{
$this->subscriptions = new ArrayCollection();
}
/**
* @return string
*/
public function __toString()
{
return $this->title;
}
/**
* Set name
*
* @param string $title A Human readable plan name.
*
* @return Plan
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* @return string
*/
public function getInnerName()
{
return $this->innerName;
}
/**
* @param string $innerName A plan inner name used for binding with plans on
* payment gateways.
*
* @return Plan
*/
public function setInnerName($innerName)
{
$this->innerName = $innerName;
return $this;
}
/**
* Set price
*
* @param float $price Monthly plan price.
*
* @return Plan
*/
public function setPrice($price)
{
$this->price = $price;
return $this;
}
/**
* Get price
*
* @return float
*/
public function getPrice()
{
return $this->price;
}
/**
* Checks that current plan is free.
*
* @return boolean
*/
public function isFree()
{
return $this->price <= 0.000001;
}
/**
* @return boolean
*/
public function isAnalytics()
{
return $this->analytics;
}
/**
* @return boolean
*/
public function isDefault()
{
return $this->is_default;
}
/**
* @param boolean $analytics Allow to use analytics or not.
*
* @return static
*/
public function setAnalytics($analytics)
{
$this->analytics = $analytics;
return $this;
}
/**
* @param boolean $is_default Allow to use default or not.
*
* @return static
*/
public function setIsDefault(bool $is_default)
{
$this->is_default = $is_default;
return $this;
}
/**
* Get specified permission.
*
* @param AppPermissionEnum $appPermission A requested permission name.
*
* @return boolean
*/
public function getPermission(AppPermissionEnum $appPermission)
{
return $this->{$appPermission->getValue()};
}
/**
* Change specified permission.
*
* @param AppPermissionEnum $appPermission A changed permission name.
* @param boolean $permission New permission value.
*
* @return $this
*/
public function setPermission(AppPermissionEnum $appPermission, $permission)
{
$this->{$appPermission->getValue()} = $permission;
return $this;
}
/**
* Add subscription
*
* @param AbstractSubscription $subscription A new subscription entity instance.
*
* @return Plan
*/
public function addSubscription(AbstractSubscription $subscription)
{
$this->subscriptions[] = $subscription;
$subscription->setPlan($this);
return $this;
}
/**
* Remove subscription
*
* @param AbstractSubscription $subscription A removed subscription entity
* instance.
*
* @return Plan
*/
public function removeSubscription(AbstractSubscription $subscription)
{
$this->subscriptions->removeElement($subscription);
$subscription->setPlan(null);
return $this;
}
/**
* Get subscriptions
*
* @return Collection
*/
public function getSubscriptions()
{
return $this->subscriptions;
}
/**
* Return default normalization groups.
*
* @return array
*/
public function defaultGroups()
{
return [ 'plan', 'id', 'name' ];
}
/**
* Get entity type
*
* @return string
*/
public function getEntityType()
{
return 'plan';
}
/**
* Return metadata for current entity.
*
* @return Metadata
*/
public function getMetadata()
{
return new Metadata(static::class, [
PropertyMetadata::createInteger('id', [ 'id' ]),
PropertyMetadata::createString('name', [ 'plan' ])
->setField('title'),
PropertyMetadata::createInteger('searchesPerDay', [ 'plan' ]),
PropertyMetadata::createInteger('savedFeeds', [ 'plan' ]),
PropertyMetadata::createInteger('masterAccounts', [ 'plan' ]),
PropertyMetadata::createInteger('subscriberAccounts', [ 'plan' ]),
PropertyMetadata::createInteger('alerts', [ 'plan' ]),
PropertyMetadata::createInteger('newsletters', [ 'plan' ]),
PropertyMetadata::createBoolean('analytics', [ 'plan' ]),
PropertyMetadata::createDouble('price', [ 'plan' ]),
PropertyMetadata::createBoolean('free', [ 'plan' ])
->setField(function () {
return $this->isFree();
}),
PropertyMetadata::createBoolean('is_default', [ 'plan' ]),
PropertyMetadata::createBoolean('news', [ 'plan' ]),
PropertyMetadata::createBoolean('blog', [ 'plan' ]),
PropertyMetadata::createBoolean('reddit', [ 'plan' ]),
PropertyMetadata::createBoolean('instagram', [ 'plan' ]),
PropertyMetadata::createBoolean('twitter', [ 'plan' ]),
]);
}
/**
* Set news
*
* @return $this
*/
public function setNews($news)
{
$this->news = $news;
return $this;
}
/**
* @return boolean
*/
public function isNews()
{
return $this->news;
}
/**
* Set blog
*
* @return $this
*/
public function setBlog($blog)
{
$this->blog = $blog;
return $this;
}
/**
* @return boolean
*/
public function isBlog()
{
return $this->blog;
}
/**
* Set reddit
*
* @return $this
*/
public function setReddit($reddit)
{
$this->reddit = $reddit;
return $this;
}
/**
* @return boolean
*/
public function isReddit()
{
return $this->reddit;
}
/**
* Set instagram
*
* @return $this
*/
public function setInstagram($instagram)
{
$this->instagram = $instagram;
return $this;
}
/**
* @return boolean
*/
public function isInstagram()
{
return $this->instagram;
}
/**
* Set twitter
*
* @return $this
*/
public function setTwitter($twitter)
{
$this->twitter = $twitter;
return $this;
}
/**
* @return boolean
*/
public function isTwitter()
{
return $this->twitter;
}
/**
* @return bool
*/
public function isPlanDowngrade(): bool
{
return $this->isPlanDowngrade;
}
/**
* @param bool $isPlanDowngrade
*/
public function setIsPlanDowngrade(bool $isPlanDowngrade): void
{
$this->isPlanDowngrade = $isPlanDowngrade;
}
/**
* Set user
*
* @param User $user A User entity instance.
*
* @return User
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* @return User
*/
public function getUser()
{
return $this->user;
}
}
+124
View File
@@ -0,0 +1,124 @@
<?php
namespace UserBundle\Entity;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use CacheBundle\Entity\Feed\AbstractFeed;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* RecentlyUsedFeed
*
* @ORM\Table(name="recently_used_feeds")
* @ORM\Entity(repositoryClass="UserBundle\Repository\RecentlyUsedFeedRepository")
*/
class RecentlyUsedFeed implements EntityInterface
{
const POOL_SIZE = 10;
use BaseEntityTrait;
/**
* @var AbstractFeed
*
* @ORM\ManyToOne(targetEntity="CacheBundle\Entity\Feed\AbstractFeed")
*/
private $feed;
/**
* @var User
*
* @ORM\ManyToOne(targetEntity="UserBundle\Entity\User")
*/
private $user;
/**
* @var \DateTime
*
* @ORM\Column(type="datetime")
*/
private $usedAt;
/**
* Constructor.
*/
public function __construct()
{
$this->usedAt = new \DateTime();
}
/**
* Set feed
*
* @param AbstractFeed $feed A AbstractFeed entity instance.
*
* @return RecentlyUsedFeed
*/
public function setFeed(AbstractFeed $feed = null)
{
$this->feed = $feed;
return $this;
}
/**
* Get feed
*
* @return AbstractFeed
*/
public function getFeed()
{
return $this->feed;
}
/**
* Set user
*
* @param User $user A User entity instance.
*
* @return RecentlyUsedFeed
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* Set usedAt
*
* @param \DateTime $usedAt A DateTime instance.
*
* @return RecentlyUsedFeed
*/
public function setUsedAt(\DateTime $usedAt = null)
{
$this->usedAt = $usedAt;
return $this;
}
/**
* Get usedAt
*
* @return \DateTime
*/
public function getUsedAt()
{
return $this->usedAt;
}
}
@@ -0,0 +1,310 @@
<?php
namespace UserBundle\Entity\Recipient;
use ApiBundle\Entity\ManageableEntityInterface;
use ApiBundle\Entity\NormalizableEntityInterface;
use AppBundle\Entity\ActivateAwareEntityTrait;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\User;
use UserBundle\Enum\NotificationTypeEnum;
/**
* Class AbstractRecipient
*
* @package UserBundle\Entity\Recipient
*
* @ORM\Table(name="recipients")
* @ORM\Entity(repositoryClass="UserBundle\Repository\RecipientRepository")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({
* "person"="PersonRecipient",
* "group"="GroupRecipient",
* })
*/
abstract class AbstractRecipient implements
EntityInterface,
NormalizableEntityInterface,
ManageableEntityInterface
{
use
BaseEntityTrait,
ActivateAwareEntityTrait;
/**
* The user who created this notification.
*
* @var User
*
* @ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="recipients")
* @ORM\JoinColumn(name="owner_id", referencedColumnName="id", onDelete="SET NULL")
*/
protected $owner;
/**
* @var string
*
* @ORM\Column
* @Assert\NotBlank
*/
protected $name;
/**
* Array of subscriber notification count by types.
*
* @var integer[]
*
* @ORM\Column(type="array")
*/
protected $subscribedCount = [];
/**
* @var \DateTime
*
* @ORM\Column(type="datetime")
*/
protected $createdAt;
/**
* @var Collection
*
* @ORM\ManyToMany(
* targetEntity="UserBundle\Entity\Notification\Notification",
* inversedBy="recipients"
* )
* @ORM\JoinTable(name="cross_recipient_notifications")
*/
protected $notifications;
/**
* @var boolean
*/
public $enrolled;
/**
* AbstractRecipient constructor.
*/
public function __construct()
{
$this->createdAt = new \DateTime();
$this->notifications = new ArrayCollection();
foreach (NotificationTypeEnum::getAvailables() as $available) {
$this->subscribedCount[$available] = 0;
}
}
/**
* Set owner
*
* @param User $owner The owner of this notification.
*
* @return static
*/
public function setOwner(User $owner = null)
{
$this->owner = $owner;
return $this;
}
/**
* Get owner
*
* @return User
*/
public function getOwner()
{
return $this->owner;
}
/**
* Checks that this entity is owned by specified user.
*
* @param User $user A User entity instance.
*
* @return boolean
*/
public function isOwnedBy(User $user)
{
return $this->owner->getId() === $user->getId();
}
/**
* Set name
*
* @param string $name Group name.
*
* @return static
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt When this recipient is created.
*
* @return static
*/
public function setCreatedAt(\DateTime $createdAt = null)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set subscribedCount
*
* @param array $subscribedCount Array of subscribed notification counts by
* type.
*
* @return static
*/
public function setSubscribedCount(array $subscribedCount)
{
$this->subscribedCount = $subscribedCount;
return $this;
}
/**
* Get subscribedCount
*
* @return integer[]
*/
public function getSubscribedCount()
{
return $this->subscribedCount;
}
/**
* @param NotificationTypeEnum $type A NotificationTypeEnum instance.
* @param integer $count New subscription count.
*
* @return static
*/
public function setSubscribedCountByType(NotificationTypeEnum $type, $count)
{
$this->subscribedCount[(string) $type] = $count;
return $this;
}
/**
* @param NotificationTypeEnum $type A NotificationTypeEnum instance.
*
* @return integer
*/
public function getSubscribedCountByType(NotificationTypeEnum $type)
{
return $this->subscribedCount[(string) $type];
}
/**
* @param NotificationTypeEnum $type A NotificationTypeEnum instance.
*
* @return static
*/
public function incSubscribedCountByType(NotificationTypeEnum $type)
{
$this->subscribedCount[(string) $type]++;
return $this;
}
/**
* @param NotificationTypeEnum $type A NotificationTypeEnum instance.
*
* @return static
*/
public function decSubscribedCountByType(NotificationTypeEnum $type)
{
$this->subscribedCount[(string) $type]--;
return $this;
}
/**
* Add notification
*
* @param Notification $notification A Notification entity instance.
*
* @return static
*/
public function addNotification(Notification $notification)
{
$this->notifications[] = $notification;
$this->incSubscribedCountByType($notification->getNotificationType());
return $this;
}
/**
* Remove notification
*
* @param Notification $notification A Notification entity instance.
*
* @return static
*/
public function removeNotification(Notification $notification)
{
$this->notifications->removeElement($notification);
$this->decSubscribedCountByType($notification->getNotificationType());
return $this;
}
/**
* Get notifications
*
* @return Collection
*/
public function getNotifications()
{
return $this->notifications;
}
/**
* Return default normalization groups.
*
* @return array
*/
public function defaultGroups()
{
return [ 'id', 'recipient' ];
}
}
@@ -0,0 +1,230 @@
<?php
namespace UserBundle\Entity\Recipient;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Enum\NotificationTypeEnum;
use UserBundle\Enum\RecipientTypeEnum;
use UserBundle\Form\GroupRecipientType;
/**
* Class GroupRecipient
*
* @package UserBundle\Entity\Recipient
*
* @ORM\Entity(repositoryClass="UserBundle\Repository\GroupRecipientRepository")
*/
class GroupRecipient extends AbstractRecipient
{
/**
* @var string
*
* @ORM\Column(type="text")
*/
protected $description;
/**
* @var integer
*
* @ORM\Column(type="integer")
*/
protected $recipientsNumber = 0;
/**
* @var Collection
*
* We use 'EXTRA_LAZY' in order to avoid fetching all recipients when we need
* only compute numbers of recipient.
*
* @ORM\ManyToMany(
* targetEntity="UserBundle\Entity\Recipient\PersonRecipient",
* inversedBy="groups",
* fetch="EXTRA_LAZY"
* )
* @ORM\JoinTable(name="cross_groups_persons")
*/
protected $recipients;
/**
* AbstractRecipient constructor.
*/
public function __construct()
{
parent::__construct();
$this->recipients = new ArrayCollection();
}
/**
* @return string
*/
public function __toString()
{
return $this->name;
}
/**
* Set description
*
* @param string $description Group description.
*
* @return GroupRecipient
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set personsCount
*
* @param integer $recipientsNumber Count of person in group.
*
* @return GroupRecipient
*/
public function setRecipientsNumber($recipientsNumber)
{
$this->recipientsNumber = $recipientsNumber;
return $this;
}
/**
* Get personsCount
*
* @return integer
*/
public function getRecipientsNumber()
{
return $this->recipientsNumber;
}
/**
* Add person
*
* @param PersonRecipient $person A PersonRecipient entity instance.
*
* @return GroupRecipient
*/
public function addRecipient(PersonRecipient $person)
{
$this->recipients[] = $person;
$this->recipientsNumber++;
return $this;
}
/**
* Remove person
*
* @param PersonRecipient $person A PersonRecipient entity instance.
*
* @return GroupRecipient
*/
public function removeRecipient(PersonRecipient $person)
{
$this->recipients->removeElement($person);
$this->recipientsNumber--;
return $this;
}
/**
* Get persons
*
* @return Collection
*/
public function getRecipients()
{
return $this->recipients;
}
/**
* Return metadata for current entity.
*
* @return \ApiBundle\Serializer\Metadata\Metadata
*/
public function getMetadata()
{
$subscriptions = array_map(function ($type) {
return PropertyMetadata::createInteger($type, [ 'recipient' ])
->setField(function () use ($type) {
return $this->subscribedCount[$type];
});
}, NotificationTypeEnum::getAvailables());
$subscriptions[] = PropertyMetadata::createArray('ids', [ 'recipient' ])
->setField(function () {
return $this->getNotifications()->map(function (Notification $notification) {
return $notification->getId();
})->toArray();
});
return new Metadata(static::class, [
PropertyMetadata::createInteger('id', [ 'id' ]),
PropertyMetadata::createString('name', [ 'recipient', 'notification', 'notification_list', 'recipient_autocompletion' ]),
PropertyMetadata::createString('email', [ 'notification', 'notification_list', 'recipient_autocompletion' ])
->setField(function () {
return '';
}),
PropertyMetadata::createString('description', [ 'recipient' ]),
PropertyMetadata::groupProperties('subscriptions', $subscriptions, [ 'recipient' ]),
PropertyMetadata::createArray('recipients', [ 'recipient' ])
->setField(function () {
return $this->getRecipients()->map(function (PersonRecipient $recipient) {
return $recipient->getId();
})->toArray();
}),
PropertyMetadata::createBoolean('active', [ 'recipient' ]),
PropertyMetadata::createBoolean('enrolled', [ 'sublist' ]),
]);
}
/**
* Get entity type
*
* @return string
*/
public function getEntityType()
{
return RecipientTypeEnum::GROUP;
}
/**
* Return fqcn of form used for creating this entity.
*
* @return string
*/
public function getCreateFormClass()
{
return GroupRecipientType::class;
}
/**
* Return fqcn of form used for updating this entity.
*
* @return string
*/
public function getUpdateFormClass()
{
return GroupRecipientType::class;
}
}
@@ -0,0 +1,370 @@
<?php
namespace UserBundle\Entity\Recipient;
use ApiBundle\Serializer\Metadata\Metadata;
use ApiBundle\Serializer\Metadata\PropertyMetadata;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\User;
use UserBundle\Enum\NotificationTypeEnum;
use UserBundle\Enum\RecipientTypeEnum;
use UserBundle\Form\PersonRecipientType;
/**
* Class PersonRecipient
*
* @package UserBundle\Entity\Recipient
*
* @ORM\Entity(repositoryClass="UserBundle\Repository\PersonRecipientRepository")
* @ORM\HasLifecycleCallbacks
*/
class PersonRecipient extends AbstractRecipient
{
/**
* @var string
*
* @ORM\Column
* @Assert\NotBlank
*/
protected $firstName;
/**
* @var string
*
* @ORM\Column
* @Assert\NotBlank
*/
protected $lastName;
/**
* @var string
*
* @ORM\Column
* @Assert\NotBlank
* @Assert\Email
*/
protected $email;
/**
* @var Collection
*
* @ORM\ManyToMany(
* targetEntity="UserBundle\Entity\Recipient\GroupRecipient",
* mappedBy="recipients"
* )
*/
protected $groups;
/**
* @var User
*
* @ORM\OneToOne(
* targetEntity="UserBundle\Entity\User",
* mappedBy="recipient",
* cascade={ "ALL" }
* )
*/
protected $associatedUser;
/**
* AbstractRecipient constructor.
*/
public function __construct()
{
parent::__construct();
$this->groups = new ArrayCollection();
}
/**
* @return string
*/
public function __toString()
{
return $this->firstName .' '. $this->lastName;
}
/**
* Create recipient from user.
*
* @param User $user A User entity instance.
*
* @return PersonRecipient
*/
public static function createFromUser(User $user)
{
return static::create()
->setFirstName($user->getFirstName())
->setLastName($user->getLastName())
->setEmail($user->getEmail());
}
/**
* Set firstName
*
* @param string $firstName Person first name.
*
* @return PersonRecipient
*/
public function setFirstName($firstName)
{
$this->firstName = trim($firstName);
$this->name = $this->firstName .' '. $this->lastName;
if (($this->associatedUser !== null) && ($this->associatedUser->getFirstName() !== $firstName)) {
$this->associatedUser->setFirstName($firstName);
}
return $this;
}
/**
* Get firstName
*
* @return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set lastName
*
* @param string $lastName Person last name.
*
* @return PersonRecipient
*/
public function setLastName($lastName)
{
$this->lastName = trim($lastName);
$this->name = $this->firstName .' '. $this->lastName;
if (($this->associatedUser !== null) && ($this->associatedUser->getLastName() !== $lastName)) {
$this->associatedUser->setLastName($lastName);
}
return $this;
}
/**
* Set name
*
* @param string $name Group name.
*
* @return AbstractRecipient
*/
public function setName($name)
{
list($firstName, $lastName) = explode(' ', $name, 2);
$this->firstName = trim($firstName);
$this->lastName = trim($lastName);
$this->name = $this->firstName .' '. $this->lastName;
return $this;
}
/**
* Get lastName
*
* @return string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Set email
*
* @param string $email Person email.
*
* @return PersonRecipient
*/
public function setEmail($email)
{
$this->email = $email;
if (($this->associatedUser !== null) && ($this->associatedUser->getEmail() !== $email)) {
$this->associatedUser->setEmail($email);
}
return $this;
}
/**
* Get email
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Get active
*
* @return boolean
*/
public function getActive()
{
return $this->active;
}
/**
* Add group
*
* @param GroupRecipient $group A GroupRecipient entity instance.
*
* @return PersonRecipient
*/
public function addGroup(GroupRecipient $group)
{
$this->groups[] = $group;
$group->addRecipient($this);
return $this;
}
/**
* Remove group
*
* @param GroupRecipient $group A GroupRecipient entity instance.
*
* @return PersonRecipient
*/
public function removeGroup(GroupRecipient $group)
{
$this->groups->removeElement($group);
$group->removeRecipient($this);
return $this;
}
/**
* Get groups
*
* @return Collection
*/
public function getGroups()
{
return $this->groups;
}
/**
* @return User $associatedUser
*/
public function getAssociatedUser()
{
return $this->associatedUser;
}
/**
* @param User $associatedUser Associated user.
*
* @return PersonRecipient
*/
public function setAssociatedUser(User $associatedUser = null)
{
if ($associatedUser === null) {
$this->associatedUser->setRecipient(null);
} else {
$associatedUser->setRecipient($this);
}
$this->associatedUser = $associatedUser;
return $this;
}
/**
* Return metadata for current entity.
*
* @return \ApiBundle\Serializer\Metadata\Metadata
*/
public function getMetadata()
{
$subscriptions = array_map(function ($type) {
return PropertyMetadata::createInteger($type, [ 'recipient' ])
->setField(function () use ($type) {
return $this->subscribedCount[$type];
});
}, NotificationTypeEnum::getAvailables());
$subscriptions[] = PropertyMetadata::createArray('ids', [ 'recipient' ])
->setField(function () {
return $this->getNotifications()->map(function (Notification $notification) {
return $notification->getId();
})->toArray();
});
return new Metadata(static::class, [
PropertyMetadata::createInteger('id', [ 'id' ]),
PropertyMetadata::createString('firstName', [ 'recipient' ]),
PropertyMetadata::createString('lastName', [ 'recipient' ]),
PropertyMetadata::createString('name', [ 'notification', 'notification_list', 'recipient_autocompletion' ]),
PropertyMetadata::createString('email', [ 'recipient', 'notification', 'notification_list', 'recipient_autocompletion' ]),
PropertyMetadata::createDate('creationDate', [ 'recipient' ])
->setField('createdAt'),
PropertyMetadata::groupProperties('subscriptions', $subscriptions, [ 'recipient' ]),
PropertyMetadata::createCollection('groups', GroupRecipient::class, [ 'recipient' ]),
PropertyMetadata::createBoolean('active', [ 'recipient' ]),
PropertyMetadata::createBoolean('enrolled', [ 'sublist' ]),
]);
}
/**
* Get entity type
*
* @return string
*/
public function getEntityType()
{
return RecipientTypeEnum::PERSON;
}
/**
* Return fqcn of form used for creating this entity.
*
* @return string
*/
public function getCreateFormClass()
{
return PersonRecipientType::class;
}
/**
* Return fqcn of form used for updating this entity.
*
* @return string
*/
public function getUpdateFormClass()
{
return PersonRecipientType::class;
}
/**
* @ORM\PreRemove
*
* @param LifecycleEventArgs $event A LifecycleEventArgs instance.
*
* @return void
*/
public function preRemove(LifecycleEventArgs $event)
{
$em = $event->getObjectManager();
foreach ($this->groups as $group) {
$group->removeRecipient($this);
$em->persist($group);
}
}
}
@@ -0,0 +1,401 @@
<?php
namespace UserBundle\Entity\Subscription;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use AppBundle\Entity\OwnerAwareEntityTrait;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use PaymentBundle\Entity\Payment;
use PaymentBundle\Enum\PaymentGatewayEnum;
use PaymentBundle\Gateway\Factory\PaymentGatewayFactoryInterface;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Plan;
use UserBundle\Entity\Traits\LimitAwareTrait;
use UserBundle\Entity\User;
use UserBundle\Enum\BillingSubscriptionTypeEnum;
/**
* AbstractSubscription
*
* @ORM\Table(name="subscriptions")
* @ORM\Entity(repositoryClass="UserBundle\Repository\SubscriptionRepository")
*
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({
* "organization"="OrganizationSubscription",
* "personal"="PersonalSubscription",
* })
*/
abstract class AbstractSubscription implements EntityInterface
{
use
OwnerAwareEntityTrait,
BaseEntityTrait,
LimitAwareTrait;
/**
* @var Plan
*
* @ORM\ManyToOne(targetEntity="UserBundle\Entity\Plan", inversedBy="subscriptions")
*/
private $plan;
/**
* @var PaymentGatewayEnum
*
* @ORM\Column(type="payment_gateway")
*/
private $gateway;
/**
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="UserBundle\Entity\User",
* mappedBy="billingSubscription",
* cascade={ "persist", "remove" }
* )
*/
private $users;
/**
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="UserBundle\Entity\Notification\Notification",
* mappedBy="billingSubscription",
* cascade={ "persist", "remove" }
* )
*/
private $notifications;
/**
* @var Collection
*
* @ORM\OneToMany(
* targetEntity="PaymentBundle\Entity\Payment",
* mappedBy="subscription",
* cascade={ "persist", "remove" }
* )
*/
private $payments;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $payed = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $isSubscriptionCancelled = false;
/**
* @var boolean
*
* @ORM\Column(type="boolean")
*/
private $isPlanDowngrade = false;
/**
* @var datetime
*
* @ORM\Column(type="datetime",nullable=true)
*/
private $startDate;
/**
* @var datetime
*
* @ORM\Column(type="datetime",nullable=true)
*/
private $endDate;
/**
* Constructor
*/
public function __construct()
{
$this->notifications = new ArrayCollection();
}
/**
* @return Plan
*/
public function getPlan()
{
return $this->plan;
}
/**
* @param Plan $plan A Plan entity instance.
*
* @return $this
*/
public function setPlan(Plan $plan = null)
{
$this->plan = $plan;
return $this;
}
/**
* @return PaymentGatewayEnum
*/
public function getGateway()
{
return $this->gateway;
}
/**
* @param PaymentGatewayEnum $gateway A used payment gateway.
*
* @return static
*/
public function setGateway(PaymentGatewayEnum $gateway)
{
$this->gateway = $gateway;
return $this;
}
/**
* Add notification
*
* @param Notification $notification A new Notification entity instance.
*
* @return AbstractSubscription
*/
public function addNotification(Notification $notification)
{
$this->notifications[] = $notification;
$notification->setBillingSubscription($this);
return $this;
}
/**
* Remove notification
*
* @param Notification $notification A removed Notification entity instance.
*
* @return AbstractSubscription
*/
public function removeNotification(Notification $notification)
{
$this->notifications->removeElement($notification);
$notification->setBillingSubscription(null);
return $this;
}
/**
* Get notifications
*
* @return Collection
*/
public function getNotifications()
{
return $this->notifications;
}
/**
* Add payment
*
* @param Payment $payment A new Payment entity instance.
*
* @return AbstractSubscription
*/
public function addPayment(Payment $payment)
{
$this->payments[] = $payment;
$payment->setSubscription($this);
return $this;
}
/**
* Remove payment
*
* @param Payment $payment A removed Payment entity instance.
*
* @return AbstractSubscription
*/
public function removePayment(Payment $payment)
{
$this->payments->removeElement($payment);
$payment->setSubscription(null);
return $this;
}
/**
* Get payments
*
* @return Collection
*/
public function getPayments()
{
return $this->payments;
}
/**
* Add user
*
* @param User $user A new User entity instance.
*
* @return static
*/
public function addUser(User $user)
{
$this->users[] = $user;
$user->setBillingSubscription($this);
return $this;
}
/**
* Remove user
*
* @param User $user A removed User entity instance.
*
* @return static
*/
public function removeUser(User $user)
{
$this->users->removeElement($user);
$user->setBillingSubscription(null);
return $this;
}
/**
* Get users
*
* @return Collection
*/
public function getUsers()
{
return $this->users;
}
/**
* @return boolean
*/
public function isPayed()
{
return $this->payed;
}
/**
* @param boolean $payed Has owner paid for this subscription or not.
*
* @return $this
*/
public function setPayed($payed)
{
$this->payed = $payed;
return $this;
}
/**
* @param PaymentGatewayFactoryInterface $factory A PaymentGatewayFactoryInterface
* instance.
* @param string $note Cancel note.
*
* @return void
*/
public function cancel(PaymentGatewayFactoryInterface $factory, $note)
{
$factory->getGateway($this->getGateway())->cancelSubscription($this, $note);
}
/**
* @return bool
*/
public function isSubscriptionCancelled(): bool
{
return $this->isSubscriptionCancelled;
}
/**
* @param bool $isSubscriptionCancelled
*/
public function setIsSubscriptionCancelled(bool $isSubscriptionCancelled): void
{
$this->isSubscriptionCancelled = $isSubscriptionCancelled;
}
/**
* @return bool
*/
public function isPlanDowngrade(): bool
{
return $this->isPlanDowngrade;
}
/**
* @param bool $isPlanDowngrade
*/
public function setIsPlanDowngrade(bool $isPlanDowngrade): void
{
$this->isPlanDowngrade = $isPlanDowngrade;
}
/**
* @return startDate
*/
public function getStartDate(): ?\DateTimeInterface
{
return $this->startDate;
}
/**
* @param startDate
*
* @return startDate
*/
public function setStartDate(\DateTimeInterface $startDate)
{
$this->startDate = $startDate;
return $this;
}
/**
* @return endDate
*/
public function getEndDate(): ?\DateTimeInterface
{
return $this->endDate;
}
/**
* @param endDate
*
* @return endDate
*/
public function setEndDate(\DateTimeInterface $endDate)
{
$this->endDate = $endDate;
return $this;
}
/**
* @return BillingSubscriptionTypeEnum
*/
abstract public function getSubscriptionType();
}
@@ -0,0 +1,134 @@
<?php
namespace UserBundle\Entity\Subscription;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Entity\Organization;
use UserBundle\Enum\BillingSubscriptionTypeEnum;
/**
* @ORM\Entity(repositoryClass="UserBundle\Repository\SubscriptionRepository")
*/
class OrganizationSubscription extends AbstractSubscription
{
/**
* @var Organization
*
* @ORM\ManyToOne(targetEntity="UserBundle\Entity\Organization", inversedBy="subscriptions")
*/
private $organization;
/**
* @var string
*
* @ORM\Column
* @Assert\NotBlank
*/
protected $organizationAddress;
/**
* @var string
*
* @ORM\Column
* @Assert\NotBlank
*/
protected $organizationEmail;
/**
* @var string
*
* @ORM\Column
* @Assert\NotBlank
*/
protected $organizationPhone;
/**
* @return Organization
*/
public function getOrganization()
{
return $this->organization;
}
/**
* @param Organization $organization A Organization entity instance.
*
* @return OrganizationSubscription
*/
public function setOrganization(Organization $organization)
{
$this->organization = $organization;
return $this;
}
/**
* @return string
*/
public function getOrganizationAddress()
{
return $this->organizationAddress;
}
/**
* @param string $organizationAddress Organization department address.
*
* @return OrganizationSubscription
*/
public function setOrganizationAddress($organizationAddress)
{
$this->organizationAddress = $organizationAddress;
return $this;
}
/**
* @return string
*/
public function getOrganizationEmail()
{
return $this->organizationEmail;
}
/**
* @param string $organizationEmail Organization department email.
*
* @return OrganizationSubscription
*/
public function setOrganizationEmail($organizationEmail)
{
$this->organizationEmail = $organizationEmail;
return $this;
}
/**
* @return string
*/
public function getOrganizationPhone()
{
return $this->organizationPhone;
}
/**
* @param string $organizationPhone Organization department phone.
*
* @return OrganizationSubscription
*/
public function setOrganizationPhone($organizationPhone)
{
$this->organizationPhone = $organizationPhone;
return $this;
}
/**
* @return BillingSubscriptionTypeEnum
*/
public function getSubscriptionType()
{
return BillingSubscriptionTypeEnum::organization();
}
}
@@ -0,0 +1,21 @@
<?php
namespace UserBundle\Entity\Subscription;
use Doctrine\ORM\Mapping as ORM;
use UserBundle\Enum\BillingSubscriptionTypeEnum;
/**
* @ORM\Entity
*/
class PersonalSubscription extends AbstractSubscription
{
/**
* @return BillingSubscriptionTypeEnum
*/
public function getSubscriptionType()
{
return BillingSubscriptionTypeEnum::personal();
}
}
@@ -0,0 +1,261 @@
<?php
namespace UserBundle\Entity\Traits;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use UserBundle\Enum\AppLimitEnum;
/**
* Trait LimitAwareTrait
*
* Contains mapping for some application limits and setter and getter for it.
*
* @package UserBundle\Entity\Traits
*/
trait LimitAwareTrait
{
/**
* @var integer
*
* @ORM\Column(type="integer")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0)
*/
private $searchesPerDay = 0;
/**
* @var integer
*
* @ORM\Column(type="integer")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0)
*/
private $savedFeeds = 0;
/**
* @var integer
*
* @ORM\Column(type="integer")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0)
*/
private $masterAccounts = 0;
/**
* @var integer
*
* @ORM\Column(type="integer")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0)
*/
private $subscriberAccounts = 0;
/**
* @var integer
*
* @ORM\Column(type="integer")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0)
*/
private $alerts = 0;
/**
* @var integer
*
* @ORM\Column(type="integer")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0)
*/
private $newsletters = 0;
/**
* @var integer
*
* @ORM\Column(type="integer")
*
* @Assert\NotBlank
* @Assert\GreaterThanOrEqual(value=0)
*/
private $webFeeds = 0;
/**
* @return integer
*/
public function getSearchesPerDay()
{
return $this->searchesPerDay;
}
/**
* Set searchesPerDay
*
* @param integer $searchesPerDay Search per day limit.
*
* @return static
*/
public function setSearchesPerDay($searchesPerDay)
{
$this->searchesPerDay = $searchesPerDay;
return $this;
}
/**
* @return mixed
*/
public function getSavedFeeds()
{
return $this->savedFeeds;
}
/**
* Set savedFeeds
*
* @param integer $savedFeeds Saved feed limit.
*
* @return static
*/
public function setSavedFeeds($savedFeeds)
{
$this->savedFeeds = $savedFeeds;
return $this;
}
/**
* @return mixed
*/
public function getWebFeeds()
{
return $this->webFeeds;
}
/**
* Set webFeeds
*
* @param integer $webFeeds Saved feed limit.
*
* @return static
*/
public function setWebFeeds($webFeeds)
{
$this->webFeeds = $webFeeds;
return $this;
}
/**
* @return integer
*/
public function getMasterAccounts()
{
return $this->masterAccounts;
}
/**
* @param integer $masterAccounts Master accounts limit.
*
* @return static
*/
public function setMasterAccounts($masterAccounts)
{
$this->masterAccounts = $masterAccounts;
return $this;
}
/**
* @return integer
*/
public function getSubscriberAccounts()
{
return $this->subscriberAccounts;
}
/**
* @param integer $subscriberAccounts Subscriber account limit.
*
* @return static
*/
public function setSubscriberAccounts($subscriberAccounts)
{
$this->subscriberAccounts = $subscriberAccounts;
return $this;
}
/**
* @return integer
*/
public function getAlerts()
{
return $this->alerts;
}
/**
* @param integer $alerts Alerts count.
*
* @return static
*/
public function setAlerts($alerts)
{
$this->alerts = $alerts;
return $this;
}
/**
* @return integer
*/
public function getNewsletters()
{
return $this->newsletters;
}
/**
* @param integer $newsletters Newsletters count.
*
* @return static
*/
public function setNewsletters($newsletters)
{
$this->newsletters = $newsletters;
return $this;
}
/**
* Get limit value for specified limit name.
*
* @param AppLimitEnum $appLimit Requested limit name.
*
* @return integer
*/
public function getLimitValue(AppLimitEnum $appLimit)
{
return $this->{'get'. ucfirst($appLimit->getValue())}();
}
/**
* Set limit value for specified limit name.
*
* @param AppLimitEnum $appLimit Changed limit name.
* @param integer $newValue New value for limit.
*
* @return $this
*/
public function setLimitValue(AppLimitEnum $appLimit, $newValue)
{
$this->{'set'. ucfirst($appLimit->getValue())}($newValue);
return $this;
}
}
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class AppLimitEnum
* @package UserBundle\Enum
*
* @method static AppLimitEnum searches()
* @method static AppLimitEnum feeds()
* @method static AppLimitEnum masterAccounts()
* @method static AppLimitEnum subscriberAccounts()
* @method static AppLimitEnum alerts()
* @method static AppLimitEnum newsletters()
* @method static AppLimitEnum webfeeds()
*/
class AppLimitEnum extends AbstractEnum
{
const SEARCHES = 'searchesPerDay';
const FEEDS = 'savedFeeds';
const MASTER_ACCOUNTS = 'masterAccounts';
const SUBSCRIBER_ACCOUNTS = 'subscriberAccounts';
const ALERTS = 'alerts';
const NEWSLETTERS = 'newsletters';
const WEBFEEDS = 'webFeeds';
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class AppPermissionEnum
* @package UserBundle\Enum
*
* @method static AppPermissionEnum analytics()
*/
class AppPermissionEnum extends AbstractEnum
{
const ANALYTICS = 'analytics';
}
@@ -0,0 +1,19 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class BillingSubscriptionTypeEnum
* @package UserBundle\Enum
*
* @method static BillingSubscriptionTypeEnum organization()
* @method static BillingSubscriptionTypeEnum personal()
*/
class BillingSubscriptionTypeEnum extends AbstractEnum
{
const ORGANIZATION = 'organization';
const PERSONAL = 'personal';
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class ThemeTypeEnum
* @package UserBundle\Enum
*
* @method static FontFamilyEnum arial()
* @method static FontFamilyEnum calibri()
* @method static FontFamilyEnum centuryGothic()
* @method static FontFamilyEnum courierNew()
* @method static FontFamilyEnum georgia()
* @method static FontFamilyEnum lucidaSansUnicode()
* @method static FontFamilyEnum myriadProRegular()
* @method static FontFamilyEnum tahoma()
* @method static FontFamilyEnum timesNewRoman()
* @method static FontFamilyEnum trebuchet()
* @method static FontFamilyEnum verdana()
*/
class FontFamilyEnum extends AbstractEnum
{
const ARIAL = 'Arial';
const CALIBRI = 'Calibri';
const CENTURY_GOTHIC = 'Century Gothic';
const COURIER_NEW = 'Courier New';
const GEORGIA = 'Georgia';
const LUCIDA_SANS_UNICODE = 'Lucida Sans Unicode';
const MYRIAD_PRO_REGULAR = 'Myriad Pro Regular';
const TAHOMA = 'Tahoma';
const TIMES_NEW_ROMAN = 'Times New Roman';
const TREBUCHET = 'Trebuchet';
const VERDANA = 'Verdana';
private static $nameToFamilyMap = [
FontFamilyEnum::ARIAL => 'Arial,helvetica,sans-serif',
FontFamilyEnum::CALIBRI => 'Calibri,Helvetica,sans-serif',
FontFamilyEnum::CENTURY_GOTHIC => '\'Century Gothic\',CenturyGothic,AppleGothic,sans-serif',
FontFamilyEnum::COURIER_NEW => '\'Courier new\',courier,monospace',
FontFamilyEnum::GEORGIA => 'Georgia,serif',
FontFamilyEnum::LUCIDA_SANS_UNICODE => '\'Lucida Sans Unicode\',sans-serif',
FontFamilyEnum::MYRIAD_PRO_REGULAR => 'MyriadPro-Regular,\'Lucida Sans Unicode\',\'Lucida Grande\',sans-serif',
FontFamilyEnum::TAHOMA => '\'Tahoma Verdana\',Segoe,sans-serif',
FontFamilyEnum::TIMES_NEW_ROMAN => 'TimesNewRoman,serif',
FontFamilyEnum::TREBUCHET => 'Trebuchet,Trebuchet MS,sans-serif',
FontFamilyEnum::VERDANA => 'Verdana,geneva,sans-serif',
];
/**
* Get css font family option.
*
* @return string
*/
public function getCss()
{
return self::$nameToFamilyMap[$this->value];
}
}
@@ -0,0 +1,31 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class NotificationTypeEnum
* @package UserBundle\Enum
*
* @method static NotificationTypeEnum alert()
* @method static NotificationTypeEnum newsletter()
*/
class NotificationTypeEnum extends AbstractEnum
{
const ALERT = 'alert';
const NEWSLETTER = 'newsletter';
/**
* @return AppLimitEnum
*/
public function toAppLimit()
{
if ($this->value === self::ALERT) {
return AppLimitEnum::alerts();
}
return AppLimitEnum::newsletters();
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
use UserBundle\Entity\Recipient\GroupRecipient;
use UserBundle\Entity\Recipient\PersonRecipient;
/**
* Class RecipientTypeEnum
* @package UserBundle\Enum
*
* @method static RecipientTypeEnum person()
* @method static RecipientTypeEnum group()
*/
class RecipientTypeEnum extends AbstractEnum
{
const PERSON = 'recipient';
const GROUP = 'group';
private static $map = [
self::PERSON => PersonRecipient::class,
self::GROUP => GroupRecipient::class,
];
/**
* Get entity class for this type.
*
* @return string
*/
public function getEntityClass()
{
return self::$map[$this->value];
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class ThemeTypeEnum
* @package UserBundle\Enum
*
* @method static StatusFilterEnum no()
* @method static StatusFilterEnum yes()
* @method static StatusFilterEnum all()
*/
class StatusFilterEnum extends AbstractEnum
{
const NO = 'no';
const YES = 'yes';
const ALL = 'all';
}
@@ -0,0 +1,21 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class ThemeOptionExtractEnum
* @package UserBundle\Enum
*
* @method static ThemeOptionExtractEnum no()
* @method static ThemeOptionExtractEnum start()
* @method static ThemeOptionExtractEnum context()
*/
class ThemeOptionExtractEnum extends AbstractEnum
{
const NO = 'no';
const START = 'start';
const CONTEXT = 'context';
}
@@ -0,0 +1,25 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class ThemeOptionsTableOfContentsEnum
* @package UserBundle\Enum
*
* @method static ThemeOptionsTableOfContentsEnum no()
* @method static ThemeOptionsTableOfContentsEnum simple()
* @method static ThemeOptionsTableOfContentsEnum headline()
* @method static ThemeOptionsTableOfContentsEnum headlineSourceDate()
* @method static ThemeOptionsTableOfContentsEnum sourceHeadlineDate()
*/
class ThemeOptionsTableOfContentsEnum extends AbstractEnum
{
const NO = 'no';
const SIMPLE = 'simple';
const HEADLINE = 'headline';
const HEADLINE_SOURCE_DATE = 'headline_source_date';
const SOURCE_HEADLINE_DATE = 'source_headline_date';
}
@@ -0,0 +1,21 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class ThemeOptionsUserCommentsEnum
* @package UserBundle\Enum
*
* @method static ThemeOptionsUserCommentsEnum no()
* @method static ThemeOptionsUserCommentsEnum withAuthorDate()
* @method static ThemeOptionsUserCommentsEnum withoutAuthorDate()
*/
class ThemeOptionsUserCommentsEnum extends AbstractEnum
{
const NO = 'no';
const WITH_AUTHOR_DATE = 'with_author_date';
const WITHOUT_AUTHOR_DATE = 'without_author_date';
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class ThemeTypeEnum
* @package UserBundle\Enum
*
* @method static ThemeTypeEnum enhanced()
* @method static ThemeTypeEnum plain()
*/
class ThemeTypeEnum extends AbstractEnum
{
const ENHANCED = 'enhanced';
const PLAIN = 'plain';
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace UserBundle\Enum;
use AppBundle\Enum\AbstractEnum;
/**
* Class UserRoleEnum
* @package UserBundle\Enum
*
* @method static UserRoleEnum subscriber()
* @method static UserRoleEnum masterUser()
* @method static UserRoleEnum admin()
* @method static UserRoleEnum superAdmin()
*/
class UserRoleEnum extends AbstractEnum
{
const SUBSCRIBER = 'ROLE_SUBSCRIBER';
const MASTER_USER = 'ROLE_MASTER_USER';
const ADMIN = 'ROLE_ADMIN';
const SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
}
+109
View File
@@ -0,0 +1,109 @@
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use UserBundle\Entity\User;
/**
* Class ChangePasswordType
* @package UserBundle\Form
*/
class ChangePasswordType extends AbstractType
{
/**
* @var UserPasswordEncoderInterface
*/
private $userPasswordEncoder;
/**
* ChangePasswordType constructor.
*
* @param UserPasswordEncoderInterface $userPasswordEncoder A UserPasswordEncoderInterface
* instance.
*/
public function __construct(UserPasswordEncoderInterface $userPasswordEncoder)
{
$this->userPasswordEncoder = $userPasswordEncoder;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('password', PasswordType::class, [
'trim' => true,
'property_path' => 'plainPassword',
'constraints' => new NotBlank(),
])
->add('oldPassword', PasswordType::class, [
'trim' => true,
'mapped' => false,
'constraints' => [
new NotBlank(),
new Callback([ $this, 'checkPassword' ]),
],
]);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
/**
* @param string|null $value Old user password.
* @param ExecutionContextInterface $context A ExecutionContextInterface
* instance.
*
* @return void
*/
public function checkPassword($value, ExecutionContextInterface $context)
{
if ($value === null) {
return;
}
/** @var FormInterface $form */
$form = $context->getRoot();
/** @var User $user */
$user = $form->getData();
if (! $this->userPasswordEncoder->isPasswordValid($user, $value)) {
$context->buildViolation('Old password don\'t match to current password')
->addViolation();
}
}
}
@@ -0,0 +1,114 @@
<?php
namespace UserBundle\Form\Extension\Core\DataMapper;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\FormInterface;
use UserBundle\Entity\Notification\Notification;
/**
* Class NotificationDataMapper
* @package UserBundle\Form\Extension\Core\DataMapper
*/
class NotificationDataMapper extends PropertyPathMapper
{
/**
* List of required form fields.
*
* @var string[]
*/
private static $requiredFields = [
'sources',
'automatic',
'plainDiff',
'enhancedDiff',
];
/**
* Maps properties of some data to a list of forms.
*
* @param mixed $data Structured data.
* @param FormInterface[]|array $forms A list of {@link FormInterface} instances.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function mapDataToForms($data, $forms)
{
// Do nothing because it's not necessary.
}
/**
* Maps the data of a list of forms into the properties of some data.
*
* @param FormInterface[]|\Traversable $forms A list of {@link FormInterface}
* instances.
* @param mixed $data Structured data.
*
* @return void
*
* @throws UnexpectedTypeException If the type of the data parameter is not
* supported.
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
if (! $data instanceof Notification) {
throw new UnexpectedTypeException($data, Notification::class);
}
//
// Check that all required fields is set.
//
// if (\Functional\some(self::$requiredFields, function ($name) use ($forms) {
// return ! isset($forms[$name]);
// })) {
if (\nspl\a\any(self::$requiredFields, function ($name) use ($forms) {
return ! isset($forms[$name]);
})) {
throw new UnexpectedTypeException(current($forms)->getParent(), Notification::class);
}
//
// Map sources.
//
$sources = $forms['sources']->getData();
// todo uncomment and refactor when analytics is added
// if (($sources['feeds'] !== null) && ($sources['charts'] !== null)) {
if (isset($sources['feeds'])) {
// Do not map data if we have null.
$data
->setFeeds($sources['feeds']);
}
//
// Map schedules.
//
$data->setSchedules($forms['automatic']->getData());
//
// Map diffs.
//
$data->setPlainThemeOptionsDiff($forms['plainDiff']->getData());
$data->setEnhancedThemeOptionsDiff($forms['enhancedDiff']->getData());
//
// Remove all processed fields.
//
// \Functional\each(self::$requiredFields, function ($name) use (&$forms) {
// unset($forms[$name]);
// });
foreach (self::$requiredFields as $name) {
unset($forms[$name]);
}
//
// Map other fields.
//
parent::mapFormsToData($forms, $data);
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace UserBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Recipient\GroupRecipient;
use UserBundle\Entity\Recipient\PersonRecipient;
use UserBundle\Entity\User;
use UserBundle\Repository\NotificationRepository;
use UserBundle\Repository\PersonRecipientRepository;
/**
* Class GroupRecipientType
* @package UserBundle\Form
*/
class GroupRecipientType extends AbstractType
{
/**
* @var TokenStorageInterface
*/
private $storage;
/**
* PersonRecipientType constructor.
*
* @param TokenStorageInterface $storage A TokenStorageInterface instance.
*/
public function __construct(TokenStorageInterface $storage)
{
$this->storage = $storage;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = \app\op\invokeIf($this->storage->getToken(), 'getUser');
$builder
->add('name')
->add('description')
->add('active', CheckboxType::class)
->add('recipients', EntityType::class, [
'class' => PersonRecipient::class,
'multiple' => true,
'by_reference' => false,
'query_builder' => function (PersonRecipientRepository $repository) use ($user) {
if ($user instanceof User) {
return $repository->getAvailableForUser($user);
}
return $repository->createQueryBuilder('Person');
},
])
->add('notifications', EntityType::class, [
'class' => Notification::class,
'multiple' => true,
'by_reference' => false,
'query_builder' => function (NotificationRepository $repository) use ($user) {
if ($user instanceof User) {
return $repository->getQueryBuilderForForm($user);
}
return $repository->createQueryBuilder('Notification');
},
]);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => GroupRecipient::class,
]);
}
}
@@ -0,0 +1,151 @@
<?php
namespace UserBundle\Form;
use Doctrine\ORM\EntityManagerInterface;
use PaymentBundle\Enum\PaymentGatewayEnum;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\Regex;
use UserBundle\Entity\Organization;
use UserBundle\Entity\Plan;
use UserBundle\Entity\Subscription\OrganizationSubscription;
use UserBundle\Entity\Subscription\PersonalSubscription;
use UserBundle\Entity\User;
use UserBundle\Enum\UserRoleEnum;
use UserBundle\Repository\OrganizationRepository;
/**
* Class HubSpotRegistrationType
* @package UserBundle\Form
*/
class HubSpotRegistrationType extends AbstractType implements DataMapperInterface
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* RegistrationType constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface instance.
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, [
'constraints' => [
new Regex([
'pattern' => '/^[a-zA-Z0-9!#$%&\'*+\/=?^_` {|}~;."-]+@[a-zA-Z0-9!#$%&\'*+\/=?^_` {|}~;."-]+\.[a-zA-Z0-9]+$/',
'message' => 'This value is not a valid email address',
]),
new Length([
'max' => 160,
'maxMessage' => 'Email address is too long. It should have {{ limit }} character or less',
]),
],
])
->add('firstName')
->add('lastName')
->add('companyName')
->add('jobFunction')
->add('numberOfEmployee')
->add('industry')
->add('websiteUrl')
// ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
// $data = $event->getData();
// $form = $event->getForm();
// })
->setDataMapper($this);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
/**
* Maps properties of some data to a list of forms.
*
* @param User|null $data Structured data.
* @param FormInterface[]|\RecursiveIteratorIterator $forms A list of
* {@link FormInterface}
* instances.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function mapDataToForms($data, $forms)
{
// Do nothing because it's not necessary method.
}
/**
* Maps the data of a list of forms into the properties of some data.
*
* @param FormInterface[]|\RecursiveIteratorIterator $forms A list of
* {@link FormInterface}
* instances.
* @param User|null $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
$data->setEmail($forms['email']->getData());
$data->setFirstName($forms['firstName']->getData());
$data->setLastName($forms['lastName']->getData());
$data->setJobFunction($forms['jobFunction']->getData());
$data->setCompanyName($forms['companyName']->getData());
$data->setNumberOfEmployee($forms['numberOfEmployee']->getData());
$data->setIndustry($forms['industry']->getData());
$data->setWebsiteUrl($forms['websiteUrl']->getData());
$data->setPhoneNumber(' ');
$data->setHubSpot(true);
}
}
+113
View File
@@ -0,0 +1,113 @@
<?php
namespace UserBundle\Form;
use AppBundle\Form\Type\EnumType;
use CacheBundle\Form\Type\CurrentUserOwnedEntityType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type as FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Notification\NotificationTheme;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Enum\NotificationTypeEnum;
use UserBundle\Enum\ThemeTypeEnum;
use UserBundle\Form\Extension\Core\DataMapper\NotificationDataMapper;
use UserBundle\Form\Type\NotificationDiffType;
use UserBundle\Form\Type\ScheduleType;
use UserBundle\Form\Type\SimpleTimeZoneType;
use UserBundle\Form\Type\SourcesType;
/**
* Class NotificationType
* @package UserBundle\Form
*/
class NotificationType extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('notificationType', EnumType::class, [
'enum_class' => NotificationTypeEnum::class,
])
->add('recipients', CurrentUserOwnedEntityType::class, [
'class' => AbstractRecipient::class,
'multiple' => true,
'expanded' => true,
'by_reference' => 'false',
'user_property' => 'owner',
])
->add('themeType', EnumType::class, [
'enum_class' => ThemeTypeEnum::class,
])
->add('theme', EntityType::class, [
'class' => NotificationTheme::class,
])
->add('subject', null, [ 'required' => false ])
->add('automatedSubject', FormType\CheckboxType::class)
->add('published', FormType\CheckboxType::class)
->add('allowUnsubscribe', FormType\CheckboxType::class)
->add('unsubscribeNotification', FormType\CheckboxType::class)
->add('sources', SourcesType::class)
->add('sendWhenEmpty', FormType\CheckboxType::class)
->add('timezone', SimpleTimeZoneType::class)
->add('automatic', FormType\CollectionType::class, [
'entry_type' => ScheduleType::class,
'description' => 'Array of daily, weekly or monthly scheduling objects.',
'allow_add' => true,
'allow_delete' => true,
'empty_data' => [],
])
->add('sendUntil', FormType\DateType::class, [ 'widget' => 'single_text' ])
->add('plainDiff', NotificationDiffType::class)
->add('enhancedDiff', NotificationDiffType::class)
->setDataMapper(new NotificationDataMapper())
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if (isset($data['automatedSubject']) && ($data['automatedSubject'] === false)) {
$options = $form->get('subject')->getConfig()->getOptions();
$options['constraints'] = new NotBlank([ 'message' => 'Subject should not be blank' ]);
$form
->remove('subject')
->add('subject', null, $options);
}
});
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', Notification::class);
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace UserBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use UserBundle\Entity\Organization;
use UserBundle\Entity\Plan;
use UserBundle\Entity\Subscription\OrganizationSubscription;
/**
* Class OrganizationType
* @package UserBundle\Form
*/
class OrganizationType extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('organizationAddress')
->add('organizationEmail')
->add('organizationPhone')
->add('organizationPhone')
->add('organization', EntityType::class, array('class' => Organization::class))
->add('billingPlanId', EntityType::class, array('class' => Plan::class, 'property_path' => 'plan'));
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => OrganizationSubscription::class,
]);
}
}
+169
View File
@@ -0,0 +1,169 @@
<?php
namespace UserBundle\Form;
use Doctrine\ORM\EntityManagerInterface;
use PaymentBundle\Enum\PaymentGatewayEnum;
use PaymentBundle\Model\PaymentData;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Constraint;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use UserBundle\Entity\User;
use UserBundle\Form\Type\CreditCardType;
/**
* Class PaymentDataType
* @package UserBundle\Form
*/
class PaymentDataType extends AbstractType implements DataMapperInterface
{
/**
* @var User[]
*/
private $userCache = [];
/**
* @var EntityManagerInterface
*/
private $em;
/**
* PaymentDataType constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface instance.
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('paymentGateway', ChoiceType::class, [
'choices' => PaymentGatewayEnum::getAvailables(),
'empty_data' => PaymentGatewayEnum::PAYPAL,
])
->add('code', null, [
'constraints' => [
new Constraint\NotBlank(),
new Constraint\Callback([ $this, 'validateCode' ]),
],
])
->add('card', CreditCardType::class)
->setDataMapper($this);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => PaymentData::class,
'empty_data' => new PaymentData(PaymentGatewayEnum::paypal()),
]);
}
/**
* Maps properties of some data to a list of forms.
*
* @param PaymentData|null $data Structured data.
* @param FormInterface[]|\Iterator $forms A list of {@link FormInterface}
* instances.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function mapDataToForms($data, $forms)
{
// Do nothing.
}
/**
* Maps the data of a list of forms into the properties of some data.
*
* @param FormInterface[]|\Iterator $forms A list of {@link FormInterface}
* instances.
* @param PaymentData|null $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
$user = $this->getUserByCode($forms['code']->getData());
try {
$data = new PaymentData(
new PaymentGatewayEnum($forms['paymentGateway']->getData()),
$user,
$forms['card']->getData()
);
} catch (\InvalidArgumentException $e) {
//
// This may occurred 'cause form don't validate values before mapping
// it data to source object.
//
throw new \RuntimeException('Can\'t create payment data', 0, $e);
}
}
/**
* @param mixed $code Validated code.
* @param ExecutionContextInterface $context A ExecutionContextInterface
* instance.
*
* @return void
*/
public function validateCode($code, ExecutionContextInterface $context)
{
$user = $this->getUserByCode($code);
if ($user === null) {
$context->buildViolation('Invalid code. Can\'t find user with specified code')
->addViolation();
}
}
/**
* @param mixed $code User confirmation code.
*
* @return User
*/
private function getUserByCode($code)
{
if (! array_key_exists($code, $this->userCache)) {
$this->userCache[$code] = $this->em->getRepository(User::class)
->findOneBy(['confirmationToken' => $code]);
}
return $this->userCache[$code];
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace UserBundle\Form;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Recipient\GroupRecipient;
use UserBundle\Entity\Recipient\PersonRecipient;
use UserBundle\Entity\User;
use UserBundle\Repository\GroupRecipientRepository;
use UserBundle\Repository\NotificationRepository;
/**
* Class PersonRecipientType
* @package UserBundle\Form
*/
class PersonRecipientType extends AbstractType
{
/**
* @var TokenStorageInterface
*/
private $storage;
/**
* PersonRecipientType constructor.
*
* @param TokenStorageInterface $storage A TokenStorageInterface instance.
*/
public function __construct(TokenStorageInterface $storage)
{
$this->storage = $storage;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = \app\op\invokeIf($this->storage->getToken(), 'getUser');
$builder
->add('firstName')
->add('lastName')
->add('email', EmailType::class)
->add('active', CheckboxType::class)
->add('groups', EntityType::class, [
'class' => GroupRecipient::class,
'multiple' => true,
'by_reference' => false,
'query_builder' => function (GroupRecipientRepository $repository) use ($user) {
if ($user instanceof User) {
return $repository->getAvailableForUser($user);
}
return $repository->createQueryBuilder('Grp');
},
])
->add('notifications', EntityType::class, [
'class' => Notification::class,
'multiple' => true,
'by_reference' => false,
'query_builder' => function (NotificationRepository $repository) use ($user) {
if ($user instanceof User) {
return $repository->getQueryBuilderForForm($user);
}
return $repository->createQueryBuilder('Notification');
},
]);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => PersonRecipient::class,
]);
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use UserBundle\Entity\Plan;
/**
* Class PlanType
* @package UserBundle\Form
*/
class PlanType extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', null, [
'constraints' => new NotBlank(),
])
->add('price', MoneyType::class, [
'currency' => 'USD',
])
->add('searchesPerDay', IntegerType::class, [ 'attr' => [ 'min' => 0 ] ])
->add('savedFeeds', IntegerType::class, [ 'attr' => [ 'min' => 0 ] ])
->add('masterAccounts', IntegerType::class, [ 'attr' => [ 'min' => 0 ] ])
->add('subscriberAccounts', IntegerType::class, [ 'attr' => [ 'min' => 0 ] ])
->add('alerts', IntegerType::class, [ 'attr' => [ 'min' => 0 ] ])
->add('newsletters', IntegerType::class, [ 'attr' => [ 'min' => 0 ] ])
->add('analytics', CheckboxType::class, [ 'required' => false ]);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Plan::class,
]);
}
}
+392
View File
@@ -0,0 +1,392 @@
<?php
namespace UserBundle\Form;
use Doctrine\ORM\EntityManagerInterface;
use PaymentBundle\Enum\PaymentGatewayEnum;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterfacePasswordType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Constraints\NotBlank;
use UserBundle\Entity\Organization;
use UserBundle\Entity\Plan;
use UserBundle\Entity\Subscription\OrganizationSubscription;
use UserBundle\Entity\Subscription\PersonalSubscription;
use UserBundle\Entity\User;
use CacheBundle\Entity\Category;
use UserBundle\Enum\UserRoleEnum;
use UserBundle\Repository\OrganizationRepository;
use Symfony\Component\Form\DataMapperInterface;
/**
* Class RegistrationType
* @package UserBundle\Form
*/
class RegistrationType extends AbstractType implements DataMapperInterface
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* RegistrationType constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface instance.
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, [
'constraints' => [
new Regex([
'pattern' => '/^[a-zA-Z0-9!#$%&\'*+\/=?^_` {|}~;."-]+@[a-zA-Z0-9!#$%&\'*+\/=?^_` {|}~;."-]+\.[a-zA-Z0-9]+$/',
'message' => 'This value is not a valid email address',
]),
new Length([
'max' => 160,
'maxMessage' => 'Email address is too long. It should have {{ limit }} character or less',
]),
new NotBlank([
'message' => 'Email address should not be blank',
]),
],
])
->add('firstName')
->add('lastName')
->add('companyName')
->add('jobFunction')
->add('industry')
->add('websiteUrl')
->add('password',PasswordType::class)
->add('numberOfEmployee');
if (!empty($options['paymentID'])) {
//->add('phoneNumber')
$builder->add('searchesPerDay', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Searches Per Day should not be blank',
]),
],
])
->add('savedFeeds', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Saved Feeds should not be blank',
]),
],
])
->add('masterAccounts', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Master Accounts should not be blank',
]),
],
])
->add('subscriberAccounts', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Subscriber Accounts should not be blank',
]),
],
])
->add('webFeeds', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Web Feeds should not be blank',
]),
],
])
->add('alerts', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Alerts should not be blank',
]),
],
])
->add('news', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'News should not be blank',
]),
],
])
->add('blog', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Blog should not be blank',
]),
],
])
->add('reddit', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Reddit should not be blank',
]),
],
])
->add('instagram', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Instagram should not be blank',
]),
],
])
->add('twitter', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Twitter should not be blank',
]),
],
])
->add('analytics', TextType::class, [
'constraints' => [
new NotBlank([
'message' => 'Analytics should not be blank',
]),
],
])
->add('paymentID');
//->add('billingPlanId', EntityType::class, [ 'class' => Plan::class ])
// ->add('privatePerson', CheckboxType::class, [ 'mapped' => false ])
// ->add('organizationName', null, [ 'description' => 'Used only for organization subscription.' ])
// ->add('organizationAddress', null, [ 'description' => 'Used only for organization subscription.' ])
// ->add('organizationEmail', null, [ 'description' => 'Used only for organization subscription.' ])
// ->add('organizationPhone', null, [ 'description' => 'Used only for organization subscription.' ])
// ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
// $data = $event->getData();
// $form = $event->getForm();
// if (isset($data['privatePerson']) && $data['privatePerson']) {
// $form
// ->remove('organizationName')
// ->remove('organizationAddress')
// ->remove('organizationEmail')
// ->remove('organizationPhone');
// }
// })
}
$builder->setDataMapper($this);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
'paymentID' => null
]);
}
/**
* Maps properties of some data to a list of forms.
*
* @param User|null $data Structured data.
* @param FormInterface[]|\RecursiveIteratorIterator $forms A list of
* {@link FormInterface}
* instances.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function mapDataToForms($data, $forms)
{
// Do nothing because it's not necessary method.
}
/**
* Maps the data of a list of forms into the properties of some data.
*
* @param FormInterface[]|\RecursiveIteratorIterator $forms A list of
* {@link FormInterface}
* instances.
* @param User|null $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
// TODO for now it's ok, but used gateway should be selected by users.
$gateway = PaymentGatewayEnum::paypal();
//Add plan
if (isset(
$forms['news'],
$forms['blog'],
$forms['reddit'],
$forms['instagram'],
$forms['twitter'],
$forms['analytics'],
$forms['searchesPerDay'],
$forms['savedFeeds'],
$forms['masterAccounts'],
$forms['subscriberAccounts'],
$forms['webFeeds'],
$forms['alerts'],
$forms['paymentID']
) &&
($forms['searchesPerDay']->getData() >= 0) &&
($forms['savedFeeds']->getData() >= 0) &&
($forms['masterAccounts']->getData() >= 0) &&
($forms['subscriberAccounts']->getData() >= 0) &&
($forms['webFeeds']->getData() >= 0) &&
($forms['alerts']->getData() >= 0)
) {
$plan = Plan::create()
->setTitle($forms['companyName']->getData())
->setInnerName('Starter')
->setPrice(0)
->setNews($forms['news']->getData())
->setBlog($forms['blog']->getData())
->setReddit($forms['reddit']->getData())
->setInstagram($forms['instagram']->getData())
->setTwitter($forms['twitter']->getData())
->setAnalytics($forms['analytics']->getData())
->setSearchesPerDay($forms['searchesPerDay']->getData())
->setSavedFeeds($forms['savedFeeds']->getData())
->setMasterAccounts($forms['masterAccounts']->getData())
->setSubscriberAccounts($forms['subscriberAccounts']->getData())
->setWebFeeds($forms['webFeeds']->getData())
->setAlerts($forms['alerts']->getData());
$this->em->persist($plan);
$subscription = OrganizationSubscription::create()
->setPlan($plan)
->setGateway($gateway)
->addUser($data)
->setOwner($data);
//Category add default
$category = new Category($data, 'My Hose');
$category->setType(Category::TYPE_MY_CONTENT);
$category = new Category($data, 'Shared Hose');
$category->setType(Category::TYPE_SHARED_CONTENT);
$category = new Category($data, 'Deleted Hose');
$category->setType(Category::TYPE_DELETED_CONTENT);
} else if (!isset($forms['paymentID'])) {
$gateway = PaymentGatewayEnum::free();
//Get a free plan
$repository = $this->em->getRepository(Plan::class);
$planObj = $repository->findOneBy([ 'title' => 'Free' ]);
$plan = Plan::create()
->setTitle($forms['companyName']->getData())
->setInnerName('Starter')
->setPrice(0)
->setNews($planObj->isNews())
->setBlog($planObj->isBlog())
->setReddit($planObj->isReddit())
->setInstagram($planObj->isInstagram())
->setTwitter($planObj->isTwitter())
->setAnalytics($planObj->isAnalytics())
->setSearchesPerDay($planObj->getSearchesPerDay())
->setSavedFeeds($planObj->getSavedFeeds())
->setMasterAccounts($planObj->getMasterAccounts())
->setSubscriberAccounts($planObj->getSubscriberAccounts())
->setAlerts($planObj->getAlerts())
->setNewsLetters($planObj->getNewsLetters())
->setWebFeeds($planObj->getWebFeeds())
->setAlerts($planObj->getAlerts());
$this->em->persist($plan);
$subscription = OrganizationSubscription::create()
->setPlan($plan)
->setGateway($gateway)
->addUser($data)
->setOwner($data);
//Category add default
$category = new Category($data, 'My Hose');
$category->setType(Category::TYPE_MY_CONTENT);
$category = new Category($data, 'Shared Hose');
$category->setType(Category::TYPE_SHARED_CONTENT);
$category = new Category($data, 'Deleted Hose');
$category->setType(Category::TYPE_DELETED_CONTENT);
}
// if (isset(
// $forms['organizationName'],
// $forms['organizationAddress'],
// $forms['organizationEmail'],
// $forms['organizationPhone']
// )) {
// //
// // Try to find already exists organization.
// //
// $orgName = $forms['organizationName']->getData();
// /** @var OrganizationRepository $repository */
// $repository = $this->em->getRepository(Organization::class);
// $organization = $repository->findOneBy([ 'name' => $orgName ]);
// if (! $organization instanceof Organization) {
// $organization = Organization::create()->setName($orgName);
// $data->setRoles([ UserRoleEnum::MASTER_USER ]);
// $this->em->persist($organization);
// } else {
// $data->setRoles([ UserRoleEnum::SUBSCRIBER ]);
// }
// $subscription = OrganizationSubscription::create()
// ->setOrganization($organization)
// ->setOrganizationAddress($forms['organizationAddress']->getData())
// ->setOrganizationEmail($forms['organizationEmail']->getData())
// ->setOrganizationPhone($forms['organizationPhone']->getData());
// }
// else {
// $subscription = PersonalSubscription::create();
// $data->setRoles([ UserRoleEnum::MASTER_USER ]);
// }
$data->setRoles([ UserRoleEnum::MASTER_USER ]);
$data->setEmail($forms['email']->getData());
$data->setFirstName($forms['firstName']->getData());
$data->setIndustry($forms['industry']->getData());
$data->setLastName($forms['lastName']->getData());
$data->setCompanyName($forms['companyName']->getData());
$data->setJobFunction($forms['jobFunction']->getData());
$data->setWebsiteUrl($forms['websiteUrl']->getData());
$data->setNumberOfEmployee($forms['numberOfEmployee']->getData());
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Class ResetPasswordType
* @package UserBundle\Form
*/
class ResetPasswordType extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, [
'constraints' => [
new NotBlank(),
new Email(),
],
]);
}
}
@@ -0,0 +1,41 @@
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use UserBundle\Entity\User;
/**
* Class ResettingConfirmType
* @package UserBundle\Form
*/
class ResettingConfirmType extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('confirmationToken', null, [ 'constraints' => new NotBlank() ])
->add('password', null, [ 'constraints' => new NotBlank() ]);
}
}
@@ -0,0 +1,42 @@
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Class ResettingRequestType
* @package UserBundle\Form
*/
class ResettingRequestType extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', EmailType::class, [
'constraints' => [
new NotBlank(),
new Email(),
],
]);
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use UserBundle\Entity\User;
/**
* Class SubscriberType
* @package UserBundle\Form
*/
class SubscriberType extends AbstractType
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
->add('firstName')
->add('lastName')
->add('position')
->add('phoneNumber')
->add('allowToCreateSavedFeeds');
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
'validation_groups' => [ 'subscribers_creation' ],
]);
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
namespace UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Class ColorType
*
* @package UserBundle\Form\Type
*/
class ColorType extends AbstractType
{
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('constraints', new Callback([ $this, 'validate' ]));
}
/**
* Returns the name of the parent type.
*
* @return string|null The name of the parent type if any, null otherwise.
*/
public function getParent()
{
return TextType::class;
}
/**
* Validate sources.
*
* @param mixed $color Color.
* @param ExecutionContextInterface $context A ExecutionContextInterface
* instance.
*
* @return void
*/
public function validate($color, ExecutionContextInterface $context)
{
if ($color === null) {
// Do not validate null values.
return;
}
$matches = [];
if (is_string($color) && preg_match('/rgba\(([0-9%.,\s]+)\)/', $color, $matches)) {
$arguments = array_map('trim', explode(',', $matches[1]));
if (count($arguments) === 4) {
$alpha = array_pop($arguments);
//
// Validate color components.
//
if (is_numeric($alpha) && $this->containsColorDigits($arguments)) {
//
// Validate alpha component.
//
$alpha = (float) $alpha;
if (($alpha >= 0.0) && ($alpha <= 1.0)) {
return;
}
}
}
}
// It's not valid 'rgba' color.
$context
->buildViolation('Color should be valid css color definition string')
->addViolation();
}
/**
* @param array $array Checked array.
*
* @return boolean
*/
private function containsColorDigits(array $array)
{
// return \Functional\every($array, function ($item) {
return \nspl\a\all($array, function ($item) {
if (! is_numeric($item)) {
return false;
}
$item = (int) $item;
return ($item >= 0) && ($item <= 255);
});
}
}
@@ -0,0 +1,90 @@
<?php
namespace UserBundle\Form\Type;
use PaymentBundle\Model\CreditCardAddress;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Class CreditCardAddressType
*
* @package UserBundle\Form\Type
*/
class CreditCardAddressType extends AbstractType implements DataMapperInterface
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('country', CountryType::class, [
'constraints' => new NotBlank(),
])
->add('city', null, [ 'constraints' => new NotBlank() ])
->add('street', null, [ 'constraints' => new NotBlank() ])
->add('postalCode', null, [ 'constraints' => new NotBlank() ])
->setDataMapper($this);
}
/**
* Maps properties of some data to a list of forms.
*
* @param CreditCardAddress|null $data Structured data.
* @param FormInterface[]|\Iterator $forms A list of {@link FormInterface}
* instances.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function mapDataToForms($data, $forms)
{
if ($data !== null) {
$forms = iterator_to_array($forms);
$forms['country']->setData($data->getCountry());
$forms['city']->setData($data->getCity());
$forms['street']->setData($data->getStreet());
$forms['postalCode']->setData($data->getPostalCode());
}
}
/**
* Maps the data of a list of forms into the properties of some data.
*
* @param FormInterface[]|\Iterator $forms A list of {@link FormInterface}
* instances.
* @param CreditCardAddress|null $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
$data = new CreditCardAddress(
$forms['country']->getData(),
$forms['city']->getData(),
$forms['street']->getData(),
$forms['postalCode']->getData()
);
}
}
+168
View File
@@ -0,0 +1,168 @@
<?php
namespace UserBundle\Form\Type;
use PaymentBundle\Model\CreditCard;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Constraint;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* Class CreditCardType
*
* @package UserBundle\Form\Type
*/
class CreditCardType extends AbstractType implements DataMapperInterface
{
private static $availableMonth = [
'01',
'02',
'03',
'04',
'05',
'06',
'07',
'08',
'09',
'10',
'11',
'12',
];
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$currentYear = (int) date('Y');
$builder
->add('creditCardNumber', null, [
'constraints' => [
new Constraint\Luhn(),
new Constraint\CardScheme([ 'schemes' => [ 'VISA', 'MASTERCARD', 'AMEX' ] ]),
],
])
->add('CVV', null, [
'constraints' => [
new Constraint\Length([
'min' => 3,
'max' => 4,
'minMessage' => 'Card Verification Code is too short. It should have 3 or 4 characters.',
'maxMessage' => 'Card Verification Code is too long. It should have 3 or 4 characters.',
]),
new Constraint\Type([ 'type' => 'numeric' ]),
],
])
->add('expireMonth', ChoiceType::class, [
'choices' => self::$availableMonth,
'constraints' => new Constraint\NotBlank(),
])
->add('expireYear', ChoiceType::class, [
'choices' => range($currentYear, $currentYear + 10),
'constraints' => new Constraint\NotBlank(),
])
->add('address', CreditCardAddressType::class)
->setDataMapper($this);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'constraints' => new Constraint\Callback([ $this, 'validate' ]),
]);
}
/**
* Maps properties of some data to a list of forms.
*
* @param CreditCard|null $data Structured data.
* @param FormInterface[]|\Iterator $forms A list of {@link FormInterface}
* instances.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function mapDataToForms($data, $forms)
{
if ($data !== null) {
$forms = iterator_to_array($forms);
$forms['creditCardNumber']->setData($data->getNumber());
$forms['CVV']->setData($data->getCvv());
$forms['expireMonth']->setData($data->getExpiresAt()->format('m'));
$forms['expireYear']->setData($data->getExpiresAt()->format('Y'));
$forms['address']->setData($data->getAddress());
}
}
/**
* Maps the data of a list of forms into the properties of some data.
*
* @param FormInterface[]|\Iterator $forms A list of {@link FormInterface}
* instances.
* @param CreditCard|null $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
$expiresAt = \DateTime::createFromFormat(
'Y-m-d',
$forms['expireYear']->getData(). '-'. $forms['expireMonth']->getData() .'-01'
)->setTime(0, 0);
$data = new CreditCard(
'First',
'Second',
$forms['creditCardNumber']->getData(),
$forms['CVV']->getData(),
$expiresAt,
$forms['address']->getData()
);
}
/**
* @param CreditCard|mixed $data Validated payment data.
* @param ExecutionContextInterface $context A ExecutionContextInterface
* instance.
*
* @return void
*/
public function validate($data, ExecutionContextInterface $context)
{
if (($data instanceof CreditCard)
&& ($data->getExpiresAt() < date_create('first day of this month 00:00:00'))) {
$context->buildViolation('Card has already expired')
->atPath('expireMonth')
->addViolation();
}
}
}
@@ -0,0 +1,490 @@
<?php
namespace UserBundle\Form\Type;
use AppBundle\Form\Type\EnumType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use UserBundle\Enum\FontFamilyEnum;
use UserBundle\Enum\ThemeOptionExtractEnum;
use UserBundle\Enum\ThemeOptionsTableOfContentsEnum;
use UserBundle\Enum\ThemeOptionsUserCommentsEnum;
/**
* Class ScheduleType
* @package UserBundle\Form\Type
*/
class NotificationDiffType extends AbstractType
{
/**
* @var array
*/
private static $typesMap = [
'summary' => [
'class' => TextType::class,
'options' => [
'description' => 'Summary text at top of notification.',
],
],
'conclusion' => [
'class' => TextType::class,
'options' => [
'description' => 'Notification conclusion text.',
],
],
'header:imageUrl' => [
'class' => TextType::class,
'options' => [
'description' => 'Path to notification logo image.',
],
],
'header:logoLink' => [
'class' => TextType::class,
'options' => [
'description' => 'Logo href.',
],
],
'header:title' => [
'class' => TextType::class,
'options' => [
'description' => 'Notification title. Enhanced only',
],
],
'fonts:header:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Header font size.',
],
],
'fonts:header:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Header font family.',
],
],
'fonts:header:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should header text bold or not.',
],
],
'fonts:header:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should header text italic or not.',
],
],
'fonts:header:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should header text underlined or not.',
],
],
'fonts:tableOfContents:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Table of contents font size.',
],
],
'fonts:tableOfContents:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Table of contents font family.',
],
],
'fonts:tableOfContents:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should table of contents text bold or not.',
],
],
'fonts:tableOfContents:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should table of contents text italic or not.',
],
],
'fonts:tableOfContents:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should table of contents text underlined or not.',
],
],
'fonts:feedTitle:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Feed title font size.',
],
],
'fonts:feedTitle:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Feed title font family.',
],
],
'fonts:feedTitle:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should feed title text bold or not.',
],
],
'fonts:feedTitle:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should feed text text italic or not.',
],
],
'fonts:feedTitle:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should feed title text underlined or not.',
],
],
'fonts:articleHeadline:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Article headline font size.',
],
],
'fonts:articleHeadline:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Article headline font family.',
],
],
'fonts:articleHeadline:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should article headline text bold or not.',
],
],
'fonts:articleHeadline:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should article headline text italic or not.',
],
],
'fonts:articleHeadline:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should article headline text underlined or not.',
],
],
'fonts:source:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Source font size.',
],
],
'fonts:source:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Source font family.',
],
],
'fonts:source:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should source text bold or not.',
],
],
'fonts:source:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should source text italic or not.',
],
],
'fonts:source:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should source text underlined or not.',
],
],
'fonts:author:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Author font size.',
],
],
'fonts:author:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Author font family.',
],
],
'fonts:author:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should author text bold or not.',
],
],
'fonts:author:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should author text italic or not.',
],
],
'fonts:author:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should author text underlined or not.',
],
],
'fonts:date:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Date font size.',
],
],
'fonts:date:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Date font family.',
],
],
'fonts:date:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should date text bold or not.',
],
],
'fonts:date:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should date text italic or not.',
],
],
'fonts:date:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should date text underlined or not.',
],
],
'fonts:articleContent:size' => [
'class' => NumberType::class,
'options' => [
'description' => 'Article content font size.',
],
],
'fonts:articleContent:family' => [
'class' => EnumType::class,
'options' => [
'enum_class' => FontFamilyEnum::class,
'description' => 'Article content font family.',
],
],
'fonts:articleContent:style:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should article content text bold or not.',
],
],
'fonts:articleContent:style:italic' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should article content text italic or not.',
],
],
'fonts:articleContent:style:underline' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should article content text underlined or not.',
],
],
'content:highlightKeywords:highlight' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should highlights search keywords or not',
],
],
'content:highlightKeywords:bold' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Should highlighted search keywords bold or not.',
],
],
'content:highlightKeywords:color' => [
'class' => ColorType::class,
'options' => [
'description' => 'Highlight color.',
],
],
'content:showInfo:userComments' => [
'class' => EnumType::class,
'options' => [
'enum_class' => ThemeOptionsUserCommentsEnum::class,
'description' => 'How user comments should shown.',
],
],
'content:showInfo:tableOfContents' => [
'class' => EnumType::class,
'options' => [
'enum_class' => ThemeOptionsTableOfContentsEnum::class,
'description' => 'How table of contents should shown.',
],
],
'content:showInfo:sourceCountry' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Show source country or not.',
],
],
'content:showInfo:articleSentiment' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Show article sentiment or not.',
],
],
'content:showInfo:articleCount' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Show article count or not.',
],
],
'content:showInfo:images' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Show images or not.',
],
],
'content:showInfo:sharingOptions' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Show section divider or not.',
],
],
'content:showInfo:sectionDivider' => [
'class' => CheckboxType::class,
'options' => [
'description' => 'Show section divider or not.',
],
],
'content:language' => [
'class' => TextType::class,
'options' => [
'description' => 'Notification language.',
],
],
'content:extract' => [
'class' => EnumType::class,
'options' => [
'enum_class' => ThemeOptionExtractEnum::class,
'description' => 'How article content should be extracted',
],
],
'colors:background:header' => [
'class' => ColorType::class,
'options' => [
'description' => 'Header background color.',
],
],
'colors:background:emailBody' => [
'class' => ColorType::class,
'options' => [
'description' => 'Email body background color.',
],
],
'colors:background:accent' => [
'class' => ColorType::class,
'options' => [
'description' => 'Accent background color.',
],
],
'colors:text:header' => [
'class' => ColorType::class,
'options' => [
'description' => 'Header text color.',
],
],
'colors:text:articleHeadline' => [
'class' => ColorType::class,
'options' => [
'description' => 'Article headline text color.',
],
],
'colors:text:articleContent' => [
'class' => ColorType::class,
'options' => [
'description' => 'Article content text color.',
],
],
'colors:text:author' => [
'class' => ColorType::class,
'options' => [
'description' => 'Author text color.',
],
],
'colors:text:publishDate' => [
'class' => ColorType::class,
'options' => [
'description' => 'Publish date text color.',
],
],
'colors:text:source' => [
'class' => ColorType::class,
'options' => [
'description' => 'Source text color.',
],
],
];
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach (self::$typesMap as $name => $config) {
$class = $config['class'];
$typeOptions = array_merge($config['options'], [ 'required' => false ]);
$builder->add($name, $class, $typeOptions);
}
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
//
// Remove not form field which don't submit.
//
$data = $event->getData();
$form = $event->getForm();
$availableDiffs = array_keys(self::$typesMap);
$submittedDiffs = array_keys(($data === null) ? [] : $data);
$notProvidedDiffs = array_diff($availableDiffs, $submittedDiffs);
foreach ($notProvidedDiffs as $name) {
$form->remove($name);
}
});
}
}
@@ -0,0 +1,111 @@
<?php
namespace UserBundle\Form\Type;
use AppBundle\Form\Transformer\OnlyReverseTransformerTrait;
use CacheBundle\Entity\Feed\AbstractFeed;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\GreaterThan;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Class NotificationSourceType
* @package UserBundle\Form\Type
*/
class NotificationSourceType extends AbstractType implements DataTransformerInterface
{
use OnlyReverseTransformerTrait;
/**
* Map between available type and actual entity class.
*
* @var array
*/
private static $types = [
'feed' => AbstractFeed::class,
];
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', ChoiceType::class, [
'choices' => array_keys(self::$types),
'constraints' => new NotBlank(),
])
->add('id', null, [
'constraints' => [
new NotBlank(),
new GreaterThan([ 'value' => 0 ]),
],
])
->addModelTransformer($this);
}
/**
* Transforms a value from the transformed representation to its original
* representation.
*
* This method is called when {@link Form::submit()} is called to transform
* the requests tainted data into an acceptable format for your data
* processing/model layer.
*
* This method must be able to deal with empty values. Usually this will
* be an empty string, but depending on your implementation other empty
* values are possible as well (such as NULL). The reasoning behind
* this is that value transformers must be chainable. If the
* reverseTransform() method of the first value transformer outputs an
* empty string, the second value transformer must be able to process that
* value.
*
* By convention, reverseTransform() should return NULL if an empty string
* is passed.
*
* @param mixed $data The value in the transformed representation.
*
* @return mixed The value in the original representation
*
* @throws TransformationFailedException When the transformation fails.
*/
public function reverseTransform($data)
{
//
// Unfortunately we can't use here 'getPartialReference' or
// 'getReference' methods for creating partial entity or proxy.
//
// * getPartialReference method is trying to instantiate specified
// entity but for feeds we use base abstract class.
//
// getReference perform query to database for AbstractFeed entity.
// https://github.com/doctrine/doctrine2/blob/v2.5.6/lib/Doctrine/ORM/EntityManager.php#L493
//
// So we just replace 'type' by entity class.
//
if (! isset($data['type'], self::$types[$data['type']])) {
return null;
}
$data['type'] = self::$types[$data['type']];
return $data;
}
}
+206
View File
@@ -0,0 +1,206 @@
<?php
namespace UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use UserBundle\Entity\Notification\Schedule\AbstractNotificationSchedule;
use UserBundle\Entity\Notification\Schedule\DailyNotificationSchedule;
use UserBundle\Entity\Notification\Schedule\MonthlyNotificationSchedule;
use UserBundle\Entity\Notification\Schedule\WeeklyNotificationSchedule;
/**
* Class ScheduleType
* @package UserBundle\Form\Type
*/
class ScheduleType extends AbstractType implements DataMapperInterface
{
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type', ChoiceType::class, [
'choices' => [
'daily',
'weekly',
'monthly',
],
'description' => 'Notification schedule type.',
])
// DailyNotificationSchedule
->add('time', ChoiceType::class, [
'choices' => DailyNotificationSchedule::getAvailableTime(),
'description' => 'Daily schedule time.',
])
->add('days', ChoiceType::class, [
'choices' => DailyNotificationSchedule::getAvailableDays(),
'description' => 'Daily schedule days.',
])
// WeeklyNotificationSchedule
->add('period', ChoiceType::class, [
'choices' => WeeklyNotificationSchedule::getAvailablePeriod(),
'description' => 'Weekly schedule period.',
])
// Common for WeeklyNotificationSchedule and MonthlyNotificationSchedule
->add('day', ChoiceType::class, [
'choices' => [], // Filled on submitting, when we known schedule
// type.
'description' => 'Weekly and monthly schedule day. For weekly: day name. For monthly: numbers from 1 to 31 and word last.',
])
->add('hour', ChoiceType::class, [
'choices' => range(0, 23),
'description' => 'Weekly and monthly schedule hour.',
])
->add('minute', ChoiceType::class, [
'choices' => range(0, 55, 5),
'description' => 'Weekly and monthly schedule minute.',
])
->setDataMapper($this)
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if (isset($data['type'])) {
//
// We should update form only if we got valid type.
//
switch ($data['type']) {
case 'daily':
$form
->remove('period')
->remove('day')
->remove('hour')
->remove('minute');
break;
case 'weekly':
$options = $form->get('day')->getConfig()->getOptions();
$form
->remove('time')
->remove('days')
->remove('day');
$options['choices'] = array_combine(
WeeklyNotificationSchedule::getAvailableDay(),
WeeklyNotificationSchedule::getAvailableDay()
);
$form->add('day', ChoiceType::class, $options);
break;
case 'monthly':
$options = $form->get('day')->getConfig()->getOptions();
$form
->remove('time')
->remove('days')
->remove('period')
->remove('day');
$options['choices'] = array_combine(
MonthlyNotificationSchedule::getAvailableDay(),
MonthlyNotificationSchedule::getAvailableDay()
);
$form->add('day', ChoiceType::class, $options);
break;
}
}
});
}
/**
* Maps properties of some data to a list of forms.
*
* @param AbstractNotificationSchedule|null $data Structured data.
* @param FormInterface[]|\RecursiveIteratorIterator $forms A list of
* {@link FormInterface}
* instances.
*
* @return void
*/
public function mapDataToForms($data, $forms)
{
$forms = iterator_to_array($forms);
switch (true) {
case ($data instanceof DailyNotificationSchedule):
$forms['time']->setData($data->getTime());
$forms['days']->setData($data->getDays());
break;
case ($data instanceof WeeklyNotificationSchedule):
$forms['period']->setData($data->getPeriod());
$forms['day']->setData($data->getDay());
$forms['hour']->setData($data->getHour());
$forms['minute']->setData($data->getMinute());
break;
case ($data instanceof MonthlyNotificationSchedule):
$forms['day']->setData($data->getDay());
$forms['hour']->setData($data->getHour());
$forms['minute']->setData($data->getMinute());
break;
}
}
/**
* Maps the data of a list of forms into the properties of some data.
*
* @param FormInterface[]|\RecursiveIteratorIterator $forms A list of
* {@link FormInterface}
* instances.
* @param AbstractNotificationSchedule|null $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
switch ($forms['type']->getData()) {
case 'daily':
$data = DailyNotificationSchedule::create()
->setTime($forms['time']->getData())
->setDays($forms['days']->getData());
break;
case 'weekly':
$data = WeeklyNotificationSchedule::create()
->setPeriod($forms['period']->getData())
->setDay($forms['day']->getData())
->setHour($forms['hour']->getData())
->setMinute($forms['minute']->getData());
break;
case 'monthly':
$data = MonthlyNotificationSchedule::create()
->setDay($forms['day']->getData())
->setHour($forms['hour']->getData())
->setMinute($forms['minute']->getData());
break;
}
}
}
@@ -0,0 +1,93 @@
<?php
namespace UserBundle\Form\Type;
use AppBundle\Form\Transformer\OnlyReverseTransformerTrait;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class SimpleTimeZoneType
* @package UserBundle\Form\Type
*/
class SimpleTimeZoneType extends AbstractType implements DataTransformerInterface
{
use OnlyReverseTransformerTrait;
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addModelTransformer($this);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('choices', \DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC));
}
/**
* Returns the name of the parent type.
*
* @return string|null The name of the parent type if any, null otherwise
*/
public function getParent()
{
return ChoiceType::class;
}
/**
* Transforms a value from the transformed representation to its original
* representation.
*
* This method is called when {@link Form::submit()} is called to transform
* the requests tainted data into an acceptable format for your data
* processing/model layer.
*
* This method must be able to deal with empty values. Usually this will
* be an empty string, but depending on your implementation other empty
* values are possible as well (such as NULL). The reasoning behind
* this is that value transformers must be chainable. If the
* reverseTransform() method of the first value transformer outputs an
* empty string, the second value transformer must be able to process that
* value.
*
* By convention, reverseTransform() should return NULL if an empty string
* is passed.
*
* @param mixed $timezone The value in the transformed representation.
*
* @return mixed The value in the original representation
*
* @throws TransformationFailedException When the transformation fails.
*/
public function reverseTransform($timezone)
{
return ($timezone !== null) ? new \DateTimeZone($timezone) : null;
}
}
+207
View File
@@ -0,0 +1,207 @@
<?php
namespace UserBundle\Form\Type;
use AppBundle\Form\Transformer\OnlyReverseTransformerTrait;
use CacheBundle\Entity\Feed\AbstractFeed;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use UserBundle\Entity\User;
/**
* Class SourcesType
* @package UserBundle\Form\Type
*/
class SourcesType extends AbstractType implements DataTransformerInterface
{
use OnlyReverseTransformerTrait;
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var TokenStorageInterface
*/
private $storage;
/**
* NotificationSourceType constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface instance.
* @param TokenStorageInterface $storage A TokenStorageInterface instance.
*/
public function __construct(
EntityManagerInterface $em,
TokenStorageInterface $storage
) {
$this->em = $em;
$this->storage = $storage;
}
/**
* Builds the form.
*
* This method is called for each type in the hierarchy starting from the
* top most type. Type extensions can further modify the form.
*
* @see FormTypeExtensionInterface::buildForm()
*
* @param FormBuilderInterface $builder The form builder.
* @param array $options The options.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addModelTransformer($this);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'entry_type' => NotificationSourceType::class,
'allow_add' => true,
'by_reference' => true,
'constraints' => new Callback([ $this, 'validate' ]),
]);
}
/**
* Validate sources.
*
* @param array $sources Array of transformed sources.
* @param ExecutionContextInterface $context A ExecutionContextInterface
* instance.
*
* @return void
*/
public function validate(array $sources, ExecutionContextInterface $context)
{
// todo uncomment and rewrite when analytic is added
// if (($sources['feeds'] === null) || ($sources['charts'] === null)) {
if ($sources['feeds'] === null) {
$context
->buildViolation('Some of sources has invalid id.')
->addViolation();
}
}
/**
* Returns the name of the parent type.
*
* @return string|null The name of the parent type if any, null otherwise.
*/
public function getParent()
{
return CollectionType::class;
}
/**
* Transforms a value from the transformed representation to its original
* representation.
*
* This method is called when {@link Form::submit()} is called to transform
* the requests tainted data into an acceptable format for your data
* processing/model layer.
*
* This method must be able to deal with empty values. Usually this will
* be an empty string, but depending on your implementation other empty
* values are possible as well (such as NULL). The reasoning behind
* this is that value transformers must be chainable. If the
* reverseTransform() method of the first value transformer outputs an
* empty string, the second value transformer must be able to process that
* value.
*
* By convention, reverseTransform() should return NULL if an empty string
* is passed.
*
* @param mixed $data The value in the transformed representation.
*
* @return mixed The value in the original representation
*
* @throws TransformationFailedException When the transformation fails.
*/
public function reverseTransform($data)
{
//
// Split feeds and charts.
// todo uncomment when analytic is added
//
// list($feedsIds, $chartsIds) = \nspl\a\partition(function (array $row) {
// return $row['type'] === AbstractFeed::class;
// }, $data);
//
// Fetch proper entities from database.
//
$feedsIds = \nspl\a\map(\nspl\op\itemGetter('id'), $data);
$feeds = $this->getEntities(AbstractFeed::class, $feedsIds);
//
// We return hash here to simplify further processing.
//
return [
'feeds' => (count($feeds) === count($feedsIds)) ? $feeds : null,
'charts' => null,
];
}
/**
* Check that all specified id is exists.
*
* @param string $class Entity fqcn.
* @param array $ids Array of entities ids.
*
* @return array
*/
private function getEntities($class, array $ids)
{
/** @var EntityRepository $repository */
$repository = $this->em->getRepository($class);
$expr = $this->em->getExpressionBuilder();
$condition = $expr->andX($expr->in('Source.id', ':ids'));
$parameters = [ 'ids' => $ids ];
//
// Filter by user only if we have it.
//
$user = \app\op\invokeIf($this->storage->getToken(), 'getUser');
if ($user instanceof User) {
$condition->add($expr->eq('Source.user', ':user'));
$parameters['user'] = $user->getId();
}
//
// We should get only ids and names of sources 'cause it will be used
// for generating response to client.
//
return $repository->createQueryBuilder('Source')
->select('partial Source.{id, name}')
->where($condition)
->setParameters($parameters)
->getQuery()
->getResult();
}
}
+161
View File
@@ -0,0 +1,161 @@
<?php
namespace UserBundle\Mailer;
use AppBundle\Entity\EmailedDocument;
use Psr\Log\LoggerInterface;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\User;
/**
* Class LoggableMailer
*
* @package UserBundle\Mailer
*/
class LoggableMailer implements MailerInterface
{
/**
* @var MailerInterface
*/
private $mailer;
/**
* @var LoggerInterface
*/
private $logger;
/**
* LoggableMailer constructor.
*
* @param MailerInterface $mailer A MailerInterface instance.
* @param LoggerInterface $logger A LoggerInterface instance.
*/
public function __construct(
MailerInterface $mailer,
LoggerInterface $logger
) {
$this->mailer = $mailer;
$this->logger = $logger;
}
/**
* Send generated password to user.
*
* @param User $user A User entity instance.
* @param string $password Generated password.
*
* @return boolean
*/
public function sendPassword(User $user, $password)
{
$this->logger->info('Send password to '. $user->getId());
return $this->mailer->sendPassword($user, $password);
}
/**
* Send password resetting confirmation email.
*
* @param User $user A User entity instance.
*
* @return boolean
*/
public function sendPasswordResettingConfirmation(User $user)
{
$this->logger->info('Send password resetting confirmation to '. $user->getId());
return $this->mailer->sendPasswordResettingConfirmation($user);
}
/**
* Send notification email to specified addresses.
*
* @param array $addresses Array of recipient emails.
* @param string $subject Notification subject.
* @param string $body Notification body.
*
* @return boolean
*/
public function sendNotificationEmail(array $addresses, $subject, $body)
{
$this->logger->info(
'Send notification to '. implode(', ', $addresses) .' recipients'
);
return $this->mailer->sendNotificationEmail($addresses, $subject, $body);
}
/**
* Send emailed document to recipients.
*
* @param EmailedDocument $emailedDocument A EmailedDocument instance.
*
* @return boolean
*/
public function sendEmailedDocument(EmailedDocument $emailedDocument)
{
$this->logger->info('Send emailed document to '. implode(', ', $emailedDocument->getEmailTo()));
return $this->mailer->sendEmailedDocument($emailedDocument);
}
/**
* Send generated password and confirm url to user.
*
* @param User $user A User entity instance.
* @param string $confirmUrl Generated confirm url.
*
* @return boolean
*/
public function sendVerificationSuccess(User $user, $confirmUrl)
{
$this->logger->info('Send verification success email to '. $user->getId());
return $this->mailer->sendVerificationSuccess($user, $confirmUrl);
}
/**
* Send email about failed verification.
*
* @param User $user A User entity instance.
*
* @return boolean
*/
public function sendVerificationRejected(User $user)
{
$this->logger->info('Send verification success email to '. $user->getId());
return $this->mailer->sendVerificationRejected($user);
}
/**
* Send unsubscribe notification.
*
* @param Notification $notification A Notification entity.
* @param User $user A User entity who unsubscribe from
* specified notification.
*
* @return boolean
*/
public function sendUnsubscribe(Notification $notification, User $user)
{
$this->logger->info('Send unsubscribe notification to '. $notification->getOwner()->getFullName());
return $this->mailer->sendUnsubscribe($notification, $user);
}
/**
* Send mail messages.
*
* Should be called only in command.
*
* @return void
*/
public function flushQueue()
{
$this->logger->info('Spool messages');
$this->mailer->flushQueue();
}
}
+337
View File
@@ -0,0 +1,337 @@
<?php
namespace UserBundle\Mailer;
use AppBundle\Configuration\ConfigurationInterface;
use AppBundle\Configuration\ParametersName;
use AppBundle\Entity\EmailedDocument;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\User;
use FOS\UserBundle\Model\UserInterface;
/**
* Class Mailer
* Default implementation of MailerInterface.
*
* @package UserBundle\Mailer
*/
class Mailer implements MailerInterface
{
/**
* @var \Swift_Mailer
*/
private $mailer;
/**
* @var \Swift_Transport
*/
private $transport;
/**
* @var \Twig_Environment
*/
private $twig;
/**
* @var ConfigurationInterface
*/
private $configuration;
/**
* @var UrlGeneratorInterface
*/
private $urlGenerator;
/**
* @param \Swift_Mailer $mailer A Swift_Mailer instance.
* @param \Swift_Transport $transport A Swift_Transport instance.
* @param \Twig_Environment $twig A \Twig_Environment instance.
* @param ConfigurationInterface $configuration A ConfigurationInterface
* instance.
* @param UrlGeneratorInterface $urlGenerator A UrlGeneratorInterface
* instance.
*/
public function __construct(
\Swift_Mailer $mailer,
\Swift_Transport $transport,
\Twig_Environment $twig,
ConfigurationInterface $configuration,
UrlGeneratorInterface $urlGenerator
) {
$this->mailer = $mailer;
$this->transport = $transport;
$this->twig = $twig;
$this->configuration = $configuration;
$this->urlGenerator = $urlGenerator;
}
/**
* Send generated password to user.
*
* @param User $user A User entity instance.
* @param string $password Generated password.
*
* @return boolean
*/
public function sendPassword(User $user, $password)
{
return $this->sendEmail(
$user->getEmail(),
'Password is changed',
ParametersName::MAIL_PASSWORD,
[
'user' => $user,
'password' => $password,
]
);
}
/**
* Send generated password and confirm url to user.
*
* @param User $user A User entity instance.
* @param string $password A user plain password.
*
* @return boolean
*/
public function sendVerificationSuccess(User $user, $password)
{
return $this->sendEmail(
$user->getEmail(),
'Verification status',
ParametersName::MAIL_VERIFICATION_SUCCESS,
[
'user' => $user,
'password' => $password,
]
);
}
/**
* Send email about failed verification.
*
* @param User $user A User entity instance.
*
* @return boolean
*/
public function sendVerificationRejected(User $user)
{
return $this->sendEmail(
$user->getEmail(),
'Verification status',
ParametersName::MAIL_VERIFICATION_REJECT,
[
'user' => $user,
]
);
}
/**
* Send password resetting confirmation email.
*
* @param User $user A User entity instance.
*
* @return boolean
*/
public function sendPasswordResettingConfirmation(User $user)
{
return $this->sendEmail(
$user->getEmail(),
'Password resetting',
ParametersName::MAIL_RESETTING_CONFIRMATION,
[
'user' => $user,
'confirmationUrl' => $this->urlGenerator->generate('app_index_index', [
'part' => 'auth/reset-password',
'resetting_token' => $user->getConfirmationToken(),
], UrlGeneratorInterface::ABSOLUTE_URL),
]
);
}
/**
* Send notification email to specified addresses.
*
* @param array $addresses Array of recipient emails.
* @param string $subject Notification subject.
* @param string $body Notification body.
*
* @return boolean
*/
public function sendNotificationEmail(array $addresses, $subject, $body)
{
$from = $this->configuration->getParameter(ParametersName::MAILER_ADDRESS);
$fromName = $this->configuration->getParameter(ParametersName::MAILER_SENDER_NAME);
$message = \Swift_Message::newInstance()
->setTo($addresses)
->setFrom($from, $fromName)
->setSubject($subject)
->setBody($body, 'text/html');
return $this->send($message) > 0;
}
/**
* @param string $renderedTemplate
* @param array|string $fromEmail
* @param array|string $toEmail
*/
public function sendEmailMessage(UserInterface $user, $baseurl)
{
// Render the email, use the first line as the subject, and the rest as the body
$parameters = array(
'user' => $user,
'confirmationUrl' => $baseurl.'/auth/confirm-account/'.$user->getConfirmationToken(),
);
$toEmail = (string) $user->getEmail();
$template = $this->twig->load('@FOSUser/Registration/email.txt.twig');
$message = (new \Swift_Message())
->setSubject('Verify your email address')
->setFrom("support@socialhose.io","SOCIALHOSE.IO")
->setTo($toEmail)
->setBody($template->render(
$parameters
));
$this->send($message);
}
/**
* Send emailed document to recipients.
*
* @param EmailedDocument $emailedDocument A EmailedDocument instance.
*
* @return boolean
*/
public function sendEmailedDocument(EmailedDocument $emailedDocument)
{
$subject = $emailedDocument->getSubject() === ''
? 'Emailed document content'
: $emailedDocument->getSubject();
$from = $this->configuration->getParameter(ParametersName::MAILER_ADDRESS);
$fromName = $this->configuration->getParameter(ParametersName::MAILER_SENDER_NAME);
$message = \Swift_Message::newInstance()
->setTo($emailedDocument->getEmailTo())
->setFrom($from, $fromName)
->setReplyTo($emailedDocument->getEmailReplyTo())
->setSubject($subject)
->setBody($emailedDocument->getContent(), 'text/html');
return $this->send($message) > 0;
}
/**
* Send unsubscribe notification.
*
* @param Notification $notification A Notification entity.
* @param User $user A User entity who unsubscribe from
* specified notification.
*
* @return boolean
*/
public function sendUnsubscribe(Notification $notification, User $user)
{
return $this->sendEmail(
$user->getEmail(),
sprintf(
'User %s unsubscribed from %s',
$user->getFullName(),
$notification->getName()
),
ParametersName::MAIL_UNSUBSCRIBE,
[
'user' => $user,
'notification' => $notification,
]
);
}
/**
* Send mail messages.
*
* Should be called only in command.
*
* @return void
*/
public function flushQueue()
{
$transport = $this->mailer->getTransport();
if ($transport instanceof \Swift_Transport_SpoolTransport) {
$spool = $transport->getSpool();
if ($spool instanceof \Swift_MemorySpool) {
$spool->flushQueue($this->transport);
}
}
}
/**
* @param string $recipient Recipient email address.
* @param string $subject Email subject.
* @param string $bodyParameterName Name of configuration parameter which is
* holds body text.
* @param array $parameters Template parameters.
*
* @return boolean
*/
private function sendEmail($recipient, $subject, $bodyParameterName, array $parameters = [])
{
$from = $this->configuration->getParameter(ParametersName::MAILER_ADDRESS);
$fromName = $this->configuration->getParameter(ParametersName::MAILER_SENDER_NAME);
$message = \Swift_Message::newInstance($subject, $this->twig->render(
'UserBundle::email_layout.html.twig',
[
'body' => $this->twig->createTemplate(
$this->configuration->getParameter($bodyParameterName)
)->render($parameters),
]
), 'text/html')
->setTo($recipient)
->setFrom($from, $fromName);
return $this->send($message) > 0;
}
/**
* @param \Swift_Message $message
* @return int
*/
private function send($message)
{
$this->resetTransport();
return $this->mailer->send($message);
}
/**
* Reset SMTP connection for each send so that connections
* are not dropped during long-running processes.
*
* See https://github.com/swiftmailer/swiftmailer/issues/490#issuecomment-72492442
*
* @return void
*/
private function resetTransport()
{
try {
if ($this->transport instanceof \Swift_Transport_AbstractSmtpTransport) {
$this->transport->reset();
}
} catch (\Exception $e) {
try {
$this->transport->stop();
} catch (\Exception $e) {
// pass
}
// $this->transport->start();
}
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace UserBundle\Mailer;
use AppBundle\Entity\EmailedDocument;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\User;
/**
* Interface MailerInterface
* Send emails to user.
*
* @package UserBundle\Mailer
*/
interface MailerInterface
{
/**
* Send generated password to user.
*
* @param User $user A User entity instance.
* @param string $password Generated password.
*
* @return boolean
*/
public function sendPassword(User $user, $password);
/**
* Send email about success verification.
*
* @param User $user A User entity instance.
* @param string $confirmUrl Generated confirm url.
*
* @return boolean
*/
public function sendVerificationSuccess(User $user, $confirmUrl);
/**
* Send email about failed verification.
*
* @param User $user A User entity instance.
*
* @return boolean
*/
public function sendVerificationRejected(User $user);
/**
* Send password resetting confirmation email.
*
* @param User $user A User entity instance.
*
* @return boolean
*/
public function sendPasswordResettingConfirmation(User $user);
/**
* Send notification email to specified addresses.
*
* @param array $addresses Array of recipient emails.
* @param string $subject Notification subject.
* @param string $body Notification body.
*
* @return boolean
*/
public function sendNotificationEmail(array $addresses, $subject, $body);
/**
* Send emailed document to recipients.
*
* @param EmailedDocument $emailedDocument A EmailedDocument instance.
*
* @return boolean
*/
public function sendEmailedDocument(EmailedDocument $emailedDocument);
/**
* Send unsubscribe notification.
*
* @param Notification $notification A Notification entity.
* @param User $user A User entity who unsubscribe from
* specified notification.
*
* @return boolean
*/
public function sendUnsubscribe(Notification $notification, User $user);
/**
* Send mail messages.
*
* Should be called only in command.
*
* @return void
*/
public function flushQueue();
}
@@ -0,0 +1,73 @@
<?php
namespace UserBundle\Manager\Notification\Computer;
use UserBundle\Entity\Notification\Schedule\AbstractNotificationSchedule;
/**
* Class NotificationScheduleComputer
*
* Compute notification schedule date's.
*
* @package UserBundle\Manager\Notification\Computer
*/
class NotificationScheduleComputer implements NotificationScheduleComputerInterface
{
/**
* Compute all notification send date's from current date to specified.
*
* All date's is unique even if notification have some interacting scheduling.
*
* @param AbstractNotificationSchedule[]|array $schedules An array of
* AbstractNotificationSchedule
* instance's.
* @param \DateTime|string $to Computing bound. If parameter
* is string expects format
* accepted by 'modify' method
* of DateTime class.
* @param \DateTimeZone $timeZone Timezone used for each
* date.
*
* @return array[]
*
* @SuppressWarnings(PHPMD.ShortVariable)
*/
public function compute(array $schedules, $to, \DateTimeZone $timeZone)
{
$start = new \DateTime('now', $timeZone);
$to = is_string($to) ? new \DateTime($to) : $to;
if (! $to instanceof \DateTime) {
throw new \InvalidArgumentException('Expects date or valid \DateTime constructor parameter.');
}
$dates = [];
foreach ($schedules as $schedule) {
$tmp = $schedule->computeDates($start, $to);
foreach ($tmp as $date) {
$key = $date->format('Y-m-d H:i');
if (! isset($dates[$key])) {
$dates[$key] = [
'date' => $date,
'ids' => [],
];
}
$dates[$key]['ids'][] = $schedule->getId();
}
}
//
// Now we should convert date's back to current timezone in order to
// simplify further processing.
//
$defaultTZ = new \DateTimeZone(date_default_timezone_get());
return array_map(function (array $row) use ($defaultTZ) {
$row['date']->setTimezone($defaultTZ);
return $row;
}, $dates);
}
}
@@ -0,0 +1,37 @@
<?php
namespace UserBundle\Manager\Notification\Computer;
use UserBundle\Entity\Notification\Schedule\AbstractNotificationSchedule;
/**
* Interface NotificationScheduleComputerInterface
*
* Compute notification schedule date's.
*
* @package UserBundle\Manager\Notification\Computer
*/
interface NotificationScheduleComputerInterface
{
/**
* Compute all notification send date's from current date to specified.
*
* All date's is unique even if notification have some interacting scheduling.
*
* @param AbstractNotificationSchedule[]|array $schedules An array of
* AbstractNotificationSchedule
* instance's.
* @param \DateTime|string $to Computing bound. If parameter
* is string expects format
* accepted by 'modify' method
* of DateTime class.
* @param \DateTimeZone $timeZone Timezone used for each
* date.
*
* @return \DateTime[]
*
* @SuppressWarnings(PHPMD.ShortVariable)
*/
public function compute(array $schedules, $to, \DateTimeZone $timeZone);
}
@@ -0,0 +1,127 @@
<?php
namespace UserBundle\Manager\Notification\Model;
use CacheBundle\Entity\Document;
use Doctrine\Common\Proxy\Exception\UnexpectedValueException;
use IndexBundle\Model\ArticleDocumentInterface;
/**
* Class FeedData
*
* Holds all necessary data for render feed in notifications.
*
* @package UserBundle\Manager\Notification\Model
*/
class FeedData implements \Countable, \IteratorAggregate
{
/**
* Feed name.
*
* @var string
*/
private $name;
/**
* Array of fetched documents.
*
* @var Document[]
*/
private $documents;
/**
* Count of documents.
*
* @var integer
*/
private $documentsCount;
/**
* FeedData constructor.
*
* @param string $name Feed name.
* @param ArticleDocumentInterface[] $documents Array of documents.
*/
public function __construct($name, array $documents)
{
$this->name = $name;
if (! \nspl\a\all($documents, \nspl\f\rpartial(\app\op\isInstanceOf, ArticleDocumentInterface::class))) {
throw new UnexpectedValueException(sprintf(
'All documents should be instances of %s',
ArticleDocumentInterface::class
));
}
$this->documents = \nspl\a\map(\nspl\op\methodCaller('normalize'), $documents);
}
/**
* Get name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Fet documents.
*
* @return ArticleDocumentInterface[]
*/
public function getDocuments()
{
return $this->documents;
}
/**
* Get documents count.
*
* @return integer
*/
public function getDocumentsCount()
{
if ($this->documentsCount === null) {
$this->documentsCount = count($this->documents);
}
return $this->documentsCount;
}
/**
* Count elements of an object
*
* @return integer The custom count as an integer.
*
* The return value is cast to an integer.
*/
public function count()
{
return $this->getDocumentsCount();
}
/**
* Retrieve an external iterator.
*
* @return \Traversable An instance of an object implementing Iterator or Traversable.
*/
public function getIterator()
{
return new \ArrayIterator($this->documents);
}
/**
* @return array
*/
public function toArray()
{
return [
'name' => $this->name,
'documents' => $this->documents,
'documentsCount' => $this->documentsCount,
];
}
}
@@ -0,0 +1,524 @@
<?php
namespace UserBundle\Manager\Notification;
use AppBundle\Configuration\ConfigurationImmutableInterface;
use CacheBundle\Document\Extractor\DocumentContentExtractorInterface;
use CacheBundle\Entity\Comment;
use CacheBundle\Entity\Document;
use CacheBundle\Entity\Feed\AbstractFeed;
use CacheBundle\Entity\Feed\QueryFeed;
use CacheBundle\Feed\Fetcher\Factory\FeedFetcherFactoryInterface;
use CacheBundle\Repository\CommentRepository;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityManagerInterface;
use IndexBundle\Model\ArticleDocumentInterface;
use IndexBundle\SearchRequest\SearchRequestBuilderInterface;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Notification\NotificationThemeOptions;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Enum\ThemeOptionsUserCommentsEnum;
use UserBundle\Manager\Notification\Computer\NotificationScheduleComputer;
use UserBundle\Manager\Notification\Computer\NotificationScheduleComputerInterface;
use UserBundle\Manager\Notification\Model\FeedData;
/**
* Class NotificationManager
* @package UserBundle\Manager\Notification
*/
class NotificationManager implements NotificationManagerInterface
{
/**
* Max row's in single insert.
*/
const BUCKET_SIZE = 100;
/**
* @var EntityManagerInterface
*/
private $em;
/**
* @var NotificationScheduleComputerInterface
*/
private $computer;
/**
* @var FeedFetcherFactoryInterface
*/
private $feedFetcherFactory;
/**
* @var ConfigurationImmutableInterface
*/
private $configuration;
/**
* @var DocumentContentExtractorInterface
*/
private $extractor;
/**
* NotificationManager constructor.
*
* @param EntityManagerInterface $em A EntityManagerInterface
* instance.
* @param FeedFetcherFactoryInterface $feedFetcherFactory A CacheInterface
* instance.
* @param ConfigurationImmutableInterface $configuration A ConfigurationImmutableInterface
* instance.
* @param DocumentContentExtractorInterface $extractor A DocumentContentExtractorInterface
* instance.
*/
public function __construct(
EntityManagerInterface $em,
FeedFetcherFactoryInterface $feedFetcherFactory,
ConfigurationImmutableInterface $configuration,
DocumentContentExtractorInterface $extractor
) {
$this->em = $em;
$this->feedFetcherFactory = $feedFetcherFactory;
$this->configuration = $configuration;
$this->extractor = $extractor;
$this->computer = new NotificationScheduleComputer();
}
/**
* Add new notification or update exists.
*
* @param Notification $notification A Notification instance.
*
* @return void
*/
public function persists(Notification $notification)
{
/**
* @param array|\Traversable $collection Filtered collection.
* @param string $keyMethod Method used for getting unique
* key.
*
* @return array
*/
$unique = static function ($collection, $keyMethod) {
$unique = [];
foreach ($collection as $item) {
$unique[$item->$keyMethod()] = $item;
}
return array_values($unique);
};
//
// We should check all schedule's record's and remove duplicates.
// Same for feeds.
//
$schedules = $unique($notification->getSchedules(), 'getKey');
$feeds = $unique($notification->getFeeds(), 'getId');
$notification
->setSchedules($schedules)
->setFeeds($feeds);
//
// Persist schedule.
//
$this->em->persist($notification);
$this->em->flush();
//
// Get notification id for further processing.
//
$id = $notification->getId();
//
// We should remove previously computed values.
//
$this->removeComputedScheduling($notification->getId());
if ($notification->isCanBeSent(date_create())) {
//
// We should'nt compute render date's if specified notification is can't
// be sent.
//
$timezone = $notification->getTimezone();
$bound = new \DateTime('+ 1 month');
$sendUntil = $notification->getSendUntil();
$bound = (($sendUntil === null) || ($bound <= $sendUntil))
? $bound
: $notification->getSendUntil();
$dates = $this->computer->compute(
$schedules,
$bound->setTimezone($timezone),
$timezone
);
$this->em->getConnection()->transactional(function (Connection $con) use ($id, $dates) {
$bucket = [];
$count = 0;
foreach ($dates as $date) {
$bucket[] = sprintf(
"('%s', %d, '%s')",
$date['date']->format('Y-m-d H:i:s'),
$id,
implode(',', $date['ids'])
);
if (++$count === self::BUCKET_SIZE) {
$con->executeQuery(
'INSERT INTO internal_notification_scheduling (date, notification_id, schedules) VALUES ' .
implode(',', $bucket)
);
$count = 0;
}
}
if ($count > 0) {
$con->executeQuery(
'INSERT INTO internal_notification_scheduling (date, notification_id, schedules) VALUES ' .
implode(',', $bucket)
);
}
});
}
}
/**
* Activate specified notifications.
*
* @param Notification|Notification[] $notifications A activated Notification
* entity instance or array
* of instances.
* @param boolean $active Activate or deactivate
* specified notifications.
*
* @return void
*/
public function activatedToggle($notifications, $active = true)
{
$notifications = $this->normalizeNotifications($notifications);
foreach ($notifications as $notification) {
$notification->setActive($active);
$this->em->persist($notification);
}
$this->em->flush();
}
/**
* Publish specified notifications.
*
* @param Notification|Notification[] $notifications A activated Notification
* entity instance or array
* of instances.
* @param boolean $publish Publish or make private
* specified notifications.
*
* @return void
*/
public function publishedToggle($notifications, $publish = true)
{
$notifications = $this->normalizeNotifications($notifications);
foreach ($notifications as $notification) {
$notification->setPublished($publish);
$this->em->persist($notification);
}
$this->em->flush();
}
/**
* Publish specified notifications.
*
* @param AbstractRecipient $recipient Who try to subscribe or
* unsubscribe from specified
* notifications.
* @param Notification|Notification[] $notifications A Notification entity
* instance or array of
* instances.
* @param boolean $subscribe Subscribe or unsubscribe
* from specified notifications.
*
* @return void
*/
public function subscriptionToggle(AbstractRecipient $recipient, $notifications, $subscribe = true)
{
$notifications = $this->normalizeNotifications($notifications);
if ($subscribe) {
//
// User should not be subscribed to notification twice so we remove
// all notification on which he already subscribed.
//
$checker = \nspl\f\compose(\nspl\f\rpartial('\nspl\a\all', function (AbstractRecipient $checked) use ($recipient) {
return $checked->getId() !== $recipient->getId();
}), \nspl\op\methodCaller('getRecipients'));
$notifications = \nspl\a\filter($checker, $notifications);
$method = \nspl\op\methodCaller('addRecipient', [ $recipient ]);
} else {
$method = \nspl\op\methodCaller('removeRecipient', [ $recipient ]);
}
foreach ($notifications as $notification) {
$method($notification);
$this->em->persist($notification);
}
$this->em->flush();
}
/**
* Remove specified notifications.
*
* @param Notification|Notification[] $notifications A removed Notification entity instance.
*
* @return void
*/
public function remove($notifications)
{
$notifications = $this->normalizeNotifications($notifications);
foreach ($notifications as $notification) {
$this->em->remove($notification);
}
$this->removeComputedScheduling(\nspl\a\map(\nspl\op\methodCaller('getId'), $notifications));
$this->em->flush();
}
/**
* Prepare specified notification for sending.
*
* @param Notification $notification A Notification instance.
*
* @return SendableNotification
*/
public function prepareToSend(Notification $notification)
{
//
// We should sync parameters.
//
$this->configuration->syncParameters();
$config = SendableNotificationConfig::fromConfiguration($this->configuration);
//
// We should not render notification if it shouldn't be rendered.
//
if (! $notification->isCanBeSent(new \DateTime())) {
return new SendableNotification($config, $notification, [], false);
}
//
// Get used notification theme with applied diff.
//
$themeOptions = $notification->getActualThemeOptions();
/**
* @param Document $document A Document entity instance.
*
* @return ArticleDocumentInterface
*/
$commentsFetcherFn = $this->createCommentsFetcherFn($themeOptions, $config);
//
// Now we should get requested number of documents for every notification
// feed.
//
$feeds = [];
/** @var AbstractFeed $feed */
foreach ($notification->getFeeds() as $feed) {
//
// Get all documents ids.
//
$builder = $this->feedFetcherFactory->get($feed)
->createRequestBuilder($feed);
if (! $builder instanceof SearchRequestBuilderInterface) {
return new SendableNotification($config, $notification, [], false);
}
$filterFactory = $builder->getIndex()->getFilterFactory();
$lastSentUTC = clone $notification->getLastSentAt();
$lastSentUTC->setTimezone(new \DateTimeZone('UTC'));
$documents = $builder
//
// We should get documents which were added after last notification
// sending.
//
->addFilter($filterFactory->gte('date_found', $lastSentUTC->format('c')))
//
// Set document limit.
// This limit is configured by super admin.
//
->setLimit($config->documentsPerFeed)
->build()
->execute()
->getDocuments();
//
// Obviously, we should not try to fetch information from database if
// we don't get any documents.
//
if (count($documents) > 0) {
//
// Get documents with necessary fields by ids which we fetch from
// index.
//
// Also we should fetch comments, extract content and convert to
// article instances.
//
$extract = $themeOptions->getContent()->getExtract();
$documents = \nspl\a\map(function (ArticleDocumentInterface $document) use ($commentsFetcherFn, $feed, $extract) {
$id = $document->getId();
return $document
->mapRawData(function (array $data) use ($commentsFetcherFn, $id) {
$data['__comments'] = $commentsFetcherFn($id);
$data['__commentsCount'] = count($data['__comments']);
return $data;
})
->mapNormalizedData(function (array $data) use ($feed, $extract) {
$query = '';
if ($feed instanceof QueryFeed) {
$query = $feed->getQuery()->getRaw();
}
$result =$this->extractor->extract(
$data['content'],
$query,
$extract,
true
);
$data['content'] = $result->getText() . (
mb_strlen($data['content']) < $result->getLength()
? '...'
: ''
);
return $data;
});
}, $documents);
$feeds[] = new FeedData(
$feed->getName(),
$documents
);
}
}
//
// Clear entity manager to avoid memory consuming grow and possible
// side-effects on flush.
//
$this->em->clear();
return new SendableNotification($config, $notification, $feeds);
}
/**
* @param integer|integer[] $notification A Notification id or array of ids.
*
* @return void
*/
private function removeComputedScheduling($notification)
{
$filteredNotifications = array_filter((array) $notification);
if (count($filteredNotifications) > 0) {
$this->em->getConnection()->executeQuery(sprintf('
DELETE FROM internal_notification_scheduling
WHERE notification_id in (%s)
', implode(',', $filteredNotifications)));
}
}
/**
* Normalize 'notifications' parameter.
*
* @param array|object $notifications Passed parameters.
*
* @return Notification[]
*/
private function normalizeNotifications($notifications)
{
if ($notifications instanceof Notification) {
$notifications = [ $notifications ];
}
$checkerFn = function ($object) {
return ! $object instanceof Notification;
};
if (! is_array($notifications) || \nspl\a\any($notifications, $checkerFn)) {
throw new \InvalidArgumentException(sprintf(
'Expects single %s or array of instances',
Notification::class
));
}
return $notifications;
}
/**
* Create proper comment fetcher for current notification.
*
* @param NotificationThemeOptions $options A NotificationThemeOptions
* instance.
* @param SendableNotificationConfig $config A SendableNotificationConfig
* instance.
*
* @return \Closure
*/
private function createCommentsFetcherFn(
NotificationThemeOptions $options,
SendableNotificationConfig $config
) {
$userComments = $options->getContent()->getShowInfo()->getUserComments();
//
// We should not fetch comments if notification don't require they.
//
if (! $userComments->is(ThemeOptionsUserCommentsEnum::no())) {
return function (Document $document) {
return $document;
};
}
/** @var CommentRepository $repository */
$repository = $this->em->getRepository(Comment::class);
//
// Find out which fields do we need for processing current notification.
//
$commentFields = [
'title',
'content',
];
if ($userComments->is(ThemeOptionsUserCommentsEnum::WITH_AUTHOR_DATE)) {
$commentFields[] = 'createdAt';
$commentFields['author'] = [
'firstName',
'lastName',
];
}
//
// Create proper fetcher.
//
return function ($id) use ($repository, $commentFields, $config) {
return $repository->getListForDocument(
$id,
$commentFields,
$config->commentsPerDocument
)->getQuery()->getResult();
};
}
}
@@ -0,0 +1,85 @@
<?php
namespace UserBundle\Manager\Notification;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Recipient\AbstractRecipient;
/**
* Interface NotificationManagerInterface
* @package UserBundle\Manager\Notification
*/
interface NotificationManagerInterface
{
/**
* Add new notification or update exists.
*
* @param Notification $notification A Notification instance.
*
* @return void
*/
public function persists(Notification $notification);
/**
* Activate specified notifications.
*
* @param Notification|Notification[] $notifications A Notification entity
* instance or array of
* instances.
* @param boolean $active Activate or deactivate
* specified notifications.
*
* @return void
*/
public function activatedToggle($notifications, $active = true);
/**
* Publish specified notifications.
*
* @param Notification|Notification[] $notifications A Notification entity
* instance or array of
* instances.
* @param boolean $publish Publish or make private
* specified notifications.
*
* @return void
*/
public function publishedToggle($notifications, $publish = true);
/**
* Publish specified notifications.
*
* @param AbstractRecipient $recipient Who try to subscribe or
* unsubscribe from specified
* notifications.
* @param Notification|Notification[] $notifications A Notification entity
* instance or array of
* instances.
* @param boolean $subscribe Subscribe or unsubscribe
* from specified notifications.
*
* @return void
*/
public function subscriptionToggle(AbstractRecipient $recipient, $notifications, $subscribe = true);
/**
* Remove specified notifications.
*
* @param Notification|Notification[] $notifications A removed Notification
* entity instance or array
* of instances.
*
* @return void
*/
public function remove($notifications);
/**
* Prepare specified notification for sending.
*
* @param Notification $notification A Notification instance.
*
* @return SendableNotification
*/
public function prepareToSend(Notification $notification);
}
@@ -0,0 +1,219 @@
<?php
namespace UserBundle\Manager\Notification;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Notification\NotificationSendHistory;
use UserBundle\Entity\Notification\Schedule\AbstractNotificationSchedule;
use UserBundle\Entity\Notification\ThemeOption\ThemeOptionHeader;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Entity\Recipient\GroupRecipient;
use UserBundle\Entity\Recipient\PersonRecipient;
use UserBundle\Enum\ThemeTypeEnum;
use UserBundle\Mailer\MailerInterface;
use UserBundle\Manager\Notification\Model\FeedData;
use UserBundle\Repository\PersonRecipientRepository;
/**
* Class SendableNotification
* @package UserBundle\Manager\Notification
*/
class SendableNotification
{
const TEMPLATE = 'UserBundle:Notification:index.html.twig';
/**
* @var SendableNotificationConfig
*/
private $config;
/**
* @var Notification
*/
private $notification;
/**
* Sent data.
*
* @var FeedData[]
*/
private $data;
/**
* @var boolean
*/
private $success;
/**
* SendableNotification constructor.
*
* @param SendableNotificationConfig $config A SendableNotificationConfig
* instance.
* @param Notification $notification A Notification instance.
* @param FeedData[]|array $data Sent data.
* @param boolean $success Flag, true if we successfully
* get data.
*/
public function __construct(
SendableNotificationConfig $config,
Notification $notification,
array $data,
$success = true
) {
$this->config = $config;
$this->notification = $notification;
$this->data = $data;
$this->success = $success;
}
/**
* Send notification.
*
* @param MailerInterface $mailer A MailerInterface instance.
* @param EngineInterface $templating A templating EngineInterface
* instance.
* @param EntityManagerInterface $em A EntityManagerInterface
* instance.
* @param integer[]|array $schedules Array of schedules entity ids.
*
* @return boolean
*/
public function send(
MailerInterface $mailer,
EngineInterface $templating,
EntityManagerInterface $em,
array $schedules
) {
// if (! $this->success) {
// return false;
// }
$body = $this->render($templating);
if ($body === null) {
return false;
}
//
// Get recipient's emails.
//
$recipients = $this->notification->getRecipients()->map(function (AbstractRecipient $recipient) use ($em) {
$emails = null;
if ($recipient instanceof GroupRecipient) {
/** @var PersonRecipientRepository $repository */
$repository = $em->getRepository(PersonRecipient::class);
$emails = $repository->getEmailsByGroup($recipient->getId());
} elseif ($recipient instanceof PersonRecipient) {
$emails = $recipient->getEmail();
}
return $emails;
})->toArray();
// $recipients = array_filter(\Functional\flatten($recipients));
$recipients = array_filter(\nspl\a\flatten($recipients));
//
// Send notification and flush queue.
//
$sent = $mailer->sendNotificationEmail(
$recipients,
$this->notification->getSubject(),
$body
);
$mailer->flushQueue();
if ($sent) {
//
// We should change date of last notification sending and store it to
// history.
//
/** @var Notification $notificationReference */
$notificationReference = $em->getReference(Notification::class, $this->notification->getId());
$schedules = $em->getRepository(AbstractNotificationSchedule::class)
->findBy([ 'id' => $schedules ]);
$schedules = array_map(function (AbstractNotificationSchedule $schedule) {
$historySchedule = clone $schedule;
$historySchedule->setNotification(null);
return $historySchedule;
}, $schedules);
$notificationReference->setLastSentAt(new \DateTime());
$history = new NotificationSendHistory(
$notificationReference,
$schedules
);
$em->persist($notificationReference);
$em->persist($history);
$em->flush();
//
// Remove old history.
//
$em->createQueryBuilder()
->delete()
->from(NotificationSendHistory::class, 'History')
->where('History.date <= :date')
->setParameter('date', date_create()->modify($this->config->historyStorePeriod))
->getQuery()
->execute();
}
return $sent;
}
/**
* Render notification template.
*
* @param EngineInterface $templating A templating EngineInterface instance.
*
* @return string|null
*/
public function render(EngineInterface $templating)
{
// if (! $this->success) {
// return null;
// }
$body = null;
if (count($this->data) > 0) {
//
// Render proper notification template.
//
$isEnhanced = $this->notification->getThemeType()->is(ThemeTypeEnum::ENHANCED);
$themeOptions = $this->notification->getActualThemeOptions();
//
// Set default logo image for enhanced layout.
//
$header = $themeOptions->getHeader();
if ($isEnhanced && ($header->getImageUrl() === '')) {
$header->setImageUrl(ThemeOptionHeader::DEFAULT_IMAGE);
} elseif (! $isEnhanced && ($header->getImageUrl() === ThemeOptionHeader::DEFAULT_IMAGE)) {
$header->setImageUrl('');
}
$body = $templating->render(self::TEMPLATE, [
'feeds' => $this->data,
'theme' => [
'options' => $themeOptions->toArray(),
'type' => $this->notification->getThemeType()->getValue(),
],
]);
} elseif ($this->notification->isSendWhenEmpty()) {
//
// Render empty notification template if notification allow empty sending.
//
$body = $this->config->emptyMessage;
}
return $body;
}
}
@@ -0,0 +1,121 @@
<?php
namespace UserBundle\Manager\Notification;
use AppBundle\Configuration\ConfigurationImmutableInterface;
use AppBundle\Configuration\ParametersName;
/**
* Class SendableNotification
* @package UserBundle\Manager\Notification
*
* @property integer documentsPerFeed()
* @property integer commentsPerDocument()
* @property integer extractContextualCharacter()
* @property integer extractFromStartCharacter()
* @property string emptyMessage()
* @property integer historyStorePeriod()
*/
class SendableNotificationConfig
{
/**
* @var integer[]
*/
private $config;
/**
* SendableNotificationConfig constructor.
*
* @param integer $documentsPerFeed Max number of documents showing
* in each feed.
* @param integer $commentsPerDocument Max number of comments showing
* in each document.
* @param integer $extractContextualCharacter Number of character before and
* after extracted document content.
* Used when notification field
* `articleExtracts` has value
* 'contextual'.
* @param integer $extractFromStartCharacter Number of character from beginning
* of document content. Used when
* notification field `articleExtracts`
* has value 'start'.
* @param string $emptyMessage Empty notification message.
* Used when notification field
* `sendWhenEmpty` set to true.
* @param integer $historyStorePeriod How long we should store render
* history.
*/
public function __construct(
$documentsPerFeed,
$commentsPerDocument,
$extractContextualCharacter,
$extractFromStartCharacter,
$emptyMessage,
$historyStorePeriod
) {
$this->config = [
'documentsPerFeed' => $documentsPerFeed,
'commentsPerDocument' => $commentsPerDocument,
'extractContextualCharacter' => $extractContextualCharacter,
'extractFromStartCharacter' => $extractFromStartCharacter,
'emptyMessage' => $emptyMessage,
'historyStorePeriod' => $historyStorePeriod,
];
}
/**
* @param ConfigurationImmutableInterface $configuration A ConfigurationImmutableInterface
* instance.
*
* @return static
*/
public static function fromConfiguration(ConfigurationImmutableInterface $configuration)
{
return new static(
$configuration->getParameter(ParametersName::NOTIFICATION_DOCUMENT_PER_FEED),
$configuration->getParameter(ParametersName::NOTIFICATION_COMMENTS_PER_DOCUMENT),
0, // TODO add proper parameter.
$configuration->getParameter(ParametersName::NOTIFICATION_START_EXTRACT_LENGTH),
$configuration->getParameter(ParametersName::NOTIFICATION_EMPTY_MESSAGE),
$configuration->getParameter(ParametersName::NOTIFICATION_SEND_HISTORY_MODIFY)
);
}
/**
* @param string $name Parameter name.
*
* @return integer
*/
public function __get($name)
{
if (isset($this->{$name})) {
return $this->config[$name];
}
throw new \InvalidArgumentException('Unknown parameter name '. $name);
}
/**
* @param string $name Parameter name.
* @param mixed $value Parameter value.
*
* @return void
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __set($name, $value)
{
throw new \LogicException('SendableNotificationConfig is immutable.');
}
/**
* @param string $name Parameter name.
*
* @return boolean
*/
public function __isset($name)
{
return isset($this->config[$name]);
}
}
@@ -0,0 +1,99 @@
<?php
namespace UserBundle\Manager\User;
use CacheBundle\Entity\Category;
use FOS\UserBundle\Doctrine\UserManager as BaseManager;
use FOS\UserBundle\Model\UserInterface;
use UserBundle\Entity\Recipient\PersonRecipient;
use UserBundle\Entity\User;
use UserBundle\Enum\UserRoleEnum;
/**
* Class UserManager
*
* @package UserBundle\Manager\User
*/
class UserManager extends BaseManager implements UserManagerInterface
{
/**
* Deletes a user.
*
* @param UserInterface $user A UserInterface entity instance.
*
* @return void
*/
public function deleteUser(UserInterface $user)
{
if (! $user instanceof User) {
throw new \InvalidArgumentException('Expects instance of ' . User::class);
}
$this->objectManager->remove($user->getRecipient());
$user->setRecipient(null);
$billingSubscriptions = $user->getBillingSubscription();
$billingSubscriptions->removeUser($user);
$user->setBillingSubscription(null);
if ($billingSubscriptions->isOwnedBy($user)) {
$this->objectManager->remove($billingSubscriptions);
}
parent::deleteUser($user);
}
/**
* Updates a user.
*
* @param UserInterface $user A UserInterface entity instance.
* @param boolean $andFlush Flush data to storage.
*
* @return void
*/
public function updateUser(UserInterface $user, $andFlush = true)
{
if (! $user instanceof User) {
throw new \InvalidArgumentException('Expects instance of ' . User::class);
}
if (($user->getId() === null)
&& ($user->hasRole(UserRoleEnum::SUBSCRIBER)
|| $user->hasRole(UserRoleEnum::MASTER_USER))
) {
//
// For all new users we create recipient with their emails.
//
$recipient = PersonRecipient::createFromUser($user)
->setAssociatedUser($user)
->setOwner($user);
$this->objectManager->persist($recipient);
}
parent::updateUser($user, $andFlush);
}
/**
* @param User $user A Confirmed user instance.
*
* @return string New password.
*/
public function confirmUser(User $user)
{
Category::createMainCategory($user);
Category::createSharedCategory($user);
Category::createTrashCategory($user);
$user
->setVerified()
->setEnabled(true)
->generatePassword();
$password = $user->getPlainPassword();
$this->updateUser($user);
return $password;
}
}
@@ -0,0 +1,22 @@
<?php
namespace UserBundle\Manager\User;
use FOS\UserBundle\Model\UserManagerInterface as BaseManagerInterface;
use UserBundle\Entity\User;
/**
* Class UserManager
*
* @package UserBundle\Manager\User
*/
interface UserManagerInterface extends BaseManagerInterface
{
/**
* @param User $user A Confirmed user instance.
*
* @return User
*/
public function confirmUser(User $user);
}
@@ -0,0 +1,212 @@
<?php
namespace UserBundle\Repository;
use AppBundle\Model\SortingOptions;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\QueryBuilder;
use UserBundle\Entity\Recipient\GroupRecipient;
use UserBundle\Entity\User;
use UserBundle\Enum\StatusFilterEnum;
use UserBundle\Utils\AdditionalConditions;
/**
* GroupRecipientRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class GroupRecipientRepository extends EntityRepository
{
/**
* Get available for specified user.
*
* @param User $user A User entity instance.
*
* @return QueryBuilder
*/
public function getAvailableForUser(User $user)
{
return $this->createQueryBuilder('Grp')
->where('Grp.owner = :user')
->setParameter('user', $user->getId());
}
/**
* @param integer $user A User entity id.
* @param SortingOptions $sortingOptions A SortingOptions instance.
* @param string $nameFilter Filter recipient groups by name.
*
* @return QueryBuilder
*/
public function getQueryBuilderForUser(
$user,
SortingOptions $sortingOptions,
$nameFilter = ''
) {
$sortField = $this->resolveSortField($sortingOptions);
$expr = $this->_em->getExpressionBuilder();
$condition = $expr->andX($expr->eq('Grp.owner', ':user'));
$parameters = new ArrayCollection([ new Parameter('user', $user) ]);
if ($nameFilter !== '') {
$condition->add($expr->like('Grp.name', ':filter'));
$parameters[] = new Parameter('filter', '%'. $nameFilter .'%');
}
return $this->createQueryBuilder('Grp')
->where($condition)
->setParameters($parameters)
->orderBy($sortField, $sortingOptions->getSortDirection());
}
/**
* @param integer $user A User entity id.
* @param integer $person A PersonRecipient entity
* id.
* @param StatusFilterEnum $statusFilter A StatusFilterEnum instance.
* @param SortingOptions $sortingOptions A SortingOptions instance.
* @param string $filter Filter recipient groups
* by name.
* @param AdditionalConditions $additionalConditions A AdditionalConditions
* instance.
*
* @return QueryBuilder
*/
public function getQueryBuilderForPerson(
$user,
$person,
StatusFilterEnum $statusFilter,
SortingOptions $sortingOptions,
$filter,
AdditionalConditions $additionalConditions
) {
$sortField = $this->resolveSortField($sortingOptions);
$expr = $this->_em->getExpressionBuilder();
$condition = $expr->andX($expr->eq('Grp.owner', ':user'));
$parameters = new ArrayCollection([
new Parameter('user', $user),
new Parameter('person', $person),
]);
$parameters = $additionalConditions->addToParameters($parameters);
if ($filter !== '') {
$condition->add($expr->like('Grp.name', ':filter'));
$parameters[] = new Parameter('filter', '%'. $filter .'%');
}
$qb = $this->createQueryBuilder('Grp');
switch ($statusFilter->getValue()) {
//
// Show only not enrolled groups.
//
case StatusFilterEnum::NO:
//
// Select groups ids which has association with specified recipient
// and remove them from results.
//
$subCondition = $expr->andX(
$expr->eq('_Person.id', ':person'),
$expr->eq('_Grp.owner', ':user')
);
$subCondition = $additionalConditions->addToConditions($subCondition, '_Grp');
$subDql = $this->createQueryBuilder('_Grp')
->select('_Grp.id')
->leftJoin('_Grp.recipients', '_Person')
->where($subCondition)
->getDQL();
$condition->add($expr->notIn('Grp', $subDql));
$qb->addSelect('0 AS enrolled');
break;
//
// Fetch only enrolled groups.
//
case StatusFilterEnum::YES:
$condition->add($expr->eq('Person.id', ':person'));
$condition = $additionalConditions->addToConditions($condition, 'Grp');
$qb
->join('Grp.recipients', 'Person')
->addSelect('1 AS enrolled');
break;
//
// If we not apply filters we should check in which groups specified
// recipient is enrolled.
//
case StatusFilterEnum::ALL:
$countCondition = $expr->andX(
$expr->eq('_Person.id', ':person'),
$expr->eq('_Grp.id', 'Grp.id')
);
$countCondition = $additionalConditions->addToConditions($countCondition, 'Grp');
$countDQL = $this->createQueryBuilder('_Grp')
->select('COUNT(_Grp.id)')
->join('_Grp.recipients', '_Person')
->where($countCondition)
->getDQL();
$qb->addSelect("(CASE WHEN ({$countDQL}) > 0 THEN 1 ELSE 0 END) AS enrolled");
break;
}
return $qb
->where($condition)
->setParameters($parameters)
->orderBy($sortField, $sortingOptions->getSortDirection());
}
/**
* Get group recipient by id.
*
* @param integer $id A GroupRecipient entity id.
*
* @return GroupRecipient|null
*/
public function get($id)
{
return $this->createQueryBuilder('Grp')
->where('Grp.id = :id')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
/**
* @param SortingOptions $sortingOptions A SortingOptions instance.
*
* @return string
*/
private function resolveSortField(SortingOptions $sortingOptions)
{
$sortField = $sortingOptions->getFieldName();
switch ($sortField) {
case 'active':
case 'name':
case 'recipientsNumber':
$sortField = "Grp.{$sortField}";
break;
case 'creationDate':
$sortField = 'Grp.createdAt';
break;
default:
throw new \InvalidArgumentException("Unknown field name '{$sortField}'.");
}
return $sortField;
}
}
@@ -0,0 +1,439 @@
<?php
namespace UserBundle\Repository;
use AppBundle\Model\SortingOptions;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\QueryBuilder;
use UserBundle\Entity\Notification\Notification;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Entity\Subscription\AbstractSubscription;
use UserBundle\Entity\User;
use UserBundle\Enum\NotificationTypeEnum;
use UserBundle\Enum\StatusFilterEnum;
/**
* NotificationRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class NotificationRepository extends EntityRepository
{
/**
* @param integer $id A Notification entity instance.
* @param NotificationTypeEnum $type A NotificationTypeEnum instance.
*
* @return Notification|null
*/
public function get($id, NotificationTypeEnum $type = null)
{
$expr = $this->_em->getExpressionBuilder();
$condition = $expr->andX(
$expr->eq('Notification.id', ':id')
);
$parameters = new ArrayCollection([
new Parameter('id', $id),
]);
if ($type !== null) {
$condition->add($expr->eq(
'Notification.notificationType',
':type'
));
$parameters[] = new Parameter('type', (string) $type);
}
return $this->createQueryBuilder('Notification')
// ->addSelect('Feed, Chart, Owner, Recipient')
->addSelect('Feed, Owner, Recipient')
->leftJoin('Notification.feeds', 'Feed')
// ->leftJoin('Notification.charts', 'Chart')
->leftJoin('Notification.owner', 'Owner')
->leftJoin('Notification.recipients', 'Recipient')
->where($condition)
->setParameters($parameters)
->getQuery()
->getOneOrNullResult();
}
/**
* Get notification instance for sending.
*
* @param integer $id A Notification entity id.
*
* @return Notification|null
*/
public function getForSending($id)
{
return $this->createQueryBuilder('Notification')
->addSelect(
'partial Feed.{id, name}, Owner, Recipient',
'partial Schedule.{id}'
)
->leftJoin('Notification.feeds', 'Feed')
->leftJoin('Notification.owner', 'Owner')
->leftJoin('Notification.recipients', 'Recipient')
->leftJoin('Notification.schedules', 'Schedule')
->where('Notification.id = :id')
->setParameter('id', $id)
->getQuery()
->getOneOrNullResult();
}
/**
* @return QueryBuilder
*/
public function getQueryBuilderForSubscription()
{
return $this->createQueryBuilder('Notification')
->addSelect('partial Owner.{id, email}')
->join('Notification.owner', 'Owner');
}
/**
* Get query builder for fetching available notifications for forms.
*
* @param User $user A User entity instance, who ask.
*
* @return QueryBuilder
*/
public function getQueryBuilderForForm(User $user)
{
$expr = $this->_em->getExpressionBuilder();
return $this->createQueryBuilder('Notification')
->where($expr->orX(
$expr->eq('Notification.owner', ':user'),
$expr->andX(
$expr->eq('Notification.published', 1),
$expr->eq('Notification.billingSubscription', ':subscription')
)
))
->setParameter('user', $user->getId())
->setParameter('subscription', $user->getBillingSubscription()->getId());
}
/**
* @param AbstractRecipient $recipient Requested AbstractRecipient
* entity instance.
* @param User $owner A User entity id.
* @param SortingOptions $sortingOptions A SortingOptions instance.
* @param StatusFilterEnum $statusFilter A StatusFilterEnum instance.
* @param string $nameFilter Part of Notification entity name
* for filtering.
*
* @return QueryBuilder
*/
public function getQueryBuilderForRecipient(
AbstractRecipient $recipient,
User $owner,
SortingOptions $sortingOptions,
StatusFilterEnum $statusFilter,
$nameFilter
) {
$expr = $this->_em->getExpressionBuilder();
$sortField = $sortingOptions->getFieldName();
$qb = $this->getQueryBuilderForForm($owner)
->addSelect(
'RecipientList',
'Schedule',
'Owner'
)
->join('Notification.owner', 'Owner')
->leftJoin('Notification.recipients', 'RecipientList')
->leftJoin('Notification.schedules', 'Schedule')
->orderBy($sortField, $sortingOptions->getSortDirection())
->setParameter('recipient', $recipient->getId());
if ($nameFilter !== '') {
$qb
->andWhere($expr->like('Notification.name', ':name'))
->setParameter('name', '%'. $nameFilter .'%');
}
switch ($statusFilter->getValue()) {
//
// Select notification ids which has association with specified recipient
// and remove them from results.
//
case StatusFilterEnum::NO:
$subDql = $this->createQueryBuilder('_Notification')
->select('_Notification.id')
->join('_Notification.recipients', '_Recipient', Join::WITH, '_Recipient.id = :recipient')
->getDQL();
$qb
->addSelect('0 AS subscribed')
->andWhere($expr->notIn('Notification.id', $subDql));
break;
//
// Fetch only subscribed notifications.
//
case StatusFilterEnum::YES:
$qb
->addSelect('1 AS subscribed')
->join('Notification.recipients', 'Recipient', Join::WITH, 'Recipient.id = :recipient');
break;
case StatusFilterEnum::ALL:
$countDQL = $this->createQueryBuilder('_Notification')
->select('COUNT(_Notification.id)')
->join('_Notification.recipients', '_Recipient', Join::WITH, '_Recipient.id = :recipient')
->where('_Notification.id = Notification.id')
->getDQL();
$qb
->addSelect("({$countDQL}) AS subscribed")
->join('Notification.recipients', 'Recipient');
break;
}
$qb = $this->resolveSortingOptions($qb, $sortingOptions);
return $qb;
}
/**
* Get query builder for notifications.
*
* @param SortingOptions $sortingOptions A SortingOptions instance.
* @param User $owner A User entity instance or null.
* @param boolean $onlyPublished Fetch only published notifications.
* @param string $nameFilter Filter notification by name.
*
* @return QueryBuilder
*/
public function getQueryBuilder(
SortingOptions $sortingOptions,
User $owner,
$onlyPublished = false,
$nameFilter = null
) {
$expr = $this->_em->getExpressionBuilder();
$condition = $expr->andX(
$expr->eq('Notification.billingSubscription', ':subscription'),
$expr->eq('Notification.owner', ':owner')
);
$parameters = new ArrayCollection([
new Parameter('subscription', $owner->getBillingSubscription()->getId()),
new Parameter('recipient', $owner->getRecipient()->getId()),
new Parameter('owner', $owner->getId()),
]);
if ($onlyPublished) {
$condition->add($expr->eq('Notification.published', 1));
}
$qb = $this->createQueryBuilder('Notification')
->addSelect(
'Recipient',
'Schedule',
'Owner',
'(CASE WHEN Recipient.id = :recipient THEN 1 ELSE 0 END) AS subscribed'
)
->join('Notification.owner', 'Owner')
->leftJoin('Notification.recipients', 'Recipient')
->leftJoin('Notification.schedules', 'Schedule')
->where($condition)
->setParameters($parameters);
if ($nameFilter !== null) {
$qb
->andWhere($expr->like('Notification.name', ':name'))
->setParameter('name', '%'. $nameFilter .'%');
}
return $this->resolveSortingOptions(
$qb,
$sortingOptions
);
}
/**
* @param QueryBuilder $qb A QueryBuilder instance.
* @param SortingOptions $sortingOptions A SortingOptions instance.
*
* @return QueryBuilder
*/
private function resolveSortingOptions(QueryBuilder $qb, SortingOptions $sortingOptions)
{
$sortField = $sortingOptions->getFieldName();
switch ($sortField) {
case 'published':
case 'active':
case 'sourcesCount':
case 'owner':
case 'name':
$sortField = "Notification.{$sortField}";
break;
case 'type':
$sortField = 'Notification.notificationType';
break;
default:
throw new \InvalidArgumentException("Unknown field name '{$sortField}'.");
}
return $qb
->orderBy($sortField, $sortingOptions->getSortDirection());
}
/**
* Get count notifications for user.
*
* @param SortingOptions $sortingOptions A SortingOptions instance.
*
* @return \Doctrine\ORM\QueryBuilder
*/
public function computeUserNotificationsCount(SortingOptions $sortingOptions)
{
$sortField = $sortingOptions->getFieldName();
return $this->createQueryBuilder('Notification')
->select(
'COUNT(Notification.id) as notifications',
'IDENTITY(Notification.owner) as id',
'Owner.email as name',
'\'owner\' as type'
)
->join('Notification.owner', 'Owner')
->orderBy($sortField, $sortingOptions->getSortDirection())
->groupBy('Notification.owner')
->getQuery()
->execute();
}
/**
* Get count notifications for recipient.
*
* @param SortingOptions $sortingOptions A SortingOptions instance.
*
* @return \Doctrine\ORM\QueryBuilder
*/
public function computeRecipientNotificationsCount(SortingOptions $sortingOptions)
{
$sortField = $sortingOptions->getFieldName();
return $this->createQueryBuilder('Notification')
->select(
'COUNT(Notification.id) as notifications',
'Recipient.id as id',
'Recipient.name as name',
'\'recipient\' as type'
)
->leftJoin('Notification.recipients', 'Recipient')
->orderBy($sortField, $sortingOptions->getSortDirection())
->groupBy('Recipient.id')
->getQuery()
->execute();
}
/**
* Get count notifications for feed.
*
* @param SortingOptions $sortingOptions A SortingOptions instance.
*
* @return \Doctrine\ORM\QueryBuilder
*/
public function getCountFeedNotifications(SortingOptions $sortingOptions)
{
$sortField = $sortingOptions->getFieldName();
return $this->createQueryBuilder('Notification')
->select(
'COUNT(Notification.id) as notifications',
'Feed.id as id',
'Feed.name as name',
'\'feed\' as type'
)
->leftJoin('Notification.feeds', 'Feed')
->orderBy($sortField, $sortingOptions->getSortDirection())
->groupBy('Feed.id')
->getQuery()
->execute();
}
/**
* Get query builder for all notifications.
*
* @param SortingOptions $sortingOptions A SortingOptions instance.
* @param AbstractSubscription $subscription A AbstractSubscription instance.
*
* @return QueryBuilder
*/
public function getNotificationsAllQueryBuilder(
SortingOptions $sortingOptions,
AbstractSubscription $subscription
) {
return $this->resolveSortingOptions(
$this->createQueryBuilder('Notification')
->addSelect(
'Recipient',
'Schedule',
'Owner'
)
->join('Notification.owner', 'Owner')
->leftJoin('Notification.recipients', 'Recipient')
->leftJoin('Notification.schedules', 'Schedule')
->where('Notification.billingSubscription = :subscription')
->setParameter('subscription', $subscription->getId())
->groupBy('Notification.id'),
$sortingOptions
);
}
/**
* @param SortingOptions $sortingOptions A SortingOptions instance.
* @param string $typeFilter One of available filter.
* @param string $filterId Filter id.
* @param User $user Filter owner.
*
* @return QueryBuilder
*/
public function getQueryBuilderForFilter(
SortingOptions $sortingOptions,
$typeFilter,
$filterId,
User $user
) {
$sortField = $sortingOptions->getFieldName();
$qb = $this->createQueryBuilder('Notification')
->addSelect(
'RecipientList',
'Schedule',
'Owner'
)
->leftJoin('Notification.owner', 'Owner')
->leftJoin('Notification.recipients', 'RecipientList')
->leftJoin('Notification.schedules', 'Schedule')
->leftJoin('Notification.billingSubscription', 'Subscription', Join::WITH, 'Subscription.masterAccounts =:masterUser')
->setParameter('masterUser', $user->getId())
->orderBy($sortField, $sortingOptions->getSortDirection());
switch ($typeFilter) {
case 'owner':
$qb->andWhere('Notification.owner =:owner')
->setParameter(':owner', $filterId);
break;
case 'recipient':
$qb->join('Notification.recipients', 'Recipient', Join::WITH, 'Recipient.id = :recipient')
->setParameter('recipient', $filterId);
break;
case 'feed':
$qb->join('Notification.feeds', 'Feed', Join::WITH, 'Feed.id = :feed')
->setParameter('feed', $filterId);
break;
}
$qb = $this->resolveSortingOptions($qb, $sortingOptions);
return $qb;
}
}
@@ -0,0 +1,103 @@
<?php
namespace UserBundle\Repository;
use AppBundle\Model\SortingOptions;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use UserBundle\Entity\Notification\Schedule\AbstractNotificationSchedule;
use UserBundle\Entity\Recipient\AbstractRecipient;
use UserBundle\Enum\NotificationTypeEnum;
/**
* NotificationRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class NotificationSendHistoryRepository extends EntityRepository
{
/**
* @param integer $notification A Notification entity id.
*
* @return \Doctrine\ORM\QueryBuilder
*/
public function getListForNotification($notification)
{
return $this->createQueryBuilder('History')
->select('History.date')
->where('History.notification = :notification')
->setParameter('notification', $notification);
}
/**
* @param AbstractRecipient $recipient A AbstractRecipient entity instance.
* @param SortingOptions $sortingOptions A SortingOptions instance.
* @param string $typeFilter Notification type filter.
*
* @return QueryBuilder
*/
public function getListForRecipient(
AbstractRecipient $recipient,
SortingOptions $sortingOptions,
$typeFilter
) {
$qb = $this->createQueryBuilder('History')
->select(
'partial History.{id, date}',
'partial Notification.{id, name, notificationType}',
'Schedule'
)
->join('History.notification', 'Notification')
->join('History.schedules', 'Schedule')
->join('Notification.recipients', 'Recipient', Join::WITH, 'Recipient.id = :recipient')
->setParameter('recipient', $recipient->getId());
$sortField = $sortingOptions->getFieldName();
switch ($sortField) {
case 'name':
$sortField = 'Notification.name';
break;
case 'type':
$sortField = 'Notification.notificationType';
break;
case 'scheduleTime':
$countDql = $this->_em->createQueryBuilder()
->select('COUNT(_Schedule.id)')
->from(AbstractNotificationSchedule::class, '_Schedule')
->where('_Schedule.history = History.id')
->getDQL();
$qb->addSelect("($countDql) AS HIDDEN scheduleCount");
$sortField = 'scheduleCount';
break;
case 'sentTime':
$sortField = 'History.date';
break;
default:
throw new \InvalidArgumentException("Unknown field name '{$sortField}'.");
}
switch ($typeFilter) {
case NotificationTypeEnum::ALERT:
$qb
->andWhere('Notification.notificationType = :type')
->setParameter('type', NotificationTypeEnum::ALERT);
break;
case NotificationTypeEnum::NEWSLETTER:
$qb
->andWhere('Notification.notificationType = :type')
->setParameter('type', NotificationTypeEnum::NEWSLETTER);
break;
}
return $qb->orderBy($sortField, $sortingOptions->getSortDirection());
}
}
@@ -0,0 +1,29 @@
<?php
namespace UserBundle\Repository;
use Doctrine\ORM\EntityRepository;
use UserBundle\Entity\Notification\NotificationTheme;
/**
* NotificationThemeRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class NotificationThemeRepository extends EntityRepository
{
/**
* Get default notification theme.
*
* @return NotificationTheme|null
*/
public function getDefault()
{
return $this->createQueryBuilder('Theme')
->where('Theme.default = 1')
->getQuery()
->getOneOrNullResult();
}
}
@@ -0,0 +1,39 @@
<?php
namespace UserBundle\Repository;
use Doctrine\ORM\EntityRepository;
use UserBundle\Entity\Subscription\OrganizationSubscription;
/**
* OrganizationRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class OrganizationRepository extends EntityRepository
{
/**
* @return \Doctrine\ORM\QueryBuilder
*/
public function getListQueryBuilder()
{
$subDQL = $this->_em->createQueryBuilder()
->select('COUNT(_User.id)')
->from(OrganizationSubscription::class, '_Subscription')
->join('_Subscription.users', '_User')
->where('_Subscription.organization = Organization')
->getDQL();
return $this->createQueryBuilder('Organization')
->select(
'Organization.id, Organization.name',
'COUNT(Subscription) as subscriptionCount',
'('. $subDQL .') as usersCount'
)
->leftJoin('Organization.subscriptions', 'Subscription')
->groupBy('Organization.id')
->orderBy('Organization.id');
}
}

Some files were not shown because too many files have changed in this diff Show More