Quantcast
Channel: Sugar 7 – Sugar Developer Blog – SugarCRM
Viewing all 93 articles
Browse latest View live

How to perform Data Anonymization for Sugar development

$
0
0

Here is another guest post from Emmanuel Dyan from the Elite SugarCRM Partner iNET Process. In it he addresses a common Sugar project requirement using an open source tool developed by iNET Process.

The problem that we will try to solve with this post is:

How do we make sure that we are never developing using actual customer data but, at the same time, work with data that reflects reality?

Data Anonymization

Usually, when we work on a customization project for a customer you have a minimum of 3 different environments: Development,  User Acceptance Testing (UAT), and Production. To make sure that we work in optimal and consistent conditions, we usually copy the database from one environment to another (preferably from production to other environments). Doing this type of manipulation has multiple drawbacks, including:

  • We have to collect a Database Dump which means that it contains unencrypted raw data. What would happen if we mistakenly expose this dump to someone who is unauthorized?
  • We have to test some functionality to make sure that it works. What would happen if we test a Campaign that sends thousand of e-mails … to the … actual customers of our customer?

Anonymizing the data is the best practice to avoid “playing” with customer data and to keep their trust in us.

The challenge with anonymizing data is figuring out how to overwrite the data with something that is completely unrecognizable. For example: “John Doe” will become “voluptatem accusantium“. His job title becomes “doloremque” and his country “magnam“. His phone number will become “569898520114457878744778” instead of “+123456789“.

Anonymization Tool

Now how do we work in realistic conditions with that kind of anonymization? Indeed, we need another solution that works with any Sugar instance. The solution we are demonstrating below is implemented in our open source CLI tool (sugarcli).

The anonymization architecture follows:

  • It uses another independent tool, called “neuralyzer” (by iNet Process) that is a command line tool to anonymize a database.
  • Neuralyzer uses a library called “Faker” to generate realistic data.
  • It is composed of two subcommands:
    • A configuration generator.  This is used to generate a configuration file automatically without destroying the system tables (config, relationships, etc).
    • The anonymizer that connects directly to the Sugar Database to perform the job. It uses iNET Process libsugarcrm to performs SQL queries (via a PDO connection). It also purges deleted records and cleans _cstm tables of deleted records. It finishes by then emptying all _audit tables.

Configuration Generator

The configuration generator reads all the Sugar database tables and tries to guess, from the field type or the field name what type of fake data needs to be generated. For example a field containing the string city will receive a random city.  If a field contains _user_id then it will be ignored in order to preserve unique IDs.

To use it, first download SugarCLI from https://github.com/inetprocess/sugarcli.

Then run:

./sugarcli.phar anonymize:config --path <sugarcrm_path>

The parameters are the following:

    --file=FILE                   Path to the configuration file [default: "../db/anonymization.yml"]
    --ignore-table=IGNORE-TABLE   Table to ignore. Can be repeated (multiple values allowed)
    --ignore-field=IGNORE-FIELD   Field to ignore. Can be repeated (multiple values allowed)
-p, --path=PATH                   Path to SugarCRM installation.

Example

The command ./sugarcli.phar anonymize:config creates a file that looks like:

guesser_version: 1.0.0
 entities:
     accounts:
         cols:
             name: { method: company }
             description: { method: sentence, params: [20] }
             facebook: { method: url }
             twitter: { method: url }
             googleplus: { method: url }
             account_type: { method: randomElement, params: [['', Analyst, Competitor, Customer, Integrator]] }
             industry: { method: randomElement, params: [['', Apparel, Banking, Biotechnology, Chemicals]] }
             annual_revenue: { method: randomNumber, params: [4] }
             phone_fax: { method: phoneNumber }
             billing_address_street: { method: streetAddress }
             billing_address_city: { method: city }
             billing_address_state: { method: state }
             billing_address_postalcode: { method: postcode }
             billing_address_country: { method: country }
             rating: { method: sentence, params: [8] }
             phone_office: { method: phoneNumber }
             phone_alternate: { method: phoneNumber }
             website: { method: url }

As you can see, the commands uses different methods to guess the type of faker methods to use:

  • If it is a dropdown then it gets the list of values from vardefs
  • If the field contains a known word it uses a pre-defined method (example .*_city = city)
  • Otherwise it will use the dbType (varchar = sentence)

You can change the content of the file once generated to match your criteria.

Run the anonymization

Warning! Do not run this on a production instance! This command will overwrite data in the target database. 

./sugarcli.phar anonymize:run --path <sugarcrm_path>

The parameters are the following:

    --file=FILE        Path to the configuration file [default: "../db/anonymization.yml"]
    --force            Run the queries
    --remove-deleted   Remove all records with deleted = 1. Won't be launched if --force is not set
    --clean-cstm       Clean all records in _cstm that are not in the main table. Won't be launched if --force is not set
    --sql              Display the SQL of UPDATE queries
    --table=TABLE      Anonymize only that table (repeat for multiple values) (multiple values allowed)
-p, --path=PATH        Path to SugarCRM installation.

 

Example

The command ./sugarcli.phar anonymize:run –table=accounts –force gives the following output:

Be careful, the anonymization is going to start
 That will overwrite every data in the Database !
If you are sure, please type "yes" in uppercase
 YES
Anonymizing accounts
50/50 [============================] 100%
Emptying accounts_audit
Emptying bugs_audit
Emptying campaigns_audit
Emptying cases_audit
Emptying contacts_audit
Emptying contracts_audit
....
Done in 0.42 sec (consuming 40.5Mb)


Now you can dump your database and send it to your development team! If you use the parameters –remove-deleted and –clean-cstm, it will be smaller too.



How platform parameter works in Sugar v10 REST API

$
0
0

What does the platform parameter mean in v10 REST API?

If you open your web browser’s network tools, a login request to the v10 REST API used in Sugar 7.6 will typically look something like the example below.

POST /rest/v10/oauth2/token HTTP/1.1
Host: localhost
Cache-Control: no-cache
Content-Type: application/json
{
     "grant_type": "password",
     "username": "admin",
     "password": "admin",
     "client_id": "sugar",
     "platform": "base",
     "client_secret": "",
     "current_language": "en_us",
     "client_info": {
         "current_language": "en_us"
     }
}

You can see in the request that we have specified a platform parameter called “base”. This parameter is optional (the default is base) so even if you have used the v10 REST API before you may not have been aware of it or what it means.

Sugar Platforms

Platforms are used by Sugar to support the needs of multiple Sugar clients.  For each client, you are allowed to specify unique Sidecar Viewdefs, custom or modified platform API endpoints, as well as the ability to support a concurrent user session.

Sugar platform specific code is found under the clients/ and modules/MODULE_NAME/clients directories.  Sidecar client metadata is implemented under clients/*/layouts|views|fields and platform API endpoints are implemented under clients/*/api.  Sugar will fall back to base platform implementation when a more specific implementation is not available.

In general, the Sugar v10 REST API will only allow a single session (specifically, a single OAuth access token) to be created for any single platform at any one time.  For example, you cannot log into Sugar from multiple different web browsers on different computers at the same time.  This is necessary to prevent users from sharing subscriptions while allowing users to simultaneously access Sugar using a mobile device and their browser.

This is allowed by the fact that the SugarCRM Mobile app uses the mobile platform identifier while the web client uses the base platform identifier.

The number of sessions that is allowed per Sugar Platform is controlled by the SugarOAuth2StoragePlatform class that is associated with with a particular platform.

For example, if you look at SugarOAuth2StorageBase.php, you will see that the base platform allows only 1 standard web session at a time.

However, examining SugarOAuth2StorageMobile.php will show that we allow 2 mobile sessions at a time to allow access from multiple personal devices such as smartphones and tablets.

Thus it is possible to change the number of allowed concurrent sessions by overriding or implementing a new a SugarOAuth2StoragePlatform class for a custom platform.

Specifying new custom platforms will generate new copies of metadata cache

Today, Sugar supports the ability to specify arbitrary platforms.  This allows you the ability to create your own custom Sidecar based clients that are configured with their own custom metadata that extends from the base Sugar metadata.

A side effect though is that Sugar will build a metadata cache for each and every platform that it comes across.  Even if no custom platform metadata is specified, it will just inherit from base metadata.

Problems caused by random platform IDs

Some developers have followed our previous advice in order to allow an integration to maintain concurrent user sessions by creating their own unique platform IDs.  However, in some cases we have found integrations using random platform IDs which causes a lot of bad side effects.

For example, generating new copies of platform metadata takes time, so there can be an impact on user interface responsiveness.  Disk usage starts to swell and the size of an instance back up grows tremendously. Also, downstream integrations and systems can be affected due to slowed API responsiveness because of all this extra processing.

Sugar 7.6 Changes

We have added a new Sugar Configuration setting called disable_unknown_platforms that is currently disabled by default.  SugarCRM will selectively enable this setting on Sugar On-Demand instances that appear to be negatively affected by use of random platform IDs.

You can enable this setting on your own instance by setting up a config override.

config_override.php

<?php
$sugar_config['disable_unknown_platforms'] = true;

When this setting is enabled then any custom platforms that are used must be registered via the custom directory.

custom/client/platforms.php

<?php
$platforms[] = 'base';
$platforms[] = 'mobile';
$platforms[] = 'portal';
// New one
$platforms[] = 'mine';

Changes in future Sugar versions

In the future, we plan to create an Extension for registering custom platforms. We also may enable this restriction more broadly in Sugar On-Demand. So it is important that Sugar Developers know how to properly use Sugar Platforms.


New releases of Sugar Test Tools for Sugar 7.7

$
0
0

To coincide with the release of Sugar 7.7, the Engineering team has released updated versions of Unit Tests and Performance tools.

Requesting access to Sugar Test Tools

Sugar Test Tools are in private Github repositories within the SugarCRM Github organization. Requesting to have your Github account added to the SugarCRM Github organization is easy, just fill out this form.  Visit the Developer Tools section of this site to learn more.

Sugar 7.7 Unit Tests

The Sugar 7.7 Unit Test framework is now available in the Sugar Unit Test repository.  This package includes all the test suites and unit test framework used to develop Sugar 7.7.

The Sugar 7.7 unit tests are in the 7_7_0 branch of the Unit Tests repository.

Sugar Load Testing Framework v2.5

The v2.5 release includes several new feature enhancements and support for load testing Sugar 7.7.  Some of the new features include the ability to adjust the rate that Create/Delete actions are run during a performance test as well as support for new APIs in Sugar 7.7 release.

The back-end load testing framework is in the backend-side branch of the Performance repository.

Sugar Client-Side Performance Testing framework

This is an all new release! The Performance team has created a framework for front-end (JavaScript) performance testing based on Cucumber.JS.  This framework can be used to automated your front-end performance tests to ensure that JavaScript performance and responsiveness does not degrade over time.

This client-side framework is in the client-side branch of the Performance repository.

Come to UnCon to learn more!

Learn how to use these tools from the Sugar Engineering team themselves at the UnCon at SugarCon on June 14th and 15th in San Francisco!

Join Yvan and Robert to learn more about writing Sugar unit tests and Alesia and Igor to learn more about Using Sugar Tools for Performance.


Use cases needed for Elasticsearch breakout and more!

$
0
0
sugarcon-2016-uncon-265x214

Brace yourself. UnCon is coming.

 

Have you registered for SugarCon yet? Remember that UnCon is right around the corner! We have 3 general sessions and 24 different technical breakout sessions at UnCon this year. That is over 24 hours worth of fantastic Sugar technical content that you do not want to miss!

Elasticsearch

We are doing multiple sessions on Elasticsearch which has been a very popular topic at past events.

Last year’s UnCon provided an example customization that allowed full text search of file attachments on Documents and Notes modules. The Elasticsearch file search package is over in the UnCon Github repository, if you want to try it out for yourself. It is worth a look!

For this year, we want to do build something different. Do you know of an Elasticsearch use case that you would like to see implemented in Sugar? Have you been asked to do a geolocation search? Perhaps a request for some fuzzy analysis of search terms?

Post a comment to the Elasticsearch Deep Dive abstract with your use cases. Our Elasticsearch wizards are standing by!

G11N, I18N, L10N and Accessibility

There are Sugar customer all over the world, so a big emphasis for our product is making sure it is ready for global users with a variety of languages, cultures, and accessibility challenges. The processes for preparing or designing software to support the needs of global and diverse user community has many names: globalization, internationalization, localization, and/or accessibility.

Have you struggled getting Right-to-Left support working in your Sugar customizations? Never used a screen reader before?

Post a comment to the G11N, I18N, L10N, and Accessibility abstract with any of your web application challenges that we should address.

DevOps / Automation at Sugar

Do you have a DevOps process? Don’t know where to get started? Allow our DevOps experts guide your way!

Post a comment to the DevOps / Automation at Sugar abstract with the details of your development processes and technology that we can focus the discussion on solutions that matter to you.

And More!

There are dozens more sessions to talk about. Please, review the abstracts in the UnCon community to learn about them all!

We hope to see you in beautiful San Francisco.

 

bridge

See you soon at The City By The Bay

 

 


Integrating to Sugar using a custom drawer action

$
0
0

There are so many possible approaches for integrating with Sugar that selecting the best one can be tricky.

When deciding to add new components into the Sugar user interface, the number of possibilities to consider is dizzying.

Should I build a new component or override an existing one?

Am I building a View, Layout, or custom Fields?

Will a Dashlet give me enough screen real estate or will I need to create a new layout?

The goal of the Sugar Integration Building Blocks open source project is to provide sample code and most importantly the underlying design patterns that makes it easy to build best practice integrations.

Some integration use cases mean that a primary action has to be added to a Record or List view. For example, you may need to launch a wizard interface such one used with a Configure Price Quote (CPQ) solution on an Opportunity record.

A common CPQ flow would be to have a custom “Create Quote” action on an Opportunity record. This would launch an expansive Configurator wizard that, when complete, will need to push the new Quote, new Revenue Line Items, etc, back to the original Opportunity record.

The standard approach for this would be to add a button to the Record View or List View that launches a Drawer.

In order to make this design approach easier, we have added an HTML iframe drawer action as a new Building Block!  Now it is very easy to build a complete integration or proof of concept that utilizes a drawer!

Screen Shot 2016-06-06 at 11.26.04 AM

An example “Open Drawer” action

Read more below to learn how it works.  It was designed in an upgrade safe way that does not interfere with any existing customizations that may exist on Record views or List views.

 

The Integration Flow

This building block contributes an action to the Opportunities Record layout but can be easily adapted to make a contribution to any other application screen (or Sidecar route).

Screen Shot 2016-06-06 at 10.38.06 AM

This new button was added to Opportunities Record view

Clicking the “Open Drawer” button will launch a configurable IframeDrawerLayout. This action uses our standard Drawer API.

app.drawer.open({
    layout: 'iframe-drawer',
    context: {
        model: model,
        url: "//httpbin.org/get?record="+model.get("id")+"&module=Opportunities",
        title: "IFrame Drawer"
    }
}, function(){
    // ... on close callback ...
});
Screen Shot 2016-06-06 at 11.38.19 AM

Active IframeDrawerLayout on an Opportunity

Of course, for the example we just use the httpbin.org service to make it easy to show what the request looks like to a downstream web application. This context can give the downstream application what it needs to appropriately configure the interface appearing in the HTML iframe.  Passing the module name and the record ID would also allow this external application to push changes back into the original Opportunity record in Sugar via the REST API.

Open drawers are placed on to the foreground, which means that when the drawer is closed by hitting the “Cancel” button that the control returns to the Opportunity record. You can still see the Opportunity at the bottom of the screenshot above. You can also close the drawer programmatically by calling the following function.

app.drawer.close();

Adding the button and event handling code

It is very common for Sugar instances to have significant customizations on Record or List view controllers. So for this approach, we want to maximize our compatibility since we may not always know when such customizations exist ahead of time in a particular Sugar instance. So we have intentionally avoided overriding a Sidecar controller in this implementation.

Adding a button that triggers a Sidecar event can be accomplished entirely using a Sidecar metadata extension, such as the one below.

<?php
/**
 * Copyright 2016 SugarCRM Inc.  Licensed by SugarCRM under the Apache 2.0 license.
 */

//Insert our custom button definition into existing Record View Buttons array for Opportunities module
array_unshift($viewdefs['Opportunities']['base']['view']['record']['buttons'],
    array(
        'name' => 'custom_button',
        'type' => 'button',
        'label' => 'Open Drawer',
        'css_class' => 'btn-primary',
        //Set target object for Sidecar Event.
        //By default, button events are triggered on current Context but can optionally set target to 'view' or 'layout'
        //'target' => 'context'
        'events' => array(
            // custom Sidecar Event to trigger on click.  Event name can be anything you want.
            'click' => 'button:open_drawer:click',
        )
    )
);

We then add the following JavaScript code into the Sugar page using a JSGroupings extension.

(function(app){
    /**
     * Copyright 2016 SugarCRM Inc.  Licensed by SugarCRM under the Apache 2.0 license.
     */

    //Run callback when Sidecar metadata is fully initialized
    app.events.on('app:sync:complete', function(){

        var openDrawerCallback = function(model){...};

        //When a record layout is loaded...
        app.router.on('route:record', function(module){

            //AND the module is Opportunities...
            if(module === 'Opportunities') {

                //AND the 'button:open_drawer:click' event occurs on the current Context
                app.controller.context.once('button:open_drawer:click', openDrawerCallback);
            }
        });

    });
})(SUGAR.App);

Our custom JavaScript waits until login and metadata sync is complete and then attaches a listener to Sidecar’s Router.  This listener is used to determine when the desired layout is being displayed and add a listener to the current Context object for our button’s click event.

Get the entire example in Github!

The full example is available in Github as a complete package!  For more information on how to work with these Module Loadable packages in this repository, check out the Packages README.


UnCon 2016 Recap!

$
0
0

Thanks to everyone who attended the UnCon! It was a whirlwind for all of us but I think we have put on the best iteration of this event we have ever had.

In brief, it went so well that there is wide agreement across the company for a significantly expanded event for Sugar Developers at SugarCon next year. So we look forward to seeing you all again for SugarCon in September 2017.

This Year’s Highlights

It was standing room only for our UnCon general sessions, especially for the Platform Update on Tuesday and the Architecting Successful Projects panel on Wednesday.

Some of the most popular breakout topics led by Sugar Engineers were on Sugar tools that can be leveraged for Sugar development and deployment.  These topics included the Sugar Unit Test framework, Performance Test framework, as well as our soon to be released Sugar command line interface and Mobile SDK.

Other popular breakout topics included our deep dive into Advanced Workflow, Sidecar, and our two different sessions on Elasticsearch.

IMG_20160614_162528434 IMG_20160614_135153282 IMG_20160614_135146342 IMG_20160614_135247906_HDR IMG_20160615_145010601 (1)

Example code, presentations, and recordings will be available to everyone!

The real value of UnCon is being able to get face to face with the Sugar Engineers and other Sugar Developers. It is a shame if you missed it. But take comfort, even if you did not make it you will still get access to all the UnCon presentations, example code, and video recordings for our UnCon general sessions.

All example code shown at UnCon is already available in the UnCon Github repository.

The slides for all the presentations shown at this year’s UnCon will be posted in the UnCon community over the next couple of weeks.

Video recordings will be edited and then posted as soon as they are available.


UnCon 2016 slides and code are available!

$
0
0

As promised, slides and code from UnCon are now available.  Video editing is still being worked on but videos of UnCon general sessions will be posted as soon as they are available. My Suga colleagues really outdid themselves this year!

UnCon 2016 Slides

All the slides from each of the general and breakout sessions have been posted in the UnCon community.

This is a great opportunity to refresh your memory or review any of the presentations that you happened to have missed this year. There are 28 presentations to go through.

Use these presentations as an aide to help you present what you learned at UnCon to your own colleagues!

UnCon 2016 Code

All example code shown at UnCon is available in the UnCon Github repository in the 2016 branch.  All the example code in this repository is licensed under Apache 2.0 unless otherwise noted.

There are a ton of code examples to learn and try out for yourselves. We presented and shared at least 2X more code than last year!

SLOC for UnCon 2016 Branch (as of June 28th)

Language files blank comment code
PHP 69 455 747 2,806
JavaScript 25 283 377 1,709
CSS 1 294 25 1,019
XML 7 0 0 347
Handlebars 16 5 63 204
JSON 3 4 0 73
LESS 1 0 3 2
SUM: 122 1,041 1,215 6,160

We also showed open source projects that exist in other Github repositories such as Sucrose.io and Sidecar Debugger Tool.


Prepare for the Backbone.js upgrade in Sugar 7.8

$
0
0

backbone

Upgrading our Backbone

Have you done some Sidecar programming lately? Then you have been using Backbone. Backbone is the err… backbone of Sidecar. It provides all the base MVC classes which are extended by Sidecar to create the Sugar 7 UI. For example, all Sidecar controllers (Views, Layouts, Fields) extend from the Backbone View class.

Ultimately, a solid background in Backbone programming will turn you into a Sidecar wizard in no time.

Screen Shot 2016-07-11 at 11.12.50 AM

All Sidecar controllers, such as the Record View controller,  extends from the Backbone View class

But if you are a Backbone aficionado then you might have noticed that Sugar 7.7 and earlier versions uses an old version of Backbone (specifically Backbone 0.9.10). We have been missing out on bug fixes and miscellaneous feature improvements. So for Sugar 7.8 we will be moving to Backbone 1.2.3.  Since Backbone.js has a hard dependency on Underscore.js, we will also upgrade the Underscore library from 1.4.4 to 1.8.3.

All Sugar Developers should check out the Backbone changelog and the Underscore changelog to see if their code customizations could be impacted by this long overdue library upgrade.

Read on to learn more about some adjustments you need to make to your Sugar code.

Changes to Sidecar controller DOM event delegation

In Backbone 1.2.0, there was an important change that affects how DOM events are delegated in Backbone Views. Emphasis mine.

Views now always delegate their events in setElement. You can no longer modify the events hash or your view’s el property in initialize.

This means that modifying this.events in the initialize() function of a Backbone View to register DOM event handlers is no longer supported by Backbone. This is because DOM events set in the events hash (this.events) are delegated before initialize() is even called. However, since this was a common practice for Sugar code and customizations we have altered the default Backbone behavior within Sidecar for the Sugar 7.8 release.

Sugar will continue to continue to call delegateEvents() during initialize() in Sugar 7.8 for compatibility but the practice is deprecated since Backbone no longer supports it. Sidecar controllers that modify this.events during initialize() will continue to work until this workaround is removed in an upcoming Sugar release.

Here is a simple example of a Sidecar view that uses this deprecated practice.

A simple example

./custom/clients/base/views/example/example.js

/** This approach is deprecated in Sugar 7.8 release  **/
({
    events: {...},
    initialize: function(options) {
        if (...) {
            this.events['click'] = function(e){...};
        }
        this._super('initialize', [options]);
    },
    ...
})

This will not work in a future Sugar release.

A Record View use case

Let’s examine a common Sidecar customization use case.

Say we need to extend the out of the box Sugar RecordView controller to launch a wizard user interface on a mouse click.

We plan to listen for a special DOM click event but we also do not want to break any existing Record view event listeners.

To implement this feature, Sugar Developers commonly used code such as the following:

./custom/…./clients/base/views/record/record.js

/** This approach is deprecated in Sugar 7.8 release  **/
({
    extendsFrom: 'RecordView',
    initialize: function(options) {
        // Extending the RecordView events in a deprecated fashion
        this.events = _.extend({}, this.events, {
            'click .wizard': '_launchWizard'
        });
        this._super('initialize', [options]);
    },
    _launchWizard: function(){
      // ... do something ...
    }
})

To reiterate, the examples above will no longer work in a future Sugar release. Sugar Developers should update any similar code to use alternative approaches listed below.

Event Delegation Alternatives

Here are some alternatives that you can use for delegating DOM events with your Sidecar controllers.

Statically define your events hash

Define your events all within the events object hash. Note that when extending controllers that this would override any events defined on a parent controller.

({
    events: {
        'mousedown .title': 'edit',
        'click .button': 'save',
        'click .open': function(e) { ... }
    }
    ...
})

Use a callback function for dynamic events

You can assign the events variable of Backbone controllers a function instead of an object hash.  This function will then be used to determine the event hash used when delegateEvents() is called by Backbone.

({
    events: function(){
        if (...) {
            return {'click .one': function(){...}};
        } else {
            return {'click .two': function(){...}};
        }
    }
    ...
})

If you must, then call delegateEvents() function directly

You can optionally pass an alternative event hash using this.delegateEvents(events). When unspecified, this.events is used by default. The delegateEvents() function removes any previously delegated events at same time so it is safe to call multiple times.

({
    extendsFrom: 'RecordView',
    oneEvents: {...},
    twoEvents: {...},
    isOne = true,
    toggleOneTwo: function(){
        if (this.isOne) {
            this.delegateEvents(_.extend({}, this.events, this.oneEvents));
        } else {
            this.delegateEvents(_.extend({}, this.events, this.twoEvents));
        }
        this.isOne = !this.isOne;
    }
    ...
})

Other important Backbone changes

  • Backbone.js no longer attaches options to the Backbone.View instance by default (as of 1.2.0). Sugar Developers should know we plan to deprecate this.options on Sidecar controllers in a future Sugar release.
  • This upgrade may also break customizations of Sidecar routes that expect URL parameters to be concatenated to the first argument passed to the Backbone router’s callback. Sugar Developers should change the signature of their router callbacks to specify the additional argument for URL parameters.

For example:

Old way:

// in a sugar7.js equivalent file
{
    name: 'search',
    route: 'search(/)(:termAndParams)',
    callback: function(termAndParams) {
        // termAndParams =&amp;gt; &amp;quot;?module=Accounts&amp;amp;amp;amp;foo=bar&amp;quot;
        // commence ugly URL parsing...
    }
}

New way:

// in a sugar7.js equivalent file
{
    name: 'search',
    route: 'search(/)(:term)',
    callback: function(term, urlParams) {
        // term =&amp;gt; &amp;quot;this is a search term&amp;quot;
        // urlParams =&amp;gt; &amp;quot;module=Accounts&amp;amp;amp;amp;foo=bar&amp;quot;
        // no more ugly URL parsing!
    }
}
  • Potential Breaking Change: Sugar customizations that override the sync method on any instances of Backbone.Model and Backbone.Collection should should be updated to match Backbone’s new signatures for the internal success/error callbacks for Model#fetch, Model#destroy, Model#save, Collection#fetch methods.

For example:

Old way, Backbone < 0.9.10:

    // in a custom sidecar controller:
    sync: function(method, model, options) {
        // custom sync method
        ...
    options.success = _.bind(function(model, data, options) {
        this.collection.reset(model, data, options);
    }, this);
// in Backbone.js's Collection#fetch method...
    fetch: function(options) {
        options = options ? _.clone(options) : {};
        if (options.parse === void 0) options.parse = true;
        var success = options.success;
        // *** Note: 'collection', 'resp', 'options' are passed ***
        options.success = function(collection, resp, options) {
            var method = options.update ? 'update' : 'reset';
            collection[method](resp, options);
            if (success) success(collection, resp, options);
        };
        return this.sync('read', this, options);
    },

New way, Backbone > 1.x:

    // in a custom sidecar controller:
    sync: function(method, model, options) {
        // custom sync method
        ...

    // *** Only data should now be passed here ***
    options.success = _.bind(function(data) {
        this.collection.reset(data);
    }, this);
// in Backbone.js's Collection#fetch method...
    fetch: function(options) {
        options = _.extend({parse: true}, options);
        var success = options.success;
        var collection = this;
        // *** Note: the success callback is now only passed 'resp' ***
        options.success = function(resp) {
            var method = options.reset ? 'reset' : 'set';
            collection[method](resp, options);
            if (success) success.call(options.context, collection, resp, options);
            collection.trigger('sync', collection, resp, options);
        };
        wrapError(this, options);
        return this.sync('read', this, options);
    },
  • Potential Breaking Change: Sugar customizations that set the id property directly on a Backbone.Model will not work with Backbone Collections. Sugar Developers should always use Backbone’s internal APIs/methods, meaning they should be using model.set(‘id’, …) instead.

For example:

var model = app.data.createBean('Accounts', {id: 'foo'});
var collection = app.data.createBeanCollection('Accounts');

collection.add(model);
model.id = 'bar';

console.log(collection.get('bar'));
Output >> undefined

Use model.set(‘id’, ‘bar’); instead:

var model = app.data.createBean('Accounts', {id: 'foo'});
var collection = app.data.createBeanCollection('Accounts');

collection.add(model);
model.set('id', 'bar');
console.log(collection.get('bar'));
Output >> model


Sugar now supports PHP 5.6 in Sugar 7.7.1

$
0
0

Sugar and older versions of PHP

The latest version of PHP that Sugar 6.5 supported was PHP 5.3 which was released back in 2009. That is a long time ago folks. A sizable portion of the Sugar Developer community was in grade school back when PHP 5.3 was cool.

While we like to keep Sugar on the cutting edge, we have not been able to keep up to date with the latest PHP release. We have needed to maintain an upgrade path in each new Sugar release for customers still on Sugar 6.5. That is, up until now.

PHP Support matrix

As of Sugar 7.7.1 release:

Sugar version  Supported PHP versions
 6.5.x   5.2   5.3
 7.6.x   5.3   5.4
 7.7.0   5.3   5.4   5.5
 7.7.1   5.3   5.4   5.5   5.6

We do not plan on supporting 4 different versions of PHP forever, so you can expect to see support for older versions of PHP, such as PHP 5.3, to be dropped in upcoming Sugar releases.

For the complete and up-to-date reference of supported PHP versions, please view our Supported Platforms documentation.

Read on to learn more about why you should upgrade.

Benefits of PHP 5.6

Fact is, there are a ton of benefits of running Sugar on PHP 5.6 instead of earlier PHP versions. Anyone running Sugar On-Site should be evaluating how soon they can upgrade.

Improved Performance

Our performance team has estimated about a 30% improvement in performance in our test environment after upgrading from PHP 5.4 to PHP 5.6.

I don’t know anybody who does not like their software fast.

Zend OPcache built-in

Who loves installing and configuring APC , Zend OPcache, or WinCache in order to get accelerated PHP?  Nobody.

With PHP 5.6, Zend OPcache is not only built-in, it is enabled by default.  It is also demonstrably faster than the alternatives.  Also worth mentioning for Windows users is that WinCache is now deprecated in favor of the built-in OPcache.

Here are some suggested settings for using Zend OPcache with Sugar instances from the Performance team. These settings are a good starting point then you can optimize based on your individual instance’s needs.

Just add the following to your php.ini file.

; Suggested OPcache settings for Sugar
opcache.max_accelerated_files = 10000
opcache.memory_consumption = 256
opcache.fast_shutdown = 1
opcache.interned_strings_buffer = 16

Ultimately, utilizing the built-in PHP accelerator helps reduces the amount of work necessary to deploy and maintain a Sugar supported stack with accelerated PHP.

Long Term Support until the end of 2018

The community support windows for older versions of PHP have been expiring. But Long Term Support for PHP 5.6 ends in December 2018. So PHP 5.6 in production environments is a safe bet for the long haul.

 

 


Watch the recordings from UnCon 2016!

$
0
0

Three UnCon General Sessions recorded and now available!

The recordings for the UnCon general sessions are now available in the UnCon space of the Sugar Community. Visit the UnCon Archive to get access to the below videos as well as slides, pictures, and links to code examples for these general sessions as well as our breakout sessions.

Tuesday, June 14th 2016

Sugar Developer Ecosystem Overview (26 minutes)

Sugar Platform Update for Developers (26 minutes)

Wednesday, June 15th 2016

Architecting Successful Projects Panel (49 minutes)


Preparing Sugar customizations for PHP 5.6 and 7.0

$
0
0

Alex Vlasov is an experienced Sugar Developer and contractor that works on the SugarCRM Engineering platform team. In this post, Alex shares advice to Sugar Developers for preparing their Sugar customizations for PHP 5.6 and PHP 7.

The Sugar team has been preparing core Sugar code for PHP 5.6 and PHP 7 support with an eye on deprecating support for older PHP versions. This means there are some actions that the Sugar Developer community needs to take in order to prepare their code for latest versions of PHP.

Summary of core Sugar app changes

In order to support newer versions of PHP, we made some changes to internal core classes that Sugar Developers need to know about. Many of these changes were made in Sugar 7.7.1 when we added PHP 5.6 support. Other changes outlined below are in upcoming Sugar releases.

PHP 7 deprecated features removed from core Sugar code

  1. Removed static calls to non-static methods
  2. Removed PHP4-style constructors

Read the full list of features deprecated in PHP7.

Additional changes to address core Sugar code hygiene and to adopt new PHP 7 features

  1. Fixed incompatible child class methods signatures
  2. Updated 3rd party libraries to support latest versions of PHP
  3. Changes to support PHP 7.0 new uniform variable syntax
  4. Adopted PHP 7’s CSPRNG API but added random_compat library to support older PHP versions

Actions for Sugar Developers

In order to properly upgrade custom code, two main tasks need to be performed:

Make your code compatible with PHP 5.6 and PHP 7.0

To make your code compatible with PHP 5.6 and 7.0, use the following checklist.

  1. Remove static calls to non-static methods within your PHP code
  2. Remove any use of PHP4-style constructors within your PHP code
  3. Remove other deprecated PHP functionality in your PHP code (PHP 5.6 deprecated features, PHP 7 deprecated features)

For example,

class Foo {

	public function __construct() { // Yes.
		// ...
	}

	public function Foo() { // No.
		// ...
	}

    public static function aStaticMethod() {
        // ...
    }

    public function aNormalMethod() {
    	// ...
    }
}

Foo::aStaticMethod(); // Yes.
Foo::aNormalMethod(); // No.

For additional detail, refer to PHP migration guides for PHP 5.6 and PHP 7.0.

Make your code compatible with changes to Sugar PHP APIs

To make custom code compatible with updated Sugar PHP APIs, use the following checklist.

  1. (7.7.1) Do not use old PHP4 constructors in Sugar PHP classes, they are removed as part of PHP 7.0 support.
  2. (7.7.1) If there is code that extends Sugar PHP classes, make sure child methods signatures are correct
  3. (7.7.1) 3rd party libraries were moved:
    1. parsecsv library was moved to the vendor directory
  4. (future) 3rd party libraries updates:
    1. Elastica library will be upgraded, so make sure you do not use deprecated Elastica APIs.
    2. Removal of mcrypt support and Crypt_Blowfish library, so make sure you are not calling these libraries directly.
  5. (future) Significant changes to be made to the following Sugar PHP APIs:
    1. ActivitiesApi::getQueryObject() will become protected and function arguments are changed
    2. RelationshipHandler class will stop extending Relationship class
    3. SearchForm class will not extend EditView class anymore
    4. Quarter*TimePeriod::buildLeaves() methods will have function arguments changed
    5. PMSEEngineFilterApi::addFieldFilter() method will be renamed

 


How to create beautiful PDFs with CSS within Sugar

$
0
0

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

Screen Shot 2016-08-05 at 2.41.58 PM

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


/**
* @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/

Announcing XHProf Viewer, REST PHP Client, and Sugar Repairs Module open source projects

$
0
0

At SugarCRM, we have been accelerating the rate at which we share technology with the Sugar Developer community. Back in June at UnCon, we shared more open source code examples and tools than ever before. In April, we announced Sucrose Charts and the Sugar REST Harness. I am pleased to announce that we have open sourced three more projects under the Apache-2 license!

SugarCRM XHProf Viewer

The SugarCRM XHProf viewer is a visual performance analysis tool designed specifically for Sugar 7.7.0 and later. This project was created by the Sugar Engineering’s Performance team. This project extends the original xhprof viewer created by Facebook. Our Viewer tracks PHP memory usage and execution time (just like the original) but we add an improved UI and detailed query logging. The Viewer logs both SQL and Elasticsearch queries including timing and stack traces.

Sugar Repairs Module

The Sugar Repairs Module is a ML package created by the Sugar Support team. Sugar Support commonly uses this module to help assist with repairing common issues that the Support team encounters in Sugar instances. This package can only be deployed on Sugar On-Site (or local development) instances because it does not pass package scanner. We recommend that you test repairs on a clone of your production instance before trying to use it in production. Ultimately, use of this tool is at your own risk!

Sugar REST PHP Client

The Sugar REST PHP client is another tool created by the Sugar Support team. It is a simple and intuitive PHP library for accessing a Sugar 7’s REST v10 API. It provides an object oriented model for accessing data from a Sugar system. This is a great tool for accelerating any Sugar integration project.

Learn more in coming weeks!

I look forward to sharing more details about each of these new projects in coming weeks. Watch this space for blog posts from the developers behind these open source projects.


Develop Sugar using Docker and DockerHub

$
0
0

Here is a guest post from Ivica Nedeljkovic from Intelestream which is one of our new SugarCRM Partners. In this post, Ivica explains how you can use Intelestream created Docker containers hosted on DockerHub to easily deploy Sugar.

If I was asked to list the top five technologies in the last 5 years, Docker would certainly be on the list.The real advantage that Docker containers have over other server virtualization software is that Docker uses shared operating systems instead of trying to virtualize hardware. This takes less resources, is easier to boot, and faster to spin up instances.

docker_28container_engine29_logo

Docker 1.12

Some performance issues were experienced with Docker containers on OS X and Windows and some security issues were also experienced because up until Docker version 1.12. Until Docker 1.12, the native OS X/Windows virtualization options were not used and 3rd party Linux virtual machines such as VirtualBox were used instead. For virtualization, Docker 1.12 for Windows uses Alpine Linux on top of a Hyper-V VM and Docker 1.12 for Mac OSX uses Alpine Linux on top of a xhyve Virtual Machine.

Docker 1.12  was released in July 2016. In addition to boosting performance it also simplifies the whole process of creating new containers, as you no longer need to use a docker-machine anymore to create virtual machines.

DockerHub

For this project, we store Docker container images on DockerHub. DockerHub allows developers to use pre-built images that are stored in the web repository. Additionally, every time a developer makes a change to a Docker config file, these changes can be pushed back to DockerHub to generate new automated builds. This simplifies the process if you want to deploy your container image on a number of servers. For example, you can set triggers and hooks. DockerHub allows you to have a number of public repositories and one private repository for free. If further private repositories are required then additional packages can be purchased.

We are using docker-compose to create different containers for web app (php 5.5, Apache), db server (Mysql 5.6), Elastic Search (required for the professional version). As a bonus we added a container with phpmyadmin that you might need to manage a mysql databases.

Launching Sugar using DockerHub Containers

Follow along to start using Sugar via DockerHub containers.

Step 1: Install Docker 1.12 or later

This is a recommendation and not a requirement. An older version of Docker or Docker Tools can be used, but in such a case you must use a docker-machine command to create a virtual machine for Docker to run inside and set environment variables. Should you choose to run one of these options instead of Docker 1.12 you will notice that the remaining steps are similar to those listed below.

Step 2: Clone intelestream/sugarcrm-docker.git repository

git clone git@github.com:intelestream/sugarcrm-docker.git

In case you already have some service listening on ports 80 (Apache), 3307 (MySql – custom port), 9200, 9300 (Elasticsearch) or 8181 (PhpMyAdmin) please disable them. Alternatively, you can also modify these ports in the docker-compose.yml file for all of these services, with the exception of the web server port. Sugar uses the file_get_contents command which does not work with ports (just for checking installation requirements).

Step 3: Run docker-compose to start images

Using the console, navigate to the folder that has been created and type:

docker-compose up

This command will download all images from the DockerHub and start building containers. Spinning up containers for the first time will be fairly slow but as soon as all images are downloaded then the process will speed up considerably.

Once everything is completed, you should have containers with Apache, MySql, and Elasticsearch running. To stop/pause them, use “CTRL+C”

At this point all services should be running. Our public web folder is www. To check if everything is running as expected you can test by creating a test.php file under there and accessing it within your browser:

http://localhost/test.php

Step 4: Place Sugar code and run Sugar installer

Copy Sugar application code to the www directory and place it in a directory named sugarcrm. The system is designed for developers to be able to have more than one instance of Sugar. All you need is multiple individual databases.

Open http://localhost/sugarcrm (or whatever name you decided to assign to the above folder) and enter the following in the Sugar installation wizard:

Host: mysql_crm

(The host needs to be mysql_crm, it will be resolved by docker.  See docker-compose.yml)

DB Name: sugarcrm

DB User: sugarcrm

DB Password: sugarcrm

root user: root

root password: sugarcrm

Elasticsearch Host: elasticsearch_crm

(See host again in docker-compose.yml)

Elasticsearch Port: 9200

Finished!

Sugar is now up and running! Happy coding!

Below are some tips for working with this server set-up.

Accessing the MySQL database

phpMyAdmin is installed if you need to administer your MySQL database. You can access phpMyAdmin by opening the following url:

http://localhost:8181

Accessing a command line for a container

In case you need to access any containers:

docker ps 

This lists all of the containers and their IDs.

In the command below replace container_id with the container id you want to connect to based on the results of the previous step:

docker exec -i -t container_id /bin/bash

Setting up Cron from command line

First, retrieve a command line for your Apache server instance using the previous steps. Then to set up a cronjob, run the following line:

cd /app/www/crm; /usr/local/bin/php -f cron.php > /dev/null 2>&1

And start Cron service:

service cron start

If you shutdown and remove container, you will need to set up the cronjob again for new instances.

Additional Considerations

  • When you repair and rebuild your caches, you will notice a decrease of around 40-50% in terms of speed in comparison with an instance of Sugar running directly on the host, because many files need to be written and synced between systems. If you do not do this action too often you will not notice a big difference between performance
  • If you do not need all containers, you can comment those you do not need in the docker-compose.yml file. For example, if you do not want to use both the web server and MySQL server because you can use them from your local host, then you can simply have only Elasticsearch running in a container so you do not need to install it. Even simpler, you could just download the Elasticsearch image from the official repository on DockerHub and start it!

SugarCRM Solution Architect Webinar Series starting on September 6

$
0
0

You may have heard that SugarCRM will be presenting a series of webinars on solution architecture for the Sugar platform, throughout this September and October.

Beginning on September 6th, Sugar University’s Solution Architect Webinar Series will present the following topics:

  • Introduction to Solution Architecture for Sugar
  • CRM Project Fundamentals
  • The Sugar Platform
  • Design
  • Integration
  • Sugar Implementation
  • Performance and Quality Assurance
  • Deployment
  • Security

Each webinar will be guided by a panel of experts from across SugarCRM, including some familiar faces from the Architecting Successful Projects panel at this year’s UnCon

Just as at UnCon, you’ll have an opportunity to engage Sugar’s experts around your topics of interest.

Customizations that developers implement on the Sugar platform must operate within the unique configurations of the Sugar implementations they are deployed to. 

Attendees will gain deeper insight into the the workings and capabilities of the Sugar platform as the webinars delve into the tasks that Sugar solution architects undertake in order to design and deploy Sugar successfully.

This series covers the same concepts that are tested by the Sugar Solution Architect certification exam. So do not pass up this opportunity to broaden your horizons and reignite your Sugar developer journey!

Enroll in Solution Architect Webinar Series today!

 



Using SugarCRM XHProf Viewer to profile Sugar server-side performance

$
0
0

Sugar Performance Engineer Vadzim Ramanenka shares some tips for profiling Sugar code using our newly launched SugarCRM XHProf Viewer open source project.

Profiling Sugar softly

Whenever you encounter that something is not working as fast as you would like, you need a way to look “under the hood” and understand what exactly is slowing things down. Sugar 7.7.0 added a built-in capability to collect detailed performance profiling data and store it in files. Recently, we released an open source SugarCRM XHProf Viewer project which can be used to visualize performance bottlenecks.

XhprofCallGraph.png

Example of a XHProf call graph diagram

Read on to learn how to configure both Sugar and the Viewer in a few simple steps.

Prerequisites

You need to have a copy of Sugar on-site installed where you have file system access such as on your local machine. We rely on the xhprof php extension so you are required to have it installed on the version of PHP configured with Sugar as well.

Enabling XHProf logging in Sugar

First of all we have to create a directory which Sugar will use to store profiling information. I use /tmp/profile_files/sugar in this example but you can choose any location. Just make sure the target directory exists and that Apache has write permissions to it.

After that, there are a few lines of configuration should be added to the config_override.php file to ask Sugar to start collecting profiling data.

This is an example of a bare minimum configuration:

$sugar_config['xhprof_config']['enable'] = true;
$sugar_config['xhprof_config']['log_to'] = '/tmp/profile_files/sugar';

By default, Sugar profiles only 10% (1 in 10) of the incoming requests. If you want to profile all requests then add this line to config_override.php:

$sugar_config['xhprof_config']['sample_rate'] = 1;

For additional configuration options please refer to the xhprof_config setting documentation in the Sugar Developer Guide.

Now open Sugar and start clicking around. If you’ve done everything right files should start appearing in /tmp/profile_files/sugar.

Configuring SugarCRM XHProf Viewer

Download the latest release of the Viewer and extract it somewhere under your server’s web root directory.

By default, the Viewer looks for profiling data under /tmp/profile_files and subdirectories. If you want to change this then create a config_override.php file in the Viewer’s root directory with the following content:

<?php
$config['profile_files_dir'] = '<PROFILE FILES LOCATION>';

And replace <PROFILE FILES LOCATION>  with the directory that you specified in Sugar’s config_override.php.

This is pretty much it. Have fun!

SugarCRM XHProf Viewer Screenshots

There are three different dimensions of information collected for every single request. Check out the screenshots below.

XhProf_Viewer_Screenshot.png

Function call summary

XHProf_Elastic_screenshot.png

Elastic query summary

XHProf_SQL_screenshot.png

SQL query summary


SugarCRM Mobile App Configurator Service now available!

$
0
0

Sugar Developers now have a new tool for building upgrade safe mobile customizations that address most branding, theming, and mobile security requirements.

Introducing Sugar MACS

Introducing the Sugar Mobile Application Configurator Service (Sugar MACS)! This tool allows Sugar Developers building solutions for Sugar Enterprise customers to create and distribute custom-branded versions of the SugarCRM Mobile app.

screen-shot-2016-09-14-at-1-51-02-pm screenshot_1473873868

Many of you have seen earlier previews of Sugar MACS at SugarCon and UnCon.

How it works

Sugar MACS provides a helpful wizard that makes it easy for developers or admins to configure the SugarCRM Mobile app.  Once configured, you can then download personalized versions of Mobile app (not a web-only facsimile) that replaces SugarCRM branding with their branding of choice. The downloaded mobile binaries can then be submitted to the Apple App Store or Google Play.

Apps can also be wrapped and deployed to end users using nearly any Mobile Device Management (MDM) solution such as AirWatch, MobileIron, XenMobile, or MaaS360. Even better, when SugarCRM releases new versions of the Mobile app then upgrading your custom app is a snap and perfectly upgrade safe. You need only log back into MACS to build and download binaries for your rebranded app using the latest SugarCRM Mobile version.

This is a level of flexibility that I do not think you will find in any other mobile CRM app.

Even better, there is no custom coding required and it is super easy to use!

 

Learn more in the Mobile Developer Community

We’ve also launched a Mobile Developers space on the Sugar Community where you can collaborate with others and get questions answered on Sugar MACS as well as the upcoming Mobile SDK.

So visit the Mobile Developers space in the Sugar Community today to learn more about how to get started with this exciting new tool!


SugarCRM Solution Architect Webinar Series continues!

$
0
0

We are half way through the SugarCRM Solution Architect Webinar Series and we have had a great turn-out so far with over 400 people coming to our first 4 webinars. Even if you miss the live webinars, you can still go back and watch the recordings on your own time.

For more information on what you can expect, you should read our recent blog post on the webinar series.

Accessing the Recordings

Visit Sugar University’s webinar library for all previous recordings. Recordings are posted usually one or two business days after the live event.

Webinars with recordings are as follows:

1. (Sep 6th) – Introduction to Solution Architecture for Sugar & CRM Project Fundamentals
2. (Sep 7th) – The Sugar Platform
3. (Sep 20th) – Design
4. (Sep 21st) – Integration

Upcoming Live Webinars

If you have not already, you can still register and join us for remainder of the live SugarCRM Solution Architect Webinar Series. It is completely free and a great training resource for anyone considering trying for their Sugar Solution Architect Professional certification.

5. (Oct 11th) – Sugar Implementation
· Implementing Sugar to satisfy customer requirements
· Applying the Sugar extension framework
· The Sugar development methodology

6. (Oct 12th) – Performance and Quality Assurance
· Testing methodologies for Sugar
· Optimizing Sugar deployments

7. (Oct 25th) – Deployment
· Components of a Sugar deployment
· Sugar deployment configurations
· Methods for migrating code changes from development through production
· Solution sizing based on customer requirements

8. (Oct 26th) – Security
· Handling common compliance policies
· Customizing the Sugar visibility model
· Using Sugar authentication


Monitoring your Email Manager Queue with Sugar CLI

$
0
0

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.php
namespace 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.php
Sugarcrm\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.php
SugarAutoLoader::addNamespace('Synolia\\EmailMan\\Console\\Command', 'custom/Synolia/EmailMan/Console/Command', 'psr4');

Perform a Quick Repair and Rebuild et voilà!

sugarcliemailmanqueue

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.


Using the Sugar REST PHP Client

$
0
0

Sugar REST PHP Client

A new open source library for working with Sugar 7’s powerful REST API has just been published! You can view the library in our GitHub account here: https://github.com/sugarcrm/rest-php-client

Full instructions for installation, usage, current included API Endpoints, and ways to contribute can all be found in the GitHub Wiki on the repository.

Who should use it?

The Sugar REST PHP Client was built initially to make working with Sugar instances easier for some of our internal tools. I wanted to provide a quick, easy, and object oriented, way to access Sugar 7’s REST API that could be used by all PHP developers. Any developer who is developing PHP applications that integrate with Sugar 7 should be interested in using the new library. The simplified architecture takes away the hassle of setting up and managing Curl connections, and allows the developer to focus on what matters most, which is working with the data in their Sugar application.

How to use it?

The REST PHP Client is setup as a composer package, and can be easily added to your existing project by adding it to your required packages in your projects composer.json file.

Once it’s installed via composer, you can access the Client in the SugarAPI\SDK namespace.

A quick example of how much easier to use this new library is, can be shown by reviewing the latest documentation on using the REST API to get a filtered list of records from a module in the Support Developer Guide, and comparing it to the following snippet of code that does the exact same API request.

$server = 'localhost';
$credentials = array(
    'username' => 'admin',
    'password' => 'asdf'
);
$SugarAPI = new \SugarAPI\SDK\SugarAPI($server, $credentials);
$SugarAPI->login();

$requestData = array(
    "filter" => array(
    array(
        '$or' => array(
           array(
                //name starts with 'a'
                "name" => array(
                    '$starts'=>"A",
                )
            ),
            array(
                //name starts with 'b'
                "name" => array(
                    '$starts'=>"b",
                    )
                )
            ),
        ),
    ),
    "max_num" => 2,
    "offset" => 0,
    "fields" => "id",
    "order_by" => "date_entered",
    "favorites" => false,
    "my_items" => false,
);

$response = $SugarAPI->filterRecords('Accounts')
                     ->execute($requestData)
                     ->getResponse();
if ($response->getStatus()=='200'){
    $records = $response->getBody();
    print_r($records);
}

Each API Endpoint in the Sugar 7 REST API is defined by an Endpoint Class in the REST PHP Client library, which is then dynamically mapped to a method on the REST PHP Client Object. This dynamic method, generates the Endpoint Object, configures the Curl Request, and returns the Endpoint Object for manipulation, such as loading in data and finally executing the actual request.

/**
Calling filterRecords() dynamic method, returns a
SugarAPI\SDK\Endpoint\POST\ModuleFilter Object
**/
$SugarAPI = new \SugarAPI\SDK\SugarAPI($server, $credentials);
$SugarAPI->login();

$FilterEndpoint = $SugarAPI->filterRecords();

These Endpoint Objects manage the Request and Response to the API, as well as manage the data sent to the API. As shown above the execute() method on the Endpoint Object takes the request data that is to be sent to the Sugar API, and submits the Request the server. In the Filter Record example above, the passed in data matches what is shown in the API Endpoints documentation, however this is not always the case. The Endpoint Classes allow for manipulation of the data before being added to the Request, which means that the REST PHP Client can shorten the needed payload to allow integrated systems to quickly build requests.

One such example of this is the Bulk API Endpoint Object included in the REST PHP Client. As the documentation shows, the API request payload can be quite complicated, so to simplify it, it seemed intuitive to make the execute() method on the Bulk Endpoint Class, accept an array of REST PHP Client Endpoints objects, as they contain all the data needed for the Bulk request, saving the developer time by not having to build out the complex payload manually.

$SugarAPI = new \SugarAPI\SDK\SugarAPI($server,$creds);
$SugarAPI->login();
$Accounts = $SugarAPI->filterRecords('Accounts')
                     ->setData(array('max_num'=> 5));
$Contacts = $SugarAPI->filterRecords('Contacts')
                     ->setData(array('max_num'=> 1));
$Notes = $SugarAPI->filterRecords('Notes')
                  ->setData(array('max_num'=> 3));
$Leads = $SugarAPI->filterRecords('Leads')
                  ->setData(array('max_num'=> 2));
$BulkCall = $SugarAPI->bulk()->execute(array(
    $Accounts,
    $Contacts,
    $Notes,
    $Leads
));
$response = $BulkCall->getResponse();
if ($response->getStatus()==‘200’){
    print_r($response->getBody());
}

As of right now, not all Endpoints have been added to the REST PHP Client library, however the major Endpoints used for manipulating data have been added, and more will be added in continued releases. Check out the current Endpoints that can be used here which also includes a short code snippet showcasing how to use each one.

What’s Next?

I would love to see more PHP developers using the library, which is why it is being released as an Open Source community project. Any issues that are found can be added to the GitHub repository under the Issues tab. Likewise, if you have features you would like added you can add them to the Issues tab on the repository as well. More detailed contribution guidelines can be found in the CONTRIBUTING doc, and in the Wiki.


Viewing all 93 articles
Browse latest View live