Jan 01 2001
Jan 01

Steve Sounder’s is an authority on web performance. He writes about everything from front end performance to browser performance. As a front end developer, it’s easy to get lost in tasks that don’t matter. There’s a saying in software development, “Don’t optimize until you need to.” In frontend development, that doesn’t apply because we don’t write complex algorithims. But, it’s important to make sure we focus our time and attention on the right stuff. So, what is the right stuff.

The best practices to focus on as a front end developer are the ones that have the most impact on site performance and user preception. To that end, here’s 4 topics that you should put special effort into:

  • Prevent jerky, flashy page loads. Restyling content after page loads is annoying and interrupts reading content.
  • Javascript, CSS and images should be minimised and compressed.
  • Images should be responsive and only as large as they need to be.
  • JavaScript and third party content should only load as needed and shouldn’t cause your site to fail or delay.

CSS at the Top

Load CSS at the top of head and JavaScript at the bottom of body. Putting all your CSS in a file at the top of the document prevents any unstyled elements from loading. Avoid inline CSS and printing CSS in JavaScript. Lastly, put your JavaScript in the footer so visitors can read the content of your website while your scripts are loading. If you can’t put your animations in the footer, use a loading graphic.

Prevent Flash of Unstyled Text

Preventing FOUT is a quick and easy win. Firefox and Internet Explorer both do a terrible job loading @font tags. These browsers cause the original unstyled text to flash, sometimes for a couple of seconds before the page is redrawn. This affect can even break your layouts on a page that uses thin custom fonts. All that’s needed is adding a bit of JavaScript when you include custom fonts:

<script src="http://www.gregboggs.com//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
<script>
WebFont.load({
google: {
families: ['Droid Sans', 'Droid Serif']
}
});
</script>

Grunt to Compress, Minify and Optimize

Grunt automates your workflow so that you never forget to put your SASS into production mode again. It allows to string together tools so you can combine, minify, and compress your static files, and optimize your images. You just type Grunt build and Grunt works it’s magic. These tools can easily save you 500KB on a large project.

Responsive Images Done Well

If you’re loading a 1400 pixel hero image on a 320 pixel smart phone, your page load is going to suck. Instead, use tools like Borealis or Picture to load the best size image for the visitor. Converting a slide show to responsive images can easily save 400KB. That’s a huge win.

Prevent Single Point of Failure

Modern websites almost always include JavaScript from Facebook, Twitter, Google, LinkedIn, AddThis, Discuss and other sources. If done incorrectly, these third party scripts can each cause your page to white screen. To avoid this is simple. You should always use asynchronous snippets. If you don’t have an Async snippet, then place the JavaScript just above the footer.

CDN Saves the Day

If you use Cloud Flare as a free content delivery network, Cloud Flare will compress and minify all your code and html. It will combine all your CSS and JavaScript and it will asynchronously load all of your JavaScript. All you have to do is set up Cloud Flare which takes only a few minutes. When you do switch to CF, you’ll want to exclude your web font loader and any other critical javascript from the “Rocket Loader” so it doesn’t get loaded last.

So, load your fonts well, clean up your code, correctly load images, and move all your JavaScript to the footer, or just use Cloud Flare and forget about performance.

Jan 01 2001
Jan 01

Using AJAX to update a Drupal 7 form could be easier. The documentation on this feature is extremely verbose, and I had a difficult time piecing together a working example. So, here’s the code to modify the values of a second field when the value of the first is selected. The code below will use the taxonomy_voc drop down to control the product model drop down values. This code does not work with field widgets that define their own AJAX.

Wrap the field that’s being controlled and add the callback and wrapper to the controller field.

Thanks to Anne Sturdivant for her assistance in writing this code.

/*
 *  Implements of hook_form_alter() to update a field via AJAX.
 */

function hitachi_module_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'contact_us_entityform_edit_form':

    // Wrap the field that being controlled
    // Add the callback and wrapper to the controller field

    // Create a wrapper for the AJAX callback to populate the model
    $form['field_product_model']['#prefix'] = '<div id="hitachi_wrapper_type">';
    $form['field_product_model']['#suffix'] = '</div>';

    // If we are editing an existing form, we already have the values
    if (!empty($form['taxonomy_voc'][LANGUAGE_NONE]['#default_value']) 
        || isset($form_state['values']['taxonomy_voc'][LANGUAGE_NONE][0])) {
      $form['field_product_model'] = _hitachi_set_model($form, $form_state);
    }

    // Create an AJAX callback. This is the magic bit that does all the AJAX.
    $form['taxonomy_voc'][LANGUAGE_NONE]['#ajax'] = array(
      'callback' => '_hitachi_set_model', //Must match the function name below
      'wrapper' => 'hitachi_wrapper_type',
    );
  }
}

/**
 * AJAX callback to grab all the nodes with a given term.
 *
 */
function _hitachi_set_model($form, $form_state) {
  if (!isset($form['#after_build_done']) && isset($form_state['triggering_element'])) {
    $product_types = array_keys($form_state['triggering_element']['#value']);

    // Populate all the product models with all the product titles from that division.
    foreach($product_types as $product_type) {
      $entities = _hitachi_module_get_nodes_from_tid($product_type);
      $nodes = node_load_multiple(array_keys($entities['node']));
      foreach($nodes as $node) {
        $title = entity_metadata_wrapper('node', $node)->title->value();
        $key = preg_replace('/[^a-z]/i','', strtolower($title));
        $form['field_product_model'][LANGUAGE_NONE]['#options'][$key] = $title;
      }
    }
  }

  return $form['field_product_model'];
}

Jan 01 2001
Jan 01

So, I’d like to stop using waterfall development for Drupal projects. The first step is to teach myself to be a good advocate for something different… Started way back in the early 90′s was a thing called scrum… It really is very different. Here are some resources that don’t suck.

http://blog.merge.nl/20110619/scrum-driven-drupal-development

http://slides.liip.ch/img/documents/agile-drupal-development-with-scrum_vienna.pdf

http://drupaldojo.com/session/scrum-revolution-embrace-future-interactive-development

Not all positive:

http://2011.doitwithdrupal.com/2011/sessions/pitfalls-being-agile

It’s funny, when I got a computer information systems degree, we all laughed about waterfall development. But, client’s demand fixed bids! Can you be agile with a fixed scope, budget, and timeline?

Jan 01 2001
Jan 01

Drupal:

grep 'DocumentRoot' /etc/httpd/vhost.d/* | awk {'print $2;'} | xargs -I {} nice find {} -name CHANGELOG.txt > ~/drupal-sites.txt

Produce clean report:

cat ~/drupal-sites.txt | xargs head -n 3 > ~/drupal-sites2.txt

WordPress:

grep 'DocumentRoot' /etc/httpd/vhost.d/* | awk {'print $2;'} | xargs -I {} nice find {} -name readme.html > ~/wp-sites.txt

Todo: Add string to pull WordPress version with pretty formatting.

Django:

nice find -L /vol/www/ -name 'PKG-INFO' | xargs grep '^Version:' > ~/django-sites.txt

Jan 01 2001
Jan 01

I’m in the process of migrating an old PHP website for Portland State University from a custom database into Drupal 7. Doing the data entry on hundreds of nodes with multiple custom fields, taxonomies, and images would have taken ages. So, instead I imported the data into Drupal with a PHP script. At first, I attempted to use phpMyAdmin, to export a CSV, and then import the CSV using the Feeds module. However, the feeds module for Drupal 7 doesn’t seem to do field matching well, and I couldn’t get the taxonomies or images to import correctly. So, with the help of my intelligent coworker, I decided to use Drush to execute a PHP script that will import the content. The process wasn’t hard for a PHP programmer, but all the directions I found on other blogs were either confusing me, or missing a few steps. Mainly the examples were missing the code to insert dynamic data from a database in a loop.

This being my first Drupal 7 project, I had a lot to learn about new Drupal features. Don’t worry, I’ve attached the code, so you can cut and paste my hard work.

Import content into Drupal 7

  1. Create the taxonomies and terms that you will need. This can be done with a script, but most websites don’t have that many categories.
  2. Create a content type for the nodes and include all the custom fields you will need. Be sure to include term reference fields for each taxonomy from step 1.
  3. Install Drush and get “Drush help” to work.
  4. Create a PHP script, and execute it with drush php-script (script name)

Drush is nifty because it loads the Drupal environment and allows you to access Drupal’s functions. This saves you from having to figure out where to insert all the node data manually.

Connect to the database and get content

// My source data was not normalized. All data was in 1 table! Lucky me. No joins.

$query = 'SELECT * FROM `tb|record`';
$result = db_query($query);
$i = 20;
foreach ($result as $row) {

Most of what Tim wrote worked perfectly, but here’s what Tim has to say about assigning content to categories…

Add a term to a node

$node-&gt;field_tags[$node-&gt;language][]['tid'] = 1;
‘field_tags’ here is the name of a term reference field attached to your content type, ‘1′ is a term id you wish to assign to a node. Simple!

Now here’s the code I actually used to insert my content into a category. It wasn’t simple:

 if ( $term = taxonomy_get_term_by_name($row-&gt;culture) ) {
$terms_array = array_keys($term);
$node-&gt;field_culture[$node-&gt;language][]['tid'] = $terms_array['0'];
}

If you review my code, you’ll notice that I failed to get my loop to read and load the taxonomies from the database, so I added them one a time. If anyone can figure out the syntax error in the block of code that’s commented out, that would be awesome. Contact me, and I’ll give you credit. But, I doubt many sites have more than a handful of categories.

The script took me about 10 hours to perfect after I threw away 2-3 different methods. Hopefully, you can copy this script and get your site converted over to Drupal in much less time.

The Full Source Code

<!--?php <br ?--> cm_load_rep_data();

function cm_load_rep_data( ) {

// My source data was not normalized. All data was in 1 table! Lucky me. No joins.
$query = ‘SELECT * FROM `tb|record`’;
$result = dbquery($query);
$i = 20;
foreach ($result as $row) {
$node = new stdClass(); // We create a new node object
$node->type = ‘piece’; // Or any other content type you want
$node->title = $row->subject;
$node->language = LANGUAGE_NONE; // Or any language code if Locale module is enabled. More on this below *
$node->uid = $i; // Or any id you wish
$url = str_replace(” “,””,$row->subject);
$node->path = array(‘alias’ => $url) ; // Setting a node path
node_object_prepare($node); // Set some default values.
$i++;

//grab the file for this node based on the “schema” from the old website…
$file_path = ‘/home/gboggs/Images_data/’
. $row->collection_alias . ‘/’
. $row->collection_alias . $row->accession_no . ‘.jpg’;

if (file_exists($file_path)) {
$file = (object) array(
‘uid’ => $i ,
‘uri’ => $file_path,
‘filemime’ => file_get_mimetype($filepath),
‘status’ => 1,
);
$file = file_copy($file, “public://”);
$node->field_image[LANGUAGE_NONE][0] = (array)$file;
}

// Grab the body content. My database had the body content repeated. You don’t need this piece.
if (!isset($row->long_des) || $row->long_des == null) {
$body = $row->short_des;
} else {
$body = $row->long_des;
}

// Insert the content into the node
$node->body[$node->language][0][‘value’] = $body;
$node->body[$node->language][0][‘format’] = ‘full_html’;

// Let’s add some CCK fields. This is pretty similar to the body example
$node->field_collection_number[$node->language][0][‘value’] = $row->accession_no;
$node->field_dimensions[$node->language][0][‘value’] = $row->dimension;
$node->field_date[$node->language][0][‘value’] = $row->date;
$node->field_artist_author[$node->language][0][‘value’] = $row->artist;
$node->field_provenance[$node->language][0][‘value’] = $row->provenance_prior_pub;
$node->field_details[$node->language][0][‘value’] = $row->object_details;

/* This doesn’t execute, and I couldn’t figure out why in < 10 minutes. so doing it the long way.
$categories = array(‘field_culture’ => ‘culture’,
‘field_medium’ => ‘medium’,
‘field_time_period’ => ‘time_periods’,
‘field_collection’ => ‘collection’,
‘field_theme’ => ‘theme’);
foreach ($categories as $field => $category) {
$term = taxonomy_get_term_by_name($row->$category);
$node->$field[$node->language][][‘tid’] = $term->tid;
}
*/

// The long way to check taxonmy terms and add them to the node.
if ( $term = taxonomy_get_term_by_name($row->culture) ) {
$terms_array = array_keys($term);
$node->field_culture[$node->language][][‘tid’] = $terms_array[‘0’];
}

if ( $term = taxonomy_get_term_by_name($row->medium) ) {
$terms_array = array_keys($term);
$node->field_medium[$node->language][][‘tid’] = $terms_array[‘0’];
}

if ( $term = taxonomy_get_term_by_name($row->time_periods) ) {
$terms_array = array_keys($term);
$node->field_time_period[$node->language][][‘tid’] = $terms_array[‘0’];
}

if ( $term = taxonomy_get_term_by_name($row->collection) ) {
$terms_array = array_keys($term);
$node->field_collection[$node->language][][‘tid’] = $terms_array[‘0’];
}

if ( $term = taxonomy_get_term_by_name($row->theme) ){
$terms_array = array_keys($term);
$node->field_theme[$node->language][][‘tid’] = $terms_array[‘0’];
}

$node = node_submit($node); // Prepare node for a submit
node_save($node); // After this call we’ll get a nid

// printing from Drush is easy
drush_print( $row->subject . ” added.\n” );
}
}
?>

Jan 01 2001
Jan 01

Internet Explorer is often the bane of a web designer’s existence. Creating a design in Firefox or Chrome is easy, if something is off, click “inspect element” and you can fix the problem inminutes. However, in Internet Explorer, there’s no such tool. Well, I had an issue with JQuery not displaying correctly on a Drupal site in IE7. It worked perfect in 8, Firefox, and Chrome. The demos for all the plugins I was using worked perfectly in Internet Explorer 7, but when I loaded them on the Drupal site, they were hosed. I checked the code over, and over, and over again. I have my reservations about Drupal. I hate how it creates so many external files, and how many “Gotchas” it has. I knew this had to be one of those weird Drupal quarks.

Optimize CSS Files

After hours of frustration trying to get JavaScript to run, I opened the CSS file for IE in Drupal:

  • CSS targeted specifically for Internet Explorer for Windows.
  • Any CSS in this file will apply to all versions of IE. You can target
  • specific versions of IE by using conditional comments. See your sub-theme’s
  • .info file for an easy way to use them.
  • While building your theme, you should be aware that IE limits Drupal to 31
  • stylesheets total. The work-around for the bug is to enable CSS aggregation
  • under: admin / settings / performance.
    */

I turned CSS caching on and all my JQuery started working perfectly! I had overrun the limit for maximum allowed external files in IE7! How can Drupal possibly include more than 31 external files? Man, Drupal is insane. It’s loading 2 files for every plugin I need. Anyway. Mystery solved. If you can’t get JQuery, or CSS to work in Drupal on IE7, optimize your CSS files and see if that fixes all your problems.

Jan 01 2001
Jan 01

Drupal sites often suffer from a less than ideal editor experience. While Drupal 8 improves on the default experience by providing inline editting, working preview, and moving the ‘advanced’ options to the sidebar, there’s still common mistakes that will lead to poor a experience. As a part of my on-going Drupal 8 Best Practices series, lets look at what we can do to build good admin interfaces.

Here are a few major principles behind the admin experience:

  • Use as few content types as reasonable.
  • Use permissions to hide unneeded options.
  • Content should not be defined in code.
  • Content and layouts should be editable by the editor without modifying code or css.
  • Content should have a contextual edit link.
  • A site should not require admin access to figure out how to edit.
  • Content should be stored in nodes, not entities or taxonomy terms.
  • Taxonomy can be used to apply additional page styles.
  • Taxonomy terms should have limited fields.
  • Vocabularies shouldn’t be too large or too deep.
  • The editor should have easy links and dashboard.
  • Editors should be given the Clear Cache permission.
  • CKeditor should be configured to allow pre-selected CSS styles.
  • Editors should have a simple tool for applying layouts to new pages.
  • Editors should be able to apply custom CSS via a drop down.

Give Permissions Carefully

Clients should be given the admin password for user 1, but every user should log into the site with their own user account that has an ‘editor’ permission set. This editor account should only be given permission to do the things they do frequently.

Content Should be Editable

There’s no reason to hard-code content into your theme or PHP. Every piece of text on the page should be editable through the hover edit link over that content. If you can’t edit something without a git push, then it should be a blocking bug.

Content Should be Stored in Nodes

This is a common mistake. Taxonomy terms are not content. They are meant to organize content. So, if you’re putting fields (especially text fields) on terms, you’re prboably making a pretty big error because taxonomy terms can’t be unpublished, promoted, or revisioned easily. If you’re putting content into custom entities, you’re probably also making a mistake because entities take a lot of extra work to get the basics that you get from nodes for free.

Think of WordPress here, do you see your user profile when you first log in? No, you see a dashboard of content to edit and other useful information. Do the same in Drupal, redirect login to admin/content if nothing else. Give folks short cut links in their menu bar as well.

Did You Clear the Cache?

Lets face it, the cache clear problem from Drupal 7 is a bit worse in Drupal 8. There are many times you need to Cache Rebuild. So, give them the button.

Use a Layout System or Layout Taxonomy

Panels and Display Suite are popular because they are powerful, stable, uesful and easier to use than default Drupal. No matter what tools you use, give your editors easy abilitiy to apply a layout to a new page. Don’t hand code each page. Build layouts, and apply them. Even a simple ‘Layout’ taxonomy field can allow editors to apply layouts to content.

When deciding how to build things in Drupal 8, always spend some thought on how to make sure each piece of content will editted. Not only will your customers thank you, you’ll be making Drupal look good.

Jan 01 2001
Jan 01

Have you ever had a client ask for everything to be red? Or perhaps it was auto playing audio on every page load? Well, check these awesome sites out.

Portland is a Drupal epicenter. The Drupal community comes together strongly in many places around the world and Portland’s Drupal User Group is no exception. Once a month we gather for conference quality presentations about all sorts of advanced Drupal topics. Last night our fantastic host, Jason, Yee (JYee) did something different. He had us break into 7 teams with the goal of using 4 specific modules to produce a website that’s awesome in 1 hour. The competition was stiff. Everything from advanced working, feature-complete Drupal builds to sparkles that actually sparkled. I appologize now if any of these screen shots make your eyes water.

Tweet with us at #pdxdug.

We ended up learning a lot about Flags and Rules. I had the luck and privlege of project managing a 1 hour project for an amazing Drupal team:

@illepic – Chris Bloom, Drupalist at Superstar Media
@mpgeek – Eric Paul, Drupal developer at Open Sourcery.
@mikey_p – Michael Prasuhn, 1⁄2 of Shomeya

The Awesome Websites

PDX DUG 1

dug1
modules: bad judgement, fivestar, userpoints
alt/bonus module: webform

PDX DUG 2

dug2
modules: nodequeue, privatemessage, viewfield (or eva)
alt/bonus module: webform

PDX DUG 3

dug3
modules: field collection, flag, userpoints
alt/bonus module: views_field_view

PDX DUG 4

dug4
modules: better exposed filters, fivestar, rules
alt/bonus module: webform

PDX DUG 5

dug5
modules: bad judgement, flag, message
alt/bonus module: userpoints

PDX DUG 6

dug6
modules: field collection, message, privatemessage
alt/bonus module: rules

PDX DUG 7

dug7
modules: field collection, message, nodequeue
alt/bonus module: privatemessage

Special thanks to O’Rielly, Drupalize.me, and Lullabot for sponsorship that included beer, pizza, coffee cards and an OScon pass! My group ended up winning, but all the sites are awesome. So, I’ll leave it up to you to guess which one was ours.

Pages

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web