sublime-phpcs now supports project based settings

With some excellent support from Handrus Stephan Nogueira, the sublime-phpcs plugin for Sublime Text now can support:

  • Default Settings
  • User Settings
  • Project Based Settings (New in 6.2)

So this means, if your PHP_CodeSniffer standard is PSR2, but for whatever reason you need the PEAR standard for a certain project you can now do this without a lot of faffing around. To achieve this, click “Project” > “Edit Project” and then you can override all sublime-phpcs settings in the following format:

{
    "folders":
    [
        {
	    "path": "/Users/Ben/git"
        }
    ],
    "settings":
    {
        "phpcs": 
        {
            "phpcs_additional_args": {
                "--standard": "PEAR"
            },
            "show_debug": true
        }
    }
}

Within the “phpcs” section, you can define all the usual settings described here.

The settings are chained so:

  • Project Settings are king and override everything
  • Followed by User Settings
  • Followed by the bog standard Default Settings

Also worth noting, in this release it automatically reloads the settings once changed. I had performance issues with this in the past, so I turned it off, however I’ve since fixed this. Makes life a little bit easier for us all.

Enjoy

How to create a many-to-many relationship between Opportunities and Emails in SugarCRM

I recently had a request to allow multiple calls/meetings and emails to be linked to multiple Opportunities. The standard in SugarCRM is a 1 to many relationship between Opportunities and Emails/Meetings/Calls.

However, our Sales people might email the Account (a contact within the account) to discuss multiple Opportunities and want to report on this data. Maybe they only talk about 3 of the 5 opportunities etc. There is a myriad of reasons why this might be necessary.

Creating a many-to-many relationship for calls and meetings can be done in Studio, however this option isn’t there for Emails, so you have to define this relationship yourself. To do this you are going to need to create the following files:

custom/Extension/application/Ext/TableDictionary/opportunities_emails_1.php
custom/Extension/modules/Emails/Ext/Language/en_UK.customopportunities_emails_1.php
custom/Extension/modules/Emails/Ext/Layoutdefs/opportunities_emails_1_Emails.php
custom/Extension/modules/Emails/Ext/Vardefs/opportunities_emails_1_Emails.php
custom/Extension/modules/Emails/Ext/WirelessLayoutdefs/opportunities_emails_1_Emails.php
custom/Extension/modules/Opportunities/Ext/Vardefs/opportunities_emails_1_Opportunities.php
custom/Extension/modules/relationships/language/Emails.php
custom/Extension/modules/relationships/layoutdefs/opportunities_emails_1_Emails.php
custom/Extension/modules/relationships/relationships/opportunities_emails_1MetaData.php
custom/Extension/modules/relationships/vardefs/opportunities_emails_1_Emails.php
custom/Extension/modules/relationships/vardefs/opportunities_emails_1_Opportunities.php
custom/Extension/modules/relationships/wirelesslayoutdefs/opportunities_emails_1_Emails.php
custom/metadata/opportunities_emails_1MetaData.php

A changeset of what is required can be found below:

custom/Extension/application/Ext/TableDictionary/opportunities_emails_1.php

<?php
include 'custom/metadata/opportunities_emails_1MetaData.php';

custom/Extension/modules/Emails/Ext/Language/en_UK.customopportunities_emails_1.php

<?php
$mod_strings['LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE'] = 'Emails';

custom/Extension/modules/Emails/Ext/Layoutdefs/opportunities_emails_1_Emails.php

<?php
$layout_defs["Emails"]["subpanel_setup"]['opportunities_emails_1'] = array (
  'order' => 100,
  'module' => 'Opportunities',
  'subpanel_name' => 'default',
  'sort_order' => 'asc',
  'sort_by' => 'id',
  'title_key' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE',
  'get_subpanel_data' => 'opportunities_emails_1',
  'top_buttons' =>
  array (
    0 =>
    array (
      'widget_class' => 'SubPanelTopButtonQuickCreate',
    ),
    1 =>
    array (
      'widget_class' => 'SubPanelTopSelectButton',
      'mode' => 'MultiSelect',
    ),
  ),
);

custom/Extension/modules/Emails/Ext/Vardefs/opportunities_emails_1_Emails.php

<?php
$dictionary["Email"]["fields"]["opportunities_emails_1"] = array (
  'name' => 'opportunities_emails_1',
  'type' => 'link',
  'relationship' => 'opportunities_emails_1',
  'source' => 'non-db',
  'vname' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE',
);

custom/Extension/modules/Emails/Ext/WirelessLayoutdefs/opportunities_emails_1_Emails.php

<?php
$layout_defs["Emails"]["subpanel_setup"]['opportunities_emails_1'] = array (
  'order' => 100,
  'module' => 'Opportunities',
  'subpanel_name' => 'default',
  'title_key' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE',
  'get_subpanel_data' => 'opportunities_emails_1',
);

custom/Extension/modules/Opportunities/Ext/Vardefs/opportunities_emails_1_Opportunities.php

<?php
$dictionary["Opportunity"]["fields"]["opportunities_emails_1"] = array (
  'name' => 'opportunities_emails_1',
  'type' => 'link',
  'relationship' => 'opportunities_emails_1',
  'source' => 'non-db',
  'vname' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_EMAILS_TITLE',
);

custom/Extension/modules/relationships/language/Emails.php

<?php
$mod_strings['LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE'] = 'Emails';

custom/Extension/modules/relationships/layoutdefs/opportunities_emails_1_Emails.php

<?php
$layout_defs["Emails"]["subpanel_setup"]['opportunities_emails_1'] = array (
  'order' => 100,
  'module' => 'Opportunities',
  'subpanel_name' => 'default',
  'sort_order' => 'asc',
  'sort_by' => 'id',
  'title_key' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE',
  'get_subpanel_data' => 'opportunities_emails_1',
  'top_buttons' =>
  array (
    0 =>
    array (
      'widget_class' => 'SubPanelTopButtonQuickCreate',
    ),
    1 =>
    array (
      'widget_class' => 'SubPanelTopSelectButton',
      'mode' => 'MultiSelect',
    ),
  ),
);

custom/Extension/modules/relationships/relationships/opportunities_emails_1MetaData.php

<?php
$dictionary["opportunities_emails_1"] = array (
  'true_relationship_type' => 'many-to-many',
  'from_studio' => true,
  'relationships' =>
  array (
    'opportunities_emails_1' =>
    array (
      'lhs_module' => 'Opportunities',
      'lhs_table' => 'opportunities',
      'lhs_key' => 'id',
      'rhs_module' => 'Emails',
      'rhs_table' => 'emails',
      'rhs_key' => 'id',
      'relationship_type' => 'many-to-many',
      'join_table' => 'opportunities_emails_1_c',
      'join_key_lhs' => 'opportunities_emails_1opportunities_ida',
      'join_key_rhs' => 'opportunities_emails_1emails_idb',
    ),
  ),
  'table' => 'opportunities_emails_1_c',
  'fields' =>
  array (
    0 =>
    array (
      'name' => 'id',
      'type' => 'varchar',
      'len' => 36,
    ),
    1 =>
    array (
      'name' => 'date_modified',
      'type' => 'datetime',
    ),
    2 =>
    array (
      'name' => 'deleted',
      'type' => 'bool',
      'len' => '1',
      'default' => '0',
      'required' => true,
    ),
    3 =>
    array (
      'name' => 'opportunities_emails_1opportunities_ida',
      'type' => 'varchar',
      'len' => 36,
    ),
    4 =>
    array (
      'name' => 'opportunities_emails_1emails_idb',
      'type' => 'varchar',
      'len' => 36,
    ),
  ),
  'indices' =>
  array (
    0 =>
    array (
      'name' => 'opportunities_emails_1spk',
      'type' => 'primary',
      'fields' =>
      array (
        0 => 'id',
      ),
    ),
    1 =>
    array (
      'name' => 'opportunities_emails_1_alt',
      'type' => 'alternate_key',
      'fields' =>
      array (
        0 => 'opportunities_emails_1opportunities_ida',
        1 => 'opportunities_emails_1emails_idb',
      ),
    ),
  ),
);

custom/Extension/modules/relationships/vardefs/opportunities_emails_1_Emails.php

<?php
$dictionary["Email"]["fields"]["opportunities_emails_1"] = array (
  'name' => 'opportunities_emails_1',
  'type' => 'link',
  'relationship' => 'opportunities_emails_1',
  'source' => 'non-db',
  'vname' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE',
);

custom/Extension/modules/relationships/vardefs/opportunities_emails_1_Opportunities.php

<?php
$dictionary["Opportunity"]["fields"]["opportunities_emails_1"] = array (
  'name' => 'opportunities_emails_1',
  'type' => 'link',
  'relationship' => 'opportunities_emails_1',
  'source' => 'non-db',
  'vname' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_EMAILS_TITLE',
);

custom/Extension/modules/relationships/wirelesslayoutdefs/opportunities_emails_1_Emails.php

<?php
$layout_defs["Emails"]["subpanel_setup"]['opportunities_emails_1'] = array (
  'order' => 100,
  'module' => 'Opportunities',
  'subpanel_name' => 'default',
  'title_key' => 'LBL_OPPORTUNITIES_EMAILS_1_FROM_OPPORTUNITIES_TITLE',
  'get_subpanel_data' => 'opportunities_emails_1',
);

custom/metadata/opportunities_emails_1MetaData.php

<?php
$dictionary["opportunities_emails_1"] = array (
  'true_relationship_type' => 'many-to-many',
  'from_studio' => true,
  'relationships' =>
  array (
    'opportunities_emails_1' =>
    array (
      'lhs_module' => 'Opportunities',
      'lhs_table' => 'opportunities',
      'lhs_key' => 'id',
      'rhs_module' => 'Emails',
      'rhs_table' => 'emails',
      'rhs_key' => 'id',
      'relationship_type' => 'many-to-many',
      'join_table' => 'opportunities_emails_1_c',
      'join_key_lhs' => 'opportunities_emails_1opportunities_ida',
      'join_key_rhs' => 'opportunities_emails_1emails_idb',
    ),
  ),
  'table' => 'opportunities_emails_1_c',
  'fields' =>
  array (
    0 =>
    array (
      'name' => 'id',
      'type' => 'varchar',
      'len' => 36,
    ),
    1 =>
    array (
      'name' => 'date_modified',
      'type' => 'datetime',
    ),
    2 =>
    array (
      'name' => 'deleted',
      'type' => 'bool',
      'len' => '1',
      'default' => '0',
      'required' => true,
    ),
    3 =>
    array (
      'name' => 'opportunities_emails_1opportunities_ida',
      'type' => 'varchar',
      'len' => 36,
    ),
    4 =>
    array (
      'name' => 'opportunities_emails_1emails_idb',
      'type' => 'varchar',
      'len' => 36,
    ),
  ),
  'indices' =>
  array (
    0 =>
    array (
      'name' => 'opportunities_emails_1spk',
      'type' => 'primary',
      'fields' =>
      array (
        0 => 'id',
      ),
    ),
    1 =>
    array (
      'name' => 'opportunities_emails_1_alt',
      'type' => 'alternate_key',
      'fields' =>
      array (
        0 => 'opportunities_emails_1opportunities_ida',
        1 => 'opportunities_emails_1emails_idb',
      ),
    ),
  ),
);

In the solution above I’ve removed the “Emails” Subpanel from the Opportunities screen, since I have created a function that can augment the Activities/History Subpanels with the many-to-many relationship data. 

Install pfff on Mac OS X using brew

I needed to install pfff to make sure a pull request worked in sublime-phpcs, and this is what I needed to do to get it working on Mac OS X Mountain Lion.

Make sure we have all the latest formulas for brew:

$ brew update

Install ocaml, which I found needed to be at least version 4.0:

$ brew install ocaml

Install pcre (8.32+):

$ brew install pcre (8.32+)

Now to get the code from GitHub:

$ git clone git://github.com/facebook/pfff.git
$ cd pfff
$ cd external/ocamlpcre; 
$ make
$ cd -

Update the PCRE_INCLUDE and PCRE_LIBS variables in the configure file with correct paths (/usr/local/opt/pcre/include/ and /usr/local/opt/pcre/lib respectively)

$ vim configure

Install the application

$ ./configure
$ make depend
$ make; make opt

To check it is all up and running:

$ ./pff

And more importantly for what I wanted, scheck:

$ ./scheck

I’ll play around with scheck more over the next few days whilst I test the pull request for sublime-phpcs.

Turn on unified_search for Users in SugarCRM

This post explains how you would turn on searching for the Users module within the Unified Search Box in SugarCRM.

Make the module and Fields Searchable

First of all, configure the Users module and fields so that they are searchable. Either update or create the following file with the contents below:

custom/Extension/modules/Users/Ext/Vardefs/vardefs.php

php

$dictionary['User']['unified_search'] = true;
$dictionary['User']['unified_search_default_enabled'] = true;
$dictionary['User']['fields']['user_name']['unified_search'] = true;
$dictionary['User']['fields']['first_name']['unified_search'] = true;
$dictionary['User']['fields']['last_name']['unified_search'] = true;
$dictionary['User']['fields']['department']['unified_search'] = true;

This basically is turning the unified search on for the “Users” module and adding the fields you would like to search.

Please Note: The file is in the “Users” directory, but the dictionary key is “User”, this can catch you out if you are not careful.

As you can see above, I’ve added the “department” field as being searchable. This needs slightly more configuration to make it work.

We need to explain how the search should be conducted on the “department” field, and this is very trivial. You can either create or copy the SearchFields.php file within metadata.

Copy SearchFields.php

To copy this file from the original module, you can do:

    $ cp modules/Users/metadata/SearchFields.php custom/Extension/modules/Users/metadata/

Once you have the copied file you simple need to add the “department” field to the end of the array:

src/custom/modules/Users/metadata/SearchFields.php

searchFields['Users'] = array(
    'user_name' => array( 'query_type'=>'default'),
    'first_name' => array( 'query_type'=>'default'),

....

    'department' => array('query_type' => 'default'),
);

Create SearchFields.php

This option you simply create the file and add the entry you need:

src/custom/modules/Users/metadata/SearchFields.php

$searchFields['Users']['department'] => array('query_type' => 'default');

Make the module visible in the UI

Lastly, we want to turn the searching on within the UI, so update the following file:

custom/modules/unified_search_modules_display.php

<?php 

$unified_search_modules_display = array(   
  'Accounts' => array(
    'visible' => true,
  ),
  'Calls' => array(
    'visible' => true,
  ),
....
  'Users' => array(
    'visible' => true
  )

That’s it, rather simple.

Set hostname on Mountain Lion

With all the machines I now use, and the fact I’m getting older and my memory is going, I needed to add hostname into my bash prompt for the first time, and came across a little issue on Apple’s Mac OS X Mountain Lion.

When I updated my PS1 setting I was getting:

(null):~

Not so good. I was also getting similar responses when I was running “hostname” on the command line.

I’ve now found out how to resolve this issue on Mountain Lion:

$ sudo scutil --set HostName [Your Hostname]

This will now allow you to use “\h” in your PS1 setting and give you the correct result when running “hostname”

phpcs, phpmd, php-cs-fixer and linter support for Sublime Text 3

EDIT: As of 5th February 2013 and version 5.0, sublime-phpcs now supports both Sublime Text 2 and 3. This is via Package Control and git

As of now there is an alpha version of sublime-phpcs for Sublime Text 3. The reason it is alpha is because I want all the code to run in Sublime Text 2 and Sublime Text 3, and I’ve only had chance to test this plugin on Mac OS X, and not Linux and Windows.

This plugin adds support for PHP CodeSniffer, PHP Coding Standards Fixer, the PHP Linter and PHP Mess Detection in Sublime Text 2 and now 3.

In order to get the alpha version running in Sublime Text 3 please follow the steps below (Example using Mac OS X):

$ cd "$HOME/Library/Application Support/Sublime Text 3/Packages/"
$ git clone git@github.com:benmatselby/sublime-phpcs.git Phpcs
$ cd Phpcs
$ git checkout python3

If you find any issues please raise bugs in GitHub. Once I have managed to test this in Windows and Linux I will push this to the master branch and tag it as version 5 of the plugin. This should then also become available in Package Control (Which is also starting to support Sublime Text 3)