Here’s a nice and simple article explaining how to add Triggers and Actions in Drupal 6. This tutorial will send an email notification to the site administrator when a comment is added to an article. I use it on this site to send an email to the articles author when a comment is added.
http://www.scribbledesigns.co.uk/2008/04/03/howto-configuring-triggers-and-actions-in-drupal-6/
This is a little helper function I wrote to send an email using Sugar CRM’s built in mail class.
/**
* A wrapper for sugar's mail class
* Sends emails to everyon in $tos array.
* $tos['user name'] = 'username@domain.com';
*
* @example : $tos['Some User'] = 'SomeUser@somewhere.com';
* $tos['Some User 2'] = 'SomeUser2@somewhere.com';
* sendSugarPHPMail($tos, 'hi', 'hello fellas');
*
* @param associative array
* @param string $subject
* @param string $body
* @return boolean
*/
function sendSugarPHPMail($tos, $subject, $body){
require_once('include/SugarPHPMailer.php');
require_once('modules/Administration/Administration.php');
$mail = new SugarPHPMailer();
$admin = new Administration();
$admin->retrieveSettings();
if ($admin->settings['mail_sendtype'] == "SMTP") {
$mail->Host = $admin->settings['mail_smtpserver'];
$mail->Port = $admin->settings['mail_smtpport'];
if ($admin->settings['mail_smtpauth_req']) {
$mail->SMTPAuth = TRUE;
$mail->Username = $admin->settings['mail_smtpuser'];
$mail->Password = $admin->settings['mail_smtppass'];
}
$mail->Mailer = "smtp";
$mail->SMTPKeepAlive = true;
}else{
$mail->mailer = 'sendmail';
}
$mail->From = $admin->settings['notify_fromaddress'];
$mail->FromName = $admin->settings['notify_fromname'];
$mail->ContentType = "text/html"; //"text/plain"
$mail->Subject = $subject;
$mail->Body = $body;
foreach ($tos as $name => $address){
$mail->AddAddress("{$address}", "{$name}");
}
if (!$mail->send()) {
$GLOBALS['log']->info("sendSugarPHPMail - Mailer error: " . $mail->ErrorInfo);
return false;
}else{
return true;
}
}
Caribbean Music Center is a streaming media website. Music is categorized based on it’s ID3 tags and stored in a MySQL database. This site was created from scratch during my spare time.
This array can be used to build out a Country drop down list.
<?php $ar_countries = array( 'AF'=>'AFGHANISTAN', 'AL'=>'ALBANIA', 'DZ'=>'ALGERIA', 'AS'=>'AMERICAN SAMOA', 'AD'=>'ANDORRA', 'AO'=>'ANGOLA', 'AI'=>'ANGUILLA', 'AQ'=>'ANTARCTICA', 'AG'=>'ANTIGUA AND BARBUDA', 'AR'=>'ARGENTINA', 'AM'=>'ARMENIA', 'AW'=>'ARUBA', 'AU'=>'AUSTRALIA', 'AT'=>'AUSTRIA', 'AZ'=>'AZERBAIJAN', 'BS'=>'BAHAMAS', 'BH'=>'BAHRAIN', 'BD'=>'BANGLADESH', 'BB'=>'BARBADOS', 'BY'=>'BELARUS', 'BE'=>'BELGIUM', 'BZ'=>'BELIZE', 'BJ'=>'BENIN', 'BM'=>'BERMUDA', 'BT'=>'BHUTAN', 'BO'=>'BOLIVIA', 'BA'=>'BOSNIA AND HERZEGOVINA', 'BW'=>'BOTSWANA', 'BV'=>'BOUVET ISLAND', 'BR'=>'BRAZIL', 'IO'=>'BRITISH INDIAN OCEAN TERRITORY', 'BN'=>'BRUNEI DARUSSALAM', 'BG'=>'BULGARIA', 'BF'=>'BURKINA FASO', 'BI'=>'BURUNDI', 'KH'=>'CAMBODIA', 'CM'=>'CAMEROON', 'CA'=>'CANADA', 'CV'=>'CAPE VERDE', 'KY'=>'CAYMAN ISLANDS', 'CF'=>'CENTRAL AFRICAN REPUBLIC', 'TD'=>'CHAD', 'CL'=>'CHILE', 'CN'=>'CHINA', 'CX'=>'CHRISTMAS ISLAND', 'CC'=>'COCOS (KEELING) ISLANDS', 'CO'=>'COLOMBIA', 'KM'=>'COMOROS', 'CG'=>'CONGO', 'CD'=>'CONGO, THE DEMOCRATIC REPUBLIC OF THE', 'CK'=>'COOK ISLANDS', 'CR'=>'COSTA RICA', 'CI'=>'COTE D IVOIRE', 'HR'=>'CROATIA', 'CU'=>'CUBA', 'CY'=>'CYPRUS', 'CZ'=>'CZECH REPUBLIC', 'DK'=>'DENMARK', 'DJ'=>'DJIBOUTI', 'DM'=>'DOMINICA', 'DO'=>'DOMINICAN REPUBLIC', 'TP'=>'EAST TIMOR', 'EC'=>'ECUADOR', 'EG'=>'EGYPT', 'SV'=>'EL SALVADOR', 'GQ'=>'EQUATORIAL GUINEA', 'ER'=>'ERITREA', 'EE'=>'ESTONIA', 'ET'=>'ETHIOPIA', 'FK'=>'FALKLAND ISLANDS (MALVINAS)', 'FO'=>'FAROE ISLANDS', 'FJ'=>'FIJI', 'FI'=>'FINLAND', 'FR'=>'FRANCE', 'GF'=>'FRENCH GUIANA', 'PF'=>'FRENCH POLYNESIA', 'TF'=>'FRENCH SOUTHERN TERRITORIES', 'GA'=>'GABON', 'GM'=>'GAMBIA', 'GE'=>'GEORGIA', 'DE'=>'GERMANY', 'GH'=>'GHANA', 'GI'=>'GIBRALTAR', 'GR'=>'GREECE', 'GL'=>'GREENLAND', 'GD'=>'GRENADA', 'GP'=>'GUADELOUPE', 'GU'=>'GUAM', 'GT'=>'GUATEMALA', 'GN'=>'GUINEA', 'GW'=>'GUINEA-BISSAU', 'GY'=>'GUYANA', 'HT'=>'HAITI', 'HM'=>'HEARD ISLAND AND MCDONALD ISLANDS', 'VA'=>'HOLY SEE (VATICAN CITY STATE)', 'HN'=>'HONDURAS', 'HK'=>'HONG KONG', 'HU'=>'HUNGARY', 'IS'=>'ICELAND', 'IN'=>'INDIA', 'ID'=>'INDONESIA', 'IR'=>'IRAN, ISLAMIC REPUBLIC OF', 'IQ'=>'IRAQ', 'IE'=>'IRELAND', 'IL'=>'ISRAEL', 'IT'=>'ITALY', 'JM'=>'JAMAICA', 'JP'=>'JAPAN', 'JO'=>'JORDAN', 'KZ'=>'KAZAKSTAN', 'KE'=>'KENYA', 'KI'=>'KIRIBATI', 'KP'=>'KOREA DEMOCRATIC PEOPLES REPUBLIC OF', 'KR'=>'KOREA REPUBLIC OF', 'KW'=>'KUWAIT', 'KG'=>'KYRGYZSTAN', 'LA'=>'LAO PEOPLES DEMOCRATIC REPUBLIC', 'LV'=>'LATVIA', 'LB'=>'LEBANON', 'LS'=>'LESOTHO', 'LR'=>'LIBERIA', 'LY'=>'LIBYAN ARAB JAMAHIRIYA', 'LI'=>'LIECHTENSTEIN', 'LT'=>'LITHUANIA', 'LU'=>'LUXEMBOURG', 'MO'=>'MACAU', 'MK'=>'MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF', 'MG'=>'MADAGASCAR', 'MW'=>'MALAWI', 'MY'=>'MALAYSIA', 'MV'=>'MALDIVES', 'ML'=>'MALI', 'MT'=>'MALTA', 'MH'=>'MARSHALL ISLANDS', 'MQ'=>'MARTINIQUE', 'MR'=>'MAURITANIA', 'MU'=>'MAURITIUS', 'YT'=>'MAYOTTE', 'MX'=>'MEXICO', 'FM'=>'MICRONESIA, FEDERATED STATES OF', 'MD'=>'MOLDOVA, REPUBLIC OF', 'MC'=>'MONACO', 'MN'=>'MONGOLIA', 'MS'=>'MONTSERRAT', 'MA'=>'MOROCCO', 'MZ'=>'MOZAMBIQUE', 'MM'=>'MYANMAR', 'NA'=>'NAMIBIA', 'NR'=>'NAURU', 'NP'=>'NEPAL', 'NL'=>'NETHERLANDS', 'AN'=>'NETHERLANDS ANTILLES', 'NC'=>'NEW CALEDONIA', 'NZ'=>'NEW ZEALAND', 'NI'=>'NICARAGUA', 'NE'=>'NIGER', 'NG'=>'NIGERIA', 'NU'=>'NIUE', 'NF'=>'NORFOLK ISLAND', 'MP'=>'NORTHERN MARIANA ISLANDS', 'NO'=>'NORWAY', 'OM'=>'OMAN', 'PK'=>'PAKISTAN', 'PW'=>'PALAU', 'PS'=>'PALESTINIAN TERRITORY, OCCUPIED', 'PA'=>'PANAMA', 'PG'=>'PAPUA NEW GUINEA', 'PY'=>'PARAGUAY', 'PE'=>'PERU', 'PH'=>'PHILIPPINES', 'PN'=>'PITCAIRN', 'PL'=>'POLAND', 'PT'=>'PORTUGAL', 'PR'=>'PUERTO RICO', 'QA'=>'QATAR', 'RE'=>'REUNION', 'RO'=>'ROMANIA', 'RU'=>'RUSSIAN FEDERATION', 'RW'=>'RWANDA', 'SH'=>'SAINT HELENA', 'KN'=>'SAINT KITTS AND NEVIS', 'LC'=>'SAINT LUCIA', 'PM'=>'SAINT PIERRE AND MIQUELON', 'VC'=>'SAINT VINCENT AND THE GRENADINES', 'WS'=>'SAMOA', 'SM'=>'SAN MARINO', 'ST'=>'SAO TOME AND PRINCIPE', 'SA'=>'SAUDI ARABIA', 'SN'=>'SENEGAL', 'SC'=>'SEYCHELLES', 'SL'=>'SIERRA LEONE', 'SG'=>'SINGAPORE', 'SK'=>'SLOVAKIA', 'SI'=>'SLOVENIA', 'SB'=>'SOLOMON ISLANDS', 'SO'=>'SOMALIA', 'ZA'=>'SOUTH AFRICA', 'GS'=>'SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS', 'ES'=>'SPAIN', 'LK'=>'SRI LANKA', 'SD'=>'SUDAN', 'SR'=>'SURINAME', 'SJ'=>'SVALBARD AND JAN MAYEN', 'SZ'=>'SWAZILAND', 'SE'=>'SWEDEN', 'CH'=>'SWITZERLAND', 'SY'=>'SYRIAN ARAB REPUBLIC', 'TW'=>'TAIWAN, PROVINCE OF CHINA', 'TJ'=>'TAJIKISTAN', 'TZ'=>'TANZANIA, UNITED REPUBLIC OF', 'TH'=>'THAILAND', 'TG'=>'TOGO', 'TK'=>'TOKELAU', 'TO'=>'TONGA', 'TT'=>'TRINIDAD AND TOBAGO', 'TN'=>'TUNISIA', 'TR'=>'TURKEY', 'TM'=>'TURKMENISTAN', 'TC'=>'TURKS AND CAICOS ISLANDS', 'TV'=>'TUVALU', 'UG'=>'UGANDA', 'UA'=>'UKRAINE', 'AE'=>'UNITED ARAB EMIRATES', 'GB'=>'UNITED KINGDOM', 'US'=>'UNITED STATES', 'UM'=>'UNITED STATES MINOR OUTLYING ISLANDS', 'UY'=>'URUGUAY', 'UZ'=>'UZBEKISTAN', 'VU'=>'VANUATU', 'VE'=>'VENEZUELA', 'VN'=>'VIET NAM', 'VG'=>'VIRGIN ISLANDS, BRITISH', 'VI'=>'VIRGIN ISLANDS, U.S.', 'WF'=>'WALLIS AND FUTUNA', 'EH'=>'WESTERN SAHARA', 'YE'=>'YEMEN', 'YU'=>'YUGOSLAVIA', 'ZM'=>'ZAMBIA', 'ZW'=>'ZIMBABWE' ); ?>
This is a simple script that will highlight required fields on a form. It requires no libraries and is just one simple function that accepts an array of field id’s that are required.
Code:
function checkRequiredFields(fieldIdsArray){
var fields = fieldIdsArray;
var errorThrown = false;
for (var i = 0; i < fields.length; i++){
var obj = document.getElementById(fields[i]);
if (obj.value == '' || obj.value == null){
obj.style.border = '1px solid red';
obj.onfocus = function(){ this.style.border = '1px solid #999999'};
errorThrown = true;
}
}
if (errorThrown){
alert('Please fill in required fields.');
return false;
}
return true;
}
Example:
var requiredFields = new Array('field_1', 'field_2');
function checkRequiredFields(fieldIdsArray){
var fields = fieldIdsArray;
var errorThrown = false;
for (var i = 0; i < fields.length; i++){
var obj = document.getElementById(fields[i]);
if (obj.value == '' || obj.value == null){
obj.style.border = '1px solid red';
obj.onfocus = function(){ this.style.border = '1px solid #999999'};
errorThrown = true;
}
}
if (errorThrown){
alert('Please fill in required fields.');
return false;
}
return true;
}
<input type="text" name="field_1" id="field_1">
<input type="text" name="field_2" id="field_2">
<input type="button" onclick="checkRequiredFields(requiredFields);">
OR
<input type="button" onclick="checkRequiredFields(['field_1','field_2']);">
There’s not much information out there about Sugar CRM, so here’s a quick example on creating a custom logic hook.
This example uses a custom helper function called ’sendSugarPHPMail’ which can be found here http://redinkdesign.net/open-source/simple-sugarcrm-e-mail-wrapper.
In this example, we’re going to email someone when a Case’s status is set to ‘Closed’.
To make this upgrade friendly we’re going to create two files in ‘custom/modules/Cases’.
First file: logic_hooks.php – Loaded automatically by Sugar.
Second: case_closed.php – Our custom code.
The contents of logic_hooks.php are:
<?php
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
$hook_array = array();
$hook_array['before_save'] = array();
// array(hook execute order, 'hook name', 'hook code location', 'hook code class name', 'hook code function to execute')
$hook_array['before_save'][] = array(1, 'case_closed', 'custom/modules/Cases/case_closed.php', 'case_closed', 'case_closed');
?>
Our array parameters must be the following:
1. hook execute order
2. hook name
3. hook code location
4. hook code class name
5. hook code function to execute
$hook_array['before_save'][] = array(1, 'case_closed', 'custom/modules/Cases/case_closed.php', 'case_closed', 'case_closed');
The contents of case_closed.php are:
<?php
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
class case_closed{
function case_closed(&$bean, $event, $arguments){
// make sure the status was actually changed to closed
// $bean->fetched_row['status'] is the stored status of the case
// $bean->status is the status it was changed to
if ($bean->fetched_row['status'] !== 'Closed' && $bean->status === 'Closed'){
$subject = "Closed: Case {$bean->case_number} - {$bean->name}";
$body = "Assigned to: {$bean->assigned_user_name}<br />";
$body .= 'Description: ' . (empty($bean->description)? ' n/a ' : $bean->description);
$to['Mr. Bean'] = 'mrbean@whatevs.com';
if (!sendSugarPHPMail($to, $subject, $body)){
$GLOBALS['log']->info("Could not send case closed notification: " . $mail->ErrorInfo);
}
}
}
}
?>
Application hooks
These hooks do not make use of the $bean argument.
after_ui_frame
Fired after the frame has been invoked and before the footer has been invoked
Available in version 4.5.0 and later
after_ui_footer
Fired after the footer has been invoked
Available in version 4.5.0 and later
server_round_trip
Fired at the end of every SugarCRM page
Available in version 4.5.0 and later
Module hooks
before_delete
Fired before a record is deleted
Available in version 4.5.0 and later
after_delete
Fired after a record is deleted
Available in version 4.5.0 and later
before_restore
Fired before a record is undeleted
Available in version 4.5.0 and later
after_restore
Fired after a record is undeleted
Available in version 4.5.0 and later
after_retrieve
Fired after a record has been retrieved from the database. This hook does not fire when you create a new record.
Available in version 4.5.0 and later
before_save
Fired before a record is saved.
Note: With certain modules, like Cases and Bugs, the human-readable ID of the record (like the case_number field in the Case module), is not available within a before_save call. This is because this business logic simply hasn’t been executed yet.
Available in version 4.5.0 and later
after_save
Fired after a record is saved.
Note: With certain modules, like Cases and Bugs, the human-readable ID of the record (like the case_number field in the Case module), is not available within a before_save call. This is because this business logic simply hasn’t been executed yet.
Available in version 4.5.0 and later
process_record
Fired immediately prior to the database query resulting in a record being made current. This gives developers an opportunity to examine and tailor the underlying queries. This is also a perfect place to set values in a record’s fields prior to display in the DetailView or ListView. This event is not fired in the EditView.
Available in version 4.5.0 and later
Hooks for Users module
before_logout
Fired before a user logs out of the system
Available in version 4.5.0 and later
after_logout
Fired after a user logs out of the system
Available in version 4.5.0 and later
after_login
Fired after a user logs into the system.
Available in version 4.5.0 and later
after_logout
Fired after a user logs out of the system.
Available in version 4.5.0 and later
before_logout
Fired before a user logs out of the system.
Available in version 4.5.0 and later
login_failed
Fired on a failed login attempt
Available in version 4.5.0 and later
Here’s a list of all available logic hooks…
eCirkit – A social media webtop – Live demo @ http://www.bradleyfarrell.com
eCirkit was created to have a desktop like UI with social networking capabilities. It’s an AJAX heavy interface with an extensible PHP 5 backend.

A simple javascript snippet to remove HTML elements dynamically.
function removeElement(elementId){
var itemToBeRemoved = document.getElementById(elementId);
var parent = itemToBeRemoved.parentNode;
parent.removeChild(itemToBeRemoved);
};