Skip navigation
All Places > Developer > Blog > Authors Cédric Mourizard

Developer

4 Posts authored by: Cédric Mourizard
This post originally appeared on the SynoLab blog hosted by Synolia, an Elite SugarCRM Partner. This post describes how to extend the new Sugar CLI framework to add commands that allow Sugar Administrators to monitor the Sugar e-mail queue.

 

Since the Sugar 7.7.1.0 version, SugarCRM introduced a Sugar CLI tool based on Symfony Console. This Sugar CLI tool is under beta version at this moment (August 2016) and can be changed in the future.

 

We will see in this article how to use this new Sugar CLI to add a command which provides some statistics from the Email Manager Queue. We want to display how many emails by campaign by module are waiting to be sent.

 

The first step is to define the Command itself

 

To perform this operation we implementSugarcrm\Sugarcrm\Console\CommandRegistry\Mode\InstanceModeInterface because our command must be executed only on an installed Sugar instance. The only required things to do is to provide the command name, the description, and the help text on the configure() method and then to implement our logic in the execute() method. We are using a SugarQuery to retrieve the data and display the result on a table. We can imagine externalizing this part and using it in an API and creating unit tests.

//File: custom/Synolia/EmailMan/Console/Command/ListQueueCommand.phpnamespace Synolia\EmailMan\Console\Command;use Sugarcrm\Sugarcrm\Console\CommandRegistry\Mode\InstanceModeInterface;use Symfony\Component\Console\Command\Command;use Symfony\Component\Console\Input\InputInterface;use Symfony\Component\Console\Output\OutputInterface;use Symfony\Component\Console\Helper\Table;use Symfony\Component\Console\Helper\TableSeparator;/**

 

*

 

* Email Manager Queue statistics

 

*

 

*/

class ListQueueCommand extends Command implements InstanceModeInterface

 

{

 

    /**

 

     * {inheritdoc}

 

     */

 

    protected function configure()

 

    {

 

        $this

 

            ->setName('synolia:emailman:queue')

 

            ->setDescription('Show Email Manager Queue statistics')

 

            ->setHelp('This command displays statistics from Email Manager Queue.')

 

        ;

 

    }

 

    /**

 

     * {inheritdoc}

 

     */

 

    protected function execute(InputInterface $input, OutputInterface $output)

 

    {

 

        $result = $this->getSugarQuery()->execute();

 

        $nbEmailsToSent = 0;

 

        $table = new Table($output);

 

        $table->setHeaders(array('Campaign', 'Module', 'Count'));

 

        foreach ($result as $row) {

 

            $table->addRow(array($row['name'], $row['related_type'], $row['record_count']));

 

            $nbEmailsToSent += $row['record_count'];

 

        }

 

        $table->addRow(new TableSeparator());

 

        $table->addRow(array('# Emails to send', '', $nbEmailsToSent));

 

        $table->render();

 

    }

 

    /**

 

     * @return \SugarQuery

 

     * @throws \SugarQueryException

 

     */

 

    protected function getSugarQuery()

 

    {

 

        $sq = new \SugarQuery();

 

        $sq->from(\BeanFactory::newBean('EmailMan'))

 

            ->joinTable('campaigns', array(

 

                    'alias' => 'campaigns',

 

                    'joinType' => 'LEFT',

 

                    'linkingTable' => true)

 

            )->on()->equalsField('campaigns.id', 'emailman.campaign_id');

 

        $sq->select(array('campaigns.name', 'emailman.related_type'))->setCountQuery();

 

        $sq->groupBy('campaigns.name')

 

            ->groupBy('emailman.related_type');

 

        return $sq;

 

    }

 

}

 

Declare the Command

 

Now we need to make this command available by using the application/Ext/Console framework:

//File: custom/Extension/application/Ext/Console/SynoliaEmailManListQueueCommand.phpSugarcrm\Sugarcrm\Console\CommandRegistry\CommandRegistry::getInstance()->addCommand(new Synolia\EmailMan\Console\Command\ListQueueCommand());

 

Add our namespace

 

To use our own namespace we can follow one of the way described in our previous article by using theapplication/Ext/Utils framework:

//File: custom/Extension/application/Ext/Utils/SynoliaEmailManConsoleCommandNamespace.phpSugarAutoLoader::addNamespace('Synolia\\EmailMan\\Console\\Command', 'custom/Synolia/EmailMan/Console/Command', 'psr4');

 

Perform a Quick Repair and Rebuild et voilà!

 

Thanks to Jelle Vink about his presentation of this new Sugar CLI tools at UnCon 2016!

 

You can find more information about Sugar CLI on the Sugar Developer Guide.

Here is another guest post from Cédric Mourizard from the Elite SugarCRM Partner Synolia.  Cedric is a well recognized expert on the Sugar PDF Manager.

 

In regard to some recent questions from Sugar Community stalwart, Francesca Shiekh, we will address some questions about how to deal with the PDF Manager that was originally introduced in Sugar 6.

 

A Common Use Case

 

In this article we want to resolve this common use case for customizing the PDF manager. When following up on a meeting, you want to be able to generate an attractive summary document that lists all the meeting details included lists of those who were invited or attended.

 

We will customize the PDF manager so that we can print a meeting summary for any Meeting record that will have all contacts and invited users listed.

 

Understanding PDF Manager and extending it

 

Sugar uses two important 3rd party libraries as part of the PDF Manager implementation. Note that Sugar 7.6.2.1 uses TinyMCE 3.5.8 and TCPdf 4.6.013.  These are not the latest available versions of these libraries.

 

The architecture of the SugarPDF API is described in the Developer Guide.  It can be customized in the usual way by creating your custom code at a special location under Sugar's custom directory. We need to create a file at custom/modules/Meetings/sugarpdf/sugarpdf.pdfmanager.php where we can implement our logic to add related record information for the meeting:

 

 

 

<?php

 

require_once 'include/Sugarpdf/sugarpdf/sugarpdf.pdfmanager.php';

 

class MeetingsSugarpdfPdfmanager extends SugarpdfPdfmanager

 

{

 

    public function preDisplay()

 

    {

 

        parent::preDisplay();

 

        $previewMode = false;

 

        if (!empty($_REQUEST['pdf_preview']) && $_REQUEST['pdf_preview'] == 1) {

 

            $previewMode = true;

 

        }

 

        if ($previewMode === false) {

 

            // Select links to add

 

            $linksToLoad = array('contacts', 'users');

 

            foreach ($linksToLoad as $linkName) {

 

                /** @var Meeting $this->bean */

 

                $this->bean->load_relationship($linkName);

 

                $linkedBeans = $this->bean->$linkName->getBeans();

 

                // Prepare data

 

                $linkedData = array();

 

                foreach ($linkedBeans as $linkedBean) {

 

                    $linkedData[] = PdfManagerHelper::parseBeanFields($linkedBean, true);

 

                }

 

                // Assign data to template

 

                $this->ss->assign($linkName, $linkedData);

 

            }

 

        }

 

    }

 

}

 

 

 

Now we can create our PDF template based on the Meeting module with the PDF Manager which can be found in the Administration panel:

 

 

 

<h1>Meeting Summary</h1>

 

Topic: {$fields.name}

 

Description: {$fields.description}

 

{if isset($contacts) and $contacts|@count gt 0}

 

<h2>Contact Guests</h2>

 

<table style="width: 100%;" border="0">

 

<tbody>

 

<tr>

 

<td><strong>Name</strong></td>

 

<td>Email</td>

 

<td>Phone</td>

 

</tr>

 

<!-- {foreach from=$contacts item="contact"} -->

 

<tr>

 

<td>{$contact.full_name}</td>

 

<td>Email {$contact.email1}</td>

 

<td>{$contact.phone_work}</td>

 

</tr>

 

<!--  {/foreach} --></tbody>

 

</table>

 

{/if} {if isset($users) and $users|@count gt 0}

 

<h2>User Guests</h2>

 

<table style="width: 100%;" border="0">

 

<tbody>

 

<tr>

 

<td><strong>Name</strong></td>

 

<td>Email</td>

 

<td>Title</td>

 

</tr>

 

<!-- {foreach from=$users item="user"} -->

 

<tr>

 

<td>{$user.full_name}</td>

 

<td>{$user.email1}</td>

 

<td>{$user.title}</td>

 

</tr>

 

<!-- {/foreach} --></tbody>

 

</table>

 

{/if}

 

 

Hey! When I copy and paste this code into the WYSIWYG editor it doesn't appear the same! What is going on here?

 

You are right! By default TinyMCE will add an HTML p tag on every line. To avoid that you can adapt the configuration by creating a file at custom/include/tinyMCEDefaultConfig.php

 

 

 

<?php

 

$defaultConfig = array(

 

    'forced_root_block' => '',

 

);

 

 

 

Want to learn more about TinyMCE configuration in Sugar? Do not hesitate to read the world famous Sugar Developer Guide section on Modifying TinyMCE Editor!

 

Tips for your own Templates

 

You can follow some of these tips when you are thinking about creating a new template:

  • Use HTML comment tag to add Smarty operators where you want
  • Use inline styles instead of a CSS declaration
  • Remember that PDF generation is performed using the language selected by the current user

 

Do not forget to perform a Quick Repair and Rebuild to make your customizations available for the application.

 

Enjoy your new PDF summary for your meetings!

Here is another guest post from Cédric Mourizard from Synolia, an Elite SugarCRM Partner, and long-time member of the Sugar Developer community.  Are you interested in posting on the Sugar Developer Blog?  Contact developers@sugarcrm.com with your idea.

 

Many years after a post about how to use Sugar on the DotCloud PaaS, it is time to continue with the same company and to speak a little bit about Docker.

 

Docker is one of the hottest topics in the DevOps world for the past two years and most of people in the Sugar Community probably already use Docker as their development platform. We will present a quick intro about how to start to use Docker with Sugar. You can also find many great resources for other ways to setup your Sugar development environment on the Sugar Developer Guide or from stalwart Sugar Community members like Enrico Simonetti or Jeff Bickart.

 

Quick introduction to Docker & Sugar

Docker allows you to package an application with all of its dependencies into a standardized unit for software development.https://www.docker.com/whatisdocker

 

Docker uses container technology to provide you a quick and easy way to launch an application and execution stack.

 

Sugar needs at least a web server with PHP, a database server, and an Elasticsearch server.  Following the currently recommended stack described on the Sugar Supported Platforms page, we will assume that the web server will be Apache with PHP 5.4, that the database server will be Percona Server 5.6, and that we will be using Elasticsearch 1.4.

 

The best way to set up docker on your development environment is to follow the official documentation.  For Windows and OSX users, you will need to additionally use Docker Machine as Docker only runs on Linux natively.

 

After that you also need to install Docker Compose which is a tool for launching multiple containers using a single description file usually named docker-compose.yml

 

For Windows users, docker-compose is not yet available as a native tool.  However Docker itself solves this problem!

Windows users, you can use a docker container to launch the docker-compose tool with this image: https://hub.docker.com/r/dduportal/docker-compose/

 

Our example implementation will follow this architecture:

 



Download the example files above from this Github project.

 

Step 1) Build your containers

 

Sugar needs a set of additional PHP modules to be fully functional, so we cannot just use the official docker image. We need to build our own images.  To do that we create a docker file based on the official docker image that we customize by adding additional modules and PHP configuration changes.images/php5.4-apache/Dockerfile

 

FROM php:5.4-apache

 

MAINTAINER cedric.mourizard@gmail.com

 

RUN    apt-get -qqy update \

 

    && apt-get install -y libpng12-dev libjpeg-dev \

 

    && apt-get -y install re2c libmcrypt-dev \

 

    && apt-get -y install zlib1g-dev \

 

    && apt-get -y install libssl-dev libc-client2007e-dev libkrb5-dev \

 

    && apt-get -y install libcurl4-gnutls-dev \

 

    && apt-get -y install libxml2-dev libxslt-dev \

 

    && apt-get -y install libssl-dev \

 

    && apt-get -y install libcurl4-openssl-dev

 

RUN    docker-php-ext-install bcmath \

 

    && docker-php-ext-configure gd --with-jpeg-dir=/usr/lib \

 

    && docker-php-ext-install gd \

 

    && docker-php-ext-configure imap --with-imap-ssl --with-kerberos \

 

    && docker-php-ext-install imap \

 

    && docker-php-ext-install mbstring \

 

    && docker-php-ext-install mcrypt \

 

    && docker-php-ext-install mysqli \

 

    && docker-php-ext-install pdo_mysql \

 

    && docker-php-ext-install zip

 

COPY config/crm.php.ini /usr/local/etc/php/conf.d/

 

RUN a2enmod headers expires deflate rewrite

 

EXPOSE 80

 

VOLUME ["/var/www"]

 

For Percona and Elasticsearch server, we can just use the official images.  The docker-compose.yml is used to describe our full stack.docker-compose.yml

 

web:

 

    container_name: web

 

    build: ./images/php5.4-apache

 

    ports:

 

        - 80:80

 

    links:

 

        - db

 

        - search       

 

    volumes:

 

        - /Users/cedric/sugar-docker-example/data/web:/var/www/html

 

db:

 

    container_name: db

 

    image: percona:5.6

 

    environment:

 

        - MYSQL_ROOT_PASSWORD=root

 

    ports:

 

        - 3306:3306

 

search:

 

    container_name: search

 

    image: elasticsearch:1.4

 

    ports:

 

        - 9200:9200

 

        - 9300:9300

 

We can see in this file:

  • Our 3 server applications
  • The web server has LINKS to the database server and the search server
  • Each application use a shared directory with the VOLUME setting. It allows you to keep the code visible on your computer and share it with your containers
  • All of them expose PORTS to provide an easy way to connect your tools into each piece of the stack. For example, you will be able to connect your MySQL Workbench to browse your database server.

 

Step 2) Launch the platform and install Sugar

 

Now we can launch the platform and start all containers in background.

$ docker-compose up -d

 

.

 

.

 

Starting search...

 

Starting db...

 

Starting web...

 

The first launch will take a few minutes because docker retrieves and build all necessary data to setup the platform. This only happens the first time you launch docker-compose or after you modify the stack.

 

Now that your stack is launched then you can visit your Sugar instance with your browser.  For example, http://localhost/mysugar/ or http://DOCKER_MACHINE_IP/mysugar/.  If using Docker Machine, then you can easily find your running machine's IP using the docker-machine ip <name> command.

 

During the setup process, you can change the NAME settings used in the docker-compose.yml file to whatever necessary. For example, the database host name is “db” and the Elasticsearch host name is “search”.

 

That’s it, you’re done!

 

A few more things

 

If you need to interact with the Sugar Scheduler or the Job Queue, then you will need to call cron.php manually.  So if you want to run PHP CLI scripts like cron.php, you can go inside your web container and obtain the bash prompt with this command.

$ docker exec -it web bash

 

root@xxxxx:/var/www/html# php -f PATH_TO_SUGAR/cron.php

 

Conclusion

 

Now you can start to play with docker and Sugar to:

  • Develop your Sugar customizations
  • Try the Sugar 7 unit test suite
  • Try configuring an advanced stack with multiple database servers

 

This post provided an introduction for setting up Sugar on docker. You will need to adapt these steps to your own context or development processes.

 

This Docker stack is best used as a development platform and not for production use where you need to take care of performance and security aspects which aren’t addressed in this post.

Here is a guest post from a couple members of the Sugar Developer community.  Yann Berges and Cédric Mourizard from Synolia, an Elite SugarCRM Partner, share some insights on how to improve the quality of Elasticsearch results in Sugar.

 

Elasticsearch in Sugar 7

Since Sugar 6.5, Elasticsearch has been included as a core feature of the Sugar Application and became a required component in Sugar 7.x releases. You can find some information regarding installation, configuration, and monitoring of Elasticsearch in the Knowledge Base.

It works very well and is pretty fast!

However, the default configuration is often too strict with diacritics such as accent and stressed letters found in languages like French.  For example, the default configuration will not match e with é during a global search which is not desirable for us.

 



Below we will explore how Elasticsearch Analyzers can be used to address this issue.  It is quite easy and does not require custom code!

 

To improve the global search with this type of capability you need to setup an Analyzer. Analyzers are how Elasticsearch generates tokens from input data to be indexed. You can also combine analyzers for multiple transformations; like converting text to lowercase, utilize a list of stopwords to exclude some words and lot of capabilities, or apply regular expressions.

 

Configuring Elasticsearch Analyzers in Sugar 7.6

One of the out of the box analyzers called asciifolding is for converting Unicode characters into lower ASCII, when they exist.  To add this type of configuration you could do that very easily with an entry in $sugar_config.

Add the following configuration setting to your config_override.php at the root of your Sugar 7.6 installation.  You may need to create this PHP file if it doesn't exist already.  The significant part is the line where the 'asciifolding' filter is added.

SugarElasticsearchConfgWithAsciifolding.php

<?php

$sugar_config['full_text_engine']['Elastic']['index_settings']['default']['index'] = array(
'analysis' => array(
'analyzer' => array(
'core_email_lowercase' => array(
'type' => 'custom',
'tokenizer' => 'uax_url_email',
'filter' => array(
'lowercase',
                ),
            ),
'standard' => array(
'type' => 'custom',
'tokenizer' => 'standard',
'filter' => array(
'asciifolding',
'lowercase',
                ),
            ),
        ),
    ),
);

After updating your config, you need to do a Quick Repair & Rebuild and perform a full system index which will rebuild the index data using this new additional analyzer.

Now if one of your modules contains, for example, cooking recipes then you can now search “Saute” and find the right results with or without accents like “Sautéed Tuna Steaks”

 



Enjoy your (Elastic)search!