Friday, March 8, 2013

How to delete a Drupal field with field_delete_instance

So here's the scenario: you've working with a bunch of other people on a Drupal site, and content types are being managed with Features.  Someone (probably you, but let's be generous and say it was someone else) has added a field to a content type that needs to be removed.  No problem!  Go into the feature, create a feature_name.install file, and add this code:

<?php

/**
 * @file
 *   Install and update scripts for the feature_name feature.
 */

/**
 * Delete my_field
 */
function feature_name_update_7001(&$sandbox) {
  // Remove the my_field field.
  field_delete_field("my_field");
  field_purge_batch(1);
}

Recreate your feature without the field you are removing, run update.php, and there you go.

"But wait!", you say. " Stop!", you say.   That wasn't the only place I was using my_field, and I want to keep it in the other content types!!  If I run that script, it will remove all instances of the field on my site! 

You are so right.  It will.  I've been bitten by that one myself....  If you want to remove only one instance of a field, you need field_delete_instance instead, and you want it in an update script so that it only fires once.

<?php

/**
 * @file
 *      Install and update scripts for the feature_name feature.
 */

/**
 * Implements hook_update_N() to remove the my_field field from *only* the feature_name ct.
 */
function feature_name_update_7001(&$sandbox) {
  // Remove my_field from *only* the specified content type.
  $instance = field_info_instance('node', 'my_field', 'content_type_name');
  field_delete_instance($instance, TRUE);
  field_purge_batch(1);
}

The field_info_instance is this:
$instance = field_info_instance('entity_type', 'field_name', 'bundle');

Helpful hint: you can find this information in the field_config_instance table of your sites database. 

And there you have it.  The field will be removed from the specified content type, left alone everywhere else, and no one has to go in and remove it in the UI.

Wednesday, January 2, 2013

How to validate the dimensions of an image, and warn the user if it is too small in Drupal

  Isn't that a magical title?  Titles are surprisingly hard to write for this sort of thing.  They are either short and entirely unhelpful, or way too long, but descriptive. At any rate, this is episode 1 of "Learning from the mistakes of others", others, in this case, being me.  Of course.
  So I was assigned an issue at work (as if I don't have plenty of my own issues)(hur hur).  The site we are working on has slideshows on several pages, that are populated by images automatically imported from feed items.  This means there is a very good chance that some of the images that come in with the feed item will be too small, and would look bad in the slideshow.  We needed to verify that any given image was big enough for the slideshow, and throw up a warning notice if it wasn't. Easy peasy, right?  Erm, no.  Not for me.  As it turns out, having no background whatsoever in php/(any programming language at all) makes for a long, hard slog through the Drupal API documents....  Fortunately, I have some awesome co-workers that don't mind helping a girl out when I get stuck.  Which is... frequently.
  If I were to go through all of the iterations of (let's be honest here) psuedo-code that I tried, we would be here all night.  Eventually, one of my worker buddies threw a bucket of cold water over me(figuratively speaking, of course), and pointed out something very valuable.  When coding, you just have to take it step by step.  Write down what you need to do, the order in which it needs to be done, and *then* begin coding.  After each step, check that it is working as you expect it to.  dpm() is your friend.  Also, turn on error reporting in your settings.php if you are working in a local environment.  That saved me oooooodles of time once I did it.  Here is my final product, although keep in mind that it hasn't been given the stamp of approval yet by my superiors, so it is *entirely* possible that there are better ways to do this.  It works, is all I know... ;) I've gone through and commented it more thoroughly, just to be super specific about what I did.

/**
 * Implements hook_form_form_id_alter().
 */
function my_module_form_my_form_id_form_alter(&$form, &$form_state, $form_id) {
  //This is where I've added my custom validate function.
  $form['#validate'][] = 'my_module_image_form_validate';
}





/**
 * Implements hook_node_validate() and image_style_load().
 *
 * Check image dimensions and return warning if image is too small.
 */
function my_module_image_form_validate(&$form, &$form_state) {
 
  // Check if fid exists. This prevents any possible errors if there isn't an image in the field.
  if (isset($form_state['values']['field_media']['und']['0']['fid'])) {
  // Get the fid.
  $fid = ($form_state['values']['field_media']['und']['0']['fid']);
  // You have to load the file before you can check its dimensions, so:
 $file = file_load($fid);

  // Now that the file is loaded, I can set my variables. Use dpm($file) to find them...
  $height = ($file->image_dimensions['height']);
  $width = ($file->image_dimensions['width']);

  // I originally had this without the next 7 lines, and the numbers were hardcoded, i.e., if ($width < 780) etc...  A wise co-worker pointed out that this would cause trouble if the image styles were ever changed, and that I should use the image style presets.  So next I:
  // Get the image style presets.
  $styles = image_styles();
  // Now I can set my variables to check against.
  $minheight = ($styles['iin_wide_780x438']['effects']['15']['data']['height']);
  $minwidth = ($styles['iin_wide_780x438']['effects']['15']['data']['width']);
  //dsm($styles); (always check your work)


  // And here is the magic:
  if (isset($height) && isset($width)) {
  if ($width < $minwidth || $height < $minheight) {

  // This just sets a message right above the image field (field_media) to warn the user that the image is too small.
  $form['field_media']['#prefix'] = '<div class="messages warning">Image is smaller than recommended size.</div>';
      }
    }
  }
}
And KA-BLOW!  Bob, so to speak, is your uncle!
And now I must go to bed.
Minion out.

Friday, December 21, 2012

How to display one of two possible Drupal fields in a node template (hint: it's easier then you think!)

Yesterday at work, I was asked to set up a field in a node template panel that would display a field called 'story' if it had content, but default back to the nodes' 'body' field if the 'story' field was empty.  This is possible using views, but it was going to require an argument to be passed from the panel to the view to allow the view to only display the content from the node being viewed. (Or edited)  In theory, I knew what to do, but I did a little research anyway, as I like to do, and I ran across this excellent post: http://blog.urbaninsight.com/2012/05/14/how-to-conditionally-display-value-from-two-fields-in-views

That confirmed my plan for the fields, but I also needed to pass an argument from the panel to the view.  This, in the end, is how I set it up:

(I am assuming here that your content type already contains *both* fields that you are trying to conflate.  I am also assuming that they are both text fields, if one of them is something fancy, like a user reference field, you will need to use a relationship to be able to add the field into your view.)

Create a view, name it, select the content type that you will be displaying fields from.  Do not create a page or a block.  Click Continue & Edit.  Click the +Add button at the top of the page, and add a content pane.  This is important.  It needs to be a content pane.  Content pane.  Got it?  Good.  Now add your body field.  In its settings, select, Exclude from display.  Now add your story field.  The order is important.  I promise.  In the story field settings, expand the 'No results behavior' section.  In the 'No results text box', you are going to put the replacement pattern for your field.  If you don't happen to know it, in the 'Rewrite the output of this field' section, there is an expandable list of replacement patterns, in which you will be able to find your previous field.  This is why the order is important.  You cannot rewrite a field in a view with content from a later field.  At any rate, the text that I needed in my 'No results text' box were this:
[body]

That's it. The Body field will display if there is text in it, and the Story field will display instead if the Body field is empty.  Bada-bing, bada-boom. (Don't forget to save your view)

Now, how, you ask, does one set the view to only display the field from the node being viewed.  Well, in my case, I passed an argument from the panel to the view content pane, like so:

Add a contextual filter: Content:Nid
Under Pane settings, click on edit for the Argument input.  Select 'Input on pane config'.
Now go to your panel, in my case, the node variant for that content type, and add your view.  (It will be in the View panes catagory.) The only setting that will be available for the field will be the argument text field.  In it (at the advice from a much wiser colleague) I put '&node:nid'.  This is very specific.  Save the panel, and you should be good to go.


Thursday, December 13, 2012

Drupal 101: Lesson 1

Ok boys and girls, here begins Drupal 101!

:)

First assignment:

  • Get a notebook to use for this "class".  You will be writing down passwords and user id's that you will need to have later, and it will be very annoying to have forgotten. ;)
  • Now go to:  https://docs.acquia.com/user/register
  • Create an account.  You are not buying anything, I promise. ;) Write down the user id and password
  • Now go to: https://www.acquia.com/downloads  and download the Dev Desktop Drupal 7 package.  This is free, Drupal is open source, and you should not ever have to pay for any of its components. 
  • There are installation instructions at this link: https://docs.acquia.com/dev-desktop/install
  • Write down anything that you have to fill out during the installation process.  User ID, email, password, Database name, etc...
In my opinion, and after trying out several different setups, I've decided that Acquia is by far the easiest way to get started with a local development environment. I don't work for them, and I am not affiliated with them in any way, for what it is worth. 

* I will not ever tell you to download anything that has to be paid for!!!  Everything that you will be using is open source, i.e. FREE!

EXTRA CREDIT

  • Open Terminal (Applications->Utitlities->Terminal)
  •  Type drush status 
    • What do you see? (answer is at the bottom of the page)

EXTRA EXTRA CREDIT

  • Read this: http://soundpostmedia.com/article/5-reasons-youll-love-using-drush-drupal
  • Watch this: http://www.leveltendesign.com/tutorial/video/drupal-7-overview

ANSWERS:

You should see something that looks like this:

 NAME OF YOUR COMPUTER:~ nameofyouruser$ drush status
 PHP configuration     :  /Applications/acquia-drupal/php5_3/bin/php.ini
 Drush version         :  5.7                                           
 Drush configuration   :

Getting started with Drupal

Drupal is a wonderful thing.  If you are reading this, you probably already have some idea of what it is, and are interested in actually learning how to use it. If you don't know what Drupal is, and wish to, here is an excellent round-up of resources:

http://drupal.ucdavis.edu/blogs/triskal/2009/04/13/how-learn-drupal

There are *tons* of tutorials out there already, mostly screencasts.  Screencasts are awesome, I've watched many of them.  However, I found them (initially) to be less helpful then written instructions.  It is possible to find out how to do almost anything with Drupal, via a simple google search.  The trick is knowing what exactly you need to learn to do, and when you need to learn it.  I came to Drupal without any background in programming, css (or anything useful, really).  What I wanted to find was a series of posts that laid out, in detail, the steps that one needed to take to get up and running with Drupal.  (Spoiler alert: I couldn't)

I couldn't find one.  (I hope I didn't ruin that for you with the spoiler) Now that I have a (small and tenuous) grasp on building a website with Drupal, I am going to make one. (Not to mention that my Aunt asked me to help her get started with Drupal, so I've already written most of this stuff up in a series of e-mails. I'm always in favor of getting the most out of my work...)

Here are a few caveats:
  • I work on a Mac, and this will all be Mac specific information.  I don't have anything against PC's, but not all of this stuff will be the same on a PC.
  • All of this will be D7 specific.
  • I am in love with the concept of using Panels Everywhere, and will therefore be gearing all of the site setup towards using that.
  • I am not an expert!!  It is entirely possible that something I write will be less then 100% correct.  If it is, and you realize it, PLEASE mention that in the comments, and I will fix the error.
  • I like bullet points.  I will probably be overusing them.
  • Because I wrote this up as a series of back-and-forth e-mails, there are often questions in a section.  I will post the answers at the end of a blog post for any questions that are in the post.
Thus ends the introduction.  Stay tuned for the first lesson!