Categories
Burningbird Technology Weblogging

WordPress modifications

Recovered from the Wayback Machine.

Rather than cover each modification I’ve made to WordPress 1.2 to run my site just so, I’m including them all here with links to downloadable source for those interested in implementing one or more of the changes.

Very few of these changes fit into the criteria of ‘Wordpress plugin’, primarily because most require at least a small code change to the existing application. The code changes within the application can be minor–such as adding a link in the menu for a new application page within the WP administration. Other changes, though, while not necessarily extensive, can be more challenging because of knowing where to make the code change; adding in the ‘moderated’ and ‘insert’ options into administrative edit page fits this second type of change. Regardless of the extensive nature of the change, upgrading to the next version of WordPress will overwrite these changes. My plan is to port whatever of these modifications haven’t been implemented in WP over to the next couple of releases, and then we’ll see what happens from there.

Hopefully, though, the WordPress team will consider incorporating some of these changes, such as the per-post moderation and full-page preview, the static generation of the syndication feeds, full text for broken up documents, and something like the self-policing comments. These are the toughest to incorporate by non-developers, primarily because it’s difficult to see where to add the code. However, these changes have made a real difference to me and from what I’ve read of others’ interest, they could make the difference for other folks, too.

Cleanup

First thing to do after installing WordPress is get rid of files you’re not going to use. Create a separate directory somewhere and move the unwanted files there, in case you discover you do want them at some future time.

For my installation, I’m only supporting Atom and RDF/RSS (RSS 1.0) syndication feeds, so I got rid of wp-rss2.php, wp-rss.php, and wp-commentsrss2.php. I also don’t support site registration or use OPML, and eliminated wp-register.php and wp-links-opml.php. In addition, I dropped the readme.html file, and wp-layout.css since I don’t need the former and I have my own stylesheets.

Within wp-admin, once everything was installed and imported, I dumped the import*.php and install*.php pages. The only ones showing in my source code downloads are those for my own modifications.

Since I’m not using sidebar links, I could also get rid of the link related pages, such as link-add.php and others, but I may come up with a purpose for this technology at some point, so I’ll leave them for now.

The reason you want to keep your site as clean as possible is that every PHP page is a doorway into your site. The door may be locked tight, and seem impossible to force – but why keep a functional page around when you’re not using it? Especially with the import and install applications: running them again may not hurt anything, but why keep them around for someone to run again?

Having said that, though, don’t delete the pages; just move them of the server, or to an area where they can’t be accessed via the web.

Creating separate category and individual files

At this time, all requests for category, individual, or archived posts go to the same file that serves the main page: index.php. However, I prefer a different look and feel with my category and individual displays, so the first modification I made with the WP installation was add these separate files.

I copied index.php to new files called individual.php and category.php. Aside from the modifications to the appearance of each page, I made the following code changes to individual.php:

1. I added in the “next-main-previous” navigation links at the top of the page, above the date. Checking out the source, this is lines 52-56, including the enclosing DIV tags. The actual code is on line 54. Note that this code has to be within The Loop as the WordPress development team refers to the post processing loop, beginning at line 38.

2. I kept some things in the individual page that I removed from the main index.php page. For instance, I kept the trackback autodiscovery function call at line 75, and the call to the embedded code to handle comments, wp-comments.php, at line 82. These aren’t useful in the main page, so why keep them around cluttering up the code?

3. Instead of a note about a post not being found, I redirect all missing posts to my main Missing Page. The reason for this is that it looks a lot better than the message; and it returns a permanent redirection status code to the calling page. A better option might be to return a 410 status code so that webbots know the resource is gone. Perhaps a future change.

I made the following changes to the category.php page:

1. I removed the RDF trackback function call and the wp-comments.php reference in category.php, as these aren’t useful in a category context. I also removed any other function call that wasn’t needed. I’ll probably do even more cleanup in this and the other pages.

2. I replaced the full post content function call from the main page (the function called ‘the_content’) with an excerpt at line 55. I prefer just showing excerpts on my category listing page.

Once the individual.php and category.php pages have been created, then it’s just a matter of redirecting requests to them rather than to the index.php. This occurs in the .htaccess page.

I used the following custom template tag structure for my Practical RDF entries:

/archives/%year%/%monthnum%/%postname%

With this, my individual items can be accessed using what we’ve come to know as ‘cruft-free’ URLS, consisting of the date and either post title or post slug (abbreviated title).

When I added this custom URL, WordPress generated text I added to my .htaccess file. It’s this text that I needed to modify to use my new pages. I’ve included my copy of .htaccess in the download, but the lines that are pertinent now are the following:

I changed this:

RewriteRule ^archives/([0-9]{4})?/?([0-9]{1,2})?/?([_0-9a-z-]+)?([0-9]+)?/?$ /index.php?year=$1&monthnum=$2&name=$3&page=$4 [QSA]

To this:

RewriteRule ^archives/([0-9]{4})?/?([0-9]{1,2})?/?([_0-9a-z-]+)?([0-9]+)?/?$ /individual.php?year=$1&monthnum=$2&name=$3&page=$4 [QSA]

And I changed this:

RewriteRule ^archives/category/?(.*) /index.php?category_name=$1 [QSA]

To this:

RewriteRule ^archives/category/?(.*) /category.php?category_name=$1 [QSA]

Now, instead of index.php getting requests for all the pages, individual post requests are directed to individual.php, and category pages are directed to category.php.

I also removed some of the other WordPress generated .htaccess entries, such as those for syndication feeds of my categories and comments. I would prefer being given the choice whether these are syndicated or not, so have removed them for now.

Fullpage Preview

What I found frustrating with most of the weblogging tools I’ve used in the past is there is no way to preview a post in context. In other words, no way to preview a post, as it would show once published, within the same page constraints and look and feel as govern the post once it’s live.

One of the first tweaks I made to WordPress was to provide full page preview. Even when WordPress 1.2 came out with a preview item at the bottom of the edit page, I opted for my approach, which was a separate preview page and a link in the edit form to open it.

(I’ve since removed the code for the in-page preview from the edit form as it plays havoc with the larger essays.)

To create the preview page, I first copied my individual page, individual.php, to a new file, calling it preview.php. Since the header file in both only shows posts that are published, I also needed to make a copy of wp-blog-header.php, calling this new page wp-blog-draft-header.php. The new header page is exactly like the old one except that in the source, I changed the code at line 312 to access posts with status of ‘draft’ rather than ‘publish’. I then replaced the call to wp-blog-header.php with wp-blog-draft-header.php in preview. php, as shown in the source at line 14.

To call this preview page from the application, I added a hypertext link to the page in my edit-form-advanced.php page, at line 162, passing in the post identifier.

Now, when editing a post, clicking on the “Preview” link opens that post for preview exactly as it would look in the individual posting page.

On demand by post comment moderation

The second major modification was to add post-by-post moderation. At this time, WordPress only supports moderation for the entire site, but I want moderation of specific posts. This additional functionality required changes in the code in a couple of different places, and also required a minor database change.

First, there is no comment_status enumerated value of ‘moderated’ in the WordPress database posts table. (An enumerated value is one where options can be accessed as numbers, or as named values, which are easier to remember. )

I used PHPMYAdmin to add ‘moderated’ to the end of the list of other enumerated values. To do this directly in the MySQL command line, use the following:

ALTER TABLE `rdf_posts` CHANGE `comment_status` `comment_status` ENUM( ‘open’, ‘closed’, ‘registered_only’, ‘moderated’ ) DEFAULT ‘open’ NOT NULL

Next, I added the ‘moderated’ option to the list of other options within the WordPress edit page, as can be seen in the source in line 98. Now when the Edit page opens, ‘moderated’ is one of the comment options.

In the front end pages, in wp-comments.php, I added a note about moderation, and the reasons that it’s turned on, in lines 78 and 84 through 87. In wp-comments-post.php, I added code starting at line 110 to turn on moderated comment handling.

One last change I made fairly recently was to automatically set a post comment_status to moderated if it’s currently open, but over 20 days old. I added this within wp-comments-post.php, from lines 76 through 85.

That’s it for comment moderation. Now, I can set a specific post to be moderated as soon as I publish it. Or I can turn comment moderation on for several posts based on date or some other factor, by running a SQL update directly against the database and setting the posts’ comment_status to ‘moderated’. Finally, a post sets itself to be moderated when a person adds a comment to the post and it’s over 20 days old.

I may pull some of this code into a separate function just to clean up the page, but the functionality will remain the same.

Comment formatting and comment/trackback split

A change I made back in WordPress 1.02, was to split the display of comments in the individual page between trackback/pingbacks and comments. I also added formatting for the comments, by wrapping the comment area in a DIV element with a specific CSS class, and by providing my own author formatting.

I created two plugins to support these modifications. The first, get-comment-type.php has one function that returns the type of comment after examining the comment contents. The second, get-comment-author.php does all sorts of formatting on the author, using the same technology I used when I formatted the author while running an MT weblog.

In wp-comments.php, the comments are looped, and each is checked for type. If the type is trackback or pingback, the item is printed out, as shown in lines 45-57. Note in the code in line 48 the call to get_comment_type to get the comment type. Also note in line 54 the call to get_comment_author.

The same look and processing is also used with comments, in lines 59-71.

Formatting is added around the comment_text function call in lines 52 and 66. The CSS styling can then be controlled with each stylesheet.

Spam Control

Control for spam is mainly handled by setting older posts to be moderated, but to stop a ‘crapflood’ or a whole of lot of spam comments being added at once, I use a modification of the code that Jacques Distler originated, I believe: only allow so many comment postings within the same time period.

The code to help prevent flooding can be found in lines 87-101. First it checks to ensure that the same person hasn’t posted a comment within the last ten seconds–code provided from the WordPress development team. My addition is the code following, which checks to make sure there have been no more than 50 comments in the last day–any comments beyond that are automatically moderated. I’m no political pundit to get a lot of comments during the day, and 50 seemed sufficient.

Static page generation

Comment flooding just isn’t as much of a problem for WordPress as it is for Movable Type because a comment in WP is not much more than an insertion of a new row in the database. With MT, re-building of pages is happening, and that’s intensive.

If you think about it, mid-range web sites that provide database activity usually get several thousand database accesses or updates in an hour, much less a day. Few webloggers will meet or exceed this. But page rebuilding using a template language library is a very CPU hungry process. It’s because of this that I don’t generate static versions of all the pages when I re-build (more on static pages later). It’s more efficient to dynamically serve the pages, then rebuild when they’re new, or have been newly commented.

However, syndication feeds are different. The only time these should be changed is when entries in these pages are added or modified. However, the little buggers get accessed frequently – much more frequently than our main pages now. It makes sense to provide static versions of these pages.

The code I use to create static pages from dynamic content is fairly well known code. First, you grab a copy of the page in memory. Then you write it out to a filem usually located in the /tmp directory in Linux. Finally, when the file has been completely written out, you copy the page from /tmp to its permanent location.

You can see this code here . This function, which has been created as a WordPress 1.2 plugin, takes two paramters: The URL for the source, and the target file name. When installed, the person using the plugin needs to modify both the static HTML directory and the name of the temp file. The function call at the end – unlink – deletes the file.

The Unix /tmp directory is nothing more than a temporary directory where most users on a machine can write intermediate results, just as I am with this code. By writing the file to the TMP directory until finished, I don’t overwrite any existing version of the file, in case a problem happens and my write isn’t complete. If the program crashes in mid-write, there are mechanisms that keep /tmp clean so breaking program cleanup is not an issue. No bits of have burnt files to worry about.

Now that a static generation function exists, I can use it in two different ways: to generate static content based on an action, or on demand.

A second plugin, gen-static-main.php generates a static version of my RDF/RSS 1.0 file and my Atom syndication file. Through the use of the WordPress plugin architecture, this function is called any time the publish_post or edit_post action is initiated. Since I am following the requirements of the architecture, my function will be past the post identifier, which I have to return.

Now, any time I edit or publish a post, my static syndication feeds are created. I then use soft links to link these to my main directory:

ln -s /home/shelley/www/weblog/static_html/index.rdf index.rdf

For creating static versions of any page, I created a new WordPress administration form, get-static.php that takes a source URL and a target file name and generates the static version of whatever I give it. It can be my weblog entries, or others – it works equally well with any web page.

To add the new form to the administrative panel, I added an entry into the menu.php file at line 26.

Simple, and a sweet addition to the application.

(Note in menu.php that I’ve also removed the link to templates and links, since I don’t currently use either of these pages at this time.)

Fulltext

I like the use of WordPress’ nextpage functionality. With this, people can link specifically to individual items rather than the document as a whole. And it helps cut down on the page length for larger articles, such as this one.

However, two valid concerns about the implementation are: there is no way of overriding the page breakups for those who want to print the page, or not scroll; and comments repeat on every page and this can be confusing. Enter my Fulltext implementation.

The function responsible for splitting content into pages in WordPress 1.2 is start_wp. I created a plugin that has my own implementation of this function, fulltext-plugin.php that does everything the original does, but replaces nextpage with spaces instead of conceptual page breaks, as seen in lines 46-48.

Next, I copied individual.php to a new file called fulltext.php. This page is exactly the same as individual.php, except that it calls start_my_wp instead of the original function, in line 39.

To add a link to this page from my weblog pages, I modified index.php to check to see if the post is a multi-page and if so, provided a link to fulltext.php. This code can be seen on line 54. I also added code into the individual.php page to add this link, but also to prevent comments from showing in any page but the last page of the post, as seen in lines 78-83.

Now, any post that is split into multiple pages, like this one, also provides the facility to view it in its entirety.

Inserted pages

I’ve found over the years that at times I want to create pages in the weblogging tool, but not have them as part of the overall page navigation; I don’t want them to show on the front page, or be part of the archival lists. To support this concept in WordPress, I created a new post status called ‘insert’.

First, just like with the previously mentioned comment_status change, I added a new enumerated type of ‘insert’ to the post_status column in the database, using the same technique described with the ‘moderated’ change.

Next, I added this new type to edit-form-advanced.php, at the end of line 92. When this option is picked, the post won’t be displayed in the category, individual, fulltext, or main weblog pages.

To display this page, I copied the preview.php and wp-blog-header.php pages. The new header page, wp-blog-all-header.php has one change, to allow any posts other than those designated as ‘private’, at line 313. This new header is called in the new individual page, insert.php. Other changes to insert.php are to remove comments, as I don’t want comments in my inserted pages.

These new pages can be used as inserts for weblog posts, or to create about pages or other information pages that need to be publicly accessible, but not archived, syndicated, or posted on the front page.

Documented elsewhere

I discussed the Pithy Bar/Linklog implementation here. My multi-weblog support was discussed here. Note that with the release of 1.2, only the wp-content and wp-images subdirectories can be shared.

The technology for live comment preview at my sites was provided by Chris Davis and it has worked out to be a marvelous addition.

Other changes and the implementation of Steve Levy’s wonderful file upload enhancement are detailed here. My initial discussion on installing WordPress is here. Implementing the category image hack and other information is detailed in this post.

You can download a copy of the files I need to run my site, in G’zipped format or in a Zipped file, and I will update the documentation pages whenever I make a change.