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
+12
View File
@@ -0,0 +1,12 @@
<?php
namespace AdminBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* Class AdminBundle
* @package AdminBundle
*/
class AdminBundle extends Bundle
{
}
@@ -0,0 +1,66 @@
<?php
namespace AdminBundle\Controller;
use AdminBundle\Form\ConfigParametersSectionType;
use AppBundle\AppBundleServices;
use AppBundle\Configuration\ConfigurationMutableInterface;
use AppBundle\Configuration\ConfigurationParameterInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
/**
* Class ConfigurationOptionsController
* @package AdminBundle\Controller
*
* @Security("has_role('ROLE_SUPER_ADMIN')")
* @Route("configuration")
*/
class ConfigurationOptionsController extends Controller
{
/**
* @Route("/", name="admin_configuration_index")
* @Template
*
* @param Request $request A Request instance.
*
* @return array|\Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
/** @var ConfigurationMutableInterface $configuration */
$configuration = $this->get(AppBundleServices::CONFIGURATION);
$oldParams = $configuration->getParameters();
/** @var ConfigurationParameterInterface[] $newParams */
$newParams = array_map(function ($object) {
return clone $object;
}, $oldParams);
$form = $this->createForm(ConfigParametersSectionType::class, $newParams);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$updated = [];
foreach ($newParams as $param) {
$name = $param->getName();
if ($oldParams[$name]->getValue() !== $param->getValue()) {
$updated[$name] = $param->getValue();
}
}
$configuration->setParameters($updated);
$configuration->sync();
// We should re-render form.
return $this->redirect($request->getRequestUri());
}
return [ 'form' => $form->createView() ];
}
}
@@ -0,0 +1,27 @@
<?php
namespace AdminBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* Class DashboardController
* @package AdminBundle\Controller
*/
class DashboardController extends Controller
{
/**
* @Security("is_granted('ROLE_SUPER_ADMIN')")
* @Route("/", name="admin_dashboard")
* @Template
*
* @return array
*/
public function indexAction()
{
return [];
}
}
@@ -0,0 +1,171 @@
<?php
namespace AdminBundle\Controller;
use AdminBundle\Form\OrganizationType;
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\Request;
use Symfony\Component\HttpFoundation\Response;
use UserBundle\Entity\Organization;
use UserBundle\Entity\Subscription\OrganizationSubscription;
use UserBundle\Repository\OrganizationRepository;
use UserBundle\Repository\SubscriptionRepository;
/**
* Class OrganizationController
* @package AdminBundle\Controller
*
* @Route("/organization")
*/
class OrganizationController extends Controller
{
const LIMIT = 20;
/**
* @Security("is_granted('ROLE_ADMIN')")
* @Route("/")
* @Template
*
* @param Request $request A HTTP Request instance.
*
* @return array
*/
public function indexAction(Request $request)
{
/** @var OrganizationRepository $repository */
$repository = $this->getDoctrine()->getRepository(Organization::class);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$repository->getListQueryBuilder(),
$request->query->getInt('page', 1),
self::LIMIT
);
return [
'organizations' => $pagination,
];
}
/**
* @Security("is_granted('ROLE_ADMIN')")
* @Route("/{id}", methods={ "GET", "POST" }, requirements={ "id": "\d+" })
* @Template
*
* @param Request $request A HTTP Request instance.
* @param integer $id A Organization entity id.
*
* @return array|Response
*/
public function editAction(Request $request, $id)
{
/** @var OrganizationRepository $repository */
$repository = $this->getDoctrine()->getRepository(Organization::class);
$organization = $repository->find($id);
if (! $organization instanceof Organization) {
throw $this->createNotFoundException();
}
/** @var SubscriptionRepository $repository */
$repository = $this->getDoctrine()->getRepository(OrganizationSubscription::class);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$repository->getForOrganization($organization->getId()),
$request->query->getInt('page', 1),
self::LIMIT
);
$form = $this->createForm(OrganizationType::class, $organization);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($organization);
$em->flush();
return $this->redirect($request->getUri());
}
return [
'form' => $form->createView(),
'organization' => $organization,
'subscriptions' => $pagination,
];
}
/**
* @Security("is_granted('ROLE_ADMIN')")
* @Route("/{id}/delete", methods={ "GET" }, requirements={ "id": "\d+" })
*
* @param integer $id A Organization entity id.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function deleteAction($id)
{
/** @var OrganizationRepository $repository */
$repository = $this->getDoctrine()->getRepository(Organization::class);
$organization = $repository->find($id);
if (! $organization instanceof Organization) {
throw $this->createNotFoundException();
}
$em = $this->getDoctrine()->getManager();
$em->remove($organization);
$em->flush();
return $this->redirectToRoute('admin_organization_index');
}
/**
* @Security("is_granted('ROLE_ADMIN')")
* @Route(
* "/{organizationId}/subscription/{subscriptionId}",
* methods={ "GET" },
* requirements={
* "organizationId": "\d+",
* "subscriptionId": "\d+"
* }
* )
*
* @param integer $organizationId A Organization entity id.
* @param integer $subscriptionId A billing Subscription entity id.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function subscriptionDeleteAction($organizationId, $subscriptionId)
{
/** @var OrganizationRepository $repository */
$repository = $this->getDoctrine()->getRepository(Organization::class);
$organization = $repository->find($organizationId);
if (! $organization instanceof Organization) {
throw $this->createNotFoundException();
}
/** @var SubscriptionRepository $repository */
$repository = $this->getDoctrine()->getRepository(OrganizationSubscription::class);
$subscription = $repository->find($subscriptionId);
if ((! $subscription instanceof OrganizationSubscription) || ($subscription->getOrganization()->getId() === $organizationId)) {
throw $this->createNotFoundException();
}
$em = $this->getDoctrine()->getManager();
$em->remove($subscription);
$em->flush();
return $this->redirectToRoute('admin_organization_edit', [
'id' => $organizationId,
]);
}
}
@@ -0,0 +1,51 @@
<?php
namespace AdminBundle\Controller;
use Knp\Component\Pager\PaginatorInterface;
use PaymentBundle\Entity\Payment;
use PaymentBundle\Repository\PaymentRepository;
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\Request;
/**
* Class PaymentController
* @package AdminBundle\Controller
*
* @Route("/payment")
*/
class PaymentController extends Controller
{
const LIMIT = 10;
/**
* @Security("is_granted('ROLE_SUPER_ADMIN')")
* @Route("/", methods={ "GET" })
* @Template
*
* @param Request $request A HTTP Request.
*
* @return array
*/
public function indexAction(Request $request)
{
/** @var PaginatorInterface $paginator */
$paginator = $this->get('knp_paginator');
/** @var PaymentRepository $repository */
$repository = $this->getDoctrine()->getRepository(Payment::class);
$pagination = $paginator->paginate(
$repository->getListQueryBuilder(),
$request->query->getInt('page', 1),
self::LIMIT
);
return [
'payments' => $pagination,
];
}
}
@@ -0,0 +1,116 @@
<?php
namespace AdminBundle\Controller;
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 AdminBundle\Controller
*
* @Route("/plans")
*/
class PlanController extends Controller
{
/**
* @Security("is_granted('ROLE_SUPER_ADMIN')")
* @Route("/", methods={ "GET" })
* @Template
*
* @return array
*/
public function indexAction()
{
/** @var PlanRepository $repository */
$repository = $this->getDoctrine()->getRepository(Plan::class);
$plans = $repository->findAll();
return [ 'plans' => $plans ];
}
/**
* @Security("is_granted('ROLE_SUPER_ADMIN')")
* @Route("/{id}", methods={ "GET", "POST" }, requirements={ "id": "\d+" })
* @Template
*
* @param Request $request A HTTP Request instance.
* @param integer $id A Plan entity id.
*
* @return array|RedirectResponse
*/
public function editAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
/** @var PlanRepository $repository */
$repository = $em->getRepository(Plan::class);
$plan = $repository->find($id);
if (! $plan instanceof Plan) {
throw $this->createNotFoundException();
}
$previousPrice = $plan->getPrice();
$form = $this->createForm(PlanType::class, $plan);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($plan);
if (abs($plan->getPrice() - $previousPrice) >= 0.000001) {
//
// Plan price was changed.
//
// For now we unsubscribe and disable all subscribed to this plan
// users, remove this plan for payment gateway and create new one
//
// todo rewrite this code when we find out that we should do with already subscribed users.
//
/** @var SubscriptionRepository $subscriptionRepository */
$subscriptionRepository = $em->getRepository(AbstractSubscription::class);
$subscriptions = $subscriptionRepository->getForPlan($plan->getId());
$paymentGateway = $this->get('payment.gateway_factory')
->getGateway(PaymentGatewayEnum::paypal());
/** @var AbstractSubscription $subscription */
foreach ($subscriptions as $subscription) {
$paymentGateway->cancelSubscription($subscription, 'Billing plan is removed');
$subscription
->setPayed(false)
->setPlan(null);
$em->persist($subscription);
}
if ($plan->isFree()) {
$paymentGateway->removePlan($plan);
} else {
$paymentGateway->updatePlan($plan);
}
}
$em->flush();
return $this->redirect($request->getUri());
}
return [
'form' => $form->createView(),
'plan' => $plan,
];
}
}
@@ -0,0 +1,82 @@
<?php
namespace AdminBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
/**
* Class SecurityController
* @package AdminBundle\Controller
*/
class SecurityController extends Controller
{
/**
* @Route("/login", name="admin_login")
* @Template
*
* @param Request $request A Request instance.
*
* @return array
*/
public function loginAction(Request $request)
{
/** @var $session \Symfony\Component\HttpFoundation\Session\Session */
$session = $request->getSession();
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
// last username entered by the user
$lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);
$csrfToken = $this->has('security.csrf.token_manager')
? $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue()
: null;
return [
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
];
}
/**
* @Route("/login_check", name="admin_login_check")
*
* @return void
*/
public function loginCheckAction()
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
}
/**
* @Route("/logout", name="admin_logging_out")
*
* @return void
*/
public function logoutAction()
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
}
}
@@ -0,0 +1,343 @@
<?php
namespace AdminBundle\Controller\User;
use AdminBundle\Form\Search;
use AdminBundle\Form\Type\SearchType;
use FOS\UserBundle\Model\UserManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use UserBundle\Entity\User;
use UserBundle\Enum\UserRoleEnum;
use UserBundle\Mailer\MailerInterface;
use UserBundle\Repository\UserRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use UserBundle\UserBundleServices;
/**
* Class AbstractUserController
* @package AdminBundle\Controller\User
*/
abstract class AbstractUserController extends Controller
{
protected static $role;
protected static $formClass;
protected static $limit = 20;
/**
* @Route("/")
* @Method({"GET", "POST"})
*
* @param Request $request A Request instance.
*
* @return Response
*/
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
/** @var UserRepository $userRepository */
$userRepository = $em->getRepository(User::class);
$search = new Search();
$searchForm = $this->createForm(SearchType::class, $search);
$searchForm->handleRequest($request);
if ($searchForm->isSubmitted() && !$searchForm->isValid()) {
$search = new Search();
}
$query = $userRepository->getUserByRoleQB(new UserRoleEnum(static::$role), $search->getHandledQuery());
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$query,
$request->query->getInt('page', 1),
self::$limit
);
return $this->render($this->getTemplate('index'), [
'users' => $pagination,
'search' => $searchForm->createView(),
'newUrl' => $this->generateCRUDUrl('new'),
'deleteRoute' => $this->getRoute('delete'),
'changeEnabledRoute' => $this->getRoute('changeEnabled'),
'resendPasswordRoute' => $this->getRoute('resendPassword'),
'showRoute' => $this->getRoute('show'),
'editRoute' => $this->getRoute('edit'),
]);
}
/**
* Creates a new user entity.
*
* @Route("/new")
* @Method({"GET", "POST"})
*
* @param Request $request A Request instance.
*
* @return Response
*/
public function newAction(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(static::$formClass, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (!$form->has('plainPassword')) {
$user->generatePassword();
$password = $user->getPlainPassword();
} else {
$password = $form->get('plainPassword')->getData();
}
$user
->setVerified(true)
->addRole(static::$role);
if (($response = $this->preCreate($user)) !== null) {
return $response;
}
$userManager->updateUser($user);
$mailer = $this->get(UserBundleServices::MAILER);
$mailer->sendPassword($user, $password);
if ($request->isXmlHttpRequest()) {
return new JsonResponse(['success' => true]);
}
return $this->redirect($this->generateCRUDUrl('show', $user->getId()));
}
if ($request->isXmlHttpRequest()) {
$resolveName = static function (FormInterface $form) use (&$resolveName) {
$parent = $form->getParent();
if ($parent === null) {
return $form->getName();
}
return $resolveName($parent) .'_'. $form->getName();
};
return new JsonResponse([
'success' => false,
'errors' => array_map(function (FormError $error) use ($resolveName) {
return [
'field' => $resolveName($error->getOrigin()),
'message' => $error->getMessage(),
];
}, iterator_to_array($form->getErrors(true, true))),
], 400);
}
return $this->render($this->getTemplate('new'), [
'user' => $user,
'form' => $form->createView(),
'indexUrl' => $this->generateCRUDUrl('index'),
]);
}
/**
* @Route("/{id}/show"), requirements={ "id": "\d+" }
* @Method({ "GET" })
*
* @param User $user A User entity instance.
*
* @return Response
*/
public function showAction(User $user)
{
return $this->render($this->getTemplate('show'), [
'user' => $user,
'listUrl' => $this->generateCRUDUrl('index'),
'editUrl' => $this->generateCRUDUrl('edit', $user->getId()),
'deleteUrl' => $this->generateCRUDUrl('delete', $user->getId()),
]);
}
/**
* @Route("/{id}/edit", requirements={ "id": "\d+" })
* @Method({"GET", "POST"})
*
* @param Request $request A Request instance.
* @param User $user A User entity instance.
*
* @return Response
*/
public function editAction(Request $request, User $user)
{
$form = $this->createForm(static::$formClass, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$manager = $this->get('user.user_manager');
if ((trim($user->getPlainPassword()) !== '') && $form->get('notifyAboutPassword')->getData()) {
/** @var MailerInterface $mailer */
$mailer = $this->get(UserBundleServices::MAILER);
$mailer->sendPassword($user, $user->getPlainPassword());
}
$manager->updateUser($user);
return $this->redirect($this->generateCRUDUrl('index'));
}
return $this->render($this->getTemplate('edit'), [
'user' => $user,
'form' => $form->createView(),
'indexUrl' => $this->generateCRUDUrl('index'),
'deleteUrl' => $this->generateCRUDUrl('delete', $user->getId()),
]);
}
/**
* @Route("/{id}/delete", requirements={ "id": "\d+" })
*
* @param User $user A User entity instance.
*
* @return Response
*/
public function deleteAction(User $user)
{
$manager = $this->get('user.user_manager');
if (($response = $this->preDelete($user)) !== null) {
return $response;
}
$manager->deleteUser($user);
return $this->redirectToRoute($this->getRoute('index'));
}
/**
* @param object $entity Deleted entity instance.
*
* @return null|Response
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function preDelete($entity)
{
return null;
}
/**
* @param string $name CRUD operation name.
*
* @return string
*/
public function getRoute($name)
{
$controllerName = strtolower(str_replace('Controller', '', \app\c\getShortName(static::class)));
$name = strtolower($name);
return "admin_user_{$controllerName}_{$name}";
}
/**
* @param string $name CRUD operation name.
*
* @return string
*/
public function getTemplate($name)
{
$controllerName = str_replace('Controller', '', \app\c\getShortName(static::class));
return "AdminBundle:User\\{$controllerName}:$name.html.twig";
}
/**
* @Route(
* "/{id}/change-enabled",
* requirements={ "id": "\d+" },
* condition="request.isXmlHttpRequest()"
* )
*
* @param User $user A User entity instance.
*
* @return JsonResponse
*/
public function changeEnabledAction(User $user)
{
$em = $this->getDoctrine()->getManager();
$user->setEnabled(! $user->isEnabled());
$em->persist($user);
$em->flush();
return new JsonResponse([
'enabled' => $user->isEnabled(),
]);
}
/**
* @Route(
* "/{id}/resend-password",
* requirements={ "id": "\d+" },
* condition="request.isXmlHttpRequest()"
* )
*
* @param User $user A User entity instance.
*
* @return JsonResponse
*/
public function resendPasswordAction(User $user)
{
$manager = $this->get('user.user_manager');
$user->generatePassword();
$password = $user->getPlainPassword();
$manager->updateUser($user);
/** @var MailerInterface $mailer */
$mailer = $this->get(UserBundleServices::MAILER);
$mailer->sendPassword($user, $password);
return new JsonResponse();
}
/**
* @param string $name Route name.
* @param integer $user A User entity id.
*
* @return string
*/
protected function generateCRUDUrl($name, $user = null)
{
$parameters = [];
if ($user !== null) {
$parameters = [ 'id' => $user ];
}
return $this->generateUrl($this->getRoute($name), $parameters);
}
/**
* @param object $entity Created entity instance.
*
* @return null|Response
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
protected function preCreate($entity)
{
return null;
}
}
@@ -0,0 +1,22 @@
<?php
namespace AdminBundle\Controller\User;
use AdminBundle\Form\User\AdminType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use UserBundle\Enum\UserRoleEnum;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
/**
* Class AdminController
* @package AdminBundle\Controller\User
*
* @Security("has_role('ROLE_SUPER_ADMIN')")
* @Route("/users/admins")
*/
class AdminController extends AbstractUserController
{
protected static $role = UserRoleEnum::ADMIN;
protected static $formClass = AdminType::class;
}
@@ -0,0 +1,100 @@
<?php
namespace AdminBundle\Controller\User;
use AdminBundle\Form\User\MasterUserType;
use AdminBundle\Form\User\SubscriberType;
use PaymentBundle\Enum\PaymentGatewayEnum;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use UserBundle\Entity\Plan;
use UserBundle\Entity\Subscription\PersonalSubscription;
use UserBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use UserBundle\Enum\UserRoleEnum;
use \Symfony\Component\HttpFoundation\Response;
/**
* User controller.
*
* @Security("has_role('ROLE_ADMIN')")
* @Route("/users/masters")
*/
class MasterController extends AbstractUserController
{
protected static $role = UserRoleEnum::MASTER_USER;
protected static $formClass = MasterUserType::class;
/**
* @Route("/{id}/show"), requirements={ "id": "\d+" }
* @Method({ "GET" })
*
* Finds and displays a user entity.
*
* @param User $user A User entity instance.
*
* @return Response
*/
public function showAction(User $user)
{
$subscriber = new User();
$subscriber->setMasterUser($user);
$form = $this->createForm(
SubscriberType::class,
$subscriber,
[
'action' => $this->generateUrl('admin_user_subscriber_new'),
'show_master_selector' => false,
]
);
return $this->render($this->getTemplate('show'), [
'user' => $user,
'subscriberForm' => $form->createView(),
'listUrl' => $this->generateCRUDUrl('index'),
'editUrl' => $this->generateCRUDUrl('edit', $user->getId()),
'deleteUrl' => $this->generateCRUDUrl('delete', $user->getId()),
]);
}
/**
* @param object|User $entity Created entity instance.
*
* @return null|Response
*/
public function preCreate($entity)
{
if ($entity->hasRole(UserRoleEnum::MASTER_USER)) {
//
// Subscribe created masters to free plan.
//
$em = $this->getDoctrine()->getManager();
$freePlan = current(array_filter(
$em->getRepository(Plan::class)->findAll(),
function (Plan $plan) {
return $plan->isFree();
}
));
if ($freePlan === null) {
$this->addFlash('admin_error', 'Can\'t create master \'cause we don\'t have free plan');
return $this->redirectToRoute($this->getRoute('index'));
}
$subscription = new PersonalSubscription();
$subscription
->setPlan($freePlan)
->setGateway(PaymentGatewayEnum::paypal())
->setPayed(true)
->setOwner($entity);
$entity->setBillingSubscription($subscription);
}
return null;
}
}
@@ -0,0 +1,75 @@
<?php
namespace AdminBundle\Controller\User;
use AdminBundle\Form\User\SubscriberType;
use AppBundle\Exception\LimitExceedException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Response;
use UserBundle\Entity\User;
use UserBundle\Enum\AppLimitEnum;
use UserBundle\Enum\UserRoleEnum;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
/**
* Class SubscriberController
* @package AdminBundle\Controller\User
*
* @Security("has_role('ROLE_ADMIN')")
* @Route("/users/subscribers")
*/
class SubscriberController extends AbstractUserController
{
protected static $role = UserRoleEnum::SUBSCRIBER;
protected static $formClass = SubscriberType::class;
/**
* @param object|User $entity Created entity instance.
*
* @return null|Response
*/
public function preCreate($entity)
{
if ($entity->hasRole(UserRoleEnum::SUBSCRIBER)) {
$master = $entity->getMasterUser();
try {
$entity->useLimit(AppLimitEnum::subscriberAccounts());
} catch (LimitExceedException $exception) {
$this->addFlash('admin_error', 'Allowed limit for creating subscribers for this master is exceeded');
return $this->redirectToRoute($this->getRoute('index'));
}
$entity->setBillingSubscription($master->getBillingSubscription());
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
}
return null;
}
/**
* @param object|User $entity Deleted entity instance.
*
* @return null|Response
*/
public function preDelete($entity)
{
if ($entity->hasRole(UserRoleEnum::SUBSCRIBER)) {
$master = $entity->getMasterUser();
if ($master !== null) { // The impossible happens.
$entity->releaseLimit(AppLimitEnum::subscriberAccounts());
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
}
}
return null;
}
}
@@ -0,0 +1,57 @@
<?php
namespace AdminBundle\Controller\User;
use AdminBundle\Form\User\AdminType;
use FOS\UserBundle\Model\UserManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* Class SuperAdminController
* @package AdminBundle\Controller\User
*
* @Security("has_role('ROLE_SUPER_ADMIN')")
* @Route("/users/super-admin")
*/
class SuperAdminController extends Controller
{
/**
* Displays a form to edit an existing user entity.
*
* @Route("/edit")
* @Method({"GET", "POST"})
* @Template
*
* @param Request $request A Request instance.
*
* @return RedirectResponse|array
*/
public function editAction(Request $request)
{
/** @var $userManager UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
$user = $this->getUser();
$form = $this->createForm(AdminType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$userManager->updateUser($user);
return $this->redirect($request->getUri());
}
return [
'user' => $user,
'form' => $form->createView(),
];
}
}
@@ -0,0 +1,143 @@
<?php
namespace AdminBundle\Controller;
use Knp\Component\Pager\PaginatorInterface;
use PaymentBundle\Agreement\AgreementManagerInterface;
use PaymentBundle\Gateway\Factory\PaymentGatewayFactoryInterface;
use PaymentBundle\PaymentBundleServices;
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\Subscription\OrganizationSubscription;
use UserBundle\Entity\User;
use UserBundle\Mailer\MailerInterface;
use UserBundle\Manager\User\UserManagerInterface;
use UserBundle\Repository\UserRepository;
use UserBundle\UserBundleServices;
/**
* Class UserVerificationController
* @package AdminBundle\Controller
*
* @Route("/users/verifications")
*/
class UserVerificationController extends Controller
{
const LIMIT = 20;
/**
* @Security("is_granted('ROLE_ADMIN')")
*
* @Route("/", methods={ "GET" })
* @Template
*
* @param Request $request A HTTP Request instance.
*
* @return array
*/
public function indexAction(Request $request)
{
/** @var UserRepository $repository */
$repository = $this->getDoctrine()->getRepository(User::class);
$notVerifiedUsers = $repository->getNotVerifiedQueryBuilder();
/** @var PaginatorInterface $paginator */
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$notVerifiedUsers,
$request->query->getInt('page', 1),
self::LIMIT
);
return [ 'users' => $pagination ];
}
/**
* @Security("is_granted('ROLE_ADMIN')")
*
* @Route("/{id}", methods={ "GET", "POST" }, requirements={ "id": "\d+" })
* @Template
*
* @param Request $request A HTTP Request instance.
* @param integer $id A not verified user instance.
*
* @return array|RedirectResponse
*/
public function showAction(Request $request, $id)
{
/** @var UserRepository $repository */
$repository = $this->getDoctrine()->getRepository(User::class);
$user = $repository->find($id);
if (! $user instanceof User) {
throw $this->createNotFoundException();
}
if ($user->isVerified() || ! $user->getBillingSubscription()->isPayed()) {
return $this->redirectToRoute('admin_userverification_index');
}
if ($request->isMethod('post')) {
/** @var $userManager UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** @var MailerInterface $mail */
$mailer = $this->get(UserBundleServices::MAILER);
if ($request->request->has('reject')) {
//
// Admin reject registration attempt.
//
/** @var PaymentGatewayFactoryInterface $factory */
$factory = $this->get(PaymentBundleServices::PAYMENT_GATEWAY_FACTORY);
/** @var AgreementManagerInterface $manager */
$manager = $this->get(PaymentBundleServices::AGREEMENT_MANAGER);
$user->getBillingSubscription()->cancel($factory, 'Account registration rejected');
$manager->removeAgreement($user->getBillingSubscription());
$userManager->deleteUser($user);
$mailer->sendVerificationRejected($user);
$this->addFlash('admin_success', sprintf(
'%s registration rejected and email sent to %s',
$user->getFullName(),
$user->getEmail()
));
} elseif ($request->request->has('verify')) {
//
// User verified so we should generate password and email it to
// user.
//
$password = $userManager->confirmUser($user);
$mailer->sendVerificationSuccess($user, $password);
$this->addFlash('admin_success', sprintf(
'%s registration verified and email sent to %s',
$user->getFullName(),
$user->getEmail()
));
}
return $this->redirectToRoute('admin_userverification_index');
}
$billingSubscription = $user->getBillingSubscription();
$organization = null;
if ($billingSubscription instanceof OrganizationSubscription) {
$organization = $billingSubscription->getOrganization();
}
return [
'user' => $user,
'plan' => $billingSubscription->getPlan(),
'subscription' => $billingSubscription,
'organization' => $organization,
];
}
}
+161
View File
@@ -0,0 +1,161 @@
<?php
namespace AdminBundle\Entity;
use AppBundle\Configuration\ConfigurationParameterInterface;
use AppBundle\Configuration\ConfigurationParameterMutableInterface;
use AppBundle\Entity\BaseEntityTrait;
use AppBundle\Entity\EntityInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* SiteSettings
*
* @ORM\Table(name="site_settings")
* @ORM\Entity
*/
class SiteSettings implements
ConfigurationParameterInterface,
ConfigurationParameterMutableInterface,
EntityInterface
{
use BaseEntityTrait;
/**
* @var string
*
* @ORM\Column
*/
private $section;
/**
* @var string
*
* @ORM\Column
*/
protected $title;
/**
* @var string
*
* @ORM\Column
*/
protected $name;
/**
* @var string
*
* @ORM\Column(type="text")
*
* @Assert\NotBlank
*/
protected $value;
/**
* Set section
*
* @param string $section Section name.
*
* @return SiteSettings
*/
public function setSection($section)
{
$this->section = $section;
return $this;
}
/**
* Get section
*
* @return string
*/
public function getSection()
{
return $this->section;
}
/**
* Set title
*
* @param string $title Human readable parameter title.
*
* @return SiteSettings
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set name
*
* @param string $name Parameter name.
*
* @return SiteSettings
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set value
*
* @param mixed $value Parameter value.
*
* @return SiteSettings
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* Get value
*
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Get entity type
*
* @return string
*/
public function getEntityType()
{
return 'config_parameter';
}
}
@@ -0,0 +1,51 @@
<?php
namespace AdminBundle\Form;
use AdminBundle\Entity\SiteSettings;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Class ConfigParametersType
* @package AdminBundle\Form
*/
class ConfigParametersSectionType 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)
{
$parameters = $builder->getData();
$checker = \nspl\f\rpartial('\app\op\isInstanceOf', SiteSettings::class);
if (! \nspl\a\all($parameters, $checker)) {
throw new \LogicException(
'Each \'parameters\' item should be instance of '
. SiteSettings::class
);
}
$parameters = \app\a\group(\nspl\op\methodCaller('getSection'), $parameters);
foreach ($parameters as $section => $sectionParams) {
$builder->add($section, ConfigParametersType::class, [
'data' => $sectionParams,
]);
}
}
}
@@ -0,0 +1,127 @@
<?php
namespace AdminBundle\Form;
use AdminBundle\Entity\SiteSettings;
use AppBundle\Configuration\ConfigurationDefinitionMap;
use AppBundle\Configuration\ConfigurationParameterInterface;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
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\Validator\Constraints\Type;
/**
* Class ConfigParametersType
* @package AdminBundle\Form
*/
class ConfigParametersType extends AbstractType implements DataMapperInterface
{
/**
* @var ConfigurationDefinitionMap
*/
private $definitionMap;
/**
* ConfigParametersType constructor.
*
* @param ConfigurationDefinitionMap $definitionMap A ConfigurationDefinitionMap
* instance.
*/
public function __construct(ConfigurationDefinitionMap $definitionMap)
{
$this->definitionMap = $definitionMap;
}
/**
* 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)
{
$parameters = $builder->getData();
/** @var SiteSettings $parameter */
foreach ($parameters as $parameter) {
$definition = $this->definitionMap->getDefinition($parameter->getName());
$name = str_replace('.', ':', $parameter->getName());
$type = $definition['type'];
$type = $type === 'integer' ? 'numeric' : $type;
$options = [
'label' => $parameter->getTitle(),
'constraints' => new Type([ 'type' => $type ]),
];
if ($definition['formType'] === CKEditorType::class) {
$options['config_name'] = 'default';
}
if ($definition['formType'] === ChoiceType::class) {
$options['choices'] = $definition['choices'];
}
$builder->add($name, $definition['formType'], $options);
}
$builder->setDataMapper($this);
}
/**
* Maps properties of some data to a list of forms.
*
* @param ConfigurationParameterInterface[]|null $data Structured data.
* @param FormInterface[]|\Iterator $forms A list of {@link FormInterface}
* instances.
*
* @return void
*/
public function mapDataToForms($data, $forms)
{
$forms = iterator_to_array($forms);
foreach ($data as $parameter) {
$forms[str_replace('.', ':', $parameter->getName())]->setData($this->definitionMap->denormalize(
$parameter->getName(),
$parameter->getValue()
));
}
}
/**
* 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 ConfigurationParameterInterface[]|null $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
/** @var FormInterface $form */
foreach ($forms as $form) {
$name = str_replace(':', '.', $form->getName());
$value = $form->getData();
settype($value, $this->definitionMap->getDefinition($name)['type']);
$data[$name]->setValue($value);
}
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace AdminBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use UserBundle\Entity\Organization;
/**
* Class OrganizationType
* @package AdminBundle\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('name');
}
/**
* 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' => Organization::class,
]);
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace AdminBundle\Form;
/**
* Class Search
* @package AdminBundle\Form
*/
class Search
{
/**
* @var string
*/
protected $query = '';
/**
* @return array
*/
public function getHandledQuery()
{
$handledQuery = explode(' ', $this->query);
if (count($handledQuery) === 1) {
if (!$handledQuery[0]) {
$handledQuery = [];
}
}
return $handledQuery;
}
/**
* @return string
*/
public function getQuery()
{
return $this->query;
}
/**
* @param string $query Search query.
*
* @return Search
*/
public function setQuery($query)
{
$this->query = $query;
return $this;
}
}
@@ -0,0 +1,107 @@
<?php
namespace AdminBundle\Form\Type;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccess;
/**
* Class HiddenEntityType
* @package AdminBundle\Form\Type
*/
class HiddenEntityType extends AbstractType
{
/**
* @var EntityManagerInterface
*/
private $em;
/**
* HiddenEntityType 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
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$idParameter = $options['id_parameter'];
$class = $options['class'];
$transformer = function ($entity) use ($class, $idParameter) {
// Transform.
if (! $entity instanceof $class) {
throw new TransformationFailedException('Entity should be instance of '. $class);
}
return PropertyAccess::createPropertyAccessor()
->getValue($entity, $idParameter);
};
$reverseTransformer = function ($id) use ($class) {
if (trim($id) === '') {
return null;
}
$entity = $this->em->find($class, $id);
if (null === $entity) {
throw new TransformationFailedException(sprintf(
'An entity with ID "%s" does not exist!',
$id
));
}
return $entity;
};
$builder->addModelTransformer(new CallbackTransformer($transformer, $reverseTransformer));
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver
->setRequired('id_parameter')
->setRequired('class')
->setDefault('id_parameter', 'id');
}
/**
* Returns the name of the parent type.
*
* @return string|null The name of the parent type if any, null otherwise
*/
public function getParent()
{
return HiddenType::class;
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace AdminBundle\Form\Type;
use AdminBundle\Form\Search;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class SearchType
* @package AdminBundle\Form\SearchType
*/
class SearchType 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('query', TextType::class, [
'required' => false,
'label' => false,
'trim' => true,
])
->setMethod('GET');
}
/**
* 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', Search::class);
}
}
@@ -0,0 +1,64 @@
<?php
namespace AdminBundle\Form\User;
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\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use UserBundle\Entity\User;
/**
* Class AbstractUserType
* @package AdminBundle\Form\User
*/
abstract class AbstractUserType 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('plainPassword', PasswordType::class, [ 'required' => false ])
->add('notifyAboutPassword', CheckboxType::class, [
'mapped' => false,
'label' => 'Notify the user about password change',
'required' => false,
'data' => true,
]);
}
/**
* 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' => [ 'admin_users_creation' ],
]);
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace AdminBundle\Form\User;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class AdminType
* @package AdminBundle\Form\User
*/
class AdminType extends AbstractUserType
{
/**
* 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)
{
parent::buildForm($builder, $options);
$builder
->add('plainPassword', RepeatedType::class, [
'type' => PasswordType::class,
'options' => [ 'translation_domain' => 'FOSUserBundle' ],
'first_options' => [ 'label' => 'form.password' ],
'second_options' => [ 'label' => 'form.password_confirmation' ],
'invalid_message' => 'fos_user.password.mismatch',
'required' => false,
]);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('validation_groups', [
'admin_administrator_creation',
]);
}
}
@@ -0,0 +1,120 @@
<?php
namespace AdminBundle\Form\User\DataMapper;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
use Symfony\Component\Form\FormInterface;
use UserBundle\Entity\Subscription\AbstractSubscription;
use UserBundle\Entity\Subscription\OrganizationSubscription;
use UserBundle\Entity\Subscription\PersonalSubscription;
use UserBundle\Entity\User;
/**
* Class MasterDataMapper
*
* @package AdminBundle\Form\User\DataMaper
*/
class MasterDataMapper extends PropertyPathMapper
{
/**
* Maps properties of some data to a list of forms.
*
* @param mixed|User $data Structured data.
* @param FormInterface[]|\Traversable $forms A list of {@link FormInterface}
* instances.
*
* @return void
*/
public function mapDataToForms($data, $forms)
{
$forms = iterator_to_array($forms);
$billingSubscription = $data->getBillingSubscription();
$forms['email']->setData($data->getEmail());
$forms['firstName']->setData($data->getFirstName());
$forms['lastName']->setData($data->getLastName());
$forms['enabled']->setData($data->isEnabled());
if ($billingSubscription instanceof AbstractSubscription) {
$forms['plan']->setData($billingSubscription->getPlan());
$forms['privatePerson']->setData($billingSubscription instanceof PersonalSubscription);
if ($billingSubscription instanceof OrganizationSubscription) {
$forms['organizationName']->setData($billingSubscription->getOrganization());
$forms['organizationAddress']->setData($billingSubscription->getOrganizationAddress());
$forms['organizationEmail']->setData($billingSubscription->getOrganizationEmail());
$forms['organizationPhone']->setData($billingSubscription->getOrganizationPhone());
}
}
$forms['expirationDay']->setData($data->getExpirationDay());
}
/**
* 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|User $data Structured data.
*
* @return void
*/
public function mapFormsToData($forms, &$data)
{
$forms = iterator_to_array($forms);
if (isset($forms['plan'], $forms['privatePerson'])) {
$personal = (boolean) $forms['privatePerson']->getData();
$oldBillingSubscription = $data->getBillingSubscription();
$newBillingSubscription = $personal
? new PersonalSubscription()
: new OrganizationSubscription();
$allowedSearches = $oldBillingSubscription->getSearchesPerDay();
$allowedSavedFeeds = $oldBillingSubscription->getSavedFeeds();
$allowedMasters = $oldBillingSubscription->getMasterAccounts();
$allowedSubscribers = $oldBillingSubscription->getSubscriberAccounts();
$allowedAlerts = $oldBillingSubscription->getAlerts();
$allowedNewsletter = $oldBillingSubscription->getNewsletters();
if (($personal && ($oldBillingSubscription instanceof PersonalSubscription))
|| (! $personal && ($oldBillingSubscription instanceof OrganizationSubscription))
) {
$newBillingSubscription = $oldBillingSubscription;
} else {
$oldBillingSubscription->removeUser($data);
}
$newBillingSubscription
->setPlan($forms['plan']->getData())
->setSearchesPerDay($allowedSearches)
->setSavedFeeds($allowedSavedFeeds)
->setMasterAccounts($allowedMasters)
->setSubscriberAccounts($allowedSubscribers)
->setAlerts($allowedAlerts)
->setNewsletters($allowedNewsletter);
if (! $personal) {
$newBillingSubscription
->setOrganization($forms['organizationName']->getData())
->setOrganizationAddress($forms['organizationAddress']->getData())
->setOrganizationEmail($forms['organizationEmail']->getData())
->setOrganizationPhone($forms['organizationPhone']->getData());
unset(
$forms['organizationName'],
$forms['organizationAddress'],
$forms['organizationEmail'],
$forms['organizationPhone']
);
}
$data->setBillingSubscription($newBillingSubscription);
unset($forms['plan'], $forms['privatePerson']);
}
parent::mapFormsToData($forms, $data);
}
}
@@ -0,0 +1,37 @@
<?php
namespace AdminBundle\Form\User;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Class MasterUserType
* @package AdminBundle\Form\User
*/
class MasterUserType extends AbstractUserType
{
/**
* 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)
{
parent::buildForm($builder, $options);
$builder
->add('enabled')
->add('expirationDay', DateType::class);
}
}
@@ -0,0 +1,79 @@
<?php
namespace AdminBundle\Form\User;
use AdminBundle\Form\Type\HiddenEntityType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use UserBundle\Entity\User;
use UserBundle\Enum\UserRoleEnum;
use UserBundle\Repository\UserRepository;
/**
* Class SubscriberType
* @package AdminBundle\Form\User
*/
class SubscriberType extends AbstractUserType
{
/**
* 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
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->add('position')
->add('phoneNumber', TextType::class, [
'attr' => [ 'data-type' => 'phone-number' ],
]);
$type = EntityType::class;
$parameters = [ 'class' => User::class ];
if ((boolean) $options['show_master_selector']) {
$parameters['choice_label'] = 'username';
$parameters['query_builder'] = function (UserRepository $repository) {
$qb = $repository->getUserByRoleQB(UserRoleEnum::masterUser(), []);
$alias = $qb->getRootAliases()[0];
return $qb
->orderBy($alias. '.username', 'DESC');
};
} else {
$type = HiddenEntityType::class;
}
$builder->add('masterUser', $type, $parameters);
}
/**
* Configures the options for this type.
*
* @param OptionsResolver $resolver The resolver for the options.
*
* @return void
*/
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver
->setDefaults([
'validation_groups' => [
'admin_users_creation',
'admin_subscribers_creation',
],
'show_master_selector' => true,
]);
}
}
@@ -0,0 +1,19 @@
services:
admin.forms.parameters:
class: 'AdminBundle\Form\ConfigParametersType'
arguments:
- '@app.configuration_definitions'
tags:
- { name: form.type }
admin.forms.hidden_entity_type:
class: 'AdminBundle\Form\Type\HiddenEntityType'
arguments:
- '@doctrine.orm.default_entity_manager'
tags:
- { name: form.type }
admin.twig_extension:
class: 'AdminBundle\Twig\AdminTwigExtension'
tags:
- { name: twig.extension }
@@ -0,0 +1,14 @@
(function ($) {
$(document).on('click', '.btn-delete', function () {
var href = this.dataset.href;
if (! href || ! confirm("Are you sure you want to delete?")) {
return;
}
this.setAttribute('disabled', true);
window.location = href;
});
})(jQuery);
@@ -0,0 +1,62 @@
(function ($) {
var modalSubscriberWindow = $("#subscriber-modal");
var modalSubscriberForm = modalSubscriberWindow.find("form");
$("#subscriber-add").click(function () {
modalSubscriberWindow.modal("show");
});
modalSubscriberForm.submit(function (event) {
event.preventDefault();
event.stopPropagation();
clearErrors(modalSubscriberForm);
$.ajax({
method: "POST",
url: this.getAttribute("action"),
processData: false,
contentType: false,
data: new FormData(this),
})
.done(function () {
window.location.reload();
})
.fail(function (jqXHR) {
var errors = jqXHR.responseJSON.errors;
for (var idx in errors) {
if (errors.hasOwnProperty(idx)) {
var error = errors[idx];
attachError(error.field, error.message);
}
}
});
});
function clearErrors(form) {
if (typeof form === "string") {
form = $(form);
}
form.find(".help-block ul").html("");
}
function attachError(field, message) {
var view = undefined;
if (field === "subscriber_masterUser") {
view = $("#subscriber").find("> .help-block ul");
} else {
view = $("#" + field)
.parent()
.find(".help-block ul");
}
view.append(
'<li><span className="glyphicon glyphicon-exclamation-sign"></span>' +
message +
"</li>"
);
}
})(jQuery);
@@ -0,0 +1,49 @@
(function ($) {
var $document = $(document);
$document.on('click', '.user-change-status', function () {
var $button = $(this);
$button.attr('disabled', true);
$.ajax({
type: 'post',
url: $button.data('href')
})
.always(function () {
$button.attr('disabled', false)
})
.done(function (data) {
$button.removeClass('btn-success btn-default');
$button.find('span').hide();
if (data.enabled) {
$button.find('.user-enabled').show();
$button.addClass('btn-success');
} else {
$button.find('.user-disabled').show();
$button.addClass('btn-default');
}
});
});
$document.on('click', '.user-password-resend', function () {
if (! confirm("Are you sure you want to generate and resend user's password?")) {
return;
}
var button = this;
button.setAttribute('disabled', true);
$.ajax({
type: 'post',
url: button.dataset.href
})
.always(function () {
button.removeAttribute('disabled');
})
.done(function () {
alert('Email with new password was sent');
});
});
})(jQuery);
@@ -0,0 +1,3 @@
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
@@ -0,0 +1,42 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>Configuration Settings</h1>
{{- form_start(form) -}}
<div>
<ul class="nav nav-tabs" role="tablist">
{%- for section in form -%}
<li role="presentation" {% if loop.first -%}class="active"{%- endif -%}>
<a href="#section_{{- loop.index0 -}}" aria-controls="home" role="tab" data-toggle="tab">
{{- section.vars.name -}}
</a>
</li>
{%- endfor -%}
</ul>
<div class="tab-content">
{%- for section in form -%}
<div role="tabpanel" class="tab-pane {% if loop.first -%}active{%- endif -%}" id="section_{{- loop.index0 -}}">
<table class="table table-bordered">
{%- for parameter in section -%}
<tr>
<td>
{{- form_label(parameter) -}}
{{- form_errors(parameter) -}}
</td>
<td>
{{- form_widget(parameter) -}}
</td>
</tr>
{%- endfor -%}
</table>
</div>
{%- endfor -%}
</div>
</div>
<button type="submit" class="btn btn-success">Save</button>
{{- form_end(form) -}}
{% endblock %}
@@ -0,0 +1,14 @@
{% extends "AdminBundle::layout-inner.html.twig" %}
{% block inner_content %}
<div class="row">
<div class="col-xs-3">
<h1>Dashboard</h1>
</div>
<div class="col-xs-3 col-xs-offset-6">
<a href="{{ path('admin_logging_out') }}">
logout
</a>
</div>
</div>
{% endblock %}
@@ -0,0 +1,59 @@
{% extends "AdminBundle::layout-inner.html.twig" %}
{% block inner_content %}
<h1>
Organization {{ organization.name -}}
</h1>
{{- form_start(form) -}}
{{- form_widget(form) -}}
<table class="table table-bordered">
<thead>
<tr>
<th>
{{ knp_pagination_sortable(subscriptions, 'Address', 'Subscription.organizationAddress') }}
</th>
<th>
{{ knp_pagination_sortable(subscriptions, 'Owner', 'Owner.email') }}
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody>
{%- for subscription in subscriptions -%}
<tr>
<td>
{{- subscription.organizationAddress -}}
</td>
<td>
<a href="{{ path('admin_user_master_show', { 'id': subscription.owner.id }) }}">
{{- subscription.owner.email -}}
</a>
</td>
<td>
<button type="button" data-href="{{- path('admin_organization_subscriptiondelete', {
'organizationId': organization.id,
'subscriptionId': subscription.id
}) -}}" class="btn btn-danger btn-block btn-delete">Delete</button>
</td>
</tr>
{%- endfor -%}
</tbody>
</table>
<div class="navigation">
{{ knp_pagination_render(subscriptions) }}
</div>
<button type="submit" class="btn btn-success">Edit</button>
<a class="btn btn-default" href="{{- path('admin_organization_index') -}}">Back</a>
{{- form_end(form) -}}
{% endblock %}
{% block javascripts %}
{{- parent() -}}
<script src="{{ asset('/bundles/admin/js/common_crud.js') }}"></script>
{% endblock javascripts %}
@@ -0,0 +1,58 @@
{% extends "AdminBundle::layout-inner.html.twig" %}
{% block inner_content %}
<h1>
Organizations
</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>
{{ knp_pagination_sortable(organizations, 'Name', 'Organization.name') }}
</th>
<th>
{{ knp_pagination_sortable(organizations, 'Departments count', 'subscriptionCount') }}
</th>
<th>
{{ knp_pagination_sortable(organizations, 'Total user count', 'usersCount') }}
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody>
{%- for organization in organizations -%}
{%- set path = path('admin_organization_edit', { 'id': organization.id }) -%}
<tr>
<td>
<a href="{{ path }}">{{- organization.name -}}</a>
</td>
<td>
{{- organization.subscriptionCount -}}
</td>
<td>
{{- organization.usersCount -}}
</td>
<td>
<a class="btn btn-info btn-block" href="{{ path }}">Edit</a>
<button type="button" class="btn btn-danger btn-block btn-delete" data-href="{{ path('admin_organization_delete', { 'id': organization.id }) }}">
Delete
</button>
</td>
</tr>
{%- endfor -%}
</tbody>
</table>
<div class="navigation">
{{ knp_pagination_render(organizations) }}
</div>
{% endblock %}
{% block javascripts %}
{{- parent() -}}
<script src="{{ asset('/bundles/admin/js/common_crud.js') }}"></script>
{% endblock javascripts %}
@@ -0,0 +1,155 @@
{% extends "AdminBundle::layout-inner.html.twig" %}
{% block stylesheets %}
{{- parent() -}}
<style>
.payment-details {
display: none;
}
</style>
{% endblock stylesheets %}
{% block inner_content %}
<h1>
Payments
</h1>
<table class="table table-bordered">
<thead>
<tr>
<th style="width: 1%">#</th>
<th>
{{- knp_pagination_sortable(payments, 'Date', 'Payment.createdAt') -}}
</th>
<th>
{{- knp_pagination_sortable(payments, 'User', 'Owner.email') -}}
</th>
<th>
{{- knp_pagination_sortable(payments, 'Amount', 'Payment.amount.amoun') -}}
</th>
<th>
{{- knp_pagination_sortable(payments, 'Currency', 'Payment.amount.currency') -}}
</th>
<th>
{{- knp_pagination_sortable(payments, 'Status', 'Payment.status') -}}
</th>
</tr>
</thead>
<tbody>
{% for payment in payments %}
<tr>
<td>
<a class="more" href="#" data-target="#payment_{{- payment.id -}}">
<i class="fa fa-plus"></i>
</a>
</td>
<td>
{{- payment.createdAt|date('Y-m-d H:i:s') -}}
</td>
<td>
{{- payment.subscription.owner.email -}}
</td>
<td>
{{- payment.amount.amount|number_format(2) -}}
</td>
<td>
{{- payment.amount.currency -}}
</td>
<td>
{{- payment.status.value -}}
</td>
</tr>
<tr id="payment_{{- payment.id -}}" class="payment-details">
<td colspan="6">
{%- set subscriptionType = payment.subscription.subscriptionType -%}
<h3>Subscription</h3>
<table class="table">
<tbody>
<tr>
<th>
Type
</th>
<td>
{{- subscriptionType.value is constant('ORGANIZATION', subscriptionType) ? 'organization' : 'personal' -}}
</td>
</tr>
{%- if subscriptionType.value is constant('ORGANIZATION', subscriptionType) -%}
<tr>
<th>
Name
</th>
<td>
{{- payment.subscription.getOrganization.name -}}
</td>
</tr>
<tr>
<th>
Department address
</th>
<td>
{{- payment.subscription.organizationAddress -}}
</td>
</tr>
<tr>
<th>
Department email
</th>
<td>
{{- payment.subscription.organizationEmail -}}
</td>
</tr>
<tr>
<th>
Department phone
</th>
<td>
{{- payment.subscription.organizationPhone -}}
</td>
</tr>
{%- endif -%}
</tbody>
</table>
</td>
</tr>
{% else %}
<tr>
<td colspan="6" class="text-center">No data.</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="pagination">
{{- knp_pagination_render(payments) -}}
</div>
{% endblock %}
{% block javascripts %}
{{- parent() -}}
<script>
(function ($) {
$(function() {
$('.more').click(function () {
var $this = $(this);
var details = $($this.data('target'));
if (details.hasClass('open')) {
details
.removeClass('open')
.hide();
$this.find('i')
.removeClass('fa-minus')
.addClass('fa-plus');
} else {
details
.addClass('open')
.show();
$this.find('i')
.removeClass('fa-plus')
.addClass('fa-minus');
}
});
});
})(jQuery);
</script>
{% endblock javascripts %}
@@ -0,0 +1,19 @@
{% extends "AdminBundle::layout-inner.html.twig" %}
{% block inner_content %}
<h1>
Edit {{ plan.title }} billing plan
</h1>
{{- form_start(form) -}}
<h3>Payment information</h3>
{{- form_widget(form.price) -}}
<hr>
<h3>Restrictions</h3>
{{- form_rest(form) -}}
<div class="btn-group">
<button type="submit" class="btn btn-success">Save</button>
<a class="btn btn-default" href="{{- path('admin_plan_index') -}}">Back</a>
</div>
{{- form_end(form) -}}
{% endblock %}
@@ -0,0 +1,30 @@
{% extends "AdminBundle::layout-inner.html.twig" %}
{% block inner_content %}
<h1>
Billing plans
</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for plan in plans %}
<tr>
<td>{{ plan.title }}</td>
<td>
$ {{ plan.price|number_format(2) -}}
</td>
<td>
<a class="btn btn-info btn-block" href="{{ path('admin_plan_edit', { 'id': plan.id }) }}">Edit</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
@@ -0,0 +1,11 @@
{% extends "AdminBundle::layout.html.twig" %}
{% block content %}
{{ include("AdminBundle:Security:login_content.html.twig", {
"error": error,
"last_username": last_username,
"csrf_token": csrf_token
}) }}
{% endblock %}
@@ -0,0 +1,37 @@
{% trans_default_domain 'FOSUserBundle' %}
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div style="padding: 20px;margin-top: 15%;background-color: #fce2d9;">
<div style="padding: 10px 0;text-align: center;font-size: 20px;">
Administrative panel
</div>
{% if error %}
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<form class="form" action="{{ path("admin_login_check") }}" method="post">
{% if csrf_token %}
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />
{% endif %}
<div class="form-group">
<label for="username">{{ 'security.login.username'|trans }}</label>
<input class="form-control" type="text" id="username" name="_username" value="{{ last_username }}" required="required" />
</div>
<div class="form-group">
<label for="password">{{ 'security.login.password'|trans }}</label>
<input class="form-control" type="password" id="password" name="_password" required="required" />
</div>
<div class="">
<input type="checkbox" id="remember_me" name="_remember_me" value="on" />
<label for="remember_me">{{ 'security.login.remember_me'|trans }}</label>
</div>
<input type="submit" class="btn btn-default" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans }}" />
</form>
</div>
</div>
</div>
@@ -0,0 +1,5 @@
{% extends 'AdminBundle:User/Layout:edit_layout.html.twig' %}
{% block edit_title %}
Administrator edit
{% endblock edit_title %}
@@ -0,0 +1,5 @@
{% extends 'AdminBundle:User/Layout:index_layout.html.twig' %}
{% block index_title %}
Administrators list
{% endblock index_title %}
@@ -0,0 +1,5 @@
{% extends 'AdminBundle:User/Layout:new_layout.html.twig' %}
{% block new_title %}
Administrator creation
{% endblock new_title %}
@@ -0,0 +1,24 @@
{% extends 'AdminBundle:User/Layout:show_layout.html.twig' %}
{% block show_title %}
Administrator
{% endblock show_title %}
{% block show_content %}
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ user.id }}</td>
</tr>
<tr>
<th>Firstname</th>
<td>{{ user.firstName }}</td>
</tr>
<tr>
<th>Lastname</th>
<td>{{ user.lastName }}</td>
</tr>
</tbody>
</table>
{% endblock show_content %}
@@ -0,0 +1,20 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>
{%- block edit_title -%}
{%- endblock edit_title -%}
</h1>
<div style="margin-bottom: 20px;">
{{ form_start(form) }}
{{ form_widget(form) }}
<div>
<div style="display: inline-block">
<input type="submit" class="btn btn-success" value="Save" />
<a class="btn btn-default" href="{{ indexUrl }}">Back to the list</a>
</div>
</div>
{{ form_end(form) }}
</div>
{% endblock %}
@@ -0,0 +1,92 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>
{%- block index_title -%}
{%- endblock index_title -%}
</h1>
<div class="row" style="margin-bottom: 20px;">
<div class="col-xs-3 col-xs-offset-9">
{{ form_start(search) }}
<div class="input-group">
<input
type="text"
name="{{ search.query.vars.full_name }}"
class="form-control"
value="{{ search.query.vars.value }}"
/>
<span class="input-group-btn">
<button class="btn btn-default" type="submit">Go!</button>
</span>
</div><!-- /input-group -->
{{ form_end(search, {'render_rest': false}) }}
</div>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>{{ knp_pagination_sortable(users, 'Email', 'User.email') }}</th>
<th>{{ knp_pagination_sortable(users, 'First Name', 'User.firstName') }}</th>
<th>{{ knp_pagination_sortable(users, 'Last Name', 'User.lastName') }}</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.email }}</td>
<td>{{ user.firstName }}</td>
<td>{{ user.lastName }}</td>
<td>
{%- block index_row_buttons -%}
<a class="btn btn-info btn-block" href="{{ path(showRoute, { 'id': user.id }) }}">Show</a>
<a class="btn btn-warning btn-block" href="{{ path(editRoute, { 'id': user.id }) }}">Edit</a>
<button data-href="{{ path(deleteRoute, {'id': user.id}) }}" class="btn btn-danger btn-block btn-delete">
Delete
</button>
<button data-href="{{ path(changeEnabledRoute, {'id': user.id}) }}" class="btn
btn-block
{% if user.isEnabled %}
btn-success
{% else %}
btn-default
{% endif %}
user-change-status">
<span class="user-enabled"
{% if not user.isEnabled %}
style="display: none"
{% endif %}
>Block</span>
<span class="user-disabled"
{% if user.isEnabled %}
style="display: none"
{% endif %}
>Unblock</span>
</button>
{%- endblock index_row_buttons -%}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="navigation">
{{ knp_pagination_render(users) }}
</div>
{%- block inner_footer -%}
<ul>
<li>
<a href="{{ newUrl }}">Create a new user</a>
</li>
</ul>
{%- endblock inner_footer -%}
{% endblock %}
{% block javascripts %}
{{- parent() -}}
<script src="{{ asset('/bundles/admin/js/common_crud.js') }}"></script>
<script src="{{ asset('/bundles/admin/js/user_crud.js') }}"></script>
{% endblock javascripts %}
@@ -0,0 +1,20 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>
{%- block new_title -%}
{%- endblock new_title -%}
</h1>
<div style="margin-bottom: 20px;">
{{ form_start(form) }}
{{ form_widget(form) }}
<div>
<div style="display: inline-block">
<input type="submit" class="btn btn-success" value="Save" />
<a class="btn btn-default" href="{{ indexUrl }}">Back to the list</a>
</div>
</div>
{{ form_end(form) }}
</div>
{% endblock %}
@@ -0,0 +1,23 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>
{%- block show_title -%}
{%- endblock show_title -%}
</h1>
{%- block show_content -%}
{%- endblock show_content -%}
<div>
{%- block show_buttons -%}
<div style="display: inline-block">
<a class="btn btn-default" href="{{ listUrl }}">Back to the list</a>
</div>
<div style="display: inline-block">
<a class="btn btn-warning" href="{{ editUrl }}">Edit</a>
<a class="btn btn-danger" href="{{ deleteUrl }}">Delete</a>
</div>
{%- endblock show_buttons -%}
</div>
{% endblock inner_content %}
@@ -0,0 +1,10 @@
{% extends 'AdminBundle:User/Layout:edit_layout.html.twig' %}
{% block edit_title %}
Master User edit
{% endblock edit_title %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('/bundles/admin/js/master_crud.js') }}"></script>
{% endblock javascripts %}
@@ -0,0 +1,15 @@
{% extends 'AdminBundle:User/Layout:index_layout.html.twig' %}
{% block index_title %}
Master User list
{% endblock index_title %}
{% block index_row_buttons %}
{{- parent() -}}
<button
data-href="{{ path(resendPasswordRoute, { 'id': user.id }) }}"
class="btn btn-default btn-block user-password-resend"
>
Resend password
</button>
{% endblock index_row_buttons %}
@@ -0,0 +1,10 @@
{% extends 'AdminBundle:User/Layout:new_layout.html.twig' %}
{% block new_title %}
Master User creation
{% endblock new_title %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('/bundles/admin/js/master_crud.js') }}"></script>
{% endblock javascripts %}
@@ -0,0 +1,185 @@
{% extends 'AdminBundle:User/Layout:show_layout.html.twig' %}
{% form_theme subscriberForm _self %}
{%- set billingType = user.billingSubscription.subscriptionType -%}
{% block show_title %}
Master User
{% endblock show_title %}
{% block show_content %}
<h3>
User information
</h3>
<table class="table">
<tbody>
<tr>
<th>First name</th>
<td>{{ user.firstName }}</td>
</tr>
<tr>
<th>Last name</th>
<td>{{ user.lastName }}</td>
</tr>
<tr>
<th>
Expiration Day
</th>
<td>{{ user.getExpirationDay|date("m/d/Y") }}</td>
</tr>
<tr>
<th>Number of subscribers</th>
<td>
{{- user.billingSubscription.getPlan.subscriberAccounts }} /
{{ user.billingSubscription.subscriberAccounts -}}
</td>
</tr>
<tr>
<th>Number of saved feeds allowed</th>
<td>
{{- user.billingSubscription.getPlan.savedFeeds }} /
{{ user.billingSubscription.savedFeeds -}}
</td>
</tr>
<tr>
<th>Number of alerts allowed</th>
<td>
{{- user.billingSubscription.getPlan.alerts }} /
{{ user.billingSubscription.alerts -}}
</td>
</tr>
<tr>
<th>Number of newsletters allowed</th>
<td>
{{- user.billingSubscription.getPlan.newsletters }} /
{{ user.billingSubscription.newsletters -}}
</td>
</tr>
<tr>
<th>Number of ad-hoc searches per day allowed</th>
<td>
{{- user.billingSubscription.getPlan.searchesPerDay }} /
{{ user.billingSubscription.searchesPerDay -}}
</td>
</tr>
<tr>
<th>
Billing subscription type
</th>
<td>
{{- billingType.value is constant('ORGANIZATION', billingType) ? 'organization' : 'personal' -}}
</td>
</tr>
</tbody>
</table>
{%- if billingType.value is constant('ORGANIZATION', billingType) -%}
<h3>
Organization information
</h3>
<table class="table">
<tbody>
<tr>
<th>
Name
</th>
<td>
{{- user.billingSubscription.getOrganization.name -}}
</td>
</tr>
<tr>
<th>
Department address
</th>
<td>
{{- user.billingSubscription.organizationAddress -}}
</td>
</tr>
<tr>
<th>
Department email
</th>
<td>
{{- user.billingSubscription.organizationEmail -}}
</td>
</tr>
<tr>
<th>
Department phone
</th>
<td>
{{- user.billingSubscription.organizationPhone -}}
</td>
</tr>
</tbody>
</table>
{%- endif -%}
<h3>
Subscribers
</h3>
<ul>
{% for subscriber in user.getSubscribers %}
<li>
<a href="{{ path('admin_user_subscriber_show', {'id': subscriber.id}) }}">
{{ subscriber.firstName }} {{ subscriber.lastName }} ({{ subscriber.email }})
</a>
</li>
{% endfor %}
</ul>
{# Add subscribers on MasterUser page Modal #}
<div class="modal fade" id="subscriber-modal" tabindex="-1" role="dialog" aria-labelledby="add_subscriber">
{{- form_start(subscriberForm) -}}
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="add_subscriber_header">
Subscriber creation
</h4>
</div>
<div class="modal-body">
{{- form_widget(subscriberForm) -}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
{{- form_end(subscriberForm) -}}
</div>
{% endblock show_content %}
{% block show_buttons %}
{{- parent() -}}
<div style="display: inline-block">
<button type="button" id="subscriber-add" class="btn btn-success">Add Subscriber</button>
</div>
{% endblock show_buttons %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('/bundles/admin/js/master_crud.js') }}"></script>
{% endblock javascripts %}
{% block form_errors -%}
<span class="help-block">
<ul class="list-unstyled">
{%- for error in errors -%}
<li><span class="glyphicon glyphicon-exclamation-sign"></span> {{ error.message }}</li>
{%- endfor -%}
</ul>
</span>
{%- endblock form_errors %}
{%- block hidden_widget -%}
<div>
{%- set type = type|default('hidden') -%}
{{ block('form_widget_simple') }}
</div>
{%- endblock hidden_widget -%}
@@ -0,0 +1,5 @@
{% extends 'AdminBundle:User/Layout:edit_layout.html.twig' %}
{% block edit_title %}
Subscriber edit
{% endblock edit_title %}
@@ -0,0 +1,15 @@
{% extends 'AdminBundle:User/Layout:index_layout.html.twig' %}
{% block index_title %}
Subscribers list
{% endblock index_title %}
{% block index_row_buttons %}
{{- parent() -}}
<button
data-href="{{ path(resendPasswordRoute, { 'id': user.id }) }}"
class="btn btn-default btn-block user-password-resend"
>
Resend password
</button>
{% endblock index_row_buttons %}
@@ -0,0 +1,5 @@
{% extends 'AdminBundle:User/Layout:new_layout.html.twig' %}
{% block new_title %}
Subscriber creation
{% endblock new_title %}
@@ -0,0 +1,129 @@
{% extends 'AdminBundle:User/Layout:show_layout.html.twig' %}
{%- set billingType = user.billingSubscription.subscriptionType -%}
{% block show_title %}
Subscriber
{% endblock show_title %}
{% block show_content %}
<h3>
User information
</h3>
<table class="table">
<tbody>
<tr>
<th>Firstname</th>
<td>{{ user.firstName }}</td>
</tr>
<tr>
<th>Lastname</th>
<td>{{ user.lastName }}</td>
</tr>
<tr>
<th>
Position
</th>
<td>
{{ user.position }}
</td>
</tr>
<tr>
<th>
Phone Number
</th>
<td>
{{ user.phoneNumber }}
</td>
</tr>
<tr>
<th>Number of saved feeds allowed</th>
<td>
{{- user.billingSubscription.getPlan.savedFeeds }} /
{{ user.billingSubscription.savedFeeds -}}
</td>
</tr>
<tr>
<th>Number of alerts allowed</th>
<td>
{{- user.billingSubscription.getPlan.alerts }} /
{{ user.billingSubscription.alerts -}}
</td>
</tr>
<tr>
<th>Number of newsletters allowed</th>
<td>
{{- user.billingSubscription.getPlan.newsletters }} /
{{ user.billingSubscription.newsletters -}}
</td>
</tr>
<tr>
<th>Number of ad-hoc searches per day allowed</th>
<td>
{{- user.billingSubscription.getPlan.searchesPerDay }} /
{{ user.billingSubscription.searchesPerDay -}}
</td>
</tr>
<tr>
<th>
Billing subscription type
</th>
<td>
{{- billingType.value is constant('ORGANIZATION', billingType) ? 'organization' : 'personal' -}}
</td>
</tr>
<tr>
<th>
Master
</th>
<td>
<a href="{{ path('admin_user_master_show', { 'id': user.masterUser.id }) }}">
{{- user.masterUser.email -}}
</a>
</td>
</tr>
</tbody>
</table>
{%- if billingType.value is constant('ORGANIZATION', billingType) -%}
<h3>
Organization information
</h3>
<table class="table">
<tbody>
<tr>
<th>
Name
</th>
<td>
{{- user.billingSubscription.getOrganization.name -}}
</td>
</tr>
<tr>
<th>
Department address
</th>
<td>
{{- user.billingSubscription.organizationAddress -}}
</td>
</tr>
<tr>
<th>
Department email
</th>
<td>
{{- user.billingSubscription.organizationEmail -}}
</td>
</tr>
<tr>
<th>
Department phone
</th>
<td>
{{- user.billingSubscription.organizationPhone -}}
</td>
</tr>
</tbody>
</table>
{%- endif -%}
{% endblock show_content %}
@@ -0,0 +1,12 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>Administrator edit</h1>
<div style="margin-bottom: 20px;">
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" class="btn btn-success" value="Save" />
{{ form_end(form) }}
</div>
{% endblock %}
@@ -0,0 +1,40 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>
Not verified users
</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>{{ knp_pagination_sortable(users, 'Email', 'User.email') }}</th>
<th>{{ knp_pagination_sortable(users, 'First Name', 'User.firstName') }}</th>
<th>{{ knp_pagination_sortable(users, 'Last Name', 'User.lastName') }}</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.email }}</td>
<td>{{ user.firstName }}</td>
<td>{{ user.lastName }}</td>
<td>
<a class="btn btn-info btn-block" href="{{-
path('admin_userverification_show', { 'id': user.id })
-}}">Show</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="4" class="text-center">No data.</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="navigation">
{{ knp_pagination_render(users) }}
</div>
{% endblock inner_content %}
@@ -0,0 +1,46 @@
{% extends 'AdminBundle::layout-inner.html.twig' %}
{% block inner_content %}
<h1>
Verify user {{ user.fullName }}
</h1>
<h3>
User information
</h3>
<div>
<div><b>Full name:</b> {{ user.fullName }}</div>
<div><b>Email:</b> {{ user.email }}</div>
<div><b>Phone:</b> {{ user.phoneNumber }}</div>
</div>
<h3>
Billing information
</h3>
<div>
<div><b>Subscription type:</b> {{ subscription.subscriptionType.value }}</div>
<div><b>Plan:</b> {{ plan.name }}</div>
</div>
{%- if organization is not null -%}
<h3>
Organization information
</h3>
<div>
<div><b>Name: </b> {{ organization.name }}</div>
<div><b>Address: </b> {{ subscription.organizationAddress }}</div>
<div><b>Email: </b> {{ subscription.organizationEmail }}</div>
<div><b>Phone: </b> {{ subscription.organizationPhone }}</div>
</div>
{%- endif -%}
<hr>
<form action="{{ path('admin_userverification_show', { 'id': user.id }) }}" method="post">
<div>
<button name="verify" type="submit" class="btn btn-success">Verify</button>
<button name="reject" type="submit" class="btn btn-danger">Reject</button>
</div>
</form>
{% endblock inner_content %}
@@ -0,0 +1,66 @@
{% extends "AdminBundle::layout.html.twig" %}
{% block content %}
<div class="row">
<div class="col-xs-2">
<div>
<a href="{{ path('admin_dashboard') }}" class="list-group-item">
Dashboard
</a>
<hr>
{% if is_granted('ROLE_SUPER_ADMIN') %}
<a href="{{ path('admin_configuration_index') }}" class="list-group-item">
Configuration
</a>
<hr>
<a href="{{ path('admin_plan_index') }}" class="list-group-item">
Billing plans
</a>
<a href="{{ path('admin_payment_index') }}" class="list-group-item">
Payments
</a>
<hr>
<a href="{{ path('admin_user_superadmin_edit') }}" class="list-group-item">
Super admin edit
</a>
<a href="{{ path('admin_user_admin_index') }}" class="list-group-item">
Admins
</a>
<hr>
{% endif %}
<a href="{{ path('admin_organization_index') }}" class="list-group-item">
Organizations
</a>
<hr>
<a href="{{ path('admin_userverification_index') }}" class="list-group-item">
Not verified users
</a>
<a href="{{ path('admin_user_master_index') }}" class="list-group-item">
Master Users
</a>
<a href="{{ path('admin_user_subscriber_index') }}" class="list-group-item">
Subscribers
</a>
</div>
</div>
<div class="col-xs-10">
{%- for message in app.session.flashBag.get('admin_success') -%}
<div class="alert alert-success">
{{- message -}}
</div>
{%- endfor -%}
{%- for message in app.session.flashBag.get('admin_error') -%}
<div class="alert alert-error">
{{- message -}}
</div>
{%- endfor -%}
{%- block inner_content -%}
{%- endblock -%}
</div>
</div>
{% endblock %}
@@ -0,0 +1,24 @@
{% extends "::base.html.twig" %}
{%- block stylesheets -%}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('libs/css/bootstrap.min.css') }}"/>
<link rel="stylesheet" href="{{ asset('libs/css/font-awesome.min.css') }}"/>
<style>
textarea {
resize: vertical;
}
</style>
{%- endblock -%}
{% block body %}
<div class="container" style="margin-top: 30px;">
{% block content %}
{% endblock %}
</div>
{% endblock %}
{% block javascripts %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="{{ asset('libs/js/bootstrap.js') }}"></script>
{% endblock javascripts %}
@@ -0,0 +1,49 @@
<?php
namespace AdminBundle\Twig;
use UserBundle\Entity\User;
use UserBundle\Enum\UserRoleEnum;
/**
* Class AdminTwigExtension
*
* @package AdminBundle\Twig
*/
class AdminTwigExtension extends \Twig_Extension
{
/**
* Map between role name and human readable role title.
*
* @var array
*/
protected static $rolesMap = [
UserRoleEnum::ADMIN => 'Admin',
UserRoleEnum::MASTER_USER => 'Master User',
UserRoleEnum::SUPER_ADMIN => 'Super Admin',
UserRoleEnum::SUBSCRIBER => 'Subscriber',
];
/**
* Returns a list of filters to add to the existing list.
*
* @return \Twig_SimpleFilter[]
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('humanReadableRoles', function (User $user) {
$roles = $user->getRoles();
$result = [];
foreach ($roles as $role) {
if (array_key_exists($role, self::$rolesMap)) {
$result[$role] = self::$rolesMap[$role];
}
}
return $result;
}),
];
}
}