All Posts in The Programming Mechanism

July 27, 2012 - Comments Off on Drupal commerce, rules, and beyond

Drupal commerce, rules, and beyond

When creating and processing orders with Commerce in Drupal, you will sometimes need to modify or manipulate the data at some stage in the checkout processes. As we've seen in earlier posts you can easily create or modify rules in Drupal to do just about anything. [hook_coffee_init() anyone?]. If you need to manipulate the current order, create a new rule and set it to React on the Completing checkout process event. Now  you will have access to the $commerce_order object and all of its data goodness.

Now that the Commerce Order is in scope we can access it and perform and action on it. We won't worry about setting any conditions for the rule for this example. Create a new Action for the rule and set it to Execute custom PHP code. You will now see that the $commerce_order object is available. You can start manipulating it with code right there in the PHP code block, or pass the $commerce_order object to a custom module. This is the way we chose to do it. Since we needed to do some extra stuff with the data such as compare some of its values with an external database application it seemed to make more sense to create a module with that code and have it accept the $commerce_order object. We were then able to do better and quicker coding and version control with code kept in external files out side of the rule.

To call your function, just enter it in the PHP code block and include the opening and closing PHP tags as per the instructions:

<?php mechmod($commerce_order); ?>

Here is a quick module sample that can accept the $commerce_order object and do something with it. Create these two files in /sites/all/modules/mechmod

mechmod.info

name = Mechanism Commerce Infandibulator
description = Do stuff with a $commerce_order object
core = 7.x
version 0.0.1beta

mechmod.module

<?php
/**
 * function to do stuff with $commerce_order object
 */
function mechmod_commerce_object_raw($commerce_order) {
  if(module_exists('devel')) { // If devel module is enabled, use dpm()
    dpm($commerce_order);
  }
  else {  // otherwise, just print it out
    print('<pre>'.print_r($commerce_order,TRUE).'</pre>');
  }
}
?>

Now enable the module:

% drush en mechmod --yes

(--yes will automatically choose 'yes' for you instead of being prompted by drush to confirm)

Now, whenever you complete the checkout process the whole $commerce_object will be printed out. This isn't terribly pretty, but it is the beginning of being able to see and then access the data in the order and from there to manipulate it.

Happy coding,

Published by: chazcheadle in The Programming Mechanism
Tags: , , ,

July 25, 2012 - Comments Off on Drush de Jour: Module machinations

Drush de Jour: Module machinations

If you are running one Drupal site, or multiple sites, or multiple muiti-site installations, you will at some point be wrangling modules. Drush can help you list, install, and uninstall modules.

% drush pml

If you want to see a specific module you can filter the result through grep.

%drush pml | grep <pattern>

e.g., %drush pml | grep token will show you modules with 'token' in the name.

 

You can also install, enable and disable modules if you need to debug or test.

Download a module:

%drush dl <module name>

 

Enable the module:

%drush en <module name>

 

Disable the module

%drush dis <module name>

 

If you need to fully uninstall a module, which will also run any hook_uninstall() in a module which can be handy for debugging.

Uninstall the module:

%drush pm-uninstall <module name>

 

You can script these commands as well with aliases, allowing you to enable/disable modules across multiple sites.

The Drush de Jour brings you the latest and possibly greatest bits of drush wisdom we use for developing and maintaining our Drupal sites.

Published by: chazcheadle in The Programming Mechanism
Tags:

July 20, 2012 - Comments Off on Drupal: Connecting to multiple databases in your module

Drupal: Connecting to multiple databases in your module

Have you ever needed your Drupal site to access data from a different database? from a different Drupal installation? The Drupal database abstraction layer has the ability to switch between databases! We are working on a project that requires a Drupal website to talk to another database on the same server. Updates to the website must also be made to the other database, in this case a membership directory.

By setting up multiple databases in the settings.php file for your site, you can load up your data from the website, then switch to the second database to make updates. If you are doing data migration from one Drupal site to another, this can be very handy.

First, in settings.php expand the default database declaration array:

[cce_php]
$databases['default']['default'] = array(
      'database' => 'drupalsite',
      'username' => 'drupalsite_user',
      'password' => 'drupalsite_pass',
      'host' => 'localhost',
      'port' => '3306',
      'driver' => 'mysql',
      'prefix' => '',
    );
$databases['external']['default'] = array(
      'database' => 'groucho_db',
      'username' => 'groucho',
      'password' => 'groucho',
      'host' => 'localhost',
      'port' => '3306',
      'driver' => 'mysql',
      'prefix' => '',
    );

Then in your new module you can load your data:

/**
 * Implements hook_user_save().
 */
function awesome_user_save(&$user) {
  $user_obj = user_load($user->uid);
  // Load the Users favorite Beatle (George, of course)
  if(isset($user->field_favorite_beatle['und']) {
    $favorite_beatle = $user_obj->field_favorite_beatle['und'][0]['value];
  } else {
    $favorite_beatle = 'George Harrison';
  }

  if(db_set_active('groucho')) {
    $result  = db_update('Users')
        ->fields(array('Fav_Beatle'=>$favorite_beatle))
        ->condition('user_id', $user->uid, '=')
        ->execute();
    // Check on success of query
    if(!$result) {
      // Error executing query
    }
    elseif($result->rowCount() > 0) {
      // Query succeeded- rowCount() will report number of rows affected
    }
    else {
      // Query ran, but no rows were affected
    }
    db_set_active();
  }
}
[/cce_php]

With some error checking in there, and ensuring that db_set_active() is called at the end, you have loaded data from your Drupal site and entered into an external database! This can be run in the other direction too. You can retrieve data from another database and populate Drupal objects like nodes and users directly. The things to watch out for with this method are that the database engines must be the same, i.e., mysql, postgres, etc., and to be sure you find more information of which database is active when you are accessing data.

Published by: chazcheadle in The Programming Mechanism
Tags: , , ,

July 17, 2012 - Comments Off on Drush de Jour: Reset user passwords and e-mail one-time login links

Drush de Jour: Reset user passwords and e-mail one-time login links

Do you have users that like to forget their passwords? Here are 2 methods for getting new ones (the passwords, not the users!)

% drush upwd username

And here is how to create a one-time login link for a user that has forgotten their password. (again?!)

% drush user-login username

The output will be something like this:

http://example.com/user/reset/17/13425300/c18Zh_dYH2XxHzBcJU9jU

Just email that link to your user and they can log in and reset their password.

If you know their email address you could be clever and do something like this:

drush uli username | mail -s 'Reset password request' useremail 
The Drush de Jour brings you the latest and possibly greatest bits of drush wisdom we use for developing and maintaining our Drupal sites.

Published by: chazcheadle in The Programming Mechanism
Tags: , ,

July 13, 2012 - Comments Off on How to Force a Download in Drupal

How to Force a Download in Drupal

Drupal is a great CMS when it comes to managing files. The large variety of extant modules allow you to support pretty much any file type you can imagine in your back end. However, browser to browser, many file types are treated quite differently. The most problematic culprit being, of course, images as all browsers view them in the window by default. So how can you cause a link to force an image download to the user's computer when it comes to such finicky files? Try the following script I adapted from one found on eLouai.


// ** http://www.*MYSITE*.com/*path to this script*/force-download.php?file=*file path NB: must be in same directory or child directory as this script*

$filename = $_GET['file'];
$filename = preg_replace("/.*/files//", '', $filename);

// required for IE, otherwise Content-disposition is ignored
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');

// addition by Jorg Weske
$file_extension = strtolower(substr(strrchr($filename,"."),1));

if( $filename == "" )
{
echo "ERROR: download file NOT SPECIFIED."; exit; } elseif ( ! file_exists( $filename ) ) { echo "ERROR: File not found, please go back and try another file.
$filename"; exit; }; switch( $file_extension ) { case "pdf": $ctype="application/pdf"; break; case "exe": $ctype="application/octet-stream"; break; case "zip": $ctype="application/zip"; break; case "doc": $ctype="application/msword"; break; case "xls": $ctype="application/vnd.ms-excel"; break; case "ppt": $ctype="application/vnd.ms-powerpoint"; break; case "gif": $ctype="image/gif"; break; case "png": $ctype="image/png"; break; case "jpeg": case "jpg": $ctype="image/jpg"; break; default: $ctype="application/force-download"; } header("Pragma: public"); // required header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: private",false); // required for certain browsers header("Content-Type: $ctype"); // change, added quotes to allow spaces in filenames, by Rajkumar Singh header("Content-Disposition: attachment; filename="".basename($filename)."";" ); header("Content-Transfer-Encoding: binary"); header("Content-Length: ".filesize($filename)); readfile("$filename"); exit();

?>

Place the following script in your files directory of your Drupal installation (our default being "sites/theme/default/files"). The script needs to be in a common root of all the files you wish users to be able to download. This is due to the only major addition to the code I made, the preg_replace on line 9. This essentially deletes anything that comes before the "files" directory in the path leaving just the rest of the file path.

While you could remove this line and put the script in your root, this could be a serious security gap so I do not recommend doing so. Moreover, restricting downloads to specific directories is useful if you want to prevent users from using the script to easily download certain files outside of the script's parent directory. You can move the script to any folder that contains your expected targets and change line 9 as below with your chosen directory replacing "files" (or in this case "**YOUR FILE DIRECTORY**").

$filename = preg_replace("/.*/**YOUR FILE DIRECTORY**//", '', $filename);

While you're at it, feel free to replace the error messages with whatever you feel is appropriate. Note that this script currently supports pdf, exe, zip, doc, xls, ppt, gif, png, jpeg and jpg file types though you can easily add more exotic file types. I'd caution against including any web related extensions (especially php) to prevent users from potentially using the script to download your source files.

To then use this script simply make your links as follows:

Force Download

Or if you have your file's URI, which you should, do this:

Force Download

Remember, such a script gives even a semi-savvy user incredible access to your site's file structure so be careful how and where you use this. If placed in the incorrect directory, a user could potentially download your source files.

Published by: benchirlin in The Programming Mechanism
Tags: ,

July 12, 2012 - Comments Off on Adding CCK fields to Apachesolr documents in Drupal 7

Adding CCK fields to Apachesolr documents in Drupal 7

Drupal's core searching functionality is awesome in that it can be replaced by other search modules and even other search engines. We're using Apache Solr for its lightning fast response time and powerful indexing and faceting. In a nutshell, Solr is a standalone index/search engine to which Drupal sends its search queries. The real beauty is that the indexed results are all cached and served back to the Drupal site as XML documents ridiculously fast, much faster than Drupal's core search. The core search module has to hit the Database and fully load each node/entity for every matching result.

By default Apache Solr will index most of your content type's fields, however many CCK fields that you add will not be included in the default indexing. That is to say, if you add a checkbox, or text field to your content type you will have to explicitly direct Solr to add it to the index. Each piece of content that is indexed by Solr is processed and stored as a Solr document which holds all of the indexed fields as well as some Solr metadata. That is how Solr can return results so quickly, it is only sending the fields you require, not loading the entire object.
Here is some sample code showing how to add some custom fields to the Solr document. These two hooks are all you need to get started, just add them to a custom module and be sure to re-index afterwards to update the Solr index.


/**
* Implements hook_apachesolr_index_document_build().
*
* Add custom fields to the solr document
*/
function themech_solr_apachesolr_index_document_build(ApacheSolrDocument $document, $entity, $entity_type, $env_id) {
if($entity->type == 'publication') {
if (isset($entity->field_publication_author[$entity->language])) {
foreach($entity->field_publication_author[$entity->language] AS $id => $obj) {
if(isset($entity->field_publication_author[$entity->language][$id])) {
$document->setMultiValue('sm_field_publication_author', $entity->field_publication_author[$entity->language][$id]['entity']->name);
}
}
}
if(isset($entity->field_publication_attachment[$entity->language])) {
foreach($entity->field_publication_attachment[$entity->language] AS $id => $obj) {
$document->setMultiValue('sm_field_publication_attachment', $entity->field_publication_attachment[$entity->language][$id]['uri']);
}
}
if(isset($entity->field_publication_recommended[$entity->language])) {
$document->setMultiValue('is_field_publication_recommended', $entity->field_publication_recommended[$entity->language][0]['value']);
}
}
}

/**
* Implementation of hook_apachesolr_query_alter($query)
*
* Add the newly indexed fields from above to the query result.
*/
function themech_solr_apachesolr_query_alter($query) {
$query->addParams(array('fl' => array('sm_field_publication_author')));
$query->addParams(array('fl' => array('sm_field_publication_attachment')));
$query->addParams(array('fl' => array('sm_field_publication_recommended')));
}

In this example, we're adding any names that were selected in a selectlist of authors, a checkbox state, and a file attachment uri. The first function checks to see if the Publication node has data in certain fields then adds them to the $document object via SetMultiValue(). The fields are now stored in Solr, but as they were custom additions to the document, you have to specify them in the query to tell Solr to pull them back out with the rest of the document.

You can index anything you can put into a content type, and each content type can have specific fields indexed. With Solr you can create thumbnail gallery search results, or integrate with your commerce site to generate product category and price range searches, as well as tune the results based on custom weights and ratings. The possibilities are almost limitless. Maybe as many as a googol (1x10^100), or in Drupal terms... a Droogol. :+)

Links:
Apache Solr
Apachesolr Search Integration

Published by: chazcheadle in The Programming Mechanism
Tags: , ,

July 10, 2012 - Comments Off on Drush de Jour: Debugging Drupal modules

Drush de Jour: Debugging Drupal modules

Ever want to see what's happening behind the scenes on your Drupal site? With Drush you can do just that. Whether you are building, debugging or unintentionally breaking a Drupal site, the system logs are an invaluable way to see what is going on. Strange behavior of a module? Rules not working as expected? the logs can tell you a great deal. You can access the logs by browsing to Reports>Recent log messages (/admin/reports/dblog) and view and filter the messages. You can however get a realtime view of everything being written to the log with Drush.

% drush watchdog-show --tail

This command will let you all the messages as they come in. Keep that terminal window open and you can see users log in, rules being fired, module/php errors, and your own custom debug() and watchdog() messages. You will even see PHP warnings being thrown that may not break your site but indicate coding errors that won't be obvious by just using a browser to test.

Logic loops in modules can get complicated and sometimes you can't print out a standard debugging message to the browser with print or dpm depending on where in the rendering process your code occurs. You can however put in watchdog() statements inside your loops to let you see where your code is taking you. You could add logging to an existing module if you want to learn how it works and how you can hook into it. Just be sure to make backups if you're not working in a development environment.

All your log are belong to Drush!

The Drush de Jour brings you the latest and possibly greatest bits of drush wisdom we use for developing and maintaining our Drupal sites.

 

Published by: chazcheadle in The Programming Mechanism
Tags: ,

July 5, 2012 - Comments Off on Digging into Drupal with node_load()

Digging into Drupal with node_load()

So, you've installed Drupal and created a custom content type. Now what?
Well, data goes in and (hopefully) data will come back out. Here are a few ways to see what data is being stored in your nodes and how it is structured. With this information you can manipulate and theme that information the Drupal way.

Every node in Drupal is stored in the backend indexed by its Node ID, or 'nid'. With the nid you can call up the node and make it dance for you. By dissecting the node as it is rendered by Drupal you can get access to the cck fields, referenced nodes, etc.

The quickest way to get node data is from the command line using our friend Drush.
% drush ev 'print_r(node_load(NID))'
This will bootstrap Drupal and evaluate the PHP expression you pass to it. The result will look something like this:

...
[field_gallery_category] => Array
(
  [und] => Array
  (
    [0] => Array
    (
      [tid] => 35
  )
    [1] => Array
    (
      [tid] => 36
    )
  )
)
[field_gallery_main_photo] => Array
(
  [und] => Array
  (
    [0] => Array
    (
      [fid] => 2072
      [alt] =>
      [title] => Antikythera Mechanim
      [width] => 515
      [height] => 438
      [filename] => antikythera-mechanism.png
      [uri] => public://images/antikythera-mechanism.png
      [filemime] => image/jpeg
      [filesize] => 43707
      [status] => 1
    )
  )
)
...

This is just a snippet of the entire node object that is returned. You can see how Taxonomy terms are associated with a node are stored in an array and how information for an attached imagefield file are stored. The parent [und] array is the language designation. An example of how you could use this is to use the image's title text as a div to be a captioned overlay at the bottom of the image. You could likewise, use the taxonomy term ID and get a list of other nodes that are tagged with that term and print out links to them. You could provide a direct download link via your template using the [uri] of the attached image. Understanding how data is presented to the theme layer of Drupal is the key to bending templates to your will.

If you have installed and enabled the Devel module you can evaluate that same php code:

print_r(node_load(...));

right from the webpage.

If you've already created a module and want to print out the node object in a nice collapsable viewer you can add this code:

/**
 * Implements hook_node_load().
 */
function custommodule_node_load($node) {
  if ($node->type == '<NODE_TYPE>') {
    dpm($node);
  }
}

and you will be rewarded with a very convenient display of the node elements.

When you get into creating and modifying custom theme templates for you site and I hope you do, being able to access the node object and its constituent elements will help in fine tuning the output as well as being a good way to see why data is or isn't appearing as expected.

Links:

devel module

node_load

 

Published by: chazcheadle in The Programming Mechanism
Tags:

July 3, 2012 - Comments Off on Drush de Jour: Getting your backup back up

Drush de Jour: Getting your backup back up

Last week we saw a couple of quick ways to get a database backup of your Drupal site with Drush and the command line. Here is a drushy way to restore that backup.

% drush vset --yes maintenance_mode 1
% drush sqlc < sitebackup.sql

Login as admin and check that everything is ok (www.yoursite.com/user).

% drush vset --yes maintenance_mode 0

We're using drush's sqlc command which is taking the sitebackup.sql file and dumping it back into your database. If you omit the < and backup file you will be taken to the mysql (or whatever database backend) cli, logged in as the drupal site sql user.

Taking your site offline ensures no one is making changes to the database and is a nice way to let people know that the site is not broken. You can additionally specify a maintenance mode:

% drush vset --yes maintenance_mode_message "The website is currently being updated. Please check back in a few minutes"

Drush vset (variable-set) and its partner in crime, vget (variable-get), allow you to access the variables table from your site's database. Wonderful things can be done with these two commands, like putting the site into maintenance mode. You can also check your site's current theme:

% drush vget theme

And yes, vget will pull all variables that match the substring you enter. so 'vget theme' will pull the admin_themedefault_theme, etc.

The Drush de Jour brings you the latest and possibly greatest bits of drush wisdom we use for developing and maintaining our Drupal sites.

Published by: chazcheadle in The Programming Mechanism
Tags: ,

June 28, 2012 - Comments Off on Drupal 7 Commerce Coupons: Unlimited redemptions

Drupal 7 Commerce Coupons: Unlimited redemptions

The Commerce module and its add-ons are very powerful. The coupon module has recently undergone a major rewrite and its one we're using for one of our sites. In this post we'll talk about making a modification to one of the coupon rules to allow for unlimited coupon redemptions if you enter '0' (zero) in the Number of uses field when editing a coupon.

In Workflow > Rules edit the rule Coupon Validation: Check the number of redemptions.
Add an AND condition
Then, add a Data Comparison condition
For the data to compare choose: coupon:commerce-coupon-number-of-uses
Choose the equals operator, and 0 as the value.
Next, we want to negate this condition. See the post on Drupal Rule(s)! for an explanation of this option.
Lastly, save the new condition and drag it and the Entity has field condition under the And operator.

Coupon conditions

Coupon conditions

That's it. Now you can redeem a coupon as many times as you want.

Drupal's well thought-out and structured framework allows for changes to functionality to be done at a high level instead of always needing to dig into code and/or write Tarzan-like, custom PHP. This in turn, saves other developers and designers a lot of time. Thanks Drupal!

Links:

Published by: chazcheadle in The Programming Mechanism
Tags: , ,