Skip navigation
All Places > Developer > Blog > Authors Francesca Shiekh

Developer

2 Posts authored by: Francesca Shiekh
Here is another Francesca Shiekh guest post! She is a Sugar Developer from Wolfram Research.

 

We had the need to notify the assigned user when an email was received that was related to a Case record. To make our application more flexible we extended this concept to be reusable for email received that was related to any module. We then further broke out the notification functions to be reusable in other scenarios.

 

Notifying an Assigned User that an Email was received

 

The following concept can be used to notify the Assigned user on a Case, Contact, Account, or basically any module that has related Emails.

 

For example, suppose that we wish to notify the Assigned User that an email has been logged on a Contact record assigned to them.

 

Detecting inbound Emails using an after_relationship_add logic hook

 

First, we can add an after_relationship_add logic hook on Contacts:

 

In ./custom/modules/Contacts/logic_hooks.php

 

 

 

<?php

 

$hook_version = 1;

 

$hook_array = Array();

 

$hook_array['after_relationship_add'] = Array();

 

$hook_array['after_relationship_add'][] = Array(1, 'email_contact_relationship', 'custom/modules/Contacts/email_contact_handling.php','email_contact_handling', 'update_on_relationship'); ?>

 

 

 

The logic hook checks if the related record is an Email then calls a custom utility function to handle the notification../custom/modules/Contacts/email_contact_handling.php

 

 

 

class email_contact_handling

 

{

 

   // Email RELATED to a Contact

 

   function update_on_relationship ($bean,$event,$arguments){

 

        require_once('custom/entry_points/Utils/notifyAssignedUser.php'); //choose your own path for your custom utility functions

 

        //bean = Contact

 

        //related_module = Emails

 

        if ($arguments['related_module'] =='Emails'){ //check that you are indeed relating an Email to this Contact

 

           notifyAssignedUser($bean, $arguments['related_id']); //related_id is the email id in this case

 

        }

 

    }

 

}

 

 

 

Notifying an Assigned User that an Email was received

 

We can also define a reusable utility function to notify the Assigned User. We choose to store our reusable functions in in ./custom/entry_points/Utils/ but you can put yours inside another custom directory.

 

In /custom/entry_points/Utils/notifyAssignedUser.php

 

 

 

<?php

 

//given a bean and an Email id notify the

 

//Assigned user on the bean that the email was received.

 

function notifyAssignedUser($bean, $email_id){

 

$email = BeanFactory::retrieveBean('Emails',$email_id);

 

$module = $bean->module_name;

 

    $u = new User();

 

    $u->retrieve($c->assigned_user_id);

 

    $user_email = $u->emailAddress->getPrimaryAddress($u);

 

    if (filter_var($user_email, FILTER_VALIDATE_EMAIL)) {

 

      include_once('custom/entry_points/Utils/sendEmail.php');

 

      $email_text = !empty($email->description_html)?$email->description_html:$email->description;

 

      $EmailBody=<<<BODY New email received for $bean->name

 

Assigned to: $u->user_name

 

From: {$email->from_addr_name} ( {$email->from_addr} )

 

To: {$email->to_addrs}

 

Cc: {$email->cc_addrs}

 

Subject: $email->name

 

See: {$sugar_config['site_url']}/#{$module}/{$bean->id}

 

{$email_text}

 

BODY;

 

      $to_address = $u->emailAddress->getPrimaryAddress($u);

 

      $subject = "New Email received for $c->name";

 

      if(!empty($to_address)&&$u->user_name!='tsreception'){

 

        sendEmail($to_address, '<your sugar email address>', $from, $subject,$EmailBody);

 

      }

 

    }

 

  return;

 

}

 

?>

 

 

 

Notifying a Group of Users based on criteria from the Users module

 

You can also choose to retrieve a group of users based on some criteria for User records.

 

For example, we have a custom field support_group_c which we use for group notifications on Case records.

 

 

 

<?php

 

// Copyright 2016 Wolfram Research, Inc.

 

// Given a support group and a Case bean, then notify the support group users that the case was created.

 

function notify_support_group_users($c, $support_group, $email=null){

 

$GLOBALS['log']->debug("Inbound Email: Prepare to notify " . $support_group);

 

  require_once('include/SugarQuery/SugarQuery.php');

 

  $sugarQuery = new SugarQuery();

 

  $sugarQuery->select(array('id'));

 

  $bean = BeanFactory::newBean('Users');

 

  $sugarQuery->from($bean, array('team_security' => false));

 

  $sugarQuery->where()->equals('support_group_c', $support_group);

 

  $result = $sugarQuery->execute();

 

  $GLOBALS['log']->debug("Inbound Email: Prepare to notify " . print_r($result,true));

 

  foreach($result as $user){

 

    $GLOBALS['log']->debug("Inbound Email: Prepare to notify " . print_r($user,true));

 

    $user = BeanFactory::getBean('Users', $user['id']);

 

    $user_email = $user->emailAddress->getPrimaryAddress($user);

 

    if (filter_var($user_email, FILTER_VALIDATE_EMAIL)) {

 

      $user_emails[] = $user_email;

 

    }

 

  }

 

  if(!empty($user_emails)){

 

    $GLOBALS['log']->debug("Inbound Email: Will notify " . print_r($user_emails,true));

 

    include_once('custom/entry_points/Utils/send_group_email.php');

 

    global $sugar_config;

 

    $toAddresses = implode(';',$user_emails);

 

    if(!empty($email)){

 

        $email_text = !empty($email->description_html)?$email->description_html:$email->description;

 

        $EmailBody=<<<BODY New email on [CASE:{$c->case_number}] $c->name

 

Current Case Status: $c->status

 

Assigned to: $user->user_name

 

From: {$email->from_addr_name} ( {$email->from_addr} )

 

To: {$email->to_addrs}

 

Cc: {$email->cc_addrs}

 

Subject: $email->name

 

See: {$sugar_config['site_url']}/#Cases/{$c->id}

 

{$email_text}

 

BODY;

 

    }else{

 

        $EmailBody=<<<BODY New Correspondence on  Case: [CASE:{$c->case_number}] $c->name

 

See: {$sugar_config['site_url']}/#Cases/{$c->id}

 

BODY;

 

    }

 

    // sendEmail($ToEmailAdd, $FromEmailAdd, $FromEmailName, $EmailSubject, $EmailBody)

 

    // defined in custom/entry_points/send_email.php

 

    $GLOBALS['log']->debug("Inbound Email: Will notify " . $toAddresses);

 

    if($c->status == 'New'){

 

      $subject = "New Case Created [CASE:{$c->case_number}] $c->name";

 

      $from = 'SugarCRM Case New';

 

    }else{

 

      $subject = "New Correspondence on Case [CASE:{$c->case_number}] $c->name";

 

      $from = 'SugarCRM Case Update';

 

    }

 

    if(!empty($toAddresses))  sendGroupEmail($toAddresses, 'sugarCRM@wolfram.com', $from, $subject,$EmailBody);

 

  }

 

  return;

 

}

 

?>

 

 

 

Sending an Email to a given set of addresses, one per addressee

 

Finally, we have a sendEmail() utility function../custom/entry_points/Utils/sendEmail.php

 

 

 

<?php

 

// Copyright 2016 Wolfram Research, Inc.

 

// Function to send Email message

 

// one email per recipient

 

function sendEmail($ToEmailAdd, $FromEmailAdd, $FromEmailName, $EmailSubject, $EmailBody) {

 

global $sugar_config;

 

$GLOBALS['log']->debug('PREPARE EMAIL to ' . print_r($ToEmailAdd, true));

 

  require_once ('modules/Emails/Email.php');

 

  if(is_array($ToEmailAdd)){

 

    $To = $ToEmailAdd;

 

  }else{

 

    $To = explode(';',$ToEmailAdd);

 

  }

 

  foreach ($To as $to_addr){

 

    $GLOBALS['log']->debug('PREPARE EMAIL TO:' . $to_addr);

 

    if (filter_var($to_addr, FILTER_VALIDATE_EMAIL)){

 

      try{

 

        $phpMailer = MailerFactory::getSystemDefaultMailer();

 

        $mailTransmissionProtocol = $phpMailer->getMailTransmissionProtocol();

 

        $FromEmailIdentity = new EmailIdentity($FromEmailAdd, $FromEmailName);

 

        $header_array = array(

 

          'From'=>$FromEmailIdentity,

 

          'ReplyTo'=>'',

 

          'Sender'=>$FromEmailIdentity, //mandatory

 

          'Subject'=>$EmailSubject,

 

        );

 

        $phpMailer->constructHeaders($header_array);

 

        $phpMailer->addRecipientsTo(new EmailIdentity($to_addr, $to_addr));

 

        $phpMailer->setHtmlBody($EmailBody);

 

        $phpMailer->send();

 

      }catch(MailerException $me) {

 

        $message = $me->getMessage();

 

        $GLOBALS["log"]->warn(

 

          "SendEmail: error sending e-mail (method: {$mailTransmissionProtocol}), (error: {$message})"

 

        );

 

      }

 

    }

 

  }

 

  return;

 

}

 

 

 

Send Email to a set of addresses as multiple To and/or CC

 

We can also modify the sendEmail() function above to send one email to the whole group with multiple recipients instead of individual emails.

 

 

 

<?php

 

// Copyright 2016 Wolfram Research, Inc.

 

//Function to send Email message

 

//Multiple recipients => multiple addresses in the To, and/or Cc

 

function sendGroupEmail($ToEmailAdd, $FromEmailAdd, $FromEmailName, $EmailSubject, $EmailBody, $CcEmailAdd = array()) {

 

  include_once('modules/Mailer/MailerFactory.php');

 

  global $sugar_config;

 

  $ToAddrs = array();

 

  $CcAddrs = array();

 

  if(is_array($ToEmailAdd)){

 

    $To = $ToEmailAdd;

 

  }else{

 

    $To = explode(';',$ToEmailAdd);

 

  }

 

  if(is_array($CcEmailAdd)){

 

    $Cc = $CcEmailAdd;

 

  }else{

 

    $Cc = explode(';',$CcEmailAdd);

 

  }

 

  try{

 

    $phpMailer = MailerFactory::getSystemDefaultMailer();

 

    $mailTransmissionProtocol = $phpMailer->getMailTransmissionProtocol();

 

    $FromEmailIdentity = new EmailIdentity($FromEmailAdd, $FromEmailName);

 

    $headers = new EmailHeaders();

 

    $header_array = array(

 

      'From'=>$FromEmailIdentity,

 

      'ReplyTo'=>'',

 

      'Sender'=>$FromEmailIdentity, //mandatory

 

      'Subject'=>$EmailSubject,

 

    );

 

    $headers->buildFromArray($header_array);

 

    $phpMailer->setHeaders($headers);

 

    //$phpMailer->setHtmlBody($EmailBody);

 

    $phpMailer->setTextBody($EmailBody);

 

    foreach($To as $to_addr){

 

      //note should be new EmailIdentity(<email_address>, <name>)

 

      //we don't have a name for these

 

      $phpMailer->addRecipientsTo(new EmailIdentity($to_addr, $to_addr));

 

    }

 

    foreach($Cc as $cc_addr){

 

      //note should be new EmailIdentity(<email_address>, <name>)

 

      //we don't have a name for these

 

      $phpMailer->addRecipientsCc(new EmailIdentity($cc_addr, $cc_addr));

 

    }

 

    $phpMailer->send();

 

  }catch(MailerException $me) {

 

    $message = $me->getMessage();

 

    $GLOBALS["log"]->warn(

 

      "SendGroupEmail: error sending e-mail (method: {$mailTransmissionProtocol}), (error: {$message})"

 

    );

 

  }

 

  return;

 

}

 

?>

 

 

Here is a guest post from Sugar Developer Francesca Shiekh who works for our customer Wolfram Research. She also happens to be one of the most active developers in the Sugar Developer Community! In this post, Francesca and Wolfram Research Systems Administrator Trey Mitchell share how you can customize Sugar to use an alternative PDF generator for the creation of PDF documents in Sugar.

 

Creating better PDFs with CSS


 

We wanted a custom Contracts module that allowed specific styling to be applied to all Contract PDFs.  We determined that we could use CSS to apply a consistent style to any and all PDF documents associated with this new custom Contracts module. But we could not achieve this with SugarPDF due to the complexity of our CSS. Also overriding sugarpdf.pfdmanager.php was not an option as it is an extension of TCPDF and TCPDF has limited CSS support.

 

It was therefore necessary for use to look at alternatives that could better handle CSS.  We found a promising open source tool called wkhtmltopdf that uses WebKit.  WebKit should be familiar because it is the same layout engine used in Safari and Google Chrome, and is therefore very accurate in rendering CSS.

 

We still wanted to leverage the PDF Manager module to create and maintain our PDFs. We planned to develop our Contract templates in HTML with our own custom CSS and then copy them into the HTML editor that was already available in PDF Manager.

 

Read more below to find out how we made it work.

 

Step 1: Create a custom PDF rowaction

 

A custom field type called wpdfaction provides the rowaction needed for the dropdown that contains all the active PDF Manager templates for the current module. This new rowaction is dynamically included in the buttons section of your module's record view much like the out of the box pdfaction.custom/modules/*/clients/base/views/record/record.php

 

 


              array (

                'type' => 'wpdfaction',

                'name' => 'download-pdf',

                'label' => 'LBL_PDF_VIEW',

                'action' => 'download',

                'acl_action' => 'view',

              ),

 

The field type wpdfaction is a slight modification of the clients/base/fields/pdfaction field where the template is unchanged and the JavaScript is modified to call a custom API that in turn generates the PDF to be downloaded.custom/clients/base/fields/wpdfaction/wpdfaction.js

 

[code language="js"]

 

/**

* @extends View.Fields.Base.RowactionField

*/


({

  extendsFrom: 'RowactionField',

  events: {

    'click [data-action=link]': 'linkClicked',

    'click [data-action=download]': 'downloadClicked',

  },

  /**

   * PDF Template collection.

   *

   * @type {Data.BeanCollection}

   */


   templateCollection: null,

   /**

   * Visibility property for available template links.

   *

   * @property {Boolean}

   */


  fetchCalled: false,

  /**

   * {@inheritDoc}

   * Create PDF Template collection in order to get available template list.

   */


  initialize: function(options) {

    this._super('initialize', [options]);

    this.templateCollection = app.data.createBeanCollection('PdfManager');

    this._fetchTemplate();

  },

  /**

   * {@inheritDoc}

   * @private

   */


  _render: function() {

    this._super('_render');

  },

  /**

   * Define proper filter for PDF template list.

   * Fetch the collection to get available template list.

   * @private

   */


  _fetchTemplate: function() {

    this.fetchCalled = true;

    var collection = this.templateCollection;

    collection.filterDef = {'$and': [{

      'base_module': this.module

    }, {

      'published': 'yes'

    }]};

      collection.fetch();

  },

  /**

   * Build download link url.

   *

   * @param {String} templateId PDF Template id.

   * @return {string} Link url.

   * @private

   */


  _buildDownloadLink: function(templateId) {

      var url = app.api.buildURL(this.module+'/'+this.model.id+'/'+'generateWPDF/'+templateId);

      return url;

  },

  /**

   * Handle the button click event.

   * Stop event propagation in order to keep the dropdown box.

   *

   * @param {Event} evt Mouse event.

   */


  linkClicked: function(evt) {

    evt.preventDefault();

    evt.stopPropagation();

    if (this.templateCollection.dataFetched) {

      this.fetchCalled = !this.fetchCalled;

    } else {

      this._fetchTemplate();

    }

    this.render();

  },

  /**

   * Handles download pdf link.

   * @param {Event} evt The `click` event.

   */


  downloadClicked: function(evt) {

    var templateId = this.$(evt.currentTarget).data('id');

    this._triggerDownload(this._buildDownloadLink(templateId));

  },

  /**

   * do the actual downloading

   * @param {String} uri The location of the cached file.

   **/


  startDownload: function(uri) {

    app.api.fileDownload(uri, {

      error: function(e) {

        app.error.handleHttpError(e, {});

      }

    }, {iframe: this.$el});

  },

  /**

   * Download the file.

   *

   * @param {String} url The url for the file download API.

   * @protected

   */


  _triggerDownload: function(url) {

    var self = this;

    app.api.call('GET', url, null, {

      success: function(o){

        self.startDownload(o);

      },

      error: function (e){

        app.error.handleHttpError(e, {});

      },

    });

  },

  /**

   * {@inheritDoc}

   * Bind listener for template collection.

   */


  bindDataChange: function() {

    this.templateCollection.on('reset', this.render, this);

    this._super('bindDataChange');

  },

  /**

   * {@inheritDoc}

   * Dispose safe for templateCollection listeners.

   */


  unbindData: function() {

    this.templateCollection.off(null, null, this);

    this.templateCollection = null;

    this._super('unbindData');

  },

  /**

   * {@inheritDoc}

   * Check additional access for PdfManager Module.

   */


  hasAccess: function() {

    var pdfAccess = app.acl.hasAccess('view', 'PdfManager');

    return pdfAccess &amp;amp;amp;&amp;amp;amp; this._super('hasAccess');

  }

})

 

 

Step 2: Create a custom API that generates the new PDFs

 

The custom API is what generates the new PDFs from HTML stored in the PDF Manager module.

 

You can put the following REST API implementation in custom/clients/base/api/generateWPDFApi.php for use in multiple modules or custom/modules/<module>/clients/base/api/generateWPDFApi.php for use in a specific module only.

 

We have to include SugarpdfHelper because it is required by PdfManagerHelper to parse the SugarBean fields needed for SugarSmarty to replace our field variables with data from the record.

 

The outputWPdf function can be changed to use your favorite PDF generator. We were able to try a few different implementations before we settled on the ease of use of wkhtmltopdf.

 

The final PDF is stored in Sugar's cache directory and ultimately downloaded using app.api.fileDownload() in the client.

If testing in Google Chrome, you need to disable the PDF Viewer to ensure that it is downloaded correctly.  To do this go to chrome://plugins and find and disable "Chrome PDF Viewer" in the list.

 

 


<php class generateWPDFApi extends SugarApi {   public function registerApiRest() {     return array(       'generateWPDF' => array(

        'reqType' => 'GET',

        'path' => array('<module>','?','generateWPDF','?'),

        'pathVars' => array('module','record','','pdf_template_id'),

        'method' => 'generateWPDF',

        'shortHelp' => 'CustomWR: Generate PDF file from PdfManager template with specified ID',

        'longHelp' => '',

      ),

    );

  } 

  function generateWPDF($api, $args)

  {

    require_once 'include/Sugarpdf/SugarpdfHelper.php'; //needed by PdfManagerHelper

    require_once 'modules/PdfManager/PdfManagerHelper.php';

    global $sugar_config;

    $this->requireArgs($args,array('module','record','pdf_template_id'));

    $this->_initSmartyInstance();

    // settings for disable smarty php tags

    $this->ss->security_settings['PHP_TAGS'] = false;

    $this->ss->security = true;

    if (defined('SUGAR_SHADOW_PATH')) {

      $this->ss->secure_dir[] = SUGAR_SHADOW_PATH;

    } 

    $pdfTemplate = BeanFactory::retrieveBean('PdfManager' , $args['pdf_template_id']);

    $this->bean = BeanFactory::retrieveBean($args['module'],$args['record']);

    $this->pdfFilename = $pdfTemplate->name . '_' . $this->bean->name;

    $this->templateLocation = $this->buildTemplateFile($pdfTemplate);

    $fields = PdfManagerHelper::parseBeanFields($this->bean, true);

    //assign values to merge fields

    $this->ss->assign('fields', $fields);

    //write the pdf from html into a cached file for downloading

    return $this->outputWPdf();

  }

  private function outputWPdf()

  {

    //using wkthmltopdf as our converter of choice.

    global $sugar_config;

    $host = 'https://'.parse_url($sugar_config['site_url'], PHP_URL_HOST);

    if(!empty($this->templateLocation)){

      //get the HTML

      $html = $this->ss->fetch($this->templateLocation);

      //add the css to the HTML and any other headers you wish to add

      $css_url = '<YOUR CSS URL HERE>';

      $html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><link rel="stylesheet" type="text/css" href="'.$css_url.'"/></head>'.$html.'</html>';

      // Run wkhtmltopdf

      $descriptorspec = array(

        0 => array('pipe', 'r'), // stdin

        1 => array('pipe', 'w'), // stdout

        2 => array('pipe', 'w'), // stderr

      );

      $page_size = 'Letter';

      // print-media-type option is essential to proper pagination during conversion so page-break-xxxx css property is respected

      // -q = quiet

      // - -  allows for the pipes to be used

      $options =  " --print-media-type "; //gets proper pagination

      $options .= " -q "; //quiet

      $options .= " --footer-spacing 5 "; //space between footer and text

      $options .= " --header-spacing 5 "; //space between header and text

      $options .= " --page-size ". $page_size;

      $cmd = '/usr/local/bin/wkhtmltopdf ' . $options . ' - - ';

      $process = proc_open( $cmd  , $descriptorspec, $pipes);

      if(is_resource($process)){

        // Send the HTML on stdin

        fwrite($pipes[0], $html);

        fclose($pipes[0]);

        // Read the outputs

        $this->pdf = stream_get_contents($pipes[1]);

        // Any errors that may arise

        $errors = stream_get_contents($pipes[2]);

        // Close all pipes and close the process

        fclose($pipes[1]);

        $return_value = proc_close($process);

        // Check for errors and Output the results

        if ($errors) {

          throw new SugarApiException('PDF generation failed:' . $errors);

        }else {

          //stamp filename with user and date stamp

          $filenamestamp = '';

          if(isset($current_user)){

            $filenamestamp .= '_'.$current_user->user_name;

          }

          $filenamestamp .= '_'.time();

          //avoid special characters in the filename

          $cr = array(' ',"\r", "\n","/","-");

          $filename = str_replace($cr, '_', $this->pdfFilename. $filenamestamp.'.pdf');

          //create the file in sugar cache

          $cachefile = sugar_cached('tmp/').$filename;

          $fp = sugar_fopen($cachefile, 'w');

          fwrite($fp, $this->pdf);

          fclose($fp);

          return($cachefile);

        }

      }else{

        throw new SugarApiException('PDF generation failed: wkhtmlpdf resource error ');

      }

    }else{

      throw new SugarApiException('PDF generation failed: No Template Location ');

    }

  }

  private function buildTemplateFile($pdfTemplate, $previewMode = FALSE)

  {

    if (!empty($pdfTemplate)) {

      if ( ! file_exists(sugar_cached('modules/PdfManager/tpls')) ) {

        mkdir_recursive(sugar_cached('modules/PdfManager/tpls'));

      }

      $tpl_filename = sugar_cached('modules/PdfManager/tpls/' . $pdfTemplate->id . '.tpl');

      $pdfTemplate->body_html = from_html($pdfTemplate->body_html);

      sugar_file_put_contents($tpl_filename, $pdfTemplate->body_html);

      return $tpl_filename;

    }

    return '';

  }

  /**

   * Init the Sugar_Smarty object.

   */


  private function _initSmartyInstance()

  {

    if ( !($this->ss instanceof Sugar_Smarty) ) {

      $this->ss = new Sugar_Smarty();

      $this->ss->security = true;

      if (defined('SUGAR_SHADOW_PATH')) {

        $this->ss->secure_dir[] = SUGAR_SHADOW_PATH;

      }

      $this->ss->assign('MOD', $GLOBALS['mod_strings']);

      $this->ss->assign('APP', $GLOBALS['app_strings']);

    }

  }

}

 

Wkhtmltopdf installation and setup

 

As stated above, we needed an HTML to PDF solution that would allow us to compose documents using a CSS and would render them correctly in PDF and chose wkhtmltopdf for its precision and ease of use.

 

We provide here a walk through for a simple manual installation of wkhtmltopdf on CentOS. Steps on other Red Hat based distributions should be identical.

 

On Debian derivatives dependency package names may differ, and you will use apt-get instead of yum to install them.

 

You will need root access or sudo to do this.

 

In the following writing ,commands requiring root access will be preceded with an asterisk (*).

 

First you need to ensure you have the proper wkhtmltopdf dependencies:

*[root@sugarcrm-test ~]# yum -y install zlib fontconfig freetype libX11 libXext libXrender

Download the latest copy of wkhtmltopdf from the project's download page.

 

We will be using the 64-bit version here:

[root@sugarcrm-test ~]# wget http://download.gna.org/wkhtmltopdf/0.12/0.12.3/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz

It's also advisable to verify the integrity of the file you downloaded:

[root@sugarcrm-test ~]# wget http://download.gna.org/wkhtmltopdf/0.12/0.12.3/SHA256SUMS

[root@sugarcrm-test ~]# sha256sum -c SHA256SUMS

You will see a lot of errors because we didn't download all the files listed in SHA256SUMS. What you are looking to verify is an OK next to the file you downloaded like this:

wkhtmltox-0.12.3_linux-generic-amd64.tar.xz: OK

Next we want to extract the archive and place the files in the proper locations.

 

This next command will do a few things

 

- Extract the archive's contents into /usr/local

 

- Print all the files that were created.

 

- write a file called wkhtmltox-files with a list of files that were extracted.

 

This is useful if you wish to remove or otherwise cleanup the install later.

*[root@sugarcrm-test ~]# tar xvJf wkhtmltox-0.12.3_linux-generic-amd64.tar.xz --strip-components=1 -C /usr/local|grep -v \/$|sed -e 's/^wkhtmltox/\/usr\/local/'|tee wkhtmtox-files

All permissions should be correct out of the box but if you wish to verify you can run the following command and check against the output below.

[root@sugarcrm-test ~]# for file in `cat wkhtmtox-files`;do ls -l $file;done

-rwxr-xr-x 1 root root 45072792 Jan 20 02:32 /usr/local/lib/libwkhtmltox.so.0.12.3

lrwxrwxrwx 1 root root 22 Jul 15 15:56 /usr/local/lib/libwkhtmltox.so.0 -&amp;amp;gt; libwkhtmltox.so.0.12.3

lrwxrwxrwx 1 root root 22 Jul 15 15:56 /usr/local/lib/libwkhtmltox.so.0.12 -&amp;amp;gt; libwkhtmltox.so.0.12.3

lrwxrwxrwx 1 root root 22 Jul 15 15:56 /usr/local/lib/libwkhtmltox.so -&amp;amp;gt; libwkhtmltox.so.0.12.3

-rw-r--r-- 1 root root 2546 Jan 20 02:34 /usr/local/share/man/man1/wkhtmltoimage.1.gz

-rw-r--r-- 1 root root 6774 Jan 20 02:33 /usr/local/share/man/man1/wkhtmltopdf.1.gz

-rw-r--r-- 1 root root 3980 Jul  7  2015 /usr/local/include/wkhtmltox/pdf.h

-rw-r--r-- 1 root root 911 Jul  7  2015 /usr/local/include/wkhtmltox/dllend.inc

-rw-r--r-- 1 root root 1475 Jul  7  2015 /usr/local/include/wkhtmltox/dllbegin.inc

-rw-r--r-- 1 root root 3407 Jul  7  2015 /usr/local/include/wkhtmltox/image.h

-rwxr-xr-x 1 root root 39666344 Jan 20 02:34 /usr/local/bin/wkhtmltoimage

-rwxr-xr-x 1 root root 39745960 Jan 20 02:33 /usr/local/bin/wkhtmltopdf

That should be it. You can verify the binary is working from command line:

[root@sugarcrm-test ~]# wkhtmltopdf http://google.com google.pdf

If you ever wish to remove the application you can do so using the file we generated upon extraction:

*[root@sugarcrm-test ~]# for file in `cat wkhtmtox-files`;do rm -I $file;done

 

There will be a stray empty directory to remove as well:

*[root@sugarcrm-test ~]# rmdir /usr/local/include/wkhtmltox/