Warning (512): /home/zastica/zastica.com/app/tmp/cache/ is not writable [CORE/cake-1.3.8/libs/cache/file.php, line 267]
Warning (512): /persistent/ is not writable [CORE/cake-1.3.8/libs/cache/file.php, line 267]
Warning (512): /models/ is not writable [CORE/cake-1.3.8/libs/cache/file.php, line 267]
Zastica • Cakephp

4 posts found in (cakephp)

Stopping Spam with Akismet

If you’ve ever owned a blog or written for one, you know how much of a problem spam comments can be. In fact just earlier today, Harry Roberts of CSS Wizardry posted this on twitter:

css wizardry on spam

When I originally created this site and the HepCMS backbone that it runs on, I didn’t include any handling of comments except “Save, display”. There was an option to manually mark comments as spam after they were posted, but they always made it to the public pages. I did it that way for a couple reasons. First, I was curious to see how long it would take, and secondly, I was being lazy. Towards the end of last week, someone finally figured out how to automate adding comments to my site. And it only took them 3 months. In fact, if you own or run a blog, or any site that allows anyone to publish content, you’re going to have to deal with spam at some point.

I did a pretty good job of manually catching the spam, but once once the spammer had automated adding things, I had to improve my defenses. enter Akismet. Akismet is the spam catcher that is used in Wordpress blogs. Via the Akismet API, anyone can add Akismet spam filtering to their site. If you’re already using Wordpress, you don’t have to worry about spam because they already use Akismet to catch spam content.

Since HepCMS is built on CakePHP, I did some searching around for already built solutions. I found one particularily interesting, “An Akismet Behavior” by Tom O’Reilly. The part about using a behavior at the model level of my application is that it’s almost automatic. The only thing I had to do to get it up and running was to add a tiny bit of code to my comments model.

var $actsAs = array('Akismet' => array(
    'content'=>'comment',
    'author'=>'author',
    'type'=>false,
    'is_spam'=>'spam'
));

For handling spam, this is a pretty quick solution, yet very powerful. I only spent about 30 minutes researching, installing the code, and testing it out. Once I was done, I saw the number of spam comments drop dramatically, and none reached the public portions of my site. The bottom line is that if you’re running a site that allows anyone to submit content, you need to deal with spam, and Akismet is the perfect way to do it. If you’re using Cake to run your application, a behavior is the perfect way to deal with spam.

Add Dates to Your <title>s

One of the things I noticed from my old blog was that old content can often get mistaken for new content by people who do not read your blog regularly. The main reason is that people can see, right in search results, how old the post is and how significant it might be. Check out this image:

example image

There are two search results here — one that has a date and one that does not. The first one doesn’t give the user any indication of how old that page might be, while you immediately know that the second search result was created on Dec 14, 2009. six months from now, that page might be irrelevant, users can decide for themselves whether they want to visit the page.

If you’re using CakePHP like I am, this is very easy. In your posts controller’s view() function, add this line of code. (Assuming that $postdata is the database entry.)

$this->pageTitle = ''.$postdata['Post']['title'].' - '.date('M j, Y', strtotime($postdata['Post']['published']));

And then in your app_controller.php file, do something like this with the beforeRender() function. beforeRender gets called automatically by Cake just before any view begins to render.

function beforeRender() {
    //create page title
    $title = 'Zastica ';

    if(!empty($this->pageTitle)) {
        $title .= ' &bull; '.$this->pageTitle;
    }
    else {
        $title .= ' &bull; '.$this->params['action'];
    }

    $this->pageTitle = ucwords($title);
}

What happens is that if you set a pageTitle in any controller or function, it is automatically formatted, and your site’s name added. If you don’t set it, it just becomes “SiteName • $action”. As you can see in the image, “Zastica • Seattle” vs. “Zastica • Business Card 1.0 - Dec 14, 2009”.

The bottom line is that it helps users figure out how relevant your site’s content is from search results.

Finding Posts in Multiple Categories

When you’re browsing around a blog, it is a big help to be able to browse posts by category — it helps you find related posts you might be interested in. For example, if you’re reading a post on something having to do with mysql, you can click on a link to see other similar posts.

That’s pretty easy, and you can do it like this:

$sql = 'SELECT post_id
FROM categories_posts
WHERE category_id = '.$cat_id;

But what if you want to see posts in two categories, say mysql and php? You can’t adjust the query to use an AND, because that just wouldn’t work, and you can’t use the IN keyword, because that equates to a logical OR. So what do you do? One possibility is doing two separate queries and comparing the results. That’s not terrible efficient especially when you get more categories to compare. So, here’s what I did:

$sql = "
    SELECT Post.id
    FROM (
        SELECT post_id, COUNT(category_id) as num
        FROM categories_posts
        WHERE category_id IN (".implode(",", $cat_ids).")
        GROUP BY post_id
        HAVING num=".count($cat_ids)."
    ) postids
    LEFT JOIN posts as Post ON post_id=Post.id
";

The inner query uses the IN keyword to find all posts in any of the categories, and then groups them by the post id and counting how many rows were found. Then the HAVING keyword filters the post group results to only those rows that have the same number of results as categories I asked for. So, If I query for posts in “mysql and php”, it finds all rows with either, groups them by the post_id, and then counts up how many rows are grouped.

If a post was tagged with just PHP, the number would be one. Just MySQL would be the same. Posts without either MySQL or PHP wouldn’t show because of the IN command, but any post with both PHP and MySQL would have a 2, and be found by the query. The outer query is designed to return the list of post ids similar to Cake’s query results, so the data can be reused later to get comment and author data.

Hello, World

This is my first post using my new CMS. It’s called HepCMS, and I developed it over the course of 6 months on my bus rides to and from work. My main goal while developing Hep was to create a simple system that would make it incredibly easy to publish content for the web. I also wanted to become more familiar with the CakePHP framework. At work, I develop and customize our custom CMS, so I also wanted a different system that would encourage me to learn in different ways.

And also, the technology that my old site used (the Symphony CMS) issued a major upgrade to their codebase, one that was incompatible with old Symphony websites. Since I was stuck in a situation where I couldn’t upgrade without some major effort, I decided to take the plunge and create my own system.

As for my old website, it’s now been archived for posterity. The content remains, albeit on a different url, and it is no longer supported or updated. Commenting is turned off, and the backend has been removed. I thought about porting the content into the new website, but honestly there was so much cruft that it wasn’t worth the effort.

The topic(s) of this site will remain the same — anything that comes to mind. And the quest to write interesting information will continue.