All Posts in drupal

December 3, 2012 - Comments Off on Drupal 7 Forms API: The One About the Date Fields

Drupal 7 Forms API: The One About the Date Fields

Drupal, as a content management system, allows you to create content types with date fields which means you can create events and calendars. There are many great modules which allow you to display content with date fields in calendars, forms, etc. But before you can fly, you must first learn to code... I think that's how the saying goes. Entering one, or even a couple of events into you site is not too difficult or cumbersome. The default node add forms are utilitarian if nothing else. I have even been know to gussy the forms up with some fieldsets, CSS and some occasional Jquery whiz-bang-iness. The challenge comes when you have to enter months worth of events at one sitting. After entering and saving a new event, the system will default to the current day (or whatever default relative date you set in the widget) the next time you add the next event. This means each time you add an event you have to set the month/day/year each time- and when this spans the next calendar year, or even several months, you finger may fall off from all the clicking needed.

The solution? Set the default date in the date widget programmatically, based on the last saved event date. This means if you are entering a lot of events in sequential order, you will have less clicking to do to set the date. It also means if you save an event several months in the future and later add a new event you will have to do the clicky-dance, but for adding many events at once, you will have saved yourself some time.

The way to change the default date in the form is to use the Drupal Forms API (formerly FAPI in D6). You will need to create a module, or add this code to an existing custom module. The Forms API, in simple terms, allows you to talk to Drupal and manipulate the forms through code. Instead of hacking away at a flat HTML file, editing <inputs> and trying to get form values to save in the right places, you can systematically address each form element and get/set values, change CSS attributes, add JS/Jquery and manipulate field settings/defaults. If you've ever installed Captcha, LoginToboggan or any other Drupal form altering module, that's exactly what they are doing. By 'hooking' into the Form API, they can perform all sorts of manipulation without rewriting or replacing the form's core- they simply alter it.

This function will find the currently displayed form and alter it if and only if it is the event form we want to alter. It next retrieves the date of the last event you saved and changes the sets that as the default value. If no date was last saved, then the form will default to the current date.

[cce_php]

function mymodule_form_alter($form, &$form_state, $form_id) {
switch ($form_id) {
case 'event_node_form' :
// Set the default start date for Events to the last saved event month
if ($form['#action'] == '/node/add/event') {
$date = variable_get('event_last_date', '');
$form['field_event_date']['und'][0]['#default_value']['value'] = $date['start'];
if (isset($date['end'])) {
$form['field_event_date']['und'][0]['#default_value']['value2'] = $date['end'];
}
}
break;
}
}

[/cce_php]

This next function reads the values of the event being saved and stores the date(s) as an array to the system table.

[cce_php]

function mymodule_node_presave($node) {
switch ($node->type) {
case 'event' :
$date['start'] = $node->field_event_date['und'][0]['value'];
if (isset($node->field_event_date['und'][0]['value2'])) {
$date['end'] = $node->field_event_date['und'][0]['value2'];
}
variable_set('event_last_date', $date);
break;
}
}

[/cce_php]

You will notice that there are 2 date fields being saved here, value and value2. If you have set the date field to allow an end date value to be set, it is called value2, and in this code is also saved. If you do not set it, or do not have it visible on the form, it will ignore it. The Second function calls hook_node_presave() which, like the Forms API, is an entry point into the Node API and allows you to interact with the node object via code. When any node is being processed for insertion/updating in Drupal, it will call this function and if it matches your 'event' type it will save the date field(s) to the system table for later retrieval by the first function. Place these two functions in a module and test it out. You will need to make sure your code matches  the content type name and CCK date field names of your site.

Modifications such as these can greatly improve the user experience of a site. When you are creating sites to be turned over to others for the content management, this attention to detail and usability can make their lives much easier.

Links:

Published by: chazcheadle in The Programming Mechanism
Tags:

November 28, 2012 - Comments Off on Drupal 7: Create Previous | Next links for nodes using CCK Date fields

Drupal 7: Create Previous | Next links for nodes using CCK Date fields

Recently we needed to implement a Next | Previous link feature for a site for two content types. For providing these links on a simple content type like a blog, the Flippy module may fit the bill. Flippy creates a themeable pager that gets its date from the node's 'created' date field. Blogs generally will benefit from this method of sorting and navigation, but what if your content type has a different, CCK, date field that you want to use for the links. The following code will take your CCK date field and use it to compare with same CCK date field of the current node:
[cce_php]
field_event_date['und'][0]['value']));
// Find the nid of the node with the timestamp just prior to $date
$prev_nid = db_query("SELECT n.nid FROM {node} n LEFT JOIN {field_data_field_event_date} f ON n.nid = f.entity_id WHERE type = 'event' AND UNIX_TIMESTAMP(f.field_event_date_value) < :posted ORDER BY field_event_date_value DESC LIMIT 1", array(':posted' => $date))->fetchField();
// Find the nid of the node with the timestamp just after $date
$next_nid = db_query("SELECT n.nid FROM {node} n LEFT JOIN {field_data_field_event_date} f ON n.nid = f.entity_id WHERE type = 'event' AND UNIX_TIMESTAMP(f.field_event_date_value) > :posted ORDER BY field_event_date_value ASC LIMIT 1", array(':posted' => $date))->fetchField();

if ($prev_nid > 0) {
$prev_link = l('Previous', "node/$prev_nid", array('html'=>TRUE, 'attributes' => array('title' => 'See Previous', 'class' => array('prev-link'))));
print($prev_link);
}

if ($next_nid > 0) {
$next_link = l('Next', "node/$next_nid", array('html'=>TRUE, 'attributes' => array('title' => 'See Previous', 'class' => array('next-link'))));
print(" | " . $next_link);
}
?>

[/cce_php]

The two db_query() calls in the code query the database for nodes based on their CCK Date field which is converted from a Mysql date (MM/DD/YY HH:MM:SS) to the unix epoch timestamp format for comparison. If there is a resulting nid, it is stored for output by drupal's l() function. The field we have is for an event and the field's name is 'field_date_event_value', seen in the SQL. You will have to dig into your content type to determine the exact name of the field you will use. Additionally you can see the syntax for the l() function for adding additional attributes to the generated link. The third argument takes a series of nested arrays that hold the 'title', 'class', 'id', etc.

This code could be extended to generate fuller pager with First, Last, Skip 5, or even Random links.

Links:

Published by: chazcheadle in The Programming Mechanism
Tags:

September 26, 2012 - Comments Off on Drupal 8: .htaccess file goodies

Drupal 8: .htaccess file goodies

Drupal 7 has been out in the wild for a little over a year now. It brought a myriad (sic, can you sic your own material?) of new features; Entities, more hooks, CCK in core... to name but a few. With Drupal 8's full release still in the distance, it will be interesting to see how it will compare with the current release. One change we noted is a potential update to the core .htaccess file, which acts as the concierge and first line of defense against malicious and accidental breeches.

The .htaccess file serves primarily to direct incoming traffic to your Drupal site by passing the request headers to index.php, which calls the appropriate data retrieval and rendering functions. In this manner, .htaccess passes a request for http://www.example.com/?q=user  to index.php which then displays the user login page if you are not already logged in. All requests to the website should be passed to index.php and only index.php. If for some reason, a malicious script made its way onto your server either intentionally or by a compromised user machine, it could be executed simply by pointing a browser at it.

This is where .htaccess steps in. In the new Drupal 8 iteration, even if a site were compromised and a script file was accidentally uploaded, the new rules will prevent them from being executed. Bear in mind that the current Drupal 7 .htaccess file already does a good job of protecting sites; this update is just an extra set of rules to help catch those pesky one-off gremlins.

If you take a peek at your .htaccess file you will also find the rules that allow you to force your website domain to add or drop the 'www' prefix. This is handy for SEO to prevent your site from showing confusing results for content located at www.example.com and example.com.

The .htaccess file is a gatekeeper, but like the inebriated porter in MacBeth, if too much crazyness goes on it can be fooled. Remember to keep your Drupal core up to date with the latest security fixes as well as all those custom and contributed modules. Check for file uploading, eval()s, stray .PHP files etc.

For the security conscious, you don't have to wait for D8 to add the new .htaccess rules, you can see the current patch here. You can try running the patch, or just copy and paste the 2 new lines of rules. The rules tell the webserver to only execute .php files found in your site's webroot folder, or in the /core directory. This should not impact your site negatively as normally only index.php should ever be served directly.

Here is the new rule. You can see that it checks for the the file being called, and if it is a .php file not in the webroot or /core directory, Apache will kick it to the curb with a big fat [F] code.

RewriteCond %{REQUEST_URI} !^/core/[^/]*.php$
RewriteRule "^.+/.*.php$" - [F]

Good luck, and happy Drupaling!

Published by: chazcheadle in The Programming Mechanism
Tags:

August 31, 2012 - Comments Off on Changing Drupal’s Current Search de-activation widget

Changing Drupal’s Current Search de-activation widget

If you've used the Current Search block on any of your search pages, then you've seen that the default, and hardcoded, symbol for deselecting/deactivating a search term is: (-)

Not the prettiest, not the ugliest... but also, not easily changed. luckily the Current Search module manipulates and renders everything through the theme layer, which means WE can manipulate it as well using a custom theme function. The hook we will be using is  theme_facetapi_link_deactivate().

Add this to your template.php file with the appropriately named theme.

<?php
function mytheme_facetapi_deactivate_widget($variables) {
  return 'x';
}
?>

You can Replace the 'x' with anything you'd like, even a link to an icon! Here is a before and after example.

 


An extension to this would be to add controls to the Current Search settings form to allow changing the widget.

Links:

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

August 23, 2012 - Comments Off on Open Source: Sharing is Caring

Open Source: Sharing is Caring

Today's post is not specifically about Drupal, unless you only read the parts about Drupal. We here at The Mechanism, work to create awesome and exciting websites for our clients, which often means creating very specific and specialized tools for them. In the process of generating all that code, we are always mindful of making the code reusable and flexible. And, since we work with Open Source technologies we look for ways to give back to the community. A couple of our recent projects have components that we're making available for others to learn from and use.

To that end, we've begun putting that code up on GitHub. GitHub is, in a nutshell, a sharable, online repository for your code and it lets you track changes. Our 'git repo' is here. Feel free to fork(copy) the code and use it and feel free to contribute or submit patches. These projects were built with a specific use in mind, but you never know what new features or directions they can go in.

In addtion to the Addressfield_link module I wrote about earlier this week, Ben has added his awesome social media caching library written in Node.js/MongoDB. This api will let you pull twitter/instagram/etc. feeds and store them in MongoDB for parsing or display on a website. Keep an eye on themechanism.com where you'll be able to see it in action starting next week.

And finally for those of you familiar with GitHub's Octocat, Ben also created our very own...

MECHTOCAT!

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

August 17, 2012 - Comments Off on Adding new fields to Drupal’s Addressfield

Adding new fields to Drupal’s Addressfield

Drupal 7's commerce module is extremely powerful due in large part to being built on the new entity framework. One of the submodules under the hood of commerce is addressfield, which stores xNAL compliant addresses for customer profiles. It stores the basic street(s), city, state, zip/postal code, country. For the purposes of commerce, this is an appropriate solution. However, if you want to capture more data for taking orders, or use this field in a non-commerce application, you may want to have additional fields included.

To this end, we can add more fields as 'formatters' to the addressfield module. There is an example module which ships with addressfield and I also found a similar sandbox project which adds phone numbers to the addressfield- I based this project largely off of that project. For future development we'll add validation handlers and possibly extend it to include email contact information as well.

Here is a screenshot of the resulting field:

Addressfield Link

The project can be found here.

Links:

Published by: chazcheadle in The Programming Mechanism
Tags:

July 31, 2012 - Comments Off on Drush de Jour: Drush aliases, aka… Drush aliases

Drush de Jour: Drush aliases, aka… Drush aliases

The complexity of developing and maintaining multiple Drupal sites can grow like kudzu. Keeping on top of them is important not only for version control of your codebase but also for streamlining development to production site transitions. Until now we've just shown examples of how to drush it up nice with a single site- go to that site's directory, type drush, be happy. But when you have multiple sites on one or more servers that you need to interact with, Drush can help make your life easier.

Instead of writing scripts to 'cd' here, or 'cd' there to get into the right Drupal installation directory to perform a Drush task, you can create aliases for each site that work like nicknames. For example, we have a 'dev' and a 'prod' site on a server. Instead of changing back and forth between the directories to Drushify, we create aliases for them and can call them from anywhere.

First, create a new file in ~/.drush called <aliasname>.alias.drushrc.php and put this code in it:

<?php
$aliases['prod'] = array(
  'root' => '/var/www/vhosts/themechanism.com',
  'uri' => 'www.themechanism.com',
  );
?>

Now, from anywhere on the system, you can call Drush and perform commands on your site.

$ drush @prod status

You can set an alias for a remote site, copy files between prod and dev, sql-sync your database between sites, etc. etc.

Further Drush alias examples can be found in drush/examples/example.aliases.drushrc.php or wherever you installed 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 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 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 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: ,