Configuring Triggers and Actions in Drupal 6

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/

Simple SugarCRM E-mail Wrapper

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;
 		}
	}

PHP Country Array

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'
	);
?>

Simple Field Validator/Highlighter

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']);">

Creating Logic Hooks for SugarCRM 5

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…

http://developers.sugarcrm.com/docs/OS/5.1/-docs-Developer_Guides-Developer_Guide_5.1-DevGuide.1.101.html

eCirkit.com

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.

Remove HTML Elements Dynamically

A simple javascript snippet to remove HTML elements dynamically.

function removeElement(elementId){
	var itemToBeRemoved = document.getElementById(elementId);
	var parent = itemToBeRemoved.parentNode;
	parent.removeChild(itemToBeRemoved);
};

Auto-complete Text Box

function showPopup(textBox, className){

// grab the textbox object
var textbox = document.getElementById(textBox);

// get the textboxes parent
var parent = textbox.parentNode;

// check to see if this popup is already on screen
var popup = document.getElementById(textBox + ‘_popup’);

if (!popup) {
// we don’t have a popup, lets create one

// create popup div
var popup = document.createElement(‘div’);
// give it a unique id
popup.id = textBox + ‘_popup’;

// attach css if passed in
popup.className = className || ‘popup’;

// set it’s left location
popup.style.left = textbox.offsetLeft;

// set it’s top location
popup.style.top = textbox.offsetHeight + textbox.offsetTop;

// make sure it’s absolute
popup.style.position = ‘absolute’;

// attach it to the text boxes parent node
parent.appendChild(popup);

}else{
popup.style.display = ‘block’;
}

// attach an event to remove the popup once it loses focus
document.onclick = function(){
if (popup){
popup.style.display = ‘none’;
delete popup;
}
};

// give it some content
popup.innerHTML += textbox.value + “
“;

}

.popupClass{
height: 150px;
width: 150px;
border: 1px solid black;
background: yellow;
overflow: auto;
}

This is a very simple script to lead you in the right direction towards making an auto-complete textbox, similar to Google Suggest (http://www.google.com/webhp?complete=1&hl=en). This script does not need any fancy framework to work.

Example:


function showPopup(textBox, className){

// grab the textbox object
var textbox = document.getElementById(textBox);

// get the textboxes parent
var parent = textbox.parentNode;

// check to see if this popup is already on screen
var popup = document.getElementById(textBox + '_popup');

if (!popup) {
// we don't have a popup, lets create one

// create popup div
var popup = document.createElement('div');
// give it a unique id
popup.id = textBox + '_popup';

// attach css if passed in
popup.className = className || 'popup';

// set it's left location
popup.style.left = textbox.offsetLeft;

// set it's top location
popup.style.top = textbox.offsetHeight + textbox.offsetTop;

// make sure it's absolute
popup.style.position = 'absolute';

// attach it to the text boxes parent node
parent.appendChild(popup);

}else{
popup.style.display = 'block';
}

// attach an event to remove the popup once it loses focus
document.onclick = function(){
if (popup){
popup.style.display = 'none';
delete popup;
}
};

// give it some content
popup.innerHTML += textbox.value + "
";

}

Google Weather API Class

Most updated code is now on Github.

https://github.com/redinkdesign/Google-Weather-API