<?php
/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Enterprise License (PEL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PEL
*/
namespace App\Controller;
use App\EventListener\AuthenticationLoginListener;
use App\Form\LoginFormType;
use App\Form\RegistrationFormHandler;
use App\Form\RegistrationFormType;
use App\Model\Customer;
use App\Services\NewsletterDoubleOptInService;
use App\Services\PasswordRecoveryService;
use CustomerManagementFrameworkBundle\CustomerProvider\CustomerProviderInterface;
use CustomerManagementFrameworkBundle\CustomerSaveValidator\Exception\DuplicateCustomerException;
use CustomerManagementFrameworkBundle\Model\CustomerInterface;
use CustomerManagementFrameworkBundle\Security\Authentication\LoginManagerInterface;
use CustomerManagementFrameworkBundle\Security\OAuth\Exception\AccountNotLinkedException;
use CustomerManagementFrameworkBundle\Security\OAuth\OAuthRegistrationHandler;
use CustomerManagementFrameworkBundle\Security\SsoIdentity\SsoIdentityServiceInterface;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\Authentication\Token\OAuthToken;
use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\Order\Listing\Filter\CustomerObject;
use Pimcore\DataObject\Consent\Service;
use Pimcore\Translation\Translator;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Uid\Uuid;
use App\Model\Order;
use App\Model\QuoteModel;
use Knp\Component\Pager\Pagination\SlidingPagination;
use Knp\Component\Pager\PaginatorInterface;
use Pimcore\Model\DataObject;
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Common\Entity\Row;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use FrontendPermissionToolkitBundle\Service as FrontenKitService;
use Pimcore\Db;
use App\Controller\CartB2CController;
/**
* Class AccountController
*
* Controller that handles all account functionality, including register, login and connect to SSO profiles
*/
class AccountB2CController extends BaseB2CController
{
const LIMIT_PER_PAGE = 10;
protected $service;
public function __construct(EventDispatcherInterface $eventDispatcher)
{
$this->service = new FrontenKitService($eventDispatcher);
}
/**
* @Route("/b2c/account/login", name="b2c-account-login")
*
* @param AuthenticationUtils $authenticationUtils
* @param OAuthRegistrationHandler $oAuthHandler
* @param SessionInterface $session
* @param Request $request
* @param UserInterface|null $user
*
* @return Response|RedirectResponse
*/
public function loginAction(
AuthenticationUtils $authenticationUtils,
OAuthRegistrationHandler $oAuthHandler,
SessionInterface $session,
Request $request,
UserInterface $user = null
) {
// //redirect user to index page if logged in
if ($user && $this->isGranted('ROLE_USER')) {
return $this->redirectToRoute('b2c-account-index');
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// OAuth handling - the OAuth authenticator is configured to return to the login page on errors
// (see failure_path configuration) - therefore we can fetch the last authentication error
// here. If the error is an AccountNotLinkedException (as thrown by our user provider) save the
// OAuth token to the session and redirect to registration with a special key which can be used
// to load the token to prepopulate the registration form with account data.
if ($error instanceof AccountNotLinkedException) {
// this can be anything - for simplicity we just use an UUID as it is unique and random
$registrationKey = (string) Uuid::v4()->toRfc4122();
$oAuthHandler->saveToken($registrationKey, $error->getToken());
return $this->redirectToRoute('b2c-account-register', [
'registrationKey' => $registrationKey
]);
}
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
$formData = [
'_username' => $lastUsername
];
$form = $this->createForm(LoginFormType::class, $formData, [
'action' => $this->generateUrl('b2c-account-login'),
]);
//store referer in session to get redirected after login
if (!$request->get('no-referer-redirect')) {
$session->set('_security.demo_frontend_b2c.target_path', $request->headers->get('referer'));
}
return $this->render('account/login.html.twig', [
'form' => $form->createView(),
'error' => $error,
'hideBreadcrumbs' => true
]);
}
/**
* If registration is called with a registration key, the key will be used to look for an existing OAuth token in
* the session. This OAuth token will be used to fetch user info which can be used to pre-populate the form and to
* link a SSO identity to the created customer object.
*
* This could be further separated into services, but was kept as single method for demonstration purposes as the
* registration process is different on every project.
*
* @Route("/b2c/account/register", name="b2c-account-register")
*
* @param Request $request
* @param CustomerProviderInterface $customerProvider
* @param OAuthRegistrationHandler $oAuthHandler
* @param LoginManagerInterface $loginManager
* @param RegistrationFormHandler $registrationFormHandler
* @param SessionInterface $session
* @param AuthenticationLoginListener $authenticationLoginListener
* @param Translator $translator
* @param Service $consentService
* @param UrlGeneratorInterface $urlGenerator
* @param NewsletterDoubleOptInService $newsletterDoubleOptInService
* @param UserInterface|null $user
*
* @return Response|RedirectResponse
*/
public function registerAction(
Request $request,
CustomerProviderInterface $customerProvider,
OAuthRegistrationHandler $oAuthHandler,
LoginManagerInterface $loginManager,
RegistrationFormHandler $registrationFormHandler,
SessionInterface $session,
AuthenticationLoginListener $authenticationLoginListener,
Translator $translator,
Service $consentService,
UrlGeneratorInterface $urlGenerator,
NewsletterDoubleOptInService $newsletterDoubleOptInService,
UserInterface $user = null,
Factory $factory
) {
if ($user && $this->isGranted('ROLE_USER')) {
return $this->redirectToRoute('b2c-account-index');
}
$form = $this->createForm(RegistrationFormType::class,[],[]);
$form->handleRequest($request);
$customer = $customerProvider->create();
$errors = [];
if ($form->isSubmitted()) {
$registrationFormHandler->setData($form,$request,$customer);
$customer->setCustomerLanguage($request->getLocale());
$customer->setActive(true);
try {
$customer->save();
$response = $this->redirectToRoute('b2c-account-index');
// log user in manually
// pass response to login manager as it adds potential remember me cookies
$loginManager->login($customer, $request, $response);
//do ecommerce framework login
$authenticationLoginListener->doEcommerceFrameworkLogin($customer);
if($customer->getUserRoles() == 'Customer'){
$cartManager = $factory->getCartManager();
$oldCart = $cartManager->getCartByName(CartB2CController::DEFAULT_CART_NAME);
if($oldCart){
if($oldCart->getItems()){
return new RedirectResponse('/b2c/cart');
}
}
}
return $response;
} catch (DuplicateCustomerException $e) {
$errors[] = $translator->trans(
'account.customer-already-exists',
[
$customer->getEmail(),
$urlGenerator->generate('account-password-send-recovery', ['email' => $customer->getEmail()])
]
);
} catch (\Exception $e) {
$errors[] = $e->getMessage();
}
}
if ($form->isSubmitted() && !$form->isValid()) {
p_r($form->getErrors());
p_r(get_Class_methods($form));
p_r($errors);exit;
$this->addFlash("error","La password deve essere identica e contenere almeno 10 caratteri.");
}
return $this->render('account/b2c_customer_registration.html.twig', [
'form' => $form->createView(),
'errors' => $errors,
'hideBreadcrumbs' => true
]);
}
/**
*
* @param array $formData
* @param UserResponseInterface $userInformation
*
* @return array
*/
private function mergeOAuthFormData(
array $formData,
UserResponseInterface $userInformation
): array {
return array_replace([
'firstname' => $userInformation->getFirstName(),
'lastname' => $userInformation->getLastName(),
'email' => $userInformation->getEmail()
], $formData);
}
/**
* Index page for account - it is restricted to ROLE_USER via security annotation
*
* @Route("/b2c/account/index", name="b2c-account-index")
* @Route("/en/b2c/account/index", name="b2c-account-index-en")
* @Security("is_granted('ROLE_USER')")
*
* @param SsoIdentityServiceInterface $identityService
* @param UserInterface|null $user
*
* @return Response
*/
public function indexAction(Request $request, UserInterface $user = null,PaginatorInterface $paginator, SessionInterface $session)
{
if(!$user){
return $this->redirectToRoute('account-login');
}
$limit = $request->get('limit',20);
$orderColumn = $request->get('column','oo_id');
$orderBy = $request->get('order','desc');
$quoteListing = new DataObject\Quote\Listing();
$quoteListing->filterByUser($user);
$quoteListing->setOrderKey($orderColumn);
$quoteListing->setOrder($orderBy);
$paginator = $paginator->paginate(
$quoteListing,
$request->get('page', 1),
$limit
);
return $this->render('account/b2c_quotation_list.html.twig', [
'paginator' => $paginator,
'column'=>$orderColumn,
'order'=>$orderBy,
'paginationVariables' => $paginator->getPaginationData(),
'limit'=>$limit
]);
}
/**
* Index page for account - it is restricted to ROLE_USER via security annotation
*
* @Route("/b2c/account/profile", name="b2c-account-profile")
* @Security("is_granted('ROLE_USER')")
*
* @param RegistrationFormHandler $registrationFormHandler
* @param UserInterface|null $user
*
* @return Response
*/
public function profileAction(Request $request,RegistrationFormHandler $registrationFormHandler, UserInterface $user = null,PaginatorInterface $paginator, SessionInterface $session)
{
//redirect user to index page if logged in
if (!$user) {
return $this->redirectToRoute('b2c-account-login');
}
$customer = $user;
$formData = $registrationFormHandler->buildFormData($customer);
$form = $this->createForm(RegistrationFormType::class,$formData);
$form->remove('password');
$form->remove('consent');
$form->handleRequest($request);
$errors = [];
if ($form->isSubmitted() && $form->isValid()) {
$registrationFormHandler->setData($form,$request,$customer,true);
$customer->setCustomerLanguage($request->getLocale());
$customer->setActive(true);
try {
$customer->save();
$response = $this->redirectToRoute('b2c-account-index');
return $response;
} catch (DuplicateCustomerException $e) {
$errors[] = $translator->trans(
'account.customer-already-exists',
[
$customer->getEmail(),
$urlGenerator->generate('account-password-send-recovery', ['email' => $customer->getEmail()])
]
);
} catch (\Exception $e) {
$errors[] = $e->getMessage();
}
}
$customerAddress = new DataObject\CustomerAddressB2C\Listing();
$customerAddress->filterByUser($user);
return $this->render('account/b2c_customer_profile.html.twig', [
'form' => $form->createView(),
'errors' => $errors,
'addresses'=>$customerAddress,
'hideBreadcrumbs' => true
]);
}
/**
* @Route("/account/send-password-recovery", name="account-password-send-recovery")
*
* @param Request $request
* @param PasswordRecoveryService $service
* @param Translator $translator
*
* @return Response
*
* @throws \Exception
*/
public function sendPasswordRecoveryMailAction(Request $request, PasswordRecoveryService $service, Translator $translator)
{
if ($request->isMethod(Request::METHOD_POST)) {
try {
$customer = $service->sendRecoveryMail($request->get('email', ''), $this->document->getProperty('password_reset_mail'));
if (!$customer instanceof CustomerInterface) {
throw new \Exception('Invalid Customer');
}
$this->addFlash('success', $translator->trans('account.reset-mail-sent-when-possible'));
} catch (\Exception $e) {
$this->addFlash('danger', $e->getMessage());
}
return $this->redirectToRoute('b2c-account-login', ['no-referer-redirect' => true]);
}
return $this->render('account/send_password_recovery_mail.html.twig', [
'hideBreadcrumbs' => true,
'emailPrefill' => $request->get('email')
]);
}
/**
* @Route("/b2c/account/reset-password", name="b2c-account-reset-password")
*
* @param Request $request
* @param PasswordRecoveryService $service
* @param Translator $translator
*
* @return Response|RedirectResponse
*/
public function resetPasswordAction(Request $request, PasswordRecoveryService $service, Translator $translator)
{
$token = $request->get('token');
$customer = $service->getCustomerByToken($token);
if (!$customer) {
//TODO render error page
throw new NotFoundHttpException('Invalid token');
}
if ($request->isMethod(Request::METHOD_POST)) {
$newPassword = $request->get('password');
$service->setPassword($token, $newPassword);
$this->addFlash('success', $translator->trans('account.password-reset-successful'));
return $this->redirectToRoute('b2c-account-login', ['no-referer-redirect' => true]);
}
return $this->render('account/reset_password.html.twig', [
'hideBreadcrumbs' => true,
'token' => $token,
'email' => $customer->getEmail()
]);
}
private function checkPermission(){
$allow = false;
$userObject = \Pimcore::getContainer()->get('security.token_storage')->getToken()->getUser();
if($this->service->isAllowed($userObject, "draft_create") || $this->service->isAllowed($userObject, "booking_create") || $this->service->isAllowed($userObject, "order_create") || $this->service->isAllowed($userObject, "order_view")) {
$allow = true;
}
return $allow;
}
/**
* @Route("/account/b2c/wish-list/index", name="b2c-wishlist-index")
* @Security("is_granted('ROLE_USER')")
* @param Request $request
* @return Response
*
* @throws \Exception
*/
public function wishListIndexAction(Request $request,UserInterface $user = null)
{
if (!$user) {
return $this->redirectToRoute('b2c-account-login');
}
$result = [];
$products = [];
$productId = $request->get('id',0);
$params = array_merge($request->query->all(), $request->attributes->all());
if($user){
$db = Db::get();
$orderBy = " ow.oo_id DESC";
if(isset($params['orderby'])){
if($params['orderby'] == "price")
{
$orderBy = 'CONSORZ ASC';
}else if($params['orderby'] == "price-desc"){
$orderBy = 'CONSORZ DESC';
}else if($params['orderby'] == "date"){
$orderBy = 'oo_id DESC';
}else if($params['orderby'] == "popularity"){
$orderBy = 'oo_id DESC';
}
}else{
$orderBy = "ow.o_modificationDate DESC";
}
$lists = [];
$sql = 'SELECT ow.oo_id FROM `object_wishlist` ow LEFT JOIN object_prod op ON ow.item__id = op.oo_id WHERE user__id = '.$user->getId().' ORDER BY '.$orderBy;
$products = $db->fetchAll($sql);
if($products)
{
foreach($products as $prod){
$lists[] = \Pimcore\Model\DataObject::getById($prod['oo_id']);
}
}
$params['products'] = $lists;
$params['orderby'] = $orderBy;
$params['isMecstoreUser'] = false;
$partner = $user->getPartner();
if($partner->getisMecstoreHQ()){
$params['isMecstoreUser'] = true;
}
}
return $this->render('product_b2c/wishlist.html.twig', $params);
}
/**
* @Route("/account/get_branch_by_province", name="account-get-branch-by-province")
* @return Response|RedirectResponse
*/
public function getBranchByRegion(Request $request){
$result = [];
$branches = [];
$user = null;
$id = $request->get('id',null);
if($id){
$db = \Pimcore\Db::get();
$provinces = $db->fetchAll("SELECT * FROM province WHERE code='$id' limit 1");
if($provinces){
$partnerBranches = new DataObject\Partners\Listing();
$partnerBranches->setCondition("Region = ? AND isMecstoreHQ != 1",[$provinces[0]['region']]);
$partnerBranches = $partnerBranches->load();
if($partnerBranches)
{
foreach($partnerBranches as $branch){
$branches[$branch->getId()] = $branch->getCompany().'-'.$branch->getCity().', '.$branch->getRegion().' - '.$branch->getZip();
}
$result = ['success'=>true,'data'=>$branches];
return $this->json($result);
}
}
}
return $this->json(['success'=>false]);
}
}