Getting a random wp-block by regex

If you’re used to developing with a CMS such as CMS Made Simple, you would be quite used to having blocks of content separate from the main content system. These blocks can be re-used through-out existing content and within the theme templates.

One might even consider this a core function for a CMS. Sadly it’s missing from WordPress. Keir Whitaker has been working on this missing option and gives us wp-blocks.

It’s a great little plugin, though it currently has a couple of niggles:

  • Apostrophes are escaped in the output: ‘
  • One can’t call a block from inside content, it has to be from the theme template.

Despite this, I’m glad that he’s made the plugin available for public use.

Now, time to extend it!

As with global content blocks in CMS Made Simple, wp-blocks does not offer a way to fetch a random content block. I fixed this in CMS Made Simple by writing the Random Global Content Block plugin.

Now I’ve done something similar for wp-blocks:

/**
 * Gets a random block data by 'regex'
 * Requires http://wordpress.org/extend/plugins/wp-blocks/
 *
 * @param string $block_slug_regex 
 * @return string
 * @author Iain Cuthbertson
 */
function get_wp_block_random($block_slug_regex) {
  global $wpdb;
  $block_slug_regex = trim($block_slug_regex);
  $data = (array)$wpdb->get_results("SELECT id FROM {$wpdb->prefix}wpb_content WHERE name REGEXP '{$block_slug_regex}' AND active = TRUE");  
  if (isset($data) && is_array($data) && count($data) > 0)
  {
    $arrayIndex = array();
    foreach ($data as $row)
    {
      $arrayIndex[] = $row->id;
    }
    $randomIndex = mt_rand(0, count($arrayIndex) - 1);
 
    return get_wp_block_by_id($arrayIndex[$randomIndex]);
  }
}

Copy that code into your theme’s functions.php and then call from within your template with, as an example:

This will then fetch a wp-block with a name starting with testimonial_.

Mimicking wordpress.com’s image resize URIs

To follow up from last night’s entry, I was determined to remove the intermediary step of my_resize_script.php.

I’ve acheived this goal in the mod_rewrite rule, file name and query string now get passed on to timthumb.php.

Updated rules:

RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} .(gif|png|jpg|jpeg)
RewriteCond %{QUERY_STRING} (w|h)=(.*)$
RewriteRule (.*) /full/path/to/timthumb.php?src=$1&%1=%2&%3=%4 [L]

This will pass both width and height variables into timthumb if they are declared.

Quick breakdown of the rules:

  1. Check that the URI exists as a file.
  2. Check that the URI in question is an image. Expand to other formats as needed.
  3. Check that one or both of width and height are being altered.
  4. Pass the file following to timthumb as variables:
    $1 = filename
    %1 = w or h
    %2 = value of w or h
    %3 = w or h
    %4 = value of w or h

If conditions 1 to 3 all return true, then the rewrite rule in 4 will be executed.

In summary, the URI
http://myrant.net/wp-content/uploads/2010/03/high_res_horsey.png?w=300&h=100

Will be passed on as
http://myrant.net/timtumb.php?src=wp-content/uploads/2010/03/high_res_horsey.png&w=300&h=100

Resize images on the fly without messing with image URLs

[EDIT: I’ve made this simpler with a follow up post: Mimicking wordpress.com’s image resize URIs]

Exporting a wordpress.com site for use on a standalone wordpress.org install is a joy to set up. The export and import system are simple to use and give little or no issues.

What is a problem is the automatic resizing of images that wordpress.com offers.

One can insert an image in the normal way and then append the image URI with some variables such as width (w) and height (h). This is also a d options, I assume this is for depth, but haven’t looked into it yet.

So an image URI might be http://myrant.net/wp-content/uploads/2010/03/high_res_horsey.png?w=300

This URI would display the image resized to 300px wide and retain the aspect ratio.

Currently, wordpress.org do not offer this facility (that I have seen). So I’ve been working on how it might be done:

1st off, we need to set up a mod_rewrite rule to cater for image URIs that have query string variables. This can be added to a vhost config or the site’s .htaccess file. I prefer to use .htaccess

RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{REQUEST_URI} .(gif|png|jpg|jpeg)
RewriteCond %{QUERY_STRING} (w|h)=(.*)$
RewriteRule (.*) /my_resize_script.php?file=$1 [L]

Then we need a script (my_resize_script.php) to make use of the variables.

 $_GET['file']);
foreach ($queryString as $bit)
{
	$varBits = explode("=", $bit);
	$urlVars[$varBits[0]] = $varBits[1];
}
?>

The output of this script as it is would be:

Array
(
    [file] => wp-content/uploads/2010/03/high_res_horsey.png
    [w] => 300
)

As you can see, I’m not actually making use of the variables yet. But all of the information that it does extract can be given to a proper resize script, such as TimThumb.

This is all a bit messy right now. I would much rather be able to pass variables directly into a resize script rather than have to use an intermediary one. If anybody wants to give some pointers, please do 🙂

Updating WordPress site URLs for different hosts

Why oh why does WordPress insist on storing the site URLs within the database?
Every other project I can think of either keeps it in a config file or doesn’t bother with one and just works with whatever URL it finds itself on.

This is most irritating when you have:

  • more than one developer working on the same project
  • more than one system for hosting the project (dev, test, live, etc)

One way to cope with this irritation is to have a collection of .sql files to handily update the database from the CLI.

For example, a WP site has the URL www.idophp.co.uk on a live host and uses idophp.iain on a dev host.

We would have 2 .sql files such as live_options.sql and dev_options.sql.

In live_options.sql:

UPDATE wp_options SET option_value = REPLACE(option_value, 'http://idophp.iain', 'http://www.idophp.co.uk') WHERE option_value LIKE 'http://idophp.iain%';

In dev_options.sql:

UPDATE wp_options SET option_value = REPLACE(option_value, 'http://www.idophp.co.uk', 'http://idophp.iain') WHERE option_value LIKE 'http://www.idophp.co.uk%';

These settings can be imported over the top of a database without altering anything but the site URLs using:

$ mysql -u idophp -p idophp < live_options.sql

How to get sfFormExtraPlugin working in Symfony 1.4 using Doctrine

I’m talking about this plugin: http://www.symfony-project.org/plugins/sfFormExtraPlugin

For some reason, getting the plugin to work took a lot of Googling and some trial and error.
So here is how I got it working. Hopefully I did this in the ‘correct’ way…

cd plugins
wget "http://plugins.symfony-project.org/get/sfFormExtraPlugin/sfFormExtraPlugin-1.1.3.tgz"
tar zxvf sfFormExtraPlugin-1.1.3.tgz
mv sfFormExtraPlugin-1.1.3 sfFormExtraPlugin
cd ..
./symfony plugin:publish-assets
cd web/js
wget "http://code.jquery.com/jquery-1.4.3.min.js"
wget "http://jqueryui.com/download/jquery-ui-1.8.5.custom.zip"
mkdir jquery-ui
cd jquery-ui
unzip ../jquery-ui-1.8.5.custom.zip
mv jquery-ui/css/smoothness ../css

Edit config/ProjectConfiguration.class.php to add sfFormExtraPlugin as an enabled plugin:

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    $this->enablePlugins(array('sfDoctrinePlugin', 'sfFormExtraPlugin'));
  }
}

Edit apps/<app_name>/config/view.yml to include the JS and CSS:

  stylesheets:    [main.css, smoothness/jquery-ui-1.8.5.custom.css]

  javascripts:    [jquery-1.4.3.min.js, jquery-ui/js/jquery-ui-1.8.5.custom.min.js]

Edit lib/form/doctrine/<your_module_name>Form.class.php to make use of the widgets you want. I’m interested in sfWidgetFormJQueryDate:

  public function configure()
  {
    $this->widgetSchema['created_at'] = new sfWidgetFormJQueryDate(array(
      'image' => '/images/silk_icons/calendar.png',
      'config' => '{}',
    ));
    $this->widgetSchema['updated_at'] = new sfWidgetFormJQueryDate(array(
      'image' => '/images/silk_icons/calendar.png',
      'config' => '{}',
    ));
  }

I have used the calendar.png icon from the famfamfam silk icon set. You can get the set here: http://www.famfamfam.com/lab/icons/silk/

I’ve used the date picker widget for filling in the created_at and updated_at fields. You can replace these values with the fields you need to manipulate in a user friendly way.

[Edit 17/10/2010:]
I’m not going mad, there is an issue with the ‘magic’ fields created_at and updated_at in that they have to be unset to become magic.
Found help here: http://levelx.me/technology/programming/symfony-1-4-doctrine-timestampable-behaviour/
Though the unset lines are better off being added just once to /lib/form/doctrine/BaseFormDoctrine.class.php, rather than each xxxForm.class.php file.

Which Windows applications are used by this web developer?

This list is partly to remind myself what essential apps need to be reinstalled after a system wipe.

But it does have me thinking, what do other web developers use on a daily basis?

Web browsers:

  1. Google Chrome
  2. FireFox
  3. IE
  4. Opera
  5. Safari

FireFox addons:

  1. Adblock Plus
  2. British English Dictionary
  3. ColorZilla
  4. Dummy Lipsum
  5. Firebug
  6. Html Validator
  7. Web Developer
  8. Xmarks

Code editing:

  1. PhpEd
  2. Eclipse PDT
  3. TextPad
  4. vim

Code tools:

  1. SQLyog
  2. WinMerge

System tools:

  1. PuTTY
  2. Vbox
  3. Synergy
  4. TightVNC

File tools:

  1. FileZilla
  2. DropBox
  3. Jungle Disk Workgroup Edition
  4. WinZip
  5. WinRar
  6. 7-Zip

Graphical manipulation:

  1. Paint.NET
  2. Adobe CS
  3. jRuler

“Productivity” apps:

  1. MS Office
  2. Google Apps Sync
  3. Foxit Reader

Communication:

  1. Skype
  2. Zoom phone adapter (use the Vista 64bit driver to get this working on 64bit Windows 7)
  3. X-Chat

CD/DVD tools:

  1. CDex
  2. DeepBurner

Audio/Visual:

  1. Spotify
  2. iTunes
  3. VLC

Dock gadgets:

  1. Digital Clock
  2. GMail Counter

Time off:

  1. Steam

Ace on DHBiT pointed me at http://ninite.com/, how very useful. Will make use of it next time 🙂

Magento: pre filter by multiple option custom attributes

Requirement: pre-filter products by customer group name to product attribute.
I was looking for the “proper” way of filtering items by multiple custom attribute settings, but couldn’t find out how.
So, the following is a brief explanation of how I’ve got this running in Magento 1.4.1.

There are two ways of editing core code in Magento.

  1. Wrong: edit files directly in app/code/core/Mage
  2. Right: extend modules and keep them in app/code/local/MyDevCompany

If you’re in a hurry, you might edit core files directly and then later copy the altered code out into correctly extended module files.
For most of my Magento work, this is the route I have taken: get it working in the core then extend it once I’m sure I’m messing with the correct file/method. Just be sure to copy the code out at some point before upgrading the core!

This is how I have extended _getProductCollection() in app/code/core/Mage/Catalog/Block/Product/List.php – the method which generates the product list when viewing a category.
The extended file would be saved to app/code/local/MyDevCompany/Catalog/Block/Product/List.php

_productCollection = $layer->getProductCollection();

    // fetch the list of schools and their associated IDs
    $_product = Mage::getModel('catalog/product');
    $_attributes = Mage::getResourceModel('eav/entity_attribute_collection')
      ->setEntityTypeFilter($_product->getResource()->getTypeId())
      ->addFieldToFilter('attribute_code', 'stv_school');
    $_attribute = $_attributes->getFirstItem()->setEntity($_product->getResource());
    $stv_schools = $_attribute->getSource()->getAllOptions(false);

    $schools = array();
    foreach ($stv_schools as $stv_school)
    {
      $schools[$stv_school['label']] = $stv_school['value'];
    }

    // Filter by school name and non specific items
    $allowedSchools = array($schools["Non specific"]);
    $customer_groupID = Mage::getSingleton('customer/session')->isLoggedIn() ? Mage::getSingleton('customer/session')->getCustomerGroupId() : null;
    if ($customer_groupID !== null)
    {
      $groupName = Mage::getSingleton('customer/group')->load($customer_groupID)->getData('customer_group_code');
      $allowedSchools[] = $schools[$groupName];
    }
    $this->_productCollection->addAttributeToFilter('stv_school', $allowedSchools);

    $this->prepareSortableFieldsByCategory($layer->getCurrentCategory());

// ... snipped - be sure to copy correctly from the original source file ... //
    }

}

Reason I have used such a long and ugly method: addAttributeToFilter() will only play ball with attribute value IDs.
By fetching possible attribute value text and IDs, flipping the data, we can successfully filter by human readable text.

Further reading on how to set up a module for extension, see http://www.magentocommerce.com/wiki/groups/174/changing_and_customizing_magento_code

White space trim for JavaScript

I originally blogged about this on my other site lazygnome.net but that was simply a quick place to record it.

I present to you a very quick and handy way of replicating PHP’s trim() function in JS:

function ws_trim(value)
{
  return value.replace(/(^s+|s+$)/g, '');
}

There it is, simple and usable.

I don’t claim to be original with this code. I’m sure that others have done exactly the same themselves. But it surprises me that JS doesn’t come with such a text manipulation method by default.

http://www.lazygnome.net/diary/594

upstart failing to manage mysqld on Ubuntu 10.04

Installed Ubuntu 10.04 from ISO onto a blank virtual box machine.

All worked well for a while, until I recently did an aptitude safe-upgrade.

The next time I booted the system, mysql failed to start and refused to do so when I issued: sudo service mysql start

It would just hang there.

Tracked the issue down to this bug. How I maged to get get it to work:

In a terminal: sudo /usr/sbin/mysqld

In another terminial:

sudo service mysql start
sudo service mysql stop
sudo killall mysqld

Now mysql will start and stop on the system as it should.

[nb: this is a rough artical, I will flesh it out when I have more time]

[edit 27-05-2010: this is not a permanent fix and requires to be executed after every system start. Looking forward to a fix on this…]

[edit 22-06-2010: see Ryan’s comment to fix this issue. I hope this helps others :)]