orocrmorocommerce

OroPlatform: Custom action on the grid rows


Context

I'm currently working on an OroPlatform project and I need to add an action to download a file from the OroPlatform grid :

Here is what I have already done :

# datagrids.yml
  business-unit-grid:
    properties:
      getpdf_link:
        type: url
        route: baltimore_action_pdf
        params:
          - id
    actions:
      getpdf:
        type: getpdf
        label: "Export garanties"
        data_identifier: u.id
        entity_name: Oro\Bundle\OrganizationBundle\Entity\BusinessUnit
        icon: file
        link: getpdf_link

<?php

namespace Baltimore\Bundle\AppBundle\Extension\Action\Actions;

use Oro\Bundle\DataGridBundle\Extension\Action\Actions\AjaxAction;

class GetPdfAction extends AjaxAction
{
    /**
     * @var array
     */
    protected $requiredOptions = ['entity_name', 'data_identifier'];

    public function getOptions()
    {
        $options = parent::getOptions();
        $options['frontend_type'] = 'getpdf';
        if (empty($options['frontend_handle'])) {
            $options['frontend_handle'] = 'getpdf';
        }
        return $options;
    }
}
<?php

namespace Baltimore\Bundle\AppBundle\Controller\Actions;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Dompdf\Dompdf;
use Dompdf\Options;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

/**
 * @Route("/action")
 */
class ActionController extends Controller
{
    /**
     * @Route("/pdfexport/{id}", requirements={"id"="\d+"}, name="baltimore_action_pdf", methods={"GET", "POST"})
     */
    public function actionPdf(Request $request)
    {
        //dump($request->get('id'));

        $pdfOptions = new Options();
        $pdfOptions->set('defaultFont', 'Arial');

        // Instantiate Dompdf with our options
        $dompdf = new Dompdf($pdfOptions);

        // Retrieve the HTML generated in our twig file
        $html = $this->renderView('BaltimoreAppBundle:pdf:mypdf.html.twig', [
            'title' => "Welcome to our PDF Test"
        ]);

        // Load HTML to Dompdf
        $dompdf->loadHtml($html);

        // (Optional) Setup the paper size and orientation 'portrait' or 'portrait'
        $dompdf->setPaper('A4', 'portrait');

        // Render the HTML as PDF
        $dompdf->render();

        // Output the generated PDF to Browser (force download)
        $dompdf->stream("mypdf.pdf", [
            "Attachment" => true
        ]);

        exit;
    }
}

To be sure that the error doesn't came from my controller, I have created the same method in a classic controller and this one works.

    /**
     * @Route("/download", name="app_vehicule_download")
     */
    public function downloadAction()
    {
        // Configure Dompdf according to your needs
        $pdfOptions = new Options();
        $pdfOptions->set('defaultFont', 'Arial');

        // Instantiate Dompdf with our options
        $dompdf = new Dompdf($pdfOptions);

        // Retrieve the HTML generated in our twig file
        $html = $this->renderView('BaltimoreAppBundle:pdf:mypdf.html.twig', [
            'title' => "Welcome to our PDF Test"
        ]);

        // Load HTML to Dompdf
        $dompdf->loadHtml($html);

        // (Optional) Setup the paper size and orientation 'portrait' or 'portrait'
        $dompdf->setPaper('A4', 'portrait');

        // Render the HTML as PDF
        $dompdf->render();

        // Output the generated PDF to Browser (force download)
        $dompdf->stream("mypdf.pdf", [
            "Attachment" => true
        ]);

        exit;
    }

Problem

Everything works pretty well, the custom button is available in the grid and I can send a JSON response. But, I had an error when I want to create a method to download a PDF.

enter image description here

When I use my code in a classic controller, it works. It seems this is related to the ajax action which needs a JSON response as a return type..


Solution

  • I finally found a solution to fix this issue. I'm sure this is not the most elegant way to do it but.. it works.

    Here what I have done :

    <?php
    
    namespace Baltimore\Bundle\AppBundle\Extension\Action\Actions;
    
    use Oro\Bundle\DataGridBundle\Extension\Action\ActionConfiguration;
    use Oro\Bundle\DataGridBundle\Extension\Action\Actions\AbstractAction;
    
    class GetPdfAction extends AbstractAction
    {
        /**
         * @var array
         */
        protected $requiredOptions = ['entity_name', 'data_identifier', 'link'];
    
        /**
         * @param ActionConfiguration $options
         */
        public function setOptions(ActionConfiguration $options)
        {
            parent::setOptions($options);
        }
    }
    
    define([
        'oro/datagrid/action/model-action'
    ], function(ModelAction) {
        'use strict';
    
        /**
         * GetPDF action
         *
         * @export  oro/datagrid/action/getpdf-action
         * @class   oro.datagrid.action.GetPdfAction
         * @extends oro.datagrid.action.ModelAction
         */
        const GetPdfAction = ModelAction.extend({
    
            /**
             * @inheritDoc
             */
            constructor: function GetPdfAction(options) {
                GetPdfAction.__super__.constructor.call(this, options);
            },
    
            /**
             * Execute download file
             */
            execute: function() {
                this.downloadFile(this.messages);
            },
    
            /**
             * PDF download
             */
            downloadFile: function() {
                const host = window.location.protocol + '//' + window.location.host;
                const link = document.createElement('a'), filename = 'file.pdf';
    
                link.href = host + this.getLink();
                link.download = filename;
                link.click()
            }
        });
    
        return GetPdfAction;
    });
    
    

    If someone knows how to improve the JS script, it will be very interesting.