Getting the WordPress Lifestream plugin to work on my blog

I’ve been thinking about including a digest of Twitter, Delicious bookmarks, Google Reader shared items, and other social activity in my weekly review. This lets me include the information in my archive, and it gives people more opportunities to bump into things I found interesting.

It took a bit of hacking, but I eventually got the Lifestream plugin for WordPress to work, with the help of another webpage and some source code diving. Here’s the code that powers this lifestream page:

<?php $options = array('limit' => 50); $events = $lifestream->get_events($options); foreach ($events as $event) { echo '<li>'; $label_inst = $event->get_label_instance($options); if ($event->feed->options['icon_url']) { echo '<img src="' . $event->feed->options['icon_url'] . '" alt="(' . $event->feed->options['feed_label'] . ') \ "> '; } echo '<a href="' . $event->data[0]['link'] . '">' . $event->data[0]['title'] . '</a> (' . date('D, M j, Y', $event->data[0]['date']) . ')'; echo '</li>'; } ?>

$event->render had been giving me problems, so I specified my own output format. It didn’t automatically pick up icon URLs, so I specified the URLs myself. (Bug: the settings get lost if you re-configure the feed.) The plugin seems to be broken out of the box, but there are enough pieces in there for a geek to make things work.

Because I don’t want to use up two of my one-post-a-day slots on weekly reviews, I’m leaving it as a web page that I can review and manually copy into my weekly review post instead of automatically publishing something.

You can see it in action in last week’s review.

Work in progress. Hope this helps!

WordPress admin screen tweaks

A few months ago, I decided to experiment with publishing (mostly) one post a day, scheduling posts to go out at 8 AM. That’s been working well for me, although I now have a backlog of 22 scheduled posts (as of March 11), and I keep reshuffling my queue because I want to post some things sooner.

I started using the Manage Posts page a lot. I checked the dates in the queue and used quick-edit to move posts around. I double-checked missed posts (grr). I looked up posts that got scheduled at 8 PM instead of 8 AM.

And then I decided to code in a whole bunch of things that would make life a little bit easier for me. =) I got rid of columns I didn’t use, increased the number of posts per page, added a few custom columns, and styled things differently. Kaizen: relentless improvement!

Just in case I find this useful in the future, or someone else wants to do something similar:

<?php
/**
 * @package Sacha_Chua
 * @author Sacha Chua
 * @version 0.1
 * Feel free to use this under the GNU General Public License v3 
 * or the Creative Commons Attribution License
 *
 * This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/*
Plugin Name: Sacha Chua's fixes
Plugin URI: http://sachachua.com/#
Description: Tweaks to make my life easier
Author: Sacha Chua
Version: 0.1
Author URI: http://sachachua.com/
*/

add_filter('manage_posts_columns', 'sacha_manage_posts_columns');
add_action('manage_posts_custom_column', 'sacha_manage_posts_custom_column');
add_action('admin_head', 'sacha_admin_head');
add_filter('edit_posts_per_page', 'sacha_edit_posts_per_page');
define('POSTS_PER_PAGE', 50);

/**
 * Increase posts per page to at least POSTS_PER_PAGE
 */
function sacha_edit_posts_per_page($page) {
  return ($page < POSTS_PER_PAGE) ? POSTS_PER_PAGE : $page;
}
/**
 * Resize the columns
 */
function sacha_admin_head() { ?>
  <style type="text/css">
  .column-time { width: 150px; }
  .column-categories { width: 300px }
  .column-status { width: 100px }
  .scheduled { color: green } 
  </style>
<?php }
/**
 * Remove the tags column
 */
function sacha_manage_posts_columns($defaults) {
  unset($defaults['tags']);
  unset($defaults['author']);
  unset($defaults['date']);
  $defaults['status'] = __('Status');
  $defaults['time'] = __('Date');
  return $defaults;
}

/**
 * Show the time if it's not 8 AM, and show the status and date
 */
function sacha_manage_posts_custom_column($column_name) {
  global $post;
  switch ($column_name) {
    case 'status':
      if ( '0000-00-00 00:00:00' == $post->post_date) {
        _e('Unpublished');
      } elseif ('publish' == $post->post_status) {
        echo '<div class="published">' . __('Published') . '</div>';
      } elseif ('future' == $post->post_status) {
        $time_diff = time() - get_post_time('G', true, $post);
        if ( $time_diff > 0 ) {
          echo '<strong class="attention">' . __('Missed schedule') . '</strong>';
        }
        else {
          echo '<div class="scheduled">' . __('Scheduled') . '</div>';
        }
      } else {
        _e('Last Modified');
      }
      break;
    case 'time':
      if ( '0000-00-00 00:00:00' != $post->post_date) {
        $t_time = get_the_time(__('g:i A'));
        if ($t_time == '8:00 AM') {
          $t_time = '';
        }
        $t_date = get_the_time(__('Y/m/d'));
        print $t_date . ' ' . $t_time;
      }
  }
}
?>

Next step might be to make a plugin that automatically handles scheduling for me. Or even rearranging my queue… Hmm… =)

Deploying to servers

I’m heading to the Philippines tomorrow, and to make life easer for the two other Windows-based PHP developers on my team, I updated the web-based deployment script I mentioned in
Development kaizen: deployment and testing. I added the ability to push a specified revision to the production server. It took me less time than I thought it would (I love it when things Just Work!), so I decided to spend time documenting it just in case I ever need to do it again (almost certainly) or just in case it breaks while I’m away (hope not).

Behind the scenes, there are a number of moving parts:

  • Key-based authentication. Because I need to copy files and run commands on the production and QA servers non-interactively, I needed to set up key-based authentication using SSH. I’m somewhat nervous about using a passphrase-less key, but I couldn’t see a way to work around this.
  • Rsync. I use rsync over ssh to transfer files to the remote system. It’s good at efficiently transferring changed files. I couldn’t use –delete to get rid of old files, though, as our source tree does not include the complete system.
  • A shell script with the suid bit. The shell script is responsible for exporting the requested revision to a temporary directory, rsyncing it over to the selected host, and running a few commands on the server in order to reset file permissions and clear the cache. The suid bit is there so that it takes my identity and uses the key that I set up. I resorted to suid because I couldn’t figure out how to make sure that Apache had its own key. I tried associating it with the user that Apache ran as, but I kept running into “no tty”-type errors. The suid workaround solved the problem quickly.
  • A PHP script that displays a form and the last 20 revisions. The form includes a drop-down box of the revisions displayed, a button for deploying to QA, and a button for deploying to the server. When submitted, the script does some error-checking, then uses system to call the relevant shell script. The script determines the list of revisions by using shell_exec to store the output of svn log … -limit 20 in a string, then using preg_match_all to match all instances of /r([0-9]+)/. Seems to work.

Emacs and PHP: On-the-fly syntax checking with Flymake

The dreaded white screen of nothingness usually means that I’ve misplaced a quotation mark or brace somewhere in my PHP code. On-the-fly syntax checking in Eclipse helped me find those errors quickly because I could scan the right scrollbar for areas marked with red. I knew that shifting to Emacs wouldn’t automatically cure me of the propensity to mismatch my parentheses. If I could get on-the-fly syntax checking working in Emacs, I’d save myself a lot of time.

It took me a while to sort out various configuration problems. Most were due to the fact that PHP didn’t report parse errors with my original configuration, even though I had uncommented the line after “Show only errors”. As it turned out, the error_reporting option in php.ini needs to include E_PARSE in order for php to report parsing error details. Here’s the value I’m currently using in /etc/php.ini:

error_reporting = E_ERROR|E_COMPILE_ERROR|E_CORE_ERROR|E_PARSE

Flymake is the package responsible for on-the-fly syntax checking in Emacs. Out of the box, it supports C, C++, XML, XHTML, Perl, Java, TeX, and IDL. To load Flymake and add support for PHP, add the following to your ~/.emacs:

(require 'flymake)

(defun flymake-php-init ()
  "Use php to check the syntax of the current file."
  (let* ((temp (flymake-init-create-temp-buffer-copy 'flymake-create-temp-inplace))
	 (local (file-relative-name temp (file-name-directory buffer-file-name))))
    (list "php" (list "-f" local "-l"))))

(add-to-list 'flymake-err-line-patterns 
  '("\\(Parse\\|Fatal\\) error: +\\(.*?\\) in \\(.*?\\) on line \\([0-9]+\\)$" 3 4 nil 2))

(add-to-list 'flymake-allowed-file-name-masks '("\\.php$" flymake-php-init))

;; Drupal-type extensions
(add-to-list 'flymake-allowed-file-name-masks '("\\.module$" flymake-php-init))
(add-to-list 'flymake-allowed-file-name-masks '("\\.install$" flymake-php-init))
(add-to-list 'flymake-allowed-file-name-masks '("\\.inc$" flymake-php-init))
(add-to-list 'flymake-allowed-file-name-masks '("\\.engine$" flymake-php-init))

(add-hook 'php-mode-hook (lambda () (flymake-mode 1)))
(define-key php-mode-map '[M-S-up] 'flymake-goto-prev-error)
(define-key php-mode-map '[M-S-down] 'flymake-goto-next-error)

Evaluate that code, open one of your PHP files, and intentionally break it. The syntax error should be highlighted. To change the highlighting, move your point to the error and type M-x customize-face. Accept the default (flymake-errline), then customize it as desired. Don’t forget to save your customizations for future sessions.

If your syntax errors are not highlighted and you get a message like this:

Configuration error occured while running (php -f test_flymake.php -l). flymake will be switched OFF

double-check your /etc/php.ini to make sure that E_PARSE is included as one of the options for error_reporting. You can check the output by running php -f yourfile.php -l. You should see the line number of the parse error. Make sure that this matches the regular expression added to flymake-err-line-patterns in your ~/.emacs. If your PHP returns a slightly different message, modify your flymake-err-line-patterns accordingly.

Flymake can save you a lot of programmer frustration for the cost of a little CPU time. Use it to check for errors before you save files or commit them to your source code control repository, and you and other developers will be much happier.

(UPDATE: Fixed typo in flymake-php-init)

Emacs and PHP tutorial: php-mode

php-mode is responsible for syntax highlighting, indentation, and other major PHP-specific modifications to your editing environment. There are a number of PHP modes available for Emacs. In this project, you’ll learn how to set up the php-mode available from http://sourceforge.net/projects/php-mode/ . At the time of this writing, the current version is 1.4.0 and the maintainer is Aaron Hawley.

Download the latest php-mode.el from http://php-mode.sourceforge.net/ and save it to a directory in your load-path. I like to organize my Emacs Lisp files in a directory called ~/elisp. To add PHP support to your Emacs, add the following lines to your ~/.emacs:

 (add-to-list 'load-path "~/elisp")
 (require 'php-mode)

This configures Emacs to automatically recognize files ending in “.php”, “.phps”, “.php3″, “.php4″, “.phtml”, and “.inc” as PHP files. To associate more extensions with PHP files, add lines like this example to your ~/.emacs:

 (add-to-list 'auto-mode-alist '("\\.module$" . php-mode))
 (add-to-list 'auto-mode-alist '("\\.inc$" . php-mode))
 (add-to-list 'auto-mode-alist '("\\.install$" . php-mode))
 (add-to-list 'auto-mode-alist '("\\.engine$" . php-mode))

This associates php-mode with the extensions used by Drupal, a PHP framework. When you open a file with the specified extension, it should be highlighted according to PHP syntax.

Here are some useful commands:

TAB c-indent-command Indent the current line
M-; comment-dwim Add a line comment, comments or uncomments the currently-selected region, or does other smart comment-related actions
C-c C-f php-search-documentation Search the online PHP manual for the current word
C-c RET php-browse-manual View the online PHP manual
C-c . c-set-style Change coding style
C-M-a, C-M-e c-beginning-of-defun, c-end-of-defun Go to the beginning or end of the current function
C-M-h c-mark-function Select the current function
M-a, M-e c-beginning-of-statement, c-end-of-statement Go to the beginning or end of the current statement

Here are some variables you may wish to customize:

indent-tabs-mode Set this to nil if you want to insert spaces instead of tabs
case-fold-search Set this to t if you want case-insensitive search.
c-basic-offset Set your tab size or number of spaces used as a basis for indentation

You can either customize these variables globally with M-x customize or set them for php-mode. Here’s an example that sets up a buffer with the coding style recommended for Drupal:

 (defun wicked/php-mode-init ()
   "Set some buffer-local variables."
   (setq case-fold-search t) 
   (setq indent-tabs-mode nil)
   (setq fill-column 78)
   (setq c-basic-offset 2)
   (c-set-offset 'arglist-cont 0)
   (c-set-offset 'arglist-intro '+)
   (c-set-offset 'case-label 2)
   (c-set-offset 'arglist-close 0))
 (add-hook 'php-mode-hook 'wicked/php-mode-init)

You can further customize the indentation by moving the point to where the indentation needs improvement and typing C-c C-o (c-set-offset).

To try automatic indentation, press C-j (newline-and-indent). If you like that behavior, you can make it the default in php-mode by adding the following line in ~/.emacs:

(define-key php-mode-map (kbd “RET”) ‘newline-and-indent)

You may also be interested in M-x show-paren-mode, which shows the matching parenthesis, bracket or brace for the character at point. You can enable it automatically by adding the following line to your ~/.emacs:

   (setq show-paren-mode t)

It’s a good idea to separate PHP and HTML code. This is not only better coding practice, but it also makes developing in Emacs much easier. php-mode focuses on PHP-specific behavior and does not have special support for HTML. Emacs has a number of packages that allow you to work with multiple modes like php-mode and html-helper-mode in a single buffer, but they don’t always work, and indentation can be confusing. If you must work with large segments of both PHP and HTML in the same file, check out MultipleModes (http://www.emacswiki.org/cgi-bin/wiki/MultipleModes) for tips.

Emacs and PHP: There’s more than one way to do it, of course

The PhpMode page on EmacsWiki lists five options for developing PHP in Emacs. I’m currently using the php-mode maintained by Aaron Hawley, and I’m quite happy with it. I’m curious about the php-mode maintained by Ahmet Usal, though, as it has extensive templating support and electric behavior. It seems to have abbreviations and argument lists for every PHP function, and I might borrow the code and convert it to a yasnippet file if I don’t end up using this. I’ll switch to Ahmet Usal’s php-mode today, see what it feels like, and post my notes. Has anyone else tried both?