Full-service Internet Marketing & Web Development
Recent Posts

Recommended Reads
|
iContact 2.0 API Integration PHP ExampleMike Peters, December 25, 2009 -- Posted under Programming |
One of the cool features of SPI's Autoresponder service, is its ability to seamlessly integrate with third party autoresponders.
Once you designate an autoresponder as a third-party one, SPI will continue applying your predefined rules for adding/removing members from that autoresponder, while notifying the third-party service (GetResponse, iContact, AWeber) automatically.
Over the last few days, we have battling with iContact's new API. iContact announced their 2.0 API back on February 2009, but it's still labeled as "beta" and there's little to no documentation available online.
The SPI-iContact integration was using version 1.0 and our intention was to continue using that version until 2.0 is officially released.
Unfortunately, with no prior warning, version 1.0 stopped working and is no longer honoring requests to add contacts / subscriptions records.
iContact's API external login page is down. The help section is down:

And the developer forums are ghost town USA.

Ok, rant over.
We really like the folks at iContact. They offer an easy to use and inexpensive entry-level autoresponder service. I just wish they would update the API documentation.
The purpose of this post is to help others who are going through integrating with the iContact 2.0 API by providing simple PHP examples for adding a contract, subscripting a contact to a list and unsubscribing.
We haven't been able to find anything like this online, so I'm pretty sure this will help others going through the integration process.
The code below is self-contained. No need to use any other libraries.
function IContactLogin($account_id, $key, $user, $pass, &$client_folder_id)
{
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Connect to iContact to retrieve the client folder id
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract client folder id from response
$client_folder_id = "";
if (($pos=strpos($buf,"<clientFolderId>"))!==false)
{
$client_folder_id = substr($buf, strlen("<clientFolderId>")+$pos);
if (($pos=strpos($client_folder_id,"<"))!==false)
{
$client_folder_id = substr($client_folder_id, 0, $pos);
}
}
// If we have a non empty client_folder_id,
// then everything worked well
$result = ($client_folder_id+0 > 0);
// Return result
return $result;
}
function IContactSubscribe($account_id, $key, $user, $pass, $email, $list_id, &$result_str)
{
// Get client folder id
if (!IContactLogin($account_id, $key, $user, $pass, &$client_folder_id))
{
$result_str = "Failed retrieving client_folder_id for '$user'";
return 0;
}
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Find contact_id for the given 'email'
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/contacts/?email=".URLEncode($email));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract contactId from response
$contact_id = "";
if (($pos=strpos($buf,"<contactId>"))!==false)
{
$contact_id = substr($buf, $pos+strlen("<contactId>"));
if (($pos=strpos($contact_id,"<"))!==false)
{
$contact_id = substr($contact_id,0,$pos);
}
}
// If we don't have a contactId, can't add subscription
if (empty($contact_id))
{
$result_str = "Failed finding a contact with the email address of '$email'";
return 0;
}
// Build subscription record
$data = '<?xml version="1.0" encoding="UTF-8"?>'."\r\n<subscriptions>\r\n";
$data.= "<subscription>\r\n";
$data.= "<contactId>$contact_id</contactId>\r\n";
$data.= "<listId>$list_id</listId>\r\n";
$data.= "<status>normal</status>\r\n";
$data.= "</subscription>\r\n</subscriptions>";
// Add subscription
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/subscriptions/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract subscriptionID from response
$subscription_id = "";
if (($pos=strpos($buf,"<subscriptionId>"))!==false)
{
$subscription_id = substr($buf, $pos+strlen("<subscriptionId>"));
if (($pos=strpos($subscription_id,"<"))!==false)
{
$subscription_id = substr($subscription_id,0,$pos);
}
}
// If we have a subscription id OR this subscription already existed, we're good
$result = !empty($subscription_id) || strpos($buf,"could not be updated")!==false;
// Set result string
$result_str = ($result ? "Updated subscription $subscription_id" : $buf);
// Return result
return $result;
}
function IContactAddContact($account_id, $key, $user, $pass, $email, $firstname, $lastname,
&$result_str)
{
// Get client folder id
if (!IContactLogin($account_id, $key, $user, $pass, &$client_folder_id))
{
$result_str = "Failed retrieving client_folder_id for '$user'";
return 0;
}
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Build contact record
$data = '<?xml version="1.0" encoding="UTF-8"?>'."\r\n<contacts>\r\n";
$data.= "<contact>\r\n";
$data.= "<email>$email</email>\r\n";
$data.= "<firstName>$firstname</firstName>\r\n";
$data.= "<lastName>$lastname</lastName>\r\n";
$data.= "<status>normal</status>\r\n";
$data.= "</contact>\r\n</contacts>";
// Add contact
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/contacts/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract contactId from response
$contact_id = "";
if (($pos=strpos($buf,"<contactId>"))!==false)
{
$contact_id = substr($buf, $pos+strlen("<contactId>"));
if (($pos=strpos($contact_id,"<"))!==false)
{
$contact_id = substr($contact_id,0,$pos);
}
}
// If we have a contact id, we're good
$result = !empty($contact_id);
// Set result string
$result_str = ($result ? "Added new contact $contact_id" : $buf);
// Return result
return $result;
}
function IContactUnsubscribe($account_id, $key, $user, $pass, $email, $list_id, &$result_str)
{
// Get client folder id
if (!IContactLogin($account_id, $key, $user, $pass, &$client_folder_id))
{
$result_str = "Failed retrieving client_folder_id for '$user'";
return 0;
}
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Find contact_id for the given 'email'
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/contacts/?email=".URLEncode($email));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract contactId from response
$contact_id = "";
if (($pos=strpos($buf,"<contactId>"))!==false)
{
$contact_id = substr($buf, $pos+strlen("<contactId>"));
if (($pos=strpos($contact_id,"<"))!==false)
{
$contact_id = substr($contact_id,0,$pos);
}
}
// If we don't have a contactId, can't add subscription
if (empty($contact_id))
{
$result_str = "Failed finding a contact with the email address of '$email'";
return 0;
}
// Build subscription record
$data = '<?xml version="1.0" encoding="UTF-8"?>'."\r\n<subscriptions>\r\n";
$data.= "<subscription>\r\n";
$data.= "<contactId>$contact_id</contactId>\r\n";
$data.= "<listId>$list_id</listId>\r\n";
$data.= "<status>unsubscribed</status>\r\n";
$data.= "</subscription>\r\n</subscriptions>";
// Add subscription
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/subscriptions/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract subscriptionID from response
$subscription_id = "";
if (($pos=strpos($buf,"<subscriptionId>"))!==false)
{
$subscription_id = substr($buf, $pos+strlen("<subscriptionId>"));
if (($pos=strpos($subscription_id,"<"))!==false)
{
$subscription_id = substr($subscription_id,0,$pos);
}
}
// If we have a subscription id OR this subscription already unsubscribed, we're good
$result = !empty($subscription_id) || strpos($buf,"could not be updated")!==false;
// Set result string
$result_str = ($result ? "Updated subscription $subscription_id" : $buf);
// Return result
return $result;
}
View 5 Comment(s)
Once you designate an autoresponder as a third-party one, SPI will continue applying your predefined rules for adding/removing members from that autoresponder, while notifying the third-party service (GetResponse, iContact, AWeber) automatically.
Over the last few days, we have battling with iContact's new API. iContact announced their 2.0 API back on February 2009, but it's still labeled as "beta" and there's little to no documentation available online.
The SPI-iContact integration was using version 1.0 and our intention was to continue using that version until 2.0 is officially released.
Unfortunately, with no prior warning, version 1.0 stopped working and is no longer honoring requests to add contacts / subscriptions records.
iContact's API external login page is down. The help section is down:

And the developer forums are ghost town USA.

Ok, rant over.
We really like the folks at iContact. They offer an easy to use and inexpensive entry-level autoresponder service. I just wish they would update the API documentation.
The purpose of this post is to help others who are going through integrating with the iContact 2.0 API by providing simple PHP examples for adding a contract, subscripting a contact to a list and unsubscribing.
We haven't been able to find anything like this online, so I'm pretty sure this will help others going through the integration process.
The code below is self-contained. No need to use any other libraries.
function IContactLogin($account_id, $key, $user, $pass, &$client_folder_id)
{
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Connect to iContact to retrieve the client folder id
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract client folder id from response
$client_folder_id = "";
if (($pos=strpos($buf,"<clientFolderId>"))!==false)
{
$client_folder_id = substr($buf, strlen("<clientFolderId>")+$pos);
if (($pos=strpos($client_folder_id,"<"))!==false)
{
$client_folder_id = substr($client_folder_id, 0, $pos);
}
}
// If we have a non empty client_folder_id,
// then everything worked well
$result = ($client_folder_id+0 > 0);
// Return result
return $result;
}
function IContactSubscribe($account_id, $key, $user, $pass, $email, $list_id, &$result_str)
{
// Get client folder id
if (!IContactLogin($account_id, $key, $user, $pass, &$client_folder_id))
{
$result_str = "Failed retrieving client_folder_id for '$user'";
return 0;
}
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Find contact_id for the given 'email'
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/contacts/?email=".URLEncode($email));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract contactId from response
$contact_id = "";
if (($pos=strpos($buf,"<contactId>"))!==false)
{
$contact_id = substr($buf, $pos+strlen("<contactId>"));
if (($pos=strpos($contact_id,"<"))!==false)
{
$contact_id = substr($contact_id,0,$pos);
}
}
// If we don't have a contactId, can't add subscription
if (empty($contact_id))
{
$result_str = "Failed finding a contact with the email address of '$email'";
return 0;
}
// Build subscription record
$data = '<?xml version="1.0" encoding="UTF-8"?>'."\r\n<subscriptions>\r\n";
$data.= "<subscription>\r\n";
$data.= "<contactId>$contact_id</contactId>\r\n";
$data.= "<listId>$list_id</listId>\r\n";
$data.= "<status>normal</status>\r\n";
$data.= "</subscription>\r\n</subscriptions>";
// Add subscription
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/subscriptions/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract subscriptionID from response
$subscription_id = "";
if (($pos=strpos($buf,"<subscriptionId>"))!==false)
{
$subscription_id = substr($buf, $pos+strlen("<subscriptionId>"));
if (($pos=strpos($subscription_id,"<"))!==false)
{
$subscription_id = substr($subscription_id,0,$pos);
}
}
// If we have a subscription id OR this subscription already existed, we're good
$result = !empty($subscription_id) || strpos($buf,"could not be updated")!==false;
// Set result string
$result_str = ($result ? "Updated subscription $subscription_id" : $buf);
// Return result
return $result;
}
function IContactAddContact($account_id, $key, $user, $pass, $email, $firstname, $lastname,
&$result_str)
{
// Get client folder id
if (!IContactLogin($account_id, $key, $user, $pass, &$client_folder_id))
{
$result_str = "Failed retrieving client_folder_id for '$user'";
return 0;
}
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Build contact record
$data = '<?xml version="1.0" encoding="UTF-8"?>'."\r\n<contacts>\r\n";
$data.= "<contact>\r\n";
$data.= "<email>$email</email>\r\n";
$data.= "<firstName>$firstname</firstName>\r\n";
$data.= "<lastName>$lastname</lastName>\r\n";
$data.= "<status>normal</status>\r\n";
$data.= "</contact>\r\n</contacts>";
// Add contact
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/contacts/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract contactId from response
$contact_id = "";
if (($pos=strpos($buf,"<contactId>"))!==false)
{
$contact_id = substr($buf, $pos+strlen("<contactId>"));
if (($pos=strpos($contact_id,"<"))!==false)
{
$contact_id = substr($contact_id,0,$pos);
}
}
// If we have a contact id, we're good
$result = !empty($contact_id);
// Set result string
$result_str = ($result ? "Added new contact $contact_id" : $buf);
// Return result
return $result;
}
function IContactUnsubscribe($account_id, $key, $user, $pass, $email, $list_id, &$result_str)
{
// Get client folder id
if (!IContactLogin($account_id, $key, $user, $pass, &$client_folder_id))
{
$result_str = "Failed retrieving client_folder_id for '$user'";
return 0;
}
// Build iContact authentication
$headers = array(
'Accept: text/xml',
'Content-Type: text/xml',
'Api-Version: 2.0',
'Api-AppId: ' . $key,
'Api-Username: ' . $user,
'Api-Password: ' . $pass
);
// Find contact_id for the given 'email'
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/contacts/?email=".URLEncode($email));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract contactId from response
$contact_id = "";
if (($pos=strpos($buf,"<contactId>"))!==false)
{
$contact_id = substr($buf, $pos+strlen("<contactId>"));
if (($pos=strpos($contact_id,"<"))!==false)
{
$contact_id = substr($contact_id,0,$pos);
}
}
// If we don't have a contactId, can't add subscription
if (empty($contact_id))
{
$result_str = "Failed finding a contact with the email address of '$email'";
return 0;
}
// Build subscription record
$data = '<?xml version="1.0" encoding="UTF-8"?>'."\r\n<subscriptions>\r\n";
$data.= "<subscription>\r\n";
$data.= "<contactId>$contact_id</contactId>\r\n";
$data.= "<listId>$list_id</listId>\r\n";
$data.= "<status>unsubscribed</status>\r\n";
$data.= "</subscription>\r\n</subscriptions>";
// Add subscription
$ch=curl_init("https://app.icontact.com/icp/a/$account_id/c/$client_folder_id/subscriptions/");
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$buf = curl_exec($ch);
curl_close($ch);
// Extract subscriptionID from response
$subscription_id = "";
if (($pos=strpos($buf,"<subscriptionId>"))!==false)
{
$subscription_id = substr($buf, $pos+strlen("<subscriptionId>"));
if (($pos=strpos($subscription_id,"<"))!==false)
{
$subscription_id = substr($subscription_id,0,$pos);
}
}
// If we have a subscription id OR this subscription already unsubscribed, we're good
$result = !empty($subscription_id) || strpos($buf,"could not be updated")!==false;
// Set result string
$result_str = ($result ? "Updated subscription $subscription_id" : $buf);
// Return result
return $result;
}
View 5 Comment(s)
|
How to run SSH on a different portMike Peters, December 21, 2009 -- Posted under Programming |
By default SSH runs on port 22. One of the easiest ways to make it more difficult for hackers to run dictionary attacks on your server, attempting to brute-force ssh-login, is to run ssh on a different port.
Open up /etc/ssh/sshd_config and add this line at the top:
Replace 1234 with the port number you'd like to run ssh on. If there is already a Port line in your sshd_config file, comment it with a #
Now, Restart sshd by issuing:
Test ssh works properly by connecting locally (replace 1234 with the new port number):
View 1 Comment(s)
Open up /etc/ssh/sshd_config and add this line at the top:
Port 1234
Replace 1234 with the port number you'd like to run ssh on. If there is already a Port line in your sshd_config file, comment it with a #
Now, Restart sshd by issuing:
/etc/rc.d/init.d/sshd restart
Test ssh works properly by connecting locally (replace 1234 with the new port number):
ssh -l USERNAME localhost -p 1234
View 1 Comment(s)
|
MapReduce 101: Parallel ProgrammingMike Peters, December 21, 2009 -- Posted under Programming |
Two of the best video tutorials explaining the basics of MapReduce.
If you're used to MySQL, you can think of "Map" as several queries running "SELECT" where data is extracted from multiple tables. And you can think of "Reduce" as the "GROUP BY" operator where data is joined together to form a short result.
For more information about MapReduce, see Google's Introduction to Parallel Programming and MapReduce
If you're used to MySQL, you can think of "Map" as several queries running "SELECT" where data is extracted from multiple tables. And you can think of "Reduce" as the "GROUP BY" operator where data is joined together to form a short result.
For more information about MapReduce, see Google's Introduction to Parallel Programming and MapReduce
|
Never use VARCHAR in MySQLMike Peters, December 15, 2009 -- Posted under Programming |
When using strings in MySQL, you have a choice between several field types:
* VARCHAR: String of 0..65,535 characters with an extra byte to designate the length.
* CHAR: String of a fixed length of 0 to 255 characters.
* TEXT / BIGTEXT: String of 1..4GB characters with an extra byte to designate the length. Unlike VARCHAR, text fields cannot be compared, can't "order by", can't use "like" on them etc.
* BLOB: For binary or unicode strings. Supports 8bits per character and unlike the previous types, the string is not converted to a character-set prior to insertion.
Quick tip: Never ever use varchar.
Replace VARCHAR with CHAR whenever possible, or use TEXT if your strings are longer than 255 characters.
While VARCHAR offers slightly improved storage space (it will only take up n+1 characters on disk), there are a few major pitfalls with this variable type:
#1. If you have any field in the table that is a CHAR (fixed size) field, all VARCHARs will be stored as n+1, so you don't get any reduced storage benefits.
#2. If you're using MySQL replication, you may end up with a few nasty "master/slave table column type mismatch" errors - this is a big one
#3. CHAR fields offer better performance over VARCHAR
-
We first learned about the issues with VARCHAR when replication began breaking with these cryptic messages:
"Table definition on master and slave does not match: Column 37 size mismatch - master has size 255, testtable on slave has size 256. Master's column size should be <= the slave's column size."
Both tables were identical and mysqldump'ing one on top of the other didn't help. It seems to be rooted to MySQL's internal implementation of VARCHAR.
To convert all table VARCHAR fields to CHAR, use this PHP snippet:
View 1 Comment(s)
* VARCHAR: String of 0..65,535 characters with an extra byte to designate the length.
* CHAR: String of a fixed length of 0 to 255 characters.
* TEXT / BIGTEXT: String of 1..4GB characters with an extra byte to designate the length. Unlike VARCHAR, text fields cannot be compared, can't "order by", can't use "like" on them etc.
* BLOB: For binary or unicode strings. Supports 8bits per character and unlike the previous types, the string is not converted to a character-set prior to insertion.
Quick tip: Never ever use varchar.
Replace VARCHAR with CHAR whenever possible, or use TEXT if your strings are longer than 255 characters.
While VARCHAR offers slightly improved storage space (it will only take up n+1 characters on disk), there are a few major pitfalls with this variable type:
#1. If you have any field in the table that is a CHAR (fixed size) field, all VARCHARs will be stored as n+1, so you don't get any reduced storage benefits.
#2. If you're using MySQL replication, you may end up with a few nasty "master/slave table column type mismatch" errors - this is a big one
#3. CHAR fields offer better performance over VARCHAR
-
We first learned about the issues with VARCHAR when replication began breaking with these cryptic messages:
"Table definition on master and slave does not match: Column 37 size mismatch - master has size 255, testtable on slave has size 256. Master's column size should be <= the slave's column size."
Both tables were identical and mysqldump'ing one on top of the other didn't help. It seems to be rooted to MySQL's internal implementation of VARCHAR.
To convert all table VARCHAR fields to CHAR, use this PHP snippet:
@mysql_query("SET GLOBAL WAIT_TIMEOUT = 600000");
@mysql_Query("SET WAIT_TIMEOUT = 600000");
$Result1 = @mysql_query("SHOW TABLES FROM $DBNAME");
$cnt1 = @mysql_num_rows($Result1);
while ($cnt1)
{
$cnt1--;
if (!($Row1 = @mysql_fetch_array($Result1)))
{
continue;
}
$tablename = $Row1[0];
$Result3 = @mysql_query("DESCRIBE $tablename");
$cnt3 = @mysql_num_rows($Result3);
while ($cnt3)
{
$cnt3--;
if (!($Row3 = @mysql_fetch_array($Result3))) continue;
if (strpos($Row3['Type'],'varchar')!==false)
{
$pos = strpos($Row3['Type'],'(');
$length = substr($Row3['Type'],$pos+1);
$pos = strpos($length,')');
$length = substr($length, 0, $pos);
// If length is over 255, nothing we can do about it
if ($length>255) continue;
if ($length<1) continue;
// Length is below 255, we can convert
$sql = "alter table $tablename change ".$Row3['Field']." ".$Row3['Field']." char($length) default '' not null";
@mysql_query($sql);
}
}
}
@mysql_Query("SET WAIT_TIMEOUT = 600000");
$Result1 = @mysql_query("SHOW TABLES FROM $DBNAME");
$cnt1 = @mysql_num_rows($Result1);
while ($cnt1)
{
$cnt1--;
if (!($Row1 = @mysql_fetch_array($Result1)))
{
continue;
}
$tablename = $Row1[0];
$Result3 = @mysql_query("DESCRIBE $tablename");
$cnt3 = @mysql_num_rows($Result3);
while ($cnt3)
{
$cnt3--;
if (!($Row3 = @mysql_fetch_array($Result3))) continue;
if (strpos($Row3['Type'],'varchar')!==false)
{
$pos = strpos($Row3['Type'],'(');
$length = substr($Row3['Type'],$pos+1);
$pos = strpos($length,')');
$length = substr($length, 0, $pos);
// If length is over 255, nothing we can do about it
if ($length>255) continue;
if ($length<1) continue;
// Length is below 255, we can convert
$sql = "alter table $tablename change ".$Row3['Field']." ".$Row3['Field']." char($length) default '' not null";
@mysql_query($sql);
}
}
}
View 1 Comment(s)
|
How to Flush DNSMike Peters, December 6, 2009 -- Posted under Programming |
At times you want to force your machine to "forget" (flush) all DNS entries, so that it reloads any recent changes.
This is useful when you've recently updated the ip-address of a domain and want your machine to see the new changes, without relying on any previously stored cache.
Here's how to flush your DNS cache:
Windows
Open a CMD dos box and run:
ipconfig /flushdns
If you get an error about "this requires elevation", do this: Click on the Windows Start button, type "cmd", then right click on the program and select "Run as Administrator"
Mac
In Mac OSX Leopard, you can use the command
dscacheutil to flush the DNS resolver cache:
dscacheutil -flushcache
In Mac OSX versions 10.5.1 and before, the command lookupd -flushcache performed the same task:
lookupd -flushcache
Linux
In Linux, the nscd daemon manages the DNS cache.
To flush the DNS cache, restart the nscd daemon.
To restart the nscd daemon, use the command:
/etc/init.d/nscd restart
This is useful when you've recently updated the ip-address of a domain and want your machine to see the new changes, without relying on any previously stored cache.
Here's how to flush your DNS cache:
Windows
Open a CMD dos box and run:
ipconfig /flushdns
If you get an error about "this requires elevation", do this: Click on the Windows Start button, type "cmd", then right click on the program and select "Run as Administrator"
Mac
In Mac OSX Leopard, you can use the command
dscacheutil to flush the DNS resolver cache:
dscacheutil -flushcache
In Mac OSX versions 10.5.1 and before, the command lookupd -flushcache performed the same task:
lookupd -flushcache
Linux
In Linux, the nscd daemon manages the DNS cache.
To flush the DNS cache, restart the nscd daemon.
To restart the nscd daemon, use the command:
/etc/init.d/nscd restart
|
Mercurial Hook: Forbid 2 headsMike Peters, December 6, 2009 -- Posted under Programming |
The Mercurial version control system offers a powerful mechanism to let you perform automated actions in response to events that occur in a repository. In some cases, you can even control Mercurial's response to those events. No comments
The name Mercurial uses for one of these actions is a hook (aka trigger).
One of the most popular hooks is the ability to reject multiple heads.
What are multiple heads?
Multiple heads are a case where the code repository contains changes from two (or more) developers, that haven't been merged. These changes may potentially overlap and break the application flow.
Having multiple heads occurs in two cases:
* As part of a merge (generally temporary)
* Bugfix versions (generally permanent)
At any time, you can view the head(s) of a repository with the command: hg heads
Pitfalls to watch-for with Multiple Heads
Consider the following scenario:
* It's Monday morning. Bob and Linda each pull a copy of the repository to their local Mercurial HG.
* Bob makes a few changes to index.php and pushes them to the server Monday afternoon.
* Linda makes a few additional changes to index.php and pushes them to the server on Tuesday morning.
Since Linda doesn't have the most recent copy of index.php (with Bob's changes), her copy of index.php will create a second "head" to the project.
There are now two development branches - the one with Bob's changes and the one with Linda's changes, neither one having all changes in place.
While TortoiseHG will often block Linda's push with a warning that it will create multiple heads, it is possible to push with -f (force). Some Mercurial plugins automatically push with -f, creating the second head on the live server repository.
Yes you can run 'hg merge' on the live server to merge Bob and Linda's changes (the two heads) locally, but you never ever want to run hg merge on a live server.
Running 'hg merge', adds "<<<<< local >>>>> other" comments into the source files, as part of its file-diff process. This temporarily breaks the syntax of your source files for the duration of the merge.
If this is a live server, your scripts will all STOP WORKING until the merge is complete and the "<<<<< local >>>>> other" notations are removed from the source files.
Rejecting Multiple heads - Do the merge offline
Instead of allowing multiple heads on the live server, a much better approach is to reject multiple heads, forcing the engineers to merge changes locally, before pushing any change that will create a second head.
In the example above, when Linda attempts to push the changes on Tuesday, her push will be rejected with an error message of "Trying to push more than one head, try run hg merge before it."
Linda then has to pull the recent changes, retrieving Bob's additions, merge the changes locally and then push a version that incorporates both her changes and Bob's into the server.
The benefits of this approach are:
#1. Server scripts are never rendered inactive by a local 'hg merge' run
#2. Engineers always need to retrieve (and test) the latest version, prior to being able to upload changes that may conflict with others.
#3. The live server always has a single development graph, containing the most recent changes.
Installing NetBeans Forbid2Heads Mercurial hook
Step 1: Locate your HG extensions folder
The folder name is 'hgext' and it is typically installed under Mercurial's home folder. For example:
/usr/home/mercurial/work/mercurial-1.3.1/hgext
Step 2: Install forbid_2heads Python script
Create a new folder named 'forbid_2head' under 'hgext' and save this file under /hgext/forbid_2head/forbid_2head.py:
# win32text.py - LF <-> CRLF translation utilities for Windows users
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
#
# To forbid pushes which creates two or more headss
#
# [hooks]
# pretxnchangegroup.forbid_2heads = python:forbid2_head.forbid_2heads
from mercurial import ui
from mercurial.i18n import gettext as _
def forbid_2heads(ui, repo, hooktype, node, **kwargs):
if len(repo.heads()) > 1:
ui.warn(_('Trying to push more than one head, try run "hg merge" before it.n'))
return True
Step 3: Add hook to Mercurial config file
Update your Mercurial hgweb.config, adding the new hook under the 'hooks' section, like this:
[hooks]
pretxnchangegroup.forbid_2heads = python:forbid_2head.forbid_2heads
--
Attempting to push changes to the repository that will create multiple heads is now blocked.
Users will be presented with an error message and the transaction rolled back, until the user merges changes locally, so that no multiple heads are created:
--
The live server repository will always have a single development branch. Engineers will be forced to merge changes locally, before new changes can be pushed:

View 1 Comment(s)
The name Mercurial uses for one of these actions is a hook (aka trigger).
One of the most popular hooks is the ability to reject multiple heads.
What are multiple heads?
Multiple heads are a case where the code repository contains changes from two (or more) developers, that haven't been merged. These changes may potentially overlap and break the application flow.
Having multiple heads occurs in two cases:
* As part of a merge (generally temporary)
* Bugfix versions (generally permanent)
At any time, you can view the head(s) of a repository with the command: hg heads
Pitfalls to watch-for with Multiple Heads
Consider the following scenario:
* It's Monday morning. Bob and Linda each pull a copy of the repository to their local Mercurial HG.
* Bob makes a few changes to index.php and pushes them to the server Monday afternoon.
* Linda makes a few additional changes to index.php and pushes them to the server on Tuesday morning.
Since Linda doesn't have the most recent copy of index.php (with Bob's changes), her copy of index.php will create a second "head" to the project.
There are now two development branches - the one with Bob's changes and the one with Linda's changes, neither one having all changes in place.
While TortoiseHG will often block Linda's push with a warning that it will create multiple heads, it is possible to push with -f (force). Some Mercurial plugins automatically push with -f, creating the second head on the live server repository.
Yes you can run 'hg merge' on the live server to merge Bob and Linda's changes (the two heads) locally, but you never ever want to run hg merge on a live server.
Running 'hg merge', adds "<<<<< local >>>>> other" comments into the source files, as part of its file-diff process. This temporarily breaks the syntax of your source files for the duration of the merge.
If this is a live server, your scripts will all STOP WORKING until the merge is complete and the "<<<<< local >>>>> other" notations are removed from the source files.
Rejecting Multiple heads - Do the merge offline
Instead of allowing multiple heads on the live server, a much better approach is to reject multiple heads, forcing the engineers to merge changes locally, before pushing any change that will create a second head.
In the example above, when Linda attempts to push the changes on Tuesday, her push will be rejected with an error message of "Trying to push more than one head, try run hg merge before it."
Linda then has to pull the recent changes, retrieving Bob's additions, merge the changes locally and then push a version that incorporates both her changes and Bob's into the server.
The benefits of this approach are:
#1. Server scripts are never rendered inactive by a local 'hg merge' run
#2. Engineers always need to retrieve (and test) the latest version, prior to being able to upload changes that may conflict with others.
#3. The live server always has a single development graph, containing the most recent changes.
Installing NetBeans Forbid2Heads Mercurial hook
Step 1: Locate your HG extensions folder
The folder name is 'hgext' and it is typically installed under Mercurial's home folder. For example:
/usr/home/mercurial/work/mercurial-1.3.1/hgext
Step 2: Install forbid_2heads Python script
Create a new folder named 'forbid_2head' under 'hgext' and save this file under /hgext/forbid_2head/forbid_2head.py:
# win32text.py - LF <-> CRLF translation utilities for Windows users
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
#
# To forbid pushes which creates two or more headss
#
# [hooks]
# pretxnchangegroup.forbid_2heads = python:forbid2_head.forbid_2heads
from mercurial import ui
from mercurial.i18n import gettext as _
def forbid_2heads(ui, repo, hooktype, node, **kwargs):
if len(repo.heads()) > 1:
ui.warn(_('Trying to push more than one head, try run "hg merge" before it.n'))
return True
Step 3: Add hook to Mercurial config file
Update your Mercurial hgweb.config, adding the new hook under the 'hooks' section, like this:
[hooks]
pretxnchangegroup.forbid_2heads = python:forbid_2head.forbid_2heads
--
Attempting to push changes to the repository that will create multiple heads is now blocked.
Users will be presented with an error message and the transaction rolled back, until the user merges changes locally, so that no multiple heads are created:
Quote:
|
pushing to http://mydomain.com/ searching for changes 4 changesets found adding changesets adding manifests adding file changes added 4 changesets with 2 changes to 7 files (+1 heads) Trying to push more than one head, try run "hg merge" before it. transaction abort! rollback completed abort: pretxnchangegroup.forbid_2heads hook failed [command returned code 1] |
--
The live server repository will always have a single development branch. Engineers will be forced to merge changes locally, before new changes can be pushed:

View 1 Comment(s)
| « Previous Posts | » Next Posts |
