≡ Menu

We recently migrated a client from SugarCRM to a lighter-weight, custom built, Drupal 7 CRM solution that more tightly met their needs. (The decision making process of moving away from SugarCRM as well as the migration/implmentation could be it’s own multi-part blog post).

Our client has several landing pages they use for lead generation and had previously used SugarCRM’s SOAP and REST APIs to capture this information directly in SugarCRM as a lead. Naturally they wanted similar functionality when moving to Drupal. We had always heard of the power of the Services module, but never had the chance to work with it in production. This was our chance!

The documentation for the services module was good, but didn’t really provide many code examples that could be used by someone looking for a quick start. I began poking around and quickly fell in love with the REST Server provided by the services module and decided it was the way to go.

With a bit of guidance from kylebrowning, I wrote a simple class to make our implementation a bit simpler by abstracting some of the details such as session tracking. I wrote DrupalREST.PHP and threw it on github.

After creating a content type in our Drupal installation with some CCK fields, we used the 19 lines of code below to create the node/lead.

$api = new DrupalRESTWebServices('http://url/to/your/endpoint', 'username', 'password', FALSE);
$node = array (
	'name' => 'creator',
	'status' => 1,
	'type' => 'content_type',
	'field_status' => array('und' =>array('key' => '1')),
	'field_first_name' => array('und' => array(array('value' => $form_values['submitted']['first_name']['#value']))),
	'field_last_name' => array('und' => array(array('value' => $form_values['submitted']['last_name']['#value']))),
	'field_phone_number' => array('und' => array(array('value' => $form_values['submitted']['phone']['#value']))),
	'field_address' => array('und' => array(array('value' => $form_values['submitted']['address']['#value']))),
	'field_city' => array('und' => array(array('value' => $form_values['submitted']['city']['#value']))),
	'field_state' => array('und' => array(array('value' => $form_values['submitted']['state']['#value']))),
	'field_zip' => array('und' => array(array('value' => $form_values['submitted']['zip']['#value']))),
	'field_email' => array('und' => array(array('value' => $form_values['submitted']['email']['#value']))),
	'field_lead_source' => array('und' => array(array('value' => $form_values['submitted']['source']['#value']))),
	'field_google_keyword' => array('und' => array(array('value' => $form_values['submitted']['keyword']['#value']))),
);
$api->login();
$api->createNode(array('node' => $node));

Let me know if this helped you get started with Drupal Services Module & the REST Server by leaving a comment!

NOTE: I ported this to C# as DrupalREST.NET.

{ 3 comments }

I was trying to automate the process of creating parked domains on a cPanel server today and found myself playing around with Park module of the cPanel API via the amazing cPanel-XML-API-PHP.

This code was really simple and effective in parking a domain

<!--?php
//Do a little setup...
$serverIP = '<IP of cPanel server-->';
$user = '';
$hash = '';
$cPaneluser = '';
$domain = '';
 
//Make sure we load the PHP Client Library for cPanel's XMLAPI
require_once('cPanel-XML-API-PHP/xmlapi.php');
 
// Create an instance of the XMLAPI, and Authenticate using our username and hash
$xmlapi = new xmlapi('$serverIP');
$xmlapi->hash_auth($user, $hash);
 
//Send park request
$xmlapi->park($cPaneluser, $domain, '');
?>

Things were moving along smoothly until I attempted to unpark a domain via the cPanel API. I got an error when trying to unpark the domain:

"Error from park wrapper: Sorry, you do not control the domain"

After reading the API1 and API2 documentation for the park method and checking out xmlapi.php, I realized it was just a minor bug in how the API was being called by the unpark() function. The function that prepares the call for API2, api2_query(), expects an associative array; while the function prepping for API1, api1_query(), does not and unpark was sending an associative array to api1_query(). After making a minor change to the unpark() function on line 2076 of xmlapi.php from

 return $this->api1_query($username, 'Park', 'unpark', $args);

to

 return $this->api2_query($username, 'Park', 'unpark', $args);

I was able to successfully unpark a domain with this code.

<!--?php
//Do a little setup...
$serverIP = '<IP of cPanel server-->';
$user = '';
$hash = '';
$cPaneluser = '';
$domain = '';
 
//Make sure we load the PHP Client Library for cPanel's XMLAPI
require_once('cPanel-XML-API-PHP/xmlapi.php');
 
// Create an instance of the XMLAPI, and Authenticate using our username and hash
$xmlapi = new xmlapi('$serverIP');
$xmlapi->hash_auth($user, $hash);
 
//Send park request
$xmlapi->unpark($cPaneluser, $domain, '');
?>

I forked the project on github and committed my minor change; however, I’m not certain if MattDees is activly maintaining the project on github 🙁

If this helped you, let me know with a quick comment!

{ 3 comments }

Some more awesome news on the Government/Drupal front!

WhiteHouse.gov announced this Friday additional contributions to Drupal. This Follows an earlier contribution in April 2010, and covers the contribution of IMCE Tools which extends the functionality of IMCE.

Key main features of IMCE Tools covered in the announcement

  1. IMCE Directory Manager – Specify user level permissions on IMCE directories via a GUI
  2. IMCE Search – Easily find upload files
  3. IMCE File Path – Display the URL of a file

While IMCE Tools was the main focus of press release, shoutouts were also given to:Open Atrium, Spaces, Boxes, Context, StrongArm, Admin, Calais, IMCE SWFupload, shortURL.

{ 0 comments }

During a recent attempt to install a .NET application, I encountered a horrible thing … The installation was bombing out without a good, descriptive error. After banging my head against and doing some amazing google/bing ninja moves, I discovered that this was in fact a ‘feature’ and not a bug related to the backwards compatibility of .NET 4.0 and there not being any other version of the .NET Framework installed.

.NET 4.0 Framework is not natively backward compatible with applications written for previous version of the .NET Framework. So if you’re a developer and are looking for a quick fix, try adding this line to the application config file.

<SUPPORTEDRUNTIME version="v4.0.x" />

If you’d like to learn more about .NET Framework comparability, check out these links to the MSDN Library: Version Compatibility in the .NET Framework & .NET Framework 4 RTM Application Compatibility Walkthrough/

{ 1 comment }

To boot a VirtualBox virtual machine from a USB device, we must first create a disk that maps to the USB drive with the command below

VBoxManage.exe internalcommands createrawvmdk -filename C:\path\to\your\HardDisks\USB.vmdk -rawdisk \\.\PhysicalDrive1 -register

Note: Use Disk Management or Diskpart to determind the number to follow “PhysicalDrive”

Now we can simply make this drive the “IDE Primary Master” under storage, and boot from the USB Device.

Thanks goes out to this blog post for providing the solution!

{ 0 comments }

I recently ran out of space on a vdi and was surprised to learn there is no “Expand” or “Resize” option within Virtual Media Manager or VBoxManage.

My initial search didn’t turn up any quick and easy solutions, so I kept searching. I finally came across this blog post which uses gparted and diskpart to get the job done on Windows based guests as well as provides the steps to accomplish the same on Linux Guests.

Something worth noting is that earlier versions of Windows don’t allow you to extend system or boot volumes, so a 3rd Party utility may be necessary if diskpart throws the error below. I ended up using EASEUS Partition Master since gparted didn’t properly expand the volume.

Diskpart failed to extend the volume. Please make sure the volume is valid for extending

{ 1 comment }

Looking for quick way to reuse a vdi I preped with Windows XP SP3 and recent updates, I decided to duplicate the vdi with Windows Explorer. Of course, this was too simple. After attempting to create a new virtual machine which used the newly created vdi, I was presented with a nasty error along the lines of  “Failed to open the hard disk ….” and “Cannot register the hard disk C:\path\to\new\vdi with UUID {xxxx} because a hard disk C:\path\to\old\vdi already exists in the media registry (C:\path to VirtualBox.xml)

VirtualBox UUID Error

Luckily there is a simple fix for this…

Lauch a command prompt and browse to where VBoxManager.exe is located (for me this is C:\Program Files\Oracle\VirtualBox) and run the comand below

VBoxManage.exe internalcommands setvdiuuid "C:\path\to\new\vdi"

NOTE: the command has been changed to sethduuid in version 4.0.4 (thanks Isaac!). So the commad in 4.0.4 would be…

VBoxManage.exe internalcommands sethduuid "C:\path\to\new\vdi"

Which should return something similar to “UUID Changed to …”

Change UUID with VBoxManager

{ 52 comments }

I’ve run a problem several times when .gitignore doesn’t appear to be working. The file I want to ignore is specified in .gitignore, but it always comes up as an unstaged change 🙁 . I always end up searching the internet for the resolution, because I can never seem to remember it. Well today, I’m making a blog post about it, so it will be easier for me to find and hopefully help others with the same problem!

What I have discovered is that at some point in time, I mistakenly added the files that I now want to ignore to my repository. For me, this is typically a .dll in a .NET project that’s recreated on every build anyway. Since git ‘knows’ about the file, it can’t ignore it. So the resolution is simple, remove everything from git’s index and add it back which can be done with the commands below…

git rm -r --cached .

git add .

git commit -m "fixing .gitignore"

June 13, 2014 EDIT:
To just target the files listed in the .gitignore, use the command below!

for file in `cat .gitignore`  ; do git rm -r --cached $file; done
{ 31 comments }

Today while in the Twitterverse I followed a link @TheEllenShow posted which was supposed to take me to a video…

110011143641-Twitter-Ellen-DeGeneres-I-had-to-get

The link: http://su.pr/1vsyp6 which redirects to a stumbleupon URL which is supposed to be a video of Matthew Broderick trying to guess Ellen’s Mystery Word

To my Surprise I received this error message

Reported-Unsafe-Website-Navigation-Blocked-Windows-Internet-Explorer

Followed by Microsoft Security Essentials saying I needed to “Clean” my computer 🙁

Following the link again did not come back with any error at all. Has anyone seen this before? What can cause this type of behavior?

{ 1 comment }

I did a Google search today and was surprised to see a new section entitled “Latest Results”.

Real time search by Google? Brilliant!

They are currently aggregating live streams from Twitter, Facebook, MySpace, FriendFeed, Jaiku and Identi.ca which brings Google one-step closer to their goal of creating “the most comprehensive, relevant and fast search in the world”

Check out the post on the the Official Google Blog, or watch the video below

{ 0 comments }