In SugarCRM you can add Schedulers to execute repetitive tasks (like CRONs in LINUX). By default, SugarCRM comes with a list of predefined jobs, that can be setup via Admin -> Scheduler.
But if you want to implement a custom job and to be accessible in this module, you must do the following. The solution is implemented in an upgrade safe manner.

1. In custom/modules/Schedulers/ create a file named _AddJobsHere.php


// this file is actually included at the end of /modules/Schedulers/_AddJobsHere.php
//You can take a look on that file for more clarification

//create a new registered job
$job_strings[] = 'customJob';
function customJob(){
//this is where you put the custom code
}
?>

2. Create the language file for new job: custom/modules/Schedulers/language/en_us.lang.php . If the path doesn’t exists you must create the directory structure.

$mod_strings['LBL_CUSTOMJOB'] = 'This string will appear in dropdown list of job field when you create or edit a new job in Sheduler ';

?>

3. Perform a Quick repair and rebuild from Admin -> Repair area.

After that, in Amin -> Scheduler, if you click Create (or Edit an existing item), you will see that a new value appear corresponding “Job” dropdown list.

That’s all folks! :)

This tutorial is for those who:
* want to create one-to-many relationship between two modules without an intermediate table (like ModuleBuilder does)
* has a relate field in a module and want to display a subpanel to the related module
* what to make this using code, in an upgrade safe manner

If you are using SugarCRM and you want to create one-to-many relationship between two modules via ModuleBuilder an aditional table will be created even if that isn’t necesary(no need for a table now for this type of relationship).
Eliminating additional table will improove the application performance and will make more easy to maintain.

Let’s consider the following scenario:
We have two modules: Invoices and Accounts. In custom Invoice module we already have a related field, billing_account_id that makes connection with Account module. We want to display in a subpanel from Accounts module all the invoices that are related.

If you have a related field in Invoices does’t mean that you have the relationship too. So we’ll have to create 4 files for that:
* /custom/Extension/modules/inv_Invoices/Ext/Vardefs/inv_invoices_accounts.php – where have to add the relationship itself and a link field between Invoices and Accounts
* /custom/Extension/modules/Accounts/Ext/Vardefs/inv_invoices_accounts.php – where have to add a link fild between the two modules
* /custom/Extension/modules/Accounts/Ext/Layoutdefs/inv_invoices_accounts.php – definition for invoices subpannel
* /custom/Extension/modules/inv_Invoices/Ext/Language/en_us.inv_invoices_accounts.php – a language variable with the name of the subpanel

Let’s see the code:

* /custom/Extension/modules/inv_Invoices/Ext/Vardefs/inv_invoices_accounts.php
$dictionary['inv_Invoices']['fields']['accounts'] = array(
'name' => 'accounts',
'type' => 'link',
'relationship' => 'inv_invoices_accounts',
'module' => 'Accounts',
'bean_name' => 'Account',
'source' => 'non-db',
'vname' => 'LBL_ACCOUNTS',
);

$dictionary['inv_Invoices']['relationships']['inv_invoices_accounts'] = array(
'lhs_module' => 'Accounts',
'lhs_table' => 'accounts',
'lhs_key' => 'id',
'rhs_module' => 'inv_Invoices',
'rhs_table' => 'inv_invoices',
'rhs_key' => 'billing_account_id',
'relationship_type' => 'one-to-many',
);

* /custom/Extension/modules/Accounts/Ext/Vardefs/inv_invoices_accounts.php
$dictionary['Account']['fields']['inv_invoices'] = array(
'name' => 'inv_invoices',
'type' => 'link',
'relationship' => 'inv_invoices_accounts',
'module' => 'inv_Invoices',
'bean_name' => 'inv_Invoices',
'source' => 'non-db',
'vname' => 'LBL_INVOICES',
);

* /custom/Extension/modules/inv_Invoices/Ext/Language/en_us.inv_invoices_accounts.php
$mod_strings['LBL_INVOICES_SUBPANEL_TITLE'] = 'Invoices';

Atention, we supose that we already have the definition for the related field in Invoice module vardefs.php :
$dictionary['inv_Invoices']['fields'] = array (
'billing_account_name'=>
array(
'name'=>'billing_account_name',
'rname'=>'name',
'group'=>'billing_address',
'id_name'=>'billing_account_id',
'vname'=>'LBL_BILLING_ACCOUNT_NAME',
'type'=>'relate',
'link'=>'billing_accounts',
'table'=>'billing_accounts',
'isnull'=>'true',
'module'=>'Accounts',
'importable' => 'required',
'required'=>true,

),
'billing_account_id' =>
array(
'name'=>'billing_account_id',
'type'=>'id',
'group'=>'billing_address',
'vname'=>'LBL_BILLING_ACCOUNT_ID',
'table' => 'accounts',
'isnull' => 'true',
'module' => 'Accounts',
'dbType' => 'id',
'reportable' => false,
'massupdate' => false,
'duplicate_merge' => 'disabled',
),
);

At the end, you’ll have to make a “quick repair and rebuid” and in Acounts modules you’ll have to see the Invoices subpanel(if the curent account was assingned to an invoice).

That’a all folks :)

In SugarCRM, a relate field can be added to EditView form using a popup from where you can select the desired entry.

In popup, you also have posibility to perform a search to filter the results from related module.
The nice part is that you can automatically send a filtering parameter using “initial_filter” from editviewdefs.php definition. So, for related field you have to add those lines:
'displayParams' => array(
'initial_filter' => "&account_name_advanced=John%20Doe",
)

That means that the popup it will open with “Account Name” prefilled with “John Doe” value. Be aware that target field is account_name but in “initial_value” the name is account_name_advanced.

This is great, but what if I want to send to pop-up a value of a field that has already been filled, not just a hardcoded value? Let suppose I have an invoice module and in EditView I have an “Acount Name” (related with Accounts) and “Payment responsible” (related with contacts). When I choose payment responsible I what to filter the results in popup by Account name. That makes sense.

To achieve this, in editviewdefs.php you must have:
array (
'name' => 'contact',
'displayParams' => array(
'initial_filter' => "&account_name_advanced=\" + document.getElementById(\"billing_account_name\").value + \"",
),
'label' => 'LBL_PAYMENT_RESPONSIBLE',
)

billing_account_name is actually the field name of “Account name”.
To understand how this works you can look at the HTML source and search the open_popup() function that is trigged by Contact button. Actually, the “initial_filter” value is a parameter in open_popup() javascript function.
So we can interfere in javascript and call a particular value from form(that we suppose is filled) using javascript function getElementById.

That’s all folks :)

In SugarCRM, global links ( such as Employees, Admin, Support, About) are defined in /include/globalControlLinks.php file. Those links appear in top right corner of the screen, usualy after “Welcome, Administrator [Log Out]” message.

If you have to modify, remove or add some new links there is an upgrade safe method to do that in /custom folder.

You just have to create a file, if not exists, at location /custom/include/globalControlLinks.php.

To remove, for example, the “Support” link you must add the following line of code:

unset($global_control_links['training']);

To add some new link you have to write somethink like:

$global_control_links['dobre_link'] = array('linkinfo' => array('Dobre.name'=>'http://www.dobre.name/'));

You can also write this code in an upgrade safe manner in /custom/application/Ext/GlobalLinks/links.ext.php
You must remember that last one has priority in front of /custom/include/globalControlLinks.php, that has priority in front of /include/globalControlLinks.php :)

To do that in an upgrade safe manner you must add some files in /custom directory.

The many-to-many relationship involves the existence of a new table that make connection of other to tables.
Lets suppose that we have many products that can be assigned to many categories. Our tables are categories and products, and the new relationship table will be products_categories. SugarCRM will automatically create the new table if the relationship definition is correctly described.

The definition for many-to-many relationship in SugarCRM is stored in $dictionary array so you have to add a new structure to it.

So, you have to add two files in “custom” directory ():

  • product_categoriesMetaData.php – in /custom/metadata/
  • products_categories.php – in /custom/Extension/application/Ext/TableDictionary/

product_categoriesMetaData.php will contain the new $dictionary array with definition of many-to-many relationship, and can be described like this:

$dictionary["products_categories"] = array (
'true_relationship_type' => 'many-to-many',
'from_studio' => false,
'relationships' =>
array (
'products_categories' =>
array (
'lhs_module' => 'Categories',
'lhs_table' => 'categories',
'lhs_key' => 'id',
'rhs_module' => 'Products',
'rhs_table' => 'products',
'rhs_key' => 'id',
'relationship_type' => 'many-to-many',
'join_table' => 'products_categories',
'join_key_lhs' => 'categories_id',
'join_key_rhs' => 'products_id',
),
),
'table' => 'products_categories',
'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' => 'categories_id',
'type' => 'varchar',
'len' => 36,
),
4 =>
array (
'name' => 'products_id',
'type' => 'varchar',
'len' => 36,
),
),
'indices' =>
array (
0 =>
array (
'name' => 'products_categories_pk',
'type' => 'primary',
'fields' =>
array (
0 => 'id',
),
),
1 =>
array (
'name' => 'products_categories_alt',
'type' => 'alternate_key',
'fields' =>
array (
0 => 'categories_id',
1 => 'products_id',
),
),
),
);

products_categories.php file from TableDictionary folder will actually make a reference to the above file to be executed and its content will be:

include('custom/metadata/products_categoriesMetaData.php');

After that, you have to go in SugarCRM administration panel and execute a “Quick Repair and Rebuild”.

At the end of the screen you’ll be noticed to execute an SQL query about a “CREATE TABLE” statement. This will actually create the new “products_categories” table.

The database layer in SugarCrm is designed in such a way that it is very agnostic, so you should never need to make calls to the actual database later functions, but rather use the exposed class methods for doing all the work needed. In listing bellow we making a query from the users table, iterating through the results, and making and updating the records if needed.

$db = DBManagerFactory::getInstance();
$res = $db->query("select * from users");
while ( $row = $db->fetchByAssoc($res) ) {
  // make some updates
  if ( $row['status'] != 'Active' ) {
      $db -> query("update users set is_admin = 0 where id = '{$row['id']}'");
  }
}

In SugarCRM it is possible to customize the way EditView template it’s displayed. In this article we explore how can add a header or a footer to the view.

What must be done?

In your <module_name> folder you have to edit editviewdefs.php from the metadata directory and adding this lines of code:

'form' => array(
      'footerTpl'=>'modules/lqd_Invoices/tpls/EditViewFooter.tpl',
      'headerTpl'=>'modules/lqd_Invoices/tpls/EditViewHeader.tpl',
      ),

to the $viewdefs array.

The fragment of your code must looks like:

$viewdefs [$module_name] =
array (
  'EditView' =>
  array (
    'templateMeta' =>
    array (
      'maxColumns' => '2',
      'widths' =>
      array (
        0 => array ('label' => '10', 'field' => '30'),
        1 => array ('label' => '10', 'field' => '30'),
      ),
      'form' => array(
 'footerTpl'=>'modules/lqd_Invoices/tpls/EditViewFooter.tpl',
 'headerTpl'=>'modules/lqd_Invoices/tpls/EditViewHeader.tpl',
 ),
      'useTabs' => false,
      'syncDetailEditViews' => true,
    )

Then you have to create in <module_name>/tpl/ the two files: EditViewFooter.tpl and EditViewHeader.tpl .

You can add your custom code here now, but don’t forget to include the generic header and footer tlp.

Your code must look like:

EditViewHeader.tpl
{$my_widget_for_header}
bla bla bla
{{include file='include/EditView/header.tpl'}}</pre>
EditViewFooter.tpl
{$my_widget_for_footer}
bla bla bla
{{include file='include/EditView/footer.tpl'}}

Don’t forget to do a “Quick Rebuild and Repair” from Administration.

That’s all :)

SOAP este un protocol simplu, utilizat pentru transferul de informaţie intr-un mediu descentralizat, distribuit.

Este un protocol bazat pe XML care presupune 3 parţi: un înveliş care defineşte un framework pentru descrierea conţinutului unui mesaj şi cum se procesează acesta, un set de reguli de codificare pentru instanţe ale unor expresii din aplicaţii – tipuri de date definite, şi o convenţie pentru reprezentarea apelurilor şi răspunsurilor procedurilor apelate la distanta. SOAP poate fi utilizat in combinaţie cu o varietate de alte protocoale, in special in combinaţie cu HTTP.

Protocolul SOAP

Exista câteva tipuri diferite ale modelelor de mesaje in SOAP, dar pe departe cel mai comun este modelul Apel de Procedura la Distanta (RPC), in care un nod de reţea (clientul) trimite un mesaj cerere către alt nod (serverul), şi serverul imediat trimite un mesaj răspuns clientului. SOAP este succesorul XML-RPC, de aceea împrumuta transportul şi neutralitatea interacţiunii şi envelope/header/body.

SOAP iniţial a fost perceput ca Simple Object Access Protocol, iar mai târziu şi ca Service Oriented Architecture Protocol, dar acum este doar SOAP. Acronimul original a fost abandonat cu Versiunea 1.2 a Standardului, care a devenit o Recomandare W3C pe 24 Iunie 2003, când denumirea a fost considerata ca fiind derutanta.

Aplicaţiile din ziua de astăzi comunica utilizând “Apelurile de Procedura la Distanta” (RPC – Remote Procedure Calls) dintre obiecte, ca DCOM sau Corba, dar HTTP nu a fost creat pentru acest lucru. RPC reprezintă o problema de compatibilitate şi securitate; firewall-urile şi serverele proxy blochează in mod normal acest tip de trafic.

O cale mai buna de comunicaţie intre aplicaţii este prin HTTP, deoarece HTTP este suportat de toate browserele şi serverele Internet. SOAP a fost creat sa îndeplinească acest aspect.

SOAP furnizează o cale de comunicaţie intre aplicaţii care rulează pe diferite sisteme de operare, cu tehnologii şi limbaje de programare diferite.

UserLand, Ariba, Commerce One, Compaq, Developmentor, HP, IBM, IONA, Lotus, Microsoft şi SAP au propus organizaţiei W3C, in Mai 2000, protocolul SOAP in speranţa ca va revoluţiona dezvoltarea aplicaţiilor prin conectarea interfeţei grafice utilizator a aplicaţiilor desktop la servere puternice Internet, utilizând standardele Internet: HTTP şi XML.

Avantaje

  • Utilizând SOAP cu HTTP este permisa comunicaţia mai buna in spatele unui proxy sau firewall decât presupunea precedenta tehnologie de apel la distanţa.
  • SOAP este destul de versatil pentru a permite utilizarea protocoalelor de transport diferite. Stiva standard utilizează HTTP ca protocol de transport, dar alte protocoale sunt de asemenea utilizabile (TCP, SNMP).

Dezavantaje

  • Din cauza lungimii formatului XML, SOAP poate fi destul de lent in comparaţie cu tehnologiile middleware, cum este CORBA. Aceasta poate sa nu fie o problema când se trimit numai mesaje scurte.
  • Când ne bazam pe HTTP ca protocol de transport, rolurile părţilor care interacţionează sunt stabilite. Doar o parte (clientul) poate utiliza serviciile altuia. Deci dezvoltătorii trebuie sa utilizeze polling in schimbul notificării in aceste cazuri comune.
  • Multe implementări SOAP limitează cantitatea de date care poate fi trimisa.

Daca am reduce populatia Pamantului la un satuc cu 100 oameni, pastrand proportiile reale, situatia ar arata astfel:

57 Asiatici
21 Europeni
14 Americani (din Nord si Sud)
8 Africani
=====================
52 femei
48 barbati
=====================
70 de alta rasa
30 albi
=====================
89 heterosexuali
11 homosexuali
=====================
6 persoane ar detine 59% din toata averea lumii si toti ar fi din USA
80 ar avea conditii proaste de viata
70 ar fi analfabeti
50 ar fi subnutriti
1 ar muri
2 s-ar naste
1 ar avea un computer
1 (unul singur) ar avea educatie superioara

Daca te vei uita astfel la lumea noastra, vei vedea ce mare nevoie avem de solidaritate, intelegere, rabdare, si educatie.

Gandeste-te la asta!

Daca azi de dimineata te-ai trezit sanatos, esti mai fericit decat 1 milion de oameni care nu o sa mai apuce saptamana urmatoare.

Daca ai supravietuit unui razboi, singuratatii dintr-o celula de inchisoare, agoniei torturii foametei, esti mai fericit decat 500 milioane de oameni din aceasta lume.

Daca poti intra sa te rogi intr-o biserica/ moschee fara teama de fi inchis sau ucis, esti mai fericit decat 3 miliarde de oameni din lume.

Daca ai mancare in frigider, ai haine si incaltari, ai un pat si un acoperis, esti mai bogat decat 75% din oamenii acestei lumi.

Daca ai un cont in banca, bani in portofel si ceva monede in pusculita, te numeri printre cei 8% oameni din lume care o duc bine.

Daca citesti acest text esti de doua ori mai norocos, deoarece:
1. cineva tocmai si-a amintit de tine
2. nu apartii celor 2 miliarde de oameni care nu pot citi.

si… Ai un computer!

Dupa cum spunea cineva intelept:
“- munceste ca si cum n-ai avea nevoie de bani,
- iubeste ca si cum nu te-ar rani nimeni niciodata,
- danseaza ca si cum nu te-ar vedea nimeni,
- canta, ca si cum nu te-ar auzi nimeni,
- traieste ca si cum Pamantul ar fi RAIUL.”

NuSOAP este o colecţie de clase PHP care permite utilizatorilor trimiterea şi recepţionarea de mesaje SOAP folosind protocolul HTTP. NuSOAP, cunoscut şi ca SOAPx4, este distribuit de Corporaţia NuSphere (http://www.nusphere.com). Aceasta librărie este open source, licenţiata sub GNU LGPL. NuSOAP este utilizată ca şi nucleu al altor librarii precum PEAR-SOAP.

Unul dintre avantajele NuSOAP este reprezentat de faptul ca nu este o extensie a PHP, şi este scris exclusiv in acest limbaj. Aceasta înseamnă ca poate fi folosit de către orice dezvoltator PHP, indiferent de restricţiile privind accesul la resursele serverului.

Interacţiunea cu serviciile web este realizata prin intermediul clasei soap_client. Aceasta clasa de nivel înalt permite utilizatorilor sa specifice opţiuni precum autorizarea prin HTTP, informaţii despre proxy HTTP, precum şi gestionarea trimiterii si primirii de mesaje SOAP.

Operaţiile SOAP pot fi executate prin pasarea numelui operaţii care se doreşte a fi executate, funcţiei call(). Daca serviciul oferă un fişier WSDL atunci, clasa soap_client preia URL-ul fişierului WDSDL ca argument in constructor, şi utilizează clasa wsdl pentru a parsa informaţia din fişierul WSDL şi a extrage datele.

Clasa soap_client foloseşte aceste date din fişierul WSDL pentru a encoda parametrii şi pentru a crea un “înveliş” SOAP când userul executa un apel al serviciului. Când apelul este executat, clasa soap_client utilizează clasa soap_transport_http pentru a trimite mesajul şi pentru a pentru a primi răspuns. Mesajul-răspuns este parsat de clasa soap_parser. Figura de mai jos descrie procesul consumării unui serviciu web utilizând NuSOAP.

Structura librariei nuSOAP

Însa daca serviciul web nu oferă un fişier WSDL, procesul este diferit. URL-ul serviciului este transmis constructorului clasei soap_client. Operaţiile sunt executate tot folosind apelul funcţiei obiectului soap_client, dar detaliile care erau oferite in cazul precedent de fişierul WSDL trebuie transmise acum ca argumente. Parametrii care sunt de tip special pot fi reprezentate folosind clasa soapval, care permite utilizatorilor sa-i personalizeze.
Atât SOAP cat şi WSDL folosesc tipurile de date descrise in specificaţiile XML. Acest lucru poate fi problematic deoarece PHP nu suporta nativ tipurile de date definite in specificaţii. De asemenea, tipurile de date in XML sunt stricte şi bine definite in timp ce PHP este un limbaj de programare mult mai permisiv, care converteşte automat tipurile de date in funcţie de situaţie. NuSOAP rezolvă aceasta problema pe trei nivele diferite:

  • In WSDL, clasa soap_client a NuSOAP va encoda tipul valorilor corespunzător tipului specificat in documentul WSDL.
  • Clasa soapval din NuSOAP permite utilizatorilor sa definească explicit tipul valorii
  • Daca nici un tip nu este declarat explicit la instanţierea obiectului soapval, NuSOAP va analiza valoarea atribuita acestuia folosind funcţiile interne ale PHP şi o va clasifica ca tip de date valid XML.

123123.jpg

Modul de utilizare a clasei NuSOAP

Următorul exemplu demonstrează procesul de baza al creării unui client SOAP, apelul unui serviciu şi transmiterea parametrilor, şi recepţionarea mesajului.
Următorul exemplu reprezintă serverul care implementează serviciul Hello.

Server:

//Includerea librariei NuSOAP
include("nusoap.php");

//Crearea instantei de server
$server = new soap_server;

//Inregistrareametodei
$server->register("hello");

//Definirea metodei ca functie PHP
function hello($name){
if(is_string($name)){
return "Hello, " . $name;
} else {
return new soap_fault("Client","","Parametrul transmis serviciului trebuie sa fie string!");
}
}

$raw_post = file_get_contents("php://input");
$server -> service($raw_post);

Pentru a permite funcţiei sa fie apelata la distanta, aceasta trebuie înregistrata in obiectul server. Daca acest lucru nu se realizează atunci, la apelul funcţiei de către client, serverul va genera o eroare, indicând faptul ca serviciul nu este disponibil. In absenta acestui proces de înregistrare, orice funcţie PHP poate fi disponibila apelului la distanta, ceea ce conduce la probleme grave de securitate.
In cazul in care parametrul transmis funcţiei nu este un sir de caractere se generează o eroare in care se specifica problema. Acesta sarcina revine clasei soap_fault, care se ocupa de tratarea erorilor şi transmiterea acestora in mesajul-răspuns.
Pasul final este reprezentat de apelul metodei service, care procesează cererea primita şi apelează funcţia corespunzătoare. Apoi aceasta formulează răspunsul şi îl printeaza.

Client:

// Se instantiaza obiectul soap_client. URL-ul serverului se transmite ca argument in constructor
$client = new soap_client("http://www.w2w.ro/ws_server/hello");
//Parametrii trebuie transmisi printr-un array  clientului SOAP
$param = array("name" => "Dobre Marian");
// Apelul metodei SOAP
$result = $client->call("hello", $param);
if($client->fault){
echo "
<h2>Fault</h2>
<pre>";
print_r($result);
echo "</pre>
";
} else {
$err = $client->getError();
if ($err) {
// Afisarea erorilor
echo "
<h2>Error</h2>
<pre>" . $err . "</pre>
";
} else {
// Afisarea rezultatului
echo "
<h2>Result</h2>
<pre>";
print_r($result);
echo "</pre>
";
}
}
echo "
<h2>Request</h2>
";
echo "
<pre>" . htmlspecialchars($client->request, ENT_QUOTES) . "</pre>
";
echo "
<h2>Response</h2>
";
echo "
<pre>" . htmlspecialchars($client->response, ENT_QUOTES) . "</pre>
";

La apelul metodei call() se specifica obiectului soap_client serviciul care se doreşte a fi accesat, apoi se transmite un array cu parametrii, iar metoda returnează răspunsul serverului. Acest răspuns este un tip de date nativ PHP, precum un string, integer sau array.
NuSOAP oferă un mecanism de detecţie al erorilor prin intermediul metodei getError() . Daca a apărut o eroare, aceasta metoda returnează un string care descrie eroarea, sau returnează false in caz de succes. In exemplul prezentat se va printa rezultatul in cazul in care nu a fost generata nici o eroare. Altfel, se va printa mesajul de eroare.

Ultima parte a codului este foarte utila pentru procesul de debug al operaţiilor efectuate de NuSOAP. Proprietăţile request şi response ale clasei soap_client conţin mesajele, inclusiv headerele HTTP transmise de fiecare.

Next Page »