Announcement

Collapse
No announcement yet.

Workaround to TCPDF css limitations

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Workaround to TCPDF css limitations

    I am using entry point "pdf" to generate a a "lease" document based on a record view of my custom "Tenancy" entity but I would like to be able to use the full css capabilities for positioning, fonts, background images, etc instead of being very limited by TCPDF css capabilities.

    One possible workaround I believe would be to output the raw HTML to a new window and use the built in "save as PDF" browser capability to create a PDF document. Is this possible and if so how would I go to implement in espoCRM ?

    Any help will be highly appreciated, I am not an experienced developer.

  • #2
    You can develop own action button, where you can build document body and open new window with this body
    https://forum.espocrm.com/forum/gene...e-for-print-it
    https://forum.espocrm.com/forum/deve...uestion-newbie

    Comment


    • #3
      Thank you so much for your reply Tanya.

      I understand how to create and action button in detail view and I also understand (thanks to the information in this forum) how to call for the pdf entry point and display the result in a new window.

      What I don't understand is where would a custom entry point functionality be defined (in which folder) and also if possible could you provide an example of a "build document body and open new window with this body" code ?


      Comment


      • #4
        I got it, here's what I did in case other newbie members like myself that might want to understand how the process of integrating a custom action button with a custom entry point works:

        Goal: Create a button at an entity's detail view that when pressed prints the entity fields using a template as html output to a new window, so the browser's "Print to PDF" capabilities render a PDF without having to be limited by TDPDF css limits.
        1. Create button inside the entity's record detail view file ("espocrm\client\custom\src\views\my-entity\record\detail.js")

          Code:
          setupActionItems: function () {
          	   this.dropdownItemList.push({
          	       label: 'Print Lease (Window)',
          	       name: 'printLeaseWindow'
          	   });
          	}
          
          actionPrintLeaseWindow: function () {
          	   var templateId = this.model.get("templateId");
          	   window.open('?entryPoint=printToWindow&entityType='+this.model.name+'&entityId='+this.model.id+'&templateId=' + templateId, '_blank');
          	}
        2. Create class ("Entry Point") containing the entry point logic in file ("espocrm\custom\Espo\Custom\EntryPoints\PrintToWindow.php")

          Code:
          namespace Espo\Custom\EntryPoints;
          
          	use \Espo\Core\Exceptions\NotFound;
          	use \Espo\Core\Exceptions\BadRequest;
          
          	class PrintToWindow extends \Espo\Core\EntryPoints\Base
          	{
          	   public static $authRequired = true;
          
          	   public function run()
          	   {
          
          	       if (empty($_GET['entityId']) || empty($_GET['entityType']) || empty($_GET['templateId'])) {
          	           throw new BadRequest();
          	       }
          	       $entityId = $_GET['entityId'];
          	       $entityType = $_GET['entityType'];
          	       $templateId = $_GET['templateId'];
          
          	       $entity = $this->getEntityManager()->getEntity($entityType, $entityId);
          	       $template = $this->getEntityManager()->getEntity('Template', $templateId);
          
          	       if (!$entity || !$template) {
          	           throw new NotFound();
          	       }
          
          	       $this->getContainer()->get('serviceFactory')->create('PrintToWindowService')->buildFromTemplate($entity, $template, true);
          
          	       exit;
          	   }
          	}
        3. Create class ("Service") providing the logic to print an entity's fields using a given template as HTML output to a new window, file ("espocrm\custom\Espo\Custom\Services\PrintToWindowService.php")

          Code:
          namespace Espo\Custom\Services;
          
          	use \Espo\Core\Exceptions\Forbidden;
          	use \Espo\Core\Exceptions\NotFound;
          	use \Espo\Core\Exceptions\Error;
          
          	use \Espo\ORM\Entity;
          
          	use \Espo\Core\Htmlizer\Htmlizer;
          
          	class PrintToWindowService extends \Espo\Core\Services\Base
          	{
          
          	   protected $fontFace = 'freesans';
          
          	   protected $fontSize = 10;
          
          	   protected $removeMassFilePeriod = '1 hour';
          
          	   protected function init()
          	   {
          	       $this->addDependency('fileManager');
          	       $this->addDependency('acl');
          	       $this->addDependency('metadata');
          	       $this->addDependency('serviceFactory');
          	       $this->addDependency('dateTime');
          	       $this->addDependency('number');
          	       $this->addDependency('entityManager');
          	       $this->addDependency('defaultLanguage');
          	   }
          
          	   protected function getAcl()
          	   {
          	       return $this->getInjection('acl');
          	   }
          
          	   protected function getMetadata()
          	   {
          	       return $this->getInjection('metadata');
          	   }
          
          	   protected function getServiceFactory()
          	   {
          	       return $this->getInjection('serviceFactory');
          	   }
          
          	   protected function getFileManager()
          	   {
          	       return $this->getInjection('fileManager');
          	   }
          
          	   protected function createHtmlizer()
          	   {
          	       return new Htmlizer(
          	           $this->getFileManager(),
          	           $this->getInjection('dateTime'),
          	           $this->getInjection('number'),
          	           $this->getAcl(),
          	           $this->getInjection('entityManager'),
          	           $this->getInjection('metadata'),
          	           $this->getInjection('defaultLanguage')
          	       );
          	   }
          
          	   protected function printEntity(Entity $entity, Entity $template, Htmlizer $htmlizer)
          	   {
          	       $htmlFooter='';
          	       $pageOrientation = 'Portrait';
          	       $pageFormat = 'A4';
          
          	       if ($template->get('fontFace')) {
          	           $fontFace = $template->get('fontFace');
          	       }
          
          	       if ($template->get('printFooter')) {
          	           $htmlFooter = $htmlizer->render($entity, $template->get('footer'));
          	       }
          
          	       if ($template->get('pageOrientation')) {
          	           $pageOrientation = $template->get('pageOrientation');
          	       }
          	       if ($template->get('pageFormat')) {
          	           $pageFormat = $template->get('pageFormat');
          	       }
          
          	       $htmlHeader = $htmlizer->render($entity, $template->get('header'));
          	       $htmlBody = $htmlizer->render($entity, $template->get('body'));
          
          	       return $htmlHeader.$htmlBody.$htmlFooter;
          	   }
          
          	   public function buildFromTemplate(Entity $entity, Entity $template)
          	   {
          	       $entityType = $entity->getEntityType();
          	       $service = $this->getServiceFactory()->create($entityType);
          	       $service->loadAdditionalFields($entity);
          
          	       if ($template->get('entityType') !== $entityType) {
          	           throw new Forbidden();
          	       }
          
          	       if (!$this->getAcl()->check($entity, 'read') || !$this->getAcl()->check($template, 'read')) {
          	           throw new Forbidden();
          	       }
          
          	       $htmlizer = $this->createHtmlizer();
          
          	       $output = $this->printEntity($entity, $template, $htmlizer);
          
          	       echo $output;
          	   }
          
          	}
        Last edited by telecastg; 11-14-2018, 07:21 AM.

        Comment


        • #5
          Hi telecastg

          Very interested in your process of printing to PDF

          When looking for the following entries I am struggling to find them in our folder structure.

          Version 5.8.4

          "espocrm\client\custom\src\views\my-entity\record\detail.js"

          and

          "espocrm\custom\Espo\Custom\EntryPoints\PrintToWin dow.php"

          Are we creating the folder paths or should they already be there?

          Thank you for your help

          MATO

          Comment


          • #6
            Hi Mato,

            All folders under the "Custom" namespace need to be created, this is where Espo looks for custom implementations and any changes made there will not be affected by upgrades.

            For front-end (js) scripts the custom path is: "client/custom"... and for back-end (php) is "custom/Espo/Custom"...

            The scripts that I described, including the first, are custom made so you will have to write and save them.

            "my-entity" is just a placeholder for the name of your custom or standard entity to which you want to apply the printing method.

            For example, if you wanted to do it with "Contact" the name of the first script would be "espocrm\client\custom\src\views\contact\record\de tail.js"

            Cheers

            Comment


            • #7
              Hi telecastg

              I have created all structures and files

              For simplicity and for testing we are using Contacts

              where should we being seeing the button at present I am on a treasure hunt.

              We have run a rebuild backend and cleared cache

              Thank you for your help

              MATO

              Comment


              • #8
                Hi Mato,

                The code samples that I posted were meant to illustrate how to implement the print to window functionality into your own scripts, not to be used as a step by step guide.

                Here is a tutorial to add the print to window functionality to the Contact entity:

                First we need to let EspoCRM know that we want to use a custom view to render the Contact detail display, which is where the "Print to Window" drop down menu option will appear and we do this by creating a custom clientDefs file for "Contact" as follows:

                custom\Espo\Custom\Resources\metadata\clientDefs\C ontact.json
                Code:
                {
                    "recordViews": {
                        "detail": "custom:views/contact/record/detail"
                    }
                }
                Next we need to define the custom view specified in the custom clientDefs:

                client\custom\src\views\contact\record\detail.js
                Code:
                Espo.define('custom:views/contact/record/detail', 'crm:views/contact/record/detail', function (Dep) {
                
                    return Dep.extend({
                
                        setupActionItems: function () {
                
                            Dep.prototype.setupActionItems.call(this);
                
                            this.dropdownItemList.push({
                                name: 'printWindow',
                                label: 'Print to Window'
                            });                            
                        },
                
                        // print a record to a new window using a template
                        actionPrintWindow: function () {
                            // find out if the model has a templateId attribute
                            if(this.model.attributes.templateId) {
                                var templateId = this.model.attributes.templateId;
                                window.open('?entryPoint=printToWindow&entityType='+this.model.name+'&entityId='+this.model.id+'&templateId=' + templateId, '_blank');
                            // if not, open the select template model and wait for the user to select a template
                            } else {
                                this.createView('windowTemplate', 'views/modals/select-template', {
                                    entityType: this.model.name
                                }, function (view) {
                                    view.render();
                                    this.listenToOnce(view, 'select', function (model) {
                                        this.clearView('windowTemplate');
                                        window.open('?entryPoint=printToWindow&entityType='+this.model.name+'&entityId='+this.model.id+'&templateId=' + model.id, '_blank');
                                    }, this);
                                });                
                            }
                        }
                
                    });
                });
                The view script above, will add the "Print to Window" menu option to the drop down menu next to the "Edit" button in a Contact's detail display, and when the option is selected, it will call the function "actionPrintWindow".

                Function "actionPrintWindow" will look for a Template object already linked to the Contact object or it will display a modal window to allow the user to select the template to be used to render the Contact object.

                Once a template is defined, the function will invoke a back end Entry Point:

                custom\Espo\Custom\EntryPoints\PrintToWindow.php
                Code:
                namespace Espo\Custom\EntryPoints;  
                
                use \Espo\Core\Exceptions\NotFound;
                use \Espo\Core\Exceptions\BadRequest;  
                
                class PrintToWindow extends \Espo\Core\EntryPoints\Base {    
                    public static $authRequired = true;    
                
                    public function run()    {        
                        if (empty($_GET['entityId']) || empty($_GET['entityType']) || empty($_GET['templateId'])) {            
                            throw new BadRequest();        
                        }        
                
                        $entityId = $_GET['entityId'];        
                        $entityType = $_GET['entityType'];        
                        $templateId = $_GET['templateId'];        
                        $entity = $this->getEntityManager()->getEntity($entityType, $entityId);        
                        $template = $this->getEntityManager()->getEntity('Template', $templateId);        
                        if (!$entity || !$template) {            
                            throw new NotFound();        
                        }        
                        $this->getContainer()->get('serviceFactory')->create('PrintToWindowService')->buildFromTemplate($entity, $template, true);        
                        exit;    
                    }
                }
                Then the entry point will request service "PrintToWindowService" which will generate and return the HTML code to actually render the document in a new window.

                custom\Espo\Custom\Services\PrintToWindowService.p hp
                Code:
                namespace Espo\Custom\Services;  
                
                use \Espo\Core\Exceptions\Forbidden;
                use \Espo\Core\Exceptions\NotFound;
                use \Espo\Core\Exceptions\Error;  
                use \Espo\ORM\Entity;  
                use \Espo\Core\Htmlizer\Htmlizer;  
                
                class PrintToWindowService extends \Espo\Core\Services\Base {    
                
                    protected $fontFace = 'freesans';    
                    protected $fontSize = 10;    
                    protected $removeMassFilePeriod = '1 hour';    
                
                    protected function init()   {      
                        $this->addDependency('fileManager');      
                        $this->addDependency('acl');      
                        $this->addDependency('metadata');      
                        $this->addDependency('serviceFactory');      
                        $this->addDependency('dateTime');      
                        $this->addDependency('number');      
                        $this->addDependency('entityManager');      
                        $this->addDependency('defaultLanguage');  
                    }    
                
                    protected function getAcl()   {      
                        return $this->getInjection('acl');  
                    }    
                
                    protected function getMetadata()   {      
                        return $this->getInjection('metadata');  
                    }    
                
                    protected function getServiceFactory()   {      
                        return $this->getInjection('serviceFactory');  
                    }    
                
                    protected function getFileManager()   {      
                        return $this->getInjection('fileManager');  
                    }    
                
                    protected function createHtmlizer()   {      
                        return new Htmlizer(          
                            $this->getFileManager(),          
                            $this->getInjection('dateTime'),          
                            $this->getInjection('number'),          
                            $this->getAcl(),          
                            $this->getInjection('entityManager'),          
                            $this->getInjection('metadata'),          
                            $this->getInjection('defaultLanguage')      
                        );  
                        }    
                
                    protected function printEntity(Entity $entity, Entity $template, Htmlizer $htmlizer)   {      
                        $htmlFooter='';      
                        $pageOrientation = 'Portrait';      
                        $pageFormat = 'A4';        
                        if ($template->get('fontFace')) {          
                            $fontFace = $template->get('fontFace');      
                        }        
                        if ($template->get('printFooter')) {          
                            $htmlFooter = $htmlizer->render($entity, $template->get('footer'));      
                        }        
                        if ($template->get('pageOrientation')) {          
                            $pageOrientation = $template->get('pageOrientation');      
                        }      
                        if ($template->get('pageFormat')) {          
                            $pageFormat = $template->get('pageFormat');      
                        }        
                        $htmlHeader = $htmlizer->render($entity, $template->get('header'));      
                        $htmlBody = $htmlizer->render($entity, $template->get('body'));        
                        return $htmlHeader.$htmlBody.$htmlFooter;  
                    }    
                
                    public function buildFromTemplate(Entity $entity, Entity $template)   {      
                        $entityType = $entity->getEntityType();      
                        $service = $this->getServiceFactory()->create($entityType);      
                        $service->loadAdditionalFields($entity);        
                        if ($template->get('entityType') !== $entityType) {          
                            throw new Forbidden();      
                        }        
                        if (!$this->getAcl()->check($entity, 'read') || !$this->getAcl()->check($template, 'read')) {          
                            throw new Forbidden();      
                        }        
                        $htmlizer = $this->createHtmlizer();        
                        $output = $this->printEntity($entity, $template, $htmlizer);        
                        echo $output;  
                    }  
                }
                Last edited by telecastg; Today, 04:56 AM.

                Comment


                • #9
                  Hi telecastg,

                  I just wanted to say thank you very much for this! I'm new to EspoCRM and your post has helped me a great deal in customizing the app for our use case.

                  Cheers!

                  Comment


                  • telecastg
                    telecastg commented
                    Editing a comment
                    You're very welcome :-)

                    As you do different things with Espo please share your findings and solutions in the forum so everybody benefits.

                    Cheers !

                • #10
                  We've created small workaround for that. We create PDF template in EspoCRM entity and after that we can create PDF as html template in FTP. Of course this html template can be connected with header and footer from templates entity. Also you can put all fields in this html template like in template entity, so for example {{name}}

                  Comment


                  • #11
                    telecastg


                    I'm getting a 404 Page Not Found after click "Print to Window", digging around a little and I notice that the URL is ?entryPoint but in some of the code is ?entryPoints (with a "s"), so I tried create a folder call entryPoint but that doesn't work either.

                    From the look of it, it can't find this file?: custom\Espo\Custom\Services\PrintToWindowService.p hp
                    But I'm sure the file exist and it in the right location with the correct filename. Also changed a couple of File Permission see if that help.
                    Also change filename to see if it case sensitive (e.g. printToWindow.php)

                    Not sure what the issue at this point.


                    My Old Post:
                    Never gotten around to this but seeing as I'm learning how to create button I decide to give it a try.

                    Unfortunately I'm stuck in the first step and need help on what to do.

                    For my Contact.json file there is already code in there so I'm getting a Syntax error, not sure how do I paste it so it will work and don't receive syntax error

                    I probably need to add a comma or semi-colon somewhere don't I?

                    Here is what I have at the moment:

                    edit: I think I figure it out. I added the code after the icon class and add a ", comma" at the end"
                    Code:
                    "iconClass": "fas fa-id-badge",
                    "recordViews": {
                    "detail": "custom:views/contact/record/detail"
                    },
                    Last edited by espcrm; Today, 12:49 AM.

                    Comment


                    • #12
                      Please post the whole code and the name with full path of the script that you are editing.

                      The first step is to create a custom Contact,json clientDefs metadata file in the "Custom" namespace so it will not interfere with the core Contact.json clientDef metadata file.

                      Comment


                      • #13
                        Hi telecastg,

                        Thank you. I decided to upload the whole customized file if you have the chance to look at it. It pretty much a copy/paste job, only change I made was to the: custom\Espo\Custom\Resources\metadata\clientDefs\C ontact.json
                        File, I also update my previous post so I think you post your message before you read my update.

                        Please check: https://www93.zippyshare.com/v/GgHXaVLr/file.html OR https://github.com/espcrm/EspoCRM-Customize/tree/master
                        I also made the folder tree/location as per guide, and also upload the file and extract it to my CRM incase I made a type with file or folder name.

                        But still no luck, not why where I went wrong at the moment.
                        Last edited by espcrm; Today, 03:34 AM.

                        Comment


                        • #14
                          Hi espcrm

                          Originally posted by espcrm View Post
                          I decided to upload the whole customized file if you have the chance to look at it.
                          Sorry but I can't help with code debugging.

                          This is NOT an extension or package that will self install so you should not compress and upload anything. What you need to do is to write 4 different scripts and save them to the correct file path.

                          The correct procedure is:

                          1) If you haven't done it yet, set up a local server (xampp for example) and install Espo in a local website

                          2) Use your code editor or IDE to create each script exactly as written and save it in the exact specified folder (path) within the Espo installation.

                          The correct names and paths for each script are: (I use forward slashes because otherwise the forum's markdown inserts unwanted spaces)

                          custom/Espo/Custom/Resources/metadata/clientDefs/Contact.json

                          client/custom/src/views/contact/record/detail.js

                          custom/Espo/Custom/EntryPoints/PrintToWindow.php

                          custom/Espo/Custom/Services/PrintToWindow/Service.php

                          If you are not familiar with Espo's file structure you can open the installation in your IDE or use windows explorer to get a better idea of where everything goes.

                          3) Clear cache and rebuild the local installation

                          4) Test

                          5) Make any modifications that you want

                          6) Test again

                          7) Upload each script individually using FTP to the correct folder in your online Espo installation.

                          8) Clear cache and rebuild the online installation

                          9) Test again.
                          Last edited by telecastg; Today, 05:13 AM.

                          Comment

                          Working...
                          X