Confused about prestashop PDF generated process. Where is the code to handle the logic? - prestashop

I want to deep understand prestahop inside structure and amend some parts. I am stoped in the PDF. I want can't find the controller used to handle the AdminPdf and generateDeliverySlipPDF
{if $order->delivery_number}
<a class="btn btn-default _blank" href="{$link->getAdminLink('AdminPdf')|escape:'html':'UTF-8'}&submitAction=generateDeliverySlipPDF&id_order={$order->id}">
<i class="icon-truck"></i>
</a>
{/if}
Who can help me figure out the inside processes? I can't find the methods to handle generateDeliverySlipPDF.

AdminPdfController is located at /controllers/admin/AdminPdfController.php.
The submitAction=generateDeliverySlipPDF part of the url will call the method processGenerateDeliverySlipPDF() inside this controller.
Here is this method:
public function processGenerateDeliverySlipPDF()
{
if (Tools::isSubmit('id_order')) {
$this->generateDeliverySlipPDFByIdOrder((int)Tools::getValue('id_order'));
} elseif (Tools::isSubmit('id_order_invoice')) {
$this->generateDeliverySlipPDFByIdOrderInvoice((int)Tools::getValue('id_order_invoice'));
} elseif (Tools::isSubmit('id_delivery')) {
$order = Order::getByDelivery((int)Tools::getValue('id_delivery'));
$this->generateDeliverySlipPDFByIdOrder((int)$order->id);
} else {
die(Tools::displayError('The order ID -- or the invoice order ID -- is missing.'));
}
}
In this Controller you'll find other methods as this one to generate Invoices, Order, ... and other PDFs.
Feel free to ask if you need more informations.
EDIT:
If you want to change format in a proper way you'll have to override those classes:
/override/classes/pdf/PDFGenerator.php:
<?php
/**
* #since 1.5
*/
class PDFGenerator extends PDFGeneratorCore
{
/**
* #param bool $use_cache
* #param string $orientation
* #param string $format
*/
public function __construct($use_cache = false, $orientation = 'P', $format = 'A4')
{
TCPDF::__construct($orientation, 'mm', $format, true, 'UTF-8', $use_cache, false);
$this->setRTL(Context::getContext()->language->is_rtl);
}
}
/override/classes/pdf/PDF.php:
<?php
/**
* #since 1.5
*/
class PDF extends PDFCore
{
/**
* #param $objects
* #param $template
* #param $smarty
* #param string $orientation
*/
public function __construct($objects, $template, $smarty, $orientation = 'P', $format = 'A4')
{
parent::__construct($objects, $template, $smarty, $orientation);
$this->pdf_renderer = new PDFGenerator((bool)Configuration::get('PS_PDF_USE_CACHE'), $orientation, $format);
}
}
/override/controllers/admin/AdminPdfController.php:
<?php
class AdminPdfController extends AdminPdfControllerCore
{
public function generatePDF($object, $template)
{
switch($template) {
case PDF::TEMPLATE_DELIVERY_SLIP:
$format = array(210, 50000); // Replace with your desired size
break;
default:
$format = 'A4';
}
$pdf = new PDF($object, $template, Context::getContext()->smarty, 'P', $format);
$pdf->render();
}
}
Now you can specify the format for each PDF.
You will find informations about $format at this place
This code has not been tested but should work as expected. Let me know if you encounter any issue.
You will need to delete /cache/class_index.php after adding those overrides to clear Prestashop internal classes path cache.

thank you for this topic,
I would like to ask you for the case of generating pdf in front?
i.e
this is a part of history.tpl
`{if $order.details.invoice_url}
<i class="material-icons"></i>`
i found $order.details.invoice_url is define in OrderPresenter.php
and OrderPresenter use HistoryController to get the invoice_url.
So i take a look in historyController
if ((bool) Configuration::get('PS_INVOICE') && OrderState::invoiceAvailable($order->current_state) && count($order->getInvoicesCollection())) {
$url_to_invoice = $context->link->getPageLink('pdf-invoice', true, null, 'id_order='.$order->id);
if ($context->cookie->is_guest) {
$url_to_invoice .= '&secure_key='.$order->secure_key;
}
}
getPageLink use "pdf-invoice" ,i take a look for getPageLink Method and see that "pdf-invoice" is a controller...
the trouble is i don't know where is this controller?
help please

Related

How to export PDF from back-end edit/update view using DynamicPDF plugin in OctoberCMS

I am going to use DynamicPDF plugin to export to pdf some fields from backend on update/edit view of my plugin in OctoberCMS, can someone help me?
on plugin controller i have this call:
<?php namespace Vimagem\Pacientes\Controllers;
use Backend\Classes\Controller;
use BackendMenu;
use Renatio\DynamicPDF\Classes\PDF;
use Renatio\DynamicPDF\Classes\PDFWrapper;
class Pacientes extends Controller
{
public $implement = [ 'Backend\Behaviors\ListController', 'Backend\Behaviors\FormController', 'Backend\Behaviors\ReorderController' ];
public $listConfig = 'config_list.yaml';
public $formConfig = 'config_form.yaml';
public $reorderConfig = 'config_reorder.yaml';
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Vimagem.Pacientes', 'main-menu-item');
}
/** PDF **/
public function pdf($id)
{
return PDF::loadTemplate('export-data-pdf')->stream('download.pdf');
}
}
On the PDF Template (export-data-pdf) i need to call some form fields from one client:
{{ name }}
{{ address }}
{{ phone }}
etc...
but i can´t get the fields show up, what its wrong ?
Thank you,
Vitor
This code was found in the plugins documents.
use Renatio\DynamicPDF\Classes\PDF; // import facade
...
public function pdf()
{
$templateCode = 'renatio::invoice'; // unique code of the template
$data = ['name' => 'John Doe']; // optional data used in template
return PDF::loadTemplate($templateCode, $data)->stream('download.pdf');
}
I have used this plugin and it works well. You need to pass in data to the PDF stream.
This is done, worked around a solution for this.
Here is the controller application:
<?php namespace Vimagem\Pacientes\Controllers;
use Backend\Classes\Controller;
use BackendMenu;
use Renatio\DynamicPDF\Classes\PDFWrapper;
use Vimagem\Pacientes\Models\Paciente;
use \October\Rain\Database\Traits\Validation;
use Str;
class Pacientes extends Controller
{
public $implement = [ 'Backend\Behaviors\ListController', 'Backend\Behaviors\FormController', 'Backend\Behaviors\ReorderController' ];
public $listConfig = 'config_list.yaml';
public $formConfig = 'config_form.yaml';
public $reorderConfig = 'config_reorder.yaml';
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Vimagem.Pacientes', 'main-menu-item');
}
/**** PDF Export ***/
public function pdf($id)
{
$paciente = Paciente::find($id);
if ($paciente === null) {
throw new ApplicationException('User not found.');
}
$filename = Str::slug($paciente->nome) . '.pdf';
try {
/** #var PDFWrapper $pdf */
$pdf = app('dynamicpdf');
$options = [
'logOutputFile' => storage_path('temp/log.htm'),
];
return $pdf
->loadTemplate('export-data-pdf', compact('paciente'))
->setOptions($options)
->stream($filename);
} catch (Exception $e) {
throw new ApplicationException($e->getMessage());
}
}
}
Now i can use partials on the template like this:
<p>{{ paciente.nome }}</p>
<p>{{ paciente.morada }}</p>
etc...
Thank you all that try to helped me.
Vitor

Sylius - Editable Product Code (SKU)

I need to implement an SKU code for products, I was just wondering has anybody any thought on the best way to do this. The SKU needs to be editable after creation.
I feel I have a couple of ways:
(Idealy) I would like to use Product.Code, but this is not an editable field after product creation. I would seem I need to override the ProductType#buildForm class/method to not use AddCodeFormSubscriber(). Although I can't seem to figure out how to get the system to use a different form.
Add SKU to the model of Product and figure out how to add it to the ProductType form and again try and figure out how to use a different form.
I am open to suggestions on how to do it the right way.
Would any of the Sylius developers care to elaborate why they decided to make the Code field un-editable?
If you want use product code as editable field in Sylius Beta.1, you can create ProductType extension to current product type and add your custom subscriber which will make code field editable. I did this in my bundle and it works:
create subscriber class wchich will change disabled state to false:
namespace App\Bundle\Form\EventListener;
/* add required namespaces */
/**
* Custom code subscriber
*/
class CustomCodeFormSubscriber implements EventSubscriberInterface
{
private $type;
private $label;
/**
* #param string $type
* #param string $label
*/
public function __construct($type = TextType::class, $label = 'sylius.ui.code')
{
$this->type = $type;
$this->label = $label;
}
/**
* {#inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SET_DATA => 'preSetData',
];
}
/**
* #param FormEvent $event
*/
public function preSetData(FormEvent $event)
{
$disabled = false;
$form = $event->getForm();
$form->add('code', $this->type, ['label' => $this->label, 'disabled' => $disabled]);
}
}
create form extension and use custom subscriber:
namespace App\Bundle\Form\Extension;
use App\Bundle\Form\EventListener\CustomCodeFormSubscriber;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Sylius\Bundle\ProductBundle\Form\Type\ProductType;
/* use other required namespaces etc */
/**
* Extended Product form type
*/
class ProductTypeExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
/* custom stuff for ur form */
$builder->addEventSubscriber(new CustomCodeFormSubscriber());
}
public function getExtendedType()
{
return ProductType::class;
}
}
register your form extension as a service:
app.form.extension.type.product:
class: App\Bundle\Form\Extension\ProductTypeExtension
tags:
- { name: form.type_extension, priority: -1, extended_type: Sylius\Bundle\ProductBundle\Form\Type\ProductType }

FOSUserBundle - changes in default configuration, overriding some default settings

I am quite new in Symfony 2. I have to build an application in Symfony 2 (I'm using 2.8 and also latest version of FOSUser Bundle). Majority of work is done, although I have to do some changes in default security settings. I was looking for them for two days and I have only really foggy concept how this can be fixed. The supposed changes are following:
REGISTRATION - I have enabled confirmation by e-mail and I don't know how to make to the confirmation link expire after one hour and/or after one usage (one click). There is setting in config.yaml settting ttl, but only for password resseting.
REGISTRATION - Before confirming users are prevented from logging in and there is Symfony 2 Exception - Disabled Account working, rendering short message about it. Actually I have to set redirection to another page (I suppose template) to render custom message that 'this account is blocked...' and link to send another link with confirmation token (Am I right? The link in email is confirmation token?).
RESETTING - As mentioned before ttl for link (token?) ressetting password is set for one hour, but I don't know how to make it expire after one usage (one click).
I know how to override some template of FOSUser, but I have no clear idea which of files should I override to change these things.
I have noticed that my FOSUser uses Symfony 2 Exceptions files and if I have changed content of message in my Exception file, it has changed also on my page, but I don't know how to make it well and override it, adding all necessary features.
I was trying to override AuthenticationListener (from FOSUser) with use of Compiler Pass (http://symfony.com/doc/current/cookbook/service_container/compiler_passes.html), but I don't know if it worked, because any changes in overriden Listener were not visible. Actually I don't know if this is the file I should override.
I have check a few Stackoverflow questions, but I haven't found an answer.
FOSUser Bundle - Prevent Disabled users from logging in
This doesn't work for me, because users are prevented and I need only override message of exception and create redirection with another link sending confirmation one.
FOS user bundle authentication
I have tried to implement the solution pointed here, but it didn't work and I am not sure if I really need such a complicated solution.
Thanks for help in advance and I someone need to see my files, configuration just write and I will post here necessary ones.
I have finally figured all these things out. If someone has a similar problem i advise to read these topics in Symfony documentation:
Overriding FOSUser B controllers
Hooking into a controller
It turns out that for described features I need to override some files form FOSUser Bundle, sometimes Controller was enough, sometimes I needed to modify EventListener (actually I have even created my own event). There is more than one way.
The hardest part was one-click link for ressetting password. I have used a flag, which is set to false while sending an e-mail and set true while clicking on link to prevent from using link once more. The problem is, that Resetting Controller is 'executed' two times, so while clicking submit there was redirection, beacuse flag was true. I have added some counting in session in order to omit the part of code which checks the flag, when you hit the submit button (second usage of the Reset Method in Resetting Controller), but it prevented only from clicking submit second time, so actually you can not use the link two times, but you can see form two times, which is not an effect I wanted to reach, but is far better than nothing. If someone has an idea how to upgrade it I will be gratefull
<?php
namespace My\UserBundle\Controller;
use FOS\UserBundle\Controller\ResettingController as FOSResettingController;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use My\UserBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use My\UserBundle\UserEvents;
/**
* Controller managing the resetting of the password
*
* #author Thibault Duplessis <thibault.duplessis#gmail.com>
* #author Christophe Coevoet <stof#notk.org>
*/
class ResettingController extends FOSResettingController
{
/**
* Request reset user password: submit form and send email
*/
public function sendEmailAction(Request $request)
{
$username = $request->request->get('username');
/** #var $user UserInterface */
$user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);
if (null === $user) {
return $this->render('FOSUserBundle:Resetting:request.html.twig', array(
'invalid_username' => $username
));
}
if ($user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
return $this->render('FOSUserBundle:Resetting:passwordAlreadyRequested.html.twig');
}
if (null === $user->getConfirmationToken()) {
/** #var $tokenGenerator \FOS\UserBundle\Util\TokenGeneratorInterface */
$tokenGenerator = $this->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
}
$this->get('fos_user.mailer')->sendResettingEmailMessage($user);
$user->setPasswordRequestedAt(new \DateTime());
$user->setPasswordRequestedClicked(false);
$this->get('fos_user.user_manager')->updateUser($user);
$_SESSION['views'] = 1;
return new RedirectResponse($this->generateUrl('fos_user_resetting_check_email',
array('email' => $this->getObfuscatedEmail($user))
));
}
/**
* Reset user password
*/
public function resetAction(Request $request, $token)
{
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.resetting.form.factory');
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->findUserByConfirmationToken($token);
//Here there is a reaction for using expired token (column confirmation token === null) - redirection to page with possibility of sending another one.
if (null === $user) {
return $this->redirectToRoute('fos_user_invalid_token_click');
}
if ($_SESSION['views'] == 1){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
} else {
$user->setPasswordRequestedClicked(true);
$userManager->updateUser($user);
$_SESSION['views']++;
$_SESSION['views']++;
}
} else {
$_SESSION['views']++;
}
if ($_SESSION['views'] == 5){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
}
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$userManager->updateUser($user);
return $this->render('FOSUserBundle:Resetting:reset.html.twig', array(
'token' => $token,
'form' => $form->createView(),
));
}
public function InvalidTokenTtlMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenTtlRes.html.twig');
}
public function InvalidTokenClickMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenClickRes.html.twig');
}
}
My listener:
<?php
namespace My\UserBundle\EventListener;
use FOS\UserBundle\EventListener\ResettingListener as FOSResettingListener;
use FOS\UserBundle\FOSUserEvents;
use My\UserBundle\UserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use My\UserBundle\Entity\User;
class ResettingListener extends FOSResettingListener
{
private $router;
private $tokenTtl;
public function __construct(UrlGeneratorInterface $router, $tokenTtl)
{
$this->router = $router;
$this->tokenTtl = $tokenTtl;
}
public static function getSubscribedEvents()
{
return array(
UserEvents::RESETTING_RESET_CLICK_CHECK => 'onResettingClickCheck',
FOSUserEvents::RESETTING_RESET_INITIALIZE => 'onResettingResetInitialize',
FOSUserEvents::RESETTING_RESET_SUCCESS => 'onResettingResetSuccess',
);
}
public function onResettingClickCheck(GetResponseUserEvent $event){
//checking if link hasn't expired due to its usage
if ($event->getUser()->isPasswordRequestedClicked() === true){
$event->setResponse(new RedirectResponse($this->router->generate('fos_user_invalid_token_click')));
}
}
public function onResettingResetInitialize(GetResponseUserEvent $event)
{
//checking if link hasn't expired due to exceeding token Ttl
if (!$event->getUser()->isPasswordRequestNonExpired($this->tokenTtl)) {
$event->setResponse(new RedirectResponse($this->router->generate('fos_user_invalid_token_ttl')));
}
}
public function onResettingResetSuccess(FormEvent $event)
{
/** #var $user \FOS\UserBundle\Model\UserInterface */
$user = $event->getForm()->getData();
$user->setConfirmationToken(null);
$user->setPasswordRequestedAt(null);
$user->setEnabled(true);
}
}
and my User entity:
<?php
namespace My\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use My\BackendBundle\Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="My\UserBundle\Repository\UserRepository")
*/
class User extends BaseUser
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable = false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Gedmo\Slug(fields={"username"})
* #ORM\Column(length=128, unique=true)
*/
private $slug;
/**
*
* #ORM\ManyToMany(targetEntity="\My\BackendBundle\Entity\Event", mappedBy="users")
* #ORM\JoinColumn(name="id", referencedColumnName="id", nullable=false)
* #ORM\OrderBy({"date"="ASC"})
*
*/
protected $events;
/**
* #var \Doctrine\Common\Collections\ArrayCollection $event_org
* #ORM\OneToMany(targetEntity="\My\BackendBundle\Entity\Event", mappedBy="user_org", cascade={"all"})
*/
protected $event_org;
/**
* #var \DateTime
* #ORM\Column(name="confirmation_token_requested_at", type="datetime")
*/
protected $confirmationTokenRequestedAt;
/**
* #var boolean
* #ORM\Column(name="password_requested_clicked", type="boolean", nullable=true)
*/
protected $passwordRequestedClicked;
public function __toString()
{
return $this->getUsername();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
public function __construct()
{
parent::__construct();
$this->event_org = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add event
*
* #param \My\BackendBundle\Entity\Event $event
*
* #return User
*/
public function addEvent(\My\BackendBundle\Entity\Event $event)
{
$this->events[] = $event;
$event->addUser($this);
return $this;
}
/**
* Remove event
*
* #param \My\BackendBundle\Entity\Event $event
*/
public function removeEvent(\My\BackendBundle\Entity\Event $event)
{
$this->events->removeElement($event);
}
/**
* Get events
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEvents()
{
return $this->events;
}
/**
* Set slug
*
* #param string $slug
*
* #return User
*/
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
/**
* Get slug
*
* #return string
*/
public function getSlug()
{
return $this->slug;
}
/**
* Add eventOrg
*
* #param \My\BackendBundle\Entity\Event $eventOrg
*
* #return User
*/
public function addEventOrg(\My\BackendBundle\Entity\Event $eventOrg)
{
$this->event_org[] = $eventOrg;
return $this;
}
/**
* Remove eventOrg
*
* #param \My\BackendBundle\Entity\Event $eventOrg
*/
public function removeEventOrg(\My\BackendBundle\Entity\Event $eventOrg)
{
$this->event_org->removeElement($eventOrg);
}
/**
* Get eventOrg
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEventOrg()
{
return $this->event_org;
}
/**
* Set confirmationTokenRequestedAt
*
* #param \DateTime $confirmationTokenRequestedAt
*
* #return User
*/
public function setConfirmationTokenRequestedAt(\DateTime $date = null)
{
$this->confirmationTokenRequestedAt = $date;
return $this;
}
/**
* Gets the timestamp that the user requested a confirmation_token.
*
* #return null|\DateTime
*/
public function getConfirmationTokenRequestedAt()
{
return $this->confirmationTokenRequestedAt;
}
public function isConfirmationTokenNonExpired($ttl)
{
return $this->getConfirmationTokenRequestedAt() instanceof \DateTime &&
$this->getConfirmationTokenRequestedAt()->getTimestamp() + $ttl > time();
}
/**
* Set passwordRequestedClicked
*
* #param boolean $passwordRequestedClicked
*
* #return User
*/
public function setPasswordRequestedClicked($boolean)
{
$this->passwordRequestedClicked = (Boolean) $boolean;
return $this;
}
/**
* Get passwordRequestedClicked
*
* #return boolean
*/
public function getPasswordRequestedClicked()
{
return $this->passwordRequestedClicked;
}
/**
* Checks whether the user has used password request.
*
*
* #return Boolean true if the user is enabled, false otherwise
*/
public function isPasswordRequestedClicked() {
return $this->passwordRequestedClicked;
}
}
If someone would like to get code for remaining problems, please write me a message and I will provide it here :).
Resetting controller:
<?php
namespace My\UserBundle\Controller;
use FOS\UserBundle\Controller\ResettingController as FOSResettingController;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use My\UserBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use My\UserBundle\UserEvents;
class ResettingController extends FOSResettingController
{
public function sendEmailAction(Request $request)
{
$username = $request->request->get('username');
$user = $this->get('fos_user.user_manager')->findUserByUsernameOrEmail($username);
if (null === $user) {
return $this->render('FOSUserBundle:Resetting:request.html.twig', array(
'invalid_username' => $username
));
}
if ($user->isPasswordRequestNonExpired($this->container->getParameter('fos_user.resetting.token_ttl'))) {
return $this->render('FOSUserBundle:Resetting:passwordAlreadyRequested.html.twig');
}
if (null === $user->getConfirmationToken()) {
$tokenGenerator = $this->get('fos_user.util.token_generator');
$user->setConfirmationToken($tokenGenerator->generateToken());
}
$this->get('fos_user.mailer')->sendResettingEmailMessage($user);
$user->setPasswordRequestedAt(new \DateTime());
$user->setPasswordRequestedClicked(false);
$this->get('fos_user.user_manager')->updateUser($user);
$_SESSION['views'] = 1;
return new RedirectResponse($this->generateUrl('fos_user_resetting_check_email',
array('email' => $this->getObfuscatedEmail($user))
));
}
public function resetAction(Request $request, $token)
{
$formFactory = $this->get('fos_user.resetting.form.factory');
$userManager = $this->get('fos_user.user_manager');
$dispatcher = $this->get('event_dispatcher');
$user = $userManager->findUserByConfirmationToken($token);
//Here there is a reaction for using expired token (column confirmation token === null) - redirection to page with possibility of sending another one.
if (null === $user) {
return $this->redirectToRoute('fos_user_invalid_token_click');
}
if ($_SESSION['views'] == 1){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
} else {
$user->setPasswordRequestedClicked(true);
$userManager->updateUser($user);
$_SESSION['views']++;
$_SESSION['views']++;
}
} else {
$_SESSION['views']++;
}
if ($_SESSION['views'] == 5){
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(UserEvents::RESETTING_RESET_CLICK_CHECK, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
}
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
$userManager->updateUser($user);
return $this->render('FOSUserBundle:Resetting:reset.html.twig', array(
'token' => $token,
'form' => $form->createView(),
));
}
public function InvalidTokenTtlMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenTtlRes.html.twig');
}
public function InvalidTokenClickMessageAction() {
return $this->render('UserBundle:Resetting:invalidTokenClickRes.html.twig');
}
}

Laravel Auth::check() only returns true on post page

I am new at Laravel and having some struggles with simple login and authorization pages.
I followed an awesome video tutorial by laracasts.com (credits for that one, really helpful).
My situation:
When implementing authorization to my page, the page after the login succeeds the authorization check.
So: loginform > press button > You are now logged in.
My problem:
After I press the back button and refresh, it still gives the login form. It shouldn't.
routes.php
<?php
Route::get('login', 'SessionsController#create');
Route::get('logout', 'SessionsController#destroy');
Route::resource('users', 'UsersController');
Route::resource('sessions', 'SessionsController');
Route::get('admin', function(){
return 'Admin page';
})->before('auth');
Route::get('dashboard', ['before' => 'auth', function(){
return 'Dashboard';
}]);
SessionsController.php:
<?php
class SessionsController extends BaseController{
public function create()
{
if ( Auth::check() )
{
Redirect::to('/admin');
}
return View::make('sessions.create');
}
public function store()
{
if( Auth::attempt(Input::only('email', 'password')) )
{
// if(Auth::check())
// {
// return 'check worked!';
// }
return 'Welcome ' . Auth::user()->username; //You are now logged in
}
return 'Failed';
}
public function destroy()
{
Auth::logout();
return Redirect::to('sessions.create');
}
}
User.php
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
public $timestamps = true;
protected $fillable = ['username', 'email', 'password'];
protected $guarded = ['id'];
public static $rules = [
'username' => 'required',
'password' => 'required'
];
public $errors;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
/**
* Get the e-mail address where password reminders are sent.
*
* #return string
*/
public function getReminderEmail()
{
return $this->email;
}
public function isValid()
{
$validation = Validator::make($this->attributes, static::$rules );
if($validation->passes() )
return true;
$this->errors = $validation->messages();
return false;
}
}
create.blade.php
#extends('layouts.default')
#section('content')
<h1>Create new user</h1>
{{Form::open([ 'route' => 'users.store' ]) }}
<div>
{{Form::label('username', 'Username: ')}}
{{Form::text('username')}}
{{$errors->first('username')}}
</div>
<div>
{{Form::label('password', 'Password: ')}}
{{Form::password('password')}}
{{$errors->first('password')}}
</div>
<div>
{{Form::submit('Create User')}}
</div>
{{Form::close() }}
#stop
So to speak: It never goes to the 'admin' route.
Your authentication code is correct and working. What you have is something going wrong in any other part of your Laravel application, web server or even PHP.
Since we are not seeing all your code, we can just guess, so my first one would be the Session not being stored correctly. Currently logged users are stored in Laravel Session. So, check your session driver, if it's in 'native', change it to 'database', but you'll have to create a sessions table, look at the docs. If you are already using 'database', change it back to 'native' or even 'file'.
Instead of run
return 'Welcome ' . Auth::user()->username; //You are now logged in
Please try
return Redirect::to('admin');

Multiple file upload with Symfony2

I'm trying to upload multiple files via a form, but I can only upload one file at a time, the last one I mark in the browser. Is there a way to upload more images with Symfony2 using a simple form?
Here is the twig template of the form I'm using to be able to mark more than one file:
{{ form_widget(form.post_image, { 'attr': {'multiple': 'multiple' }}) }}
Ok binding issue solved (enctype syntax error) : i'll give you the code i use. maybe it will help...
I have a Gallery Entity
class Gallery
{
protected $id;
protected $name;
protected $description;
private $urlName;
public $files; // the array which will contain the array of Uploadedfiles
// GETTERS & SETTERS ...
public function getFiles() {
return $this->files;
}
public function setFiles(array $files) {
$this->files = $files;
}
public function __construct() {
$files = array();
}
}
I have a form class that generate the form
class Create extends AbstractType {
public function buildForm(FormBuilder $builder, array $options) {
$builder->add('name','text',array(
"label" => "Name",
"required" => TRUE,
));
$builder->add('description','textarea',array(
"label" => "Description",
"required" => FALSE,
));
$builder->add('files','file',array(
"label" => "Fichiers",
"required" => FALSE,
"attr" => array(
"accept" => "image/*",
"multiple" => "multiple",
)
));
}
}
Now in the controller
class GalleryController extends Controller
{
public function createAction() {
$gallery = new Gallery();
$form = $this->createForm(new Create(), $gallery);
// Altering the input field name attribute
$formView = $form->createView();
$formView->getChild('files')->set('full_name', 'create[files][]');
$request = $this->getRequest();
if($request->getMethod() == "POST")
{
$form->bindRequest($request);
// print "<pre>".print_r($gallery->getFiles(),1)."</pre>";
if($form->isValid())
{
// Do what you want with your files
$this->get('gallery_manager')->save($gallery);
return $this->redirect($this->generateUrl("_gallery_overview"));
}
}
return $this->render("GalleryBundle:Admin:create.html.twig", array("form" => $formView));
}
}
Hope this help...
NB: If someone know a better way to alter this f** name attribute, maybe in the FormView class or by declaring a new field type, feel free to show us your method...
No extra classes needed (except the gallery_manger service but the issue you describe happens before...)
I don't really know what's wrong. Check for your template (maybe wrong enctype... or name attr missmatching)
first try to do a single file upload, check the documentation:
file Field Type
How to handle File Uploads with Doctrine
Once it works, you have to edit some lines.
add input file multiple attribute.
append [] at the end of the input file name attribute (mine is
create...[] because my form class name is create, if your is
createType it will be createType...[])
init $files as an array.
Copy/paste your code here.
All the suggestions I've found here are workarounds for the real situation.
In order to be able to have multiple attachments, you should use form collection.
Quote from the documentation:
In this entry, you'll learn how to create a form that embeds a collection of many other forms. This could be useful, for example, if you had a Task class and you wanted to edit/create/remove many Tag objects related to that Task, right inside the same form.
http://symfony.com/doc/2.0/cookbook/form/form_collections.html
Example case: You have a document, which form is specified by DocumentType. The document must have multiple attachments, which you can have by defining AttachmentType form and adding it as a collection to the DocumentType form.
For sf > 2.2 :
In you form type class, add this overrided method :
public function finishView(FormView $view, FormInterface $form, array $options) {
$view->vars['form']->children['files']->vars['full_name'] .= '[]';
}
Note that i try to do the same thing in sf2 using this syntax:
In the controller:
public function stuffAction() {
$form = $this->createFormBuilder()
->add('files','file',array(
"attr" => array(
"accept" => "image/*",
"multiple" => "multiple",
)
))
->getForm();
$formView = $form->createView();
$formView->getChild('files')->set('full_name', 'form[files][]');
// name param (eg 'form[files][]') need to be the generated name followed by []
// try doing this : $formView->getChild('files')->get('full_name') . '[]'
$request = $this->getRequest();
if($request->getMethod() == "POST") {
$form->bindRequest($request);
$data = $form->getData();
$files = $data["files"];
// do stuff with your files
}
}
return $this->render('Bundle:Dir:index.html.twig',array("form" => $formView));
}
$files will be an array of uploaded files...
Calling $form->createView() to alter the name attribute is certainly not the best way / cleanest way to do it but it's the only one i found that keeps the csrf functionality working, because altering the name attribute in a twig template makes it invalid...
Now I still have an issue using a form class which generate the form, I don't know why during the binding of the form data & object attached to the form my array of uploaded files is transformed in array of (file) name ???
use this methode :
$form = $this->createFormBuilder()
->add('attachments','file', array('required' => true,"attr" => array(
"multiple" => "multiple",
)))
->add('save', 'submit', array(
'attr' => array('class' => 'btn btn-primary btn-block btn-lg'),
'label' => 'save'
))
->getForm();
then you add [] to the name of your input via jQuery :
<input id="form_attachments" name="form[attachments]" required="required" multiple="multiple" type="file">
jQuery code :
<script>
$(document).ready(function() {
$('#form_attachments').attr('name',$('#form_attachments').attr('name')+"[]");
});
</script>
Here is easy example to upload multiple files. I have similar problem with upload files.
https://github.com/marekz/example_symfony_multiply_files_example
For symfony 3.*
First: Both form declatartion:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Form\FilesType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
class UserType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('lastName')
->add('files', CollectionType::class,array(
'entry_type' => FilesType::class,
'allow_add' => true,
'by_reference' => false,
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_user';
}
}
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class FilesType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('file');
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Files'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_files';
}
}
Now, my entities:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="lastName", type="string", length=255)
*/
private $lastName;
/**
* #ORM\ManyToMany(targetEntity="Files", cascade={"persist"})
*/
private $files;
function __construct() {
$this->files = new ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId() {
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return User
*/
public function setName($name) {
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName() {
return $this->name;
}
/**
* Set lastName
*
* #param string $lastName
*
* #return User
*/
public function setLastName($lastName) {
$this->lastName = $lastName;
return $this;
}
/**
* Get lastName
*
* #return string
*/
public function getLastName() {
return $this->lastName;
}
/**
* Get files
*
* #return ArrayCollection
*/
function getFiles() {
return $this->files;
}
/**
* Set files
* #param type $files
*/
function setFiles($files) {
$this->files = $files;
}
}
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Files
*
* #ORM\Table(name="files")
* #ORM\Entity(repositoryClass="AppBundle\Repository\FilesRepository")
*/
class Files
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="file", type="string", length=255, unique=true)
* #Assert\NotBlank(message="Please, upload the product brochure as a PDF file.")
* #Assert\File(mimeTypes={ "application/pdf" })
*/
private $file;
/**
*
* #return Files
*/
function getUser() {
return $this->user;
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set file
*
* #param string $file
*
* #return Files
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
/**
* Get file
*
* #return string
*/
public function getFile()
{
return $this->file;
}
}
Finaly, Symfony Controller:
/**
* Creates a new user entity.
*
* #Route("/new", name="user_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request) {
$user = new User();
$form = $this->createForm('AppBundle\Form\UserType', $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$attachments = $user->getFiles();
if ($attachments) {
foreach($attachments as $attachment)
{
$file = $attachment->getFile();
var_dump($attachment);
$filename = md5(uniqid()) . '.' .$file->guessExtension();
$file->move(
$this->getParameter('upload_path'), $filename
);
var_dump($filename);
$attachment->setFile($filename);
}
}
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $this->redirectToRoute('user_show', array('id' => $user->getId()));
}
return $this->render('user/new.html.twig', array(
'user' => $user,
'form' => $form->createView(),
));
}
You need to alter the input file name attribute which need to map an array.
<input type="file" name="name[]" multiple />
Methods getChild and set() were removed in 2.3.
Instead of this you should use children[] and vars properties
before:
$formView->getChild('files')->set('full_name', 'form[files][]');
after:
$formView->children['files']->vars = array_replace($formView->children['files']->vars, array('full_name', 'form[files][]'));
Symfony introduced 'multiple' option to file field type in symfony 2.5
$builder->add('file', 'file', array('multiple' => TRUE));
What would happen if there would be some validation errors? Will Symfony Form repost multiple file upload field. Because I tried it and I think for this purpose you need to use collection of file fields. Than symfony form must render all fields have added before correctly.