Plugins

Posts filed under Plugins

Using Really Simple CAPTCHA Plugin for Comments

Filed in Web DevelopmentTags: CAPTCHA, Plugins, WordPress

I use the Contact Form 7 plugin for my site's contact form. The plugin's developer, Takayuki Miyoshi, created a complimentary plugin, Really Simple CAPTCHA, that incorporates with Contact Form 7 to provide a CAPTCHA for forms. In a nice example of forward thinking, Miyoshi created Really Simple CAPTCHA as a stand-alone plugin, complete with API, to facilitate other plugins' incorporation of CAPTCHA functionality. I had been using the excellent Math Comment Spam Protection plugin for my comments form.

However, continued development of the plugin has been dormant (the last update was almost three years ago), and the developer hasn't kept it updated with newer WordPress releases (I had to hack it to ensure compatibility with WordPress 3.0). Not wanting to take on maintaining a fork of Math Comment Spam Protection, realizing that I was actually using two different CAPTCHA solutions on my site, and wanting to take on the challenge of figuring out how to use Really Simple CAPTCHA's API, I decided to try to implement it for my comment form, directly into my Theme via functions.php.

As you can see: I managed to figure it out - through a combination of parsing the plugin files for Really Simple CAPTCHA, Contact Form 7, and Math Comment Spam Protection. Since I assume others might find this technique useful, I will try to explain the process. Integrating Really Simple CAPTCHA consists of two steps:

  1. Creating the CAPTCHA image, and adding associated fields to the comment form.
  2. Validating the CAPTCHA response when the comment form is submitted.

Creating the CAPTCHA image, and adding associated fields to the comment form

I will assume that the Theme uses a call to comment_form() rather than a hard-coded comment form, and thus cannot hard-code CAPTCHA fields into the comment form. Fortunately, WordPress has several built-in, comment form-related hooks. For our purposes, the comment_form_after_fields action hook works perfectly. This hook will allow us to add our form fields immediately after the Name/Email/URL fields, and before the comment text field. So, let's add the hook 1:

if ( ( ! $user_ID ) && ( class_exists('ReallySimpleCaptcha') ) ) {
	add_action( 'comment_form_after_fields' , 'mytheme_comment_captcha' );
}

Next, we need the function called by the hook:

function mytheme_comment_captcha() { 

}

Now to build the function. The first step is to instantiate the ReallySimpleCaptcha() class:

$comment_captcha = new ReallySimpleCaptcha();

The instantiated class uses several variables, the defaults for many of which can be modified if you choose to do so. Here are the defaults for several useful ones:

// Characters to use in CAPTCHA image.
$comment_captcha->chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
// Number of characters in CAPTCHA image.
$comment_captcha->char_length = '4';
// Width/Height dimensions of CAPTCHA image.
$comment_captcha->img_size = array( '72', '24' );
// Font color of CAPTCHA characters, in RGB (0 - 255).
$comment_captcha->fg = array( '0', '0', '0' );
// Background color of CAPTCHA image, in RGB (0 - 255).
$comment_captcha->bg = array( '255', '255', '255' );
// Font Size of CAPTCHA characters.
$comment_captcha->font_size = '16';
// Width between CAPTCHA characters.
$comment_captcha->font_char_width = '15';
// CAPTCHA image type. Can be 'png', 'jpeg', or 'gif'
$comment_captcha->img_type = 'png';

Once you've (optionally) set variables, the next step is to generate a random prefix, which will be used to generate the CAPTCHA image:

// Generate random word and image prefix
$comment_captcha_word = $comment_captcha->generate_random_word();
$comment_captcha_prefix = mt_rand();
// Generate CAPTCHA image
$comment_captcha_image_name = $comment_captcha->generate_image($comment_captcha_prefix, $comment_captcha_word);

Next, define some variables for use in the comment form CAPTCHA fields:

$comment_captcha_image_url =  get_bloginfo('wpurl') . '/wp-content/plugins/really-simple-captcha/tmp/';
$comment_captcha_image_src = $comment_captcha_image_url . $comment_captcha_image_name;
$comment_captcha_image_width = $comment_captcha->img_size[0];
$comment_captcha_image_height = $comment_captcha->img_size[1];
$comment_captcha_field_size = $comment_captcha->char_length;
$comment_captcha_form_label = 'Anti-Spam:';

And finally, output the fields for the comment form:

?>
<p class="comment-form-captcha">
<img src="<?php echo $comment_captcha_image_src; ?>"
 alt="captcha"
 width="<?php echo $comment_captcha_image_width; ?>"
 height="<?php echo $comment_captcha_image_height; ?>" />
<label for="captcha_code"><?php echo $comment_captcha_form_label; ?></label>
<input id="comment_captcha_code" name="comment_captcha_code"
 size="<?php echo $comment_captcha_field_size; ?>" type="text" />
<input id="comment_captcha_prefix" name="comment_captcha_prefix" type="hidden"
 value="<?php echo $comment_captcha_prefix; ?>" />
</p>
<?php

Here it is, all put together:

// Instantiate the ReallySimpleCaptcha class, which will handle all of the heavy lifting
$comment_captcha = new ReallySimpleCaptcha();

// ReallySimpleCaptcha class option defaults.
// Changing these values will hav no impact. For now, these are here merely for reference.
// If you want to configure these options, see "Set Really Simple CAPTCHA Options", below
// TODO: Add admin page to allow configuration of options.
$comment_captcha_defaults = array(
'chars' => 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789',
'char_length' => '4',
'img_size' => array( '72', '24' ),
'fg' => array( '0', '0', '0' ),
'bg' => array( '255', '255', '255' ),
'font_size' => '16',
'font_char_width' => '15',
'img_type' => 'png',
'base' => array( '6', '18'),
);

/**************************************
* All configurable options are below  *
***************************************/

// Set Really Simple CAPTCHA Options
$comment_captcha->chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$comment_captcha->char_length = '4';
$comment_captcha->img_size = array( '72', '24' );
$comment_captcha->fg = array( '0', '0', '0' );
$comment_captcha->bg = array( '255', '255', '255' );
$comment_captcha->font_size = '16';
$comment_captcha->font_char_width = '15';
$comment_captcha->img_type = 'png';
$comment_captcha->base = array( '6', '18' );

// Set Comment Form Options
$comment_captcha_form_label = 'Anti-Spam:';

/********************************************************************
* Nothing else to edit.  No configurable options below this point.  *
*********************************************************************/

// Generate random word and image prefix
$comment_captcha_word = $comment_captcha->generate_random_word();
$comment_captcha_prefix = mt_rand();
// Generate CAPTCHA image
$comment_captcha_image_name = $comment_captcha->generate_image($comment_captcha_prefix, $comment_captcha_word);
// Define values for comment form CAPTCHA fields
$comment_captcha_image_url =  get_bloginfo('wpurl') . '/wp-content/plugins/really-simple-captcha/tmp/';
$comment_captcha_image_src = $comment_captcha_image_url . $comment_captcha_image_name;
$comment_captcha_image_width = $comment_captcha->img_size[0];
$comment_captcha_image_height = $comment_captcha->img_size[1];
$comment_captcha_field_size = $comment_captcha->char_length;
// Output the comment form CAPTCHA fields
?>
<p class="comment-form-captcha">
<img src="<?php echo $comment_captcha_image_src; ?>"
 alt="captcha"
 width="<?php echo $comment_captcha_image_width; ?>"
 height="<?php echo $comment_captcha_image_height; ?>" />
<label for="captcha_code"><?php echo $comment_captcha_form_label; ?></label>
<input id="comment_captcha_code" name="comment_captcha_code"
 size="<?php echo $comment_captcha_field_size; ?>" type="text" />
<input id="comment_captcha_prefix" name="comment_captcha_prefix" type="hidden"
 value="<?php echo $comment_captcha_prefix; ?>" />
</p>
<?php
}
if ( ( ! $user_ID ) && ( class_exists('ReallySimpleCaptcha') ) ) {
	add_action( 'comment_form_after_fields' , 'mytheme_comment_captcha' );
}

At this point, your comment form should have a (non-functional) CAPTCHA field and image. Now we need to validate the CAPTCHA when the form is submitted.

Validating the CAPTCHA response when the comment form is submitted

For this part, we will use the preprocess_comment filter hook 2:

if ( ( ! $user_ID ) && ( $comment_data['comment_type'] == '' ) && ( class_exists('ReallySimpleCaptcha') ) ) {
	add_filter('preprocess_comment', 'mytheme_check_comment_captcha', 0);
}

And as before, we define the function added by the filter:

function mytheme_check_comment_captcha( $comment_data  ) {

}

Once again, our first step is to instantiate the ReallySimpleCaptcha() class.

$comment_captcha = new ReallySimpleCaptcha();

Then, declare a variable to hold the validation result, which we will default to false:

$comment_captcha_correct = false;

Next, we will use the "check" function built into the ReallySimpleCaptcha class to validate the CAPTCHA response from the comment form:

$comment_captcha_prefix = $_POST['comment_captcha_prefix'];
$comment_captcha_code = $_POST['comment_captcha_code'];
$comment_captcha_check = $comment_captcha->check( $comment_captcha_prefix, $comment_captcha_code );
$comment_captcha_correct = $comment_captcha_check;

Now that CAPTCHA validation is complete, we don't need the files hanging around the temp directory. So, we'll clean them up. The first function removes the files we just used. The second will remove any files older than 60 minutes (in case any are hanging around for some reason):

$comment_captcha->remove($_POST['comment_captcha_prefix']);
$comment_captcha->cleanup();

And finally, if the CAPTCHA response is incorrect, return an error; otherwise, process the comment as per normal:

if ( ! $comment_captcha_correct ) {
	wp_die('You have entered an incorrect CAPTCHA value. Click the BACK button on your browser, and try again.');
	break;
}
return $comment_data;

Here's the comment-processing function, all put together:

function mytheme_check_comment_captcha( $comment_data  ) {
$comment_captcha = new ReallySimpleCaptcha();
// This variable holds the CAPTCHA image prefix, which corresponds to the correct answer
$comment_captcha_prefix = $_POST['comment_captcha_prefix'];
// This variable holds the CAPTCHA response, entered by the user
$comment_captcha_code = $_POST['comment_captcha_code'];
// This variable will hold the result of the CAPTCHA validation. Set to 'false' until CAPTCHA validation passes
$comment_captcha_correct = false;
// Validate the CAPTCHA response
$comment_captcha_check = $comment_captcha->check( $comment_captcha_prefix, $comment_captcha_code );
// Set to 'true' if validation passes, and 'false' if validation fails
$comment_captcha_correct = $comment_captcha_check;
// clean up the tmp directory
$comment_captcha->remove($comment_captcha_prefix);
$comment_captcha->cleanup();
// If CAPTCHA validation fails (incorrect value entered in CAPTCHA field) don't process the comment.
if ( ! $comment_captcha_correct ) {
	wp_die('You have entered an incorrect CAPTCHA value. Click the BACK button on your browser, and try again.');
	break;
}
// if CAPTCHA validation passes (correct value entered in CAPTCHA field), process the comment as per normal
return $comment_data;
}
if ( ( ! $user_ID ) && ( $comment_data['comment_type'] == '' ) && ( class_exists('ReallySimpleCaptcha') ) ) {
	add_filter('preprocess_comment', 'mytheme_check_comment_captcha', 0);
}

That's it! Now you should have a fully functional CAPTCHA for your comment form. To use the same technique, just drop the above two functions and corresponding hooks into your Theme's functions.php file, then install and activate the Really Simple CAPTCHA plugin.

In case you would rather not mess with your Theme's functions.php file, I have also created a plugin to accomplish the same thing: cbnet Really Simple CAPTCHA Comments. I've added inline documentation, and cleaned up the code a bit. In a subsequent version, I will include an admin options page for setting the configurable options. For now, options can be set in the PHP file.

Notes:

  1. I've wrapped the hook in two conditionals:

    1. ( ! $user_ID ) - if $user_ID is set, then the user is logged in, so we don't need to add a CAPTCHA.
    2. ( class_exists('ReallySimpleCaptcha') )- the ReallySimpleCaptcha() class corresponds to the Really Simple CAPTCHA plugin, upon which our code depends. If it's not installed/activated, we don't want to try to use it.

  2. For this hook, in addition to the same two conditionals, we add a third:

    • ( $comment_data['comment_type'] == '' ) - only validate the CAPTCHA for comments, and not for Trackbacks or Pingbacks

WordPress Themes, GPL, and Copyright Case Law

Filed in Web DevelopmentTags: Copyright, GPL, Judiciary, Plugins, Themes, WordPress

Within the WordPress community, the question of GPL inheritance of WordPress themes erupts into contentious debate with the reliability - if not the frequency - of Old Faithful. While I understand that, according to the GPL interpretation of Matt Mullenweg, the Free Software Foundation (FSF), and the Software Freedom Law Center (SFLC), WordPress themes are derivative of WordPress and therefore must necessarily inherit WordPress' GPL, I would like to investigate the issue not in light of their interpretation but rather in light of copyright law and precedent case law.

Before I begin, let me add an important caveat: I have no qualms with the GPL. I have always released - and will continue to release - under GPL anything I develop related to WordPress. I do so because I choose to do so, as a means of making even a minor contribution to a project from which I believe I have personally benefited. I do have issues with how the GPL-inheritance question has been handled - but those issues are out-of-scope for this post.

Having (hopefully) made that point clear, let's begin!

What US Copyright Law Says

US Copyright law defines a "derivative work" as such:

A “derivative work” is a work based upon one or more preexisting works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which a work may be recast, transformed, or adapted. A work consisting of editorial revisions, annotations, elaborations, or other modifications, which, as a whole, represent an original work of authorship, is a “derivative work”.

Note the key adjectives: recast, transformed, and adapted.

Consider also Section 102(b), which states:

In no case does copyright protection for an original work of authorship extend to any idea, procedure, process, system, method of operation, concept, principle, or discovery, regardless of the form in which it is described, explained, illustrated, or embodied in such work.

This clause establishes the boundary around copyright between copyrightable expression, and non-copyrightable ideas.

Summarizing GPL Inheritance Requirements

To summarize GPL requirements regarding license inheritance for derivative works 1:

  1. The GPL only applies to distribution of a (modified or unmodified) GPL-licensed work, or a derivative work. Any activity involving use, modification, or creation of derivative works that does not involve distribution is outside of the scope of the GPL.
  2. Distribution of a (modified or unmodified) GPL-licensed work, or a derivative work, requires that such distribution be licensed under GPL.

The GPL is what is now referred to as a "copyleft" license: a modified public-domain license that takes advantage of the exclusive rights granted by copyright law to prevent derivative works from being restrictively licensed. Since the copyright owner has exclusive right to produce and to distribute derivative works based on the copyrighted work, the GPL intends to grant unlimited usage rights (to use, study, modify, etc.) to the end-user, while forcing follow-on developers of derivative works to release those works under the same license.

It is important to understand that, because the GPL explicitly defines any activity not involving distribution to be out of the scope of the license, and since right of distribution is solely derived from copyright law, that GPL derives its legal basis from copyright law alone. This distinction separates the GPL from most other traditional software licenses, which derive their basis for usage and modification restrictions not from copyright law, but from contract law.

Notes:

  1. WordPress is released under GPL version 2.0. I'll try to summarize below the parts of the license germane to derivative works.

    First, from the Preamble:

    The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such.

    ...

    The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library.

    Terms and Conditions, Clause 0:

    The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)

    Terms and Conditions, Clause 2:

    These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

    Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.

    Terms and Conditions, Clause 5:

    A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.

    However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.

JR Protection: Anatomy of a Spammy/SEO Plugin

Filed in Web DevelopmentTags: Plugins, Spam, WordPress

The JR Protection plugin (h/t: WP Jedi) purports to add content-scraping protection to one's blog posts - namely, by using javascript to block right-click and highlight functionality, and by disabling the RSS feed.

Personally, I think the plugin's functionality is ineffective toward its purported objective; but that's not the biggest complaint I have with this plugin. Despite being ineffective, the plugin is an SEO/spam Trojan horse.

Plugin Options Admin Page Spam

First up: spam injected in the plugin's options admin page.

Take a look at Line 280 of the plugin file:

<iframe src="http://www.jakeruston.co.uk/plugins/index.php" width="100%" height="20%">iframe support is required to see this.</iframe>

The contents of the linked page currently displays the following in the plugin's options admin page:

Spammy advertising injected into the JR Protection plugin's options admin page via iframe

And it's a huge banner, too: it takes up 100% of screen width, and 20% of screen height. In other words, on my screen, it takes up about three times the space taken up by the actual options presented on the page.

And that this advertising spam is injected using an iframe, rather than an image bundled with the plugin, presents a huge, potential vector for security threats. For one, the plugin author has complete control over the contents of the URL linked by the iframe. I subscribe to the TNO - Trust No One - security model. Besides, even if the plugin developer has only benevolent intent with this iframe, his site could get hacked, giving the hacker potential access to every site on which the plugin is installed.

wp_footer SEO/Spam Link Injection

Moving on to the even-more-spammy links injected into wp_footer.

The guidelines for hosting a plugin in the wordpress.org plugin repository are quite clear. Here is Restriction #4:

The plugin must not embed external links on the public site (like a "powered by" link) without explicitly asking the user's permission.

In the not-too-distant past, much discussion and argument have taken place around these restrictions, which led Mark Jaquith to clarify and expound upon them. Although his post was written in a somewhat tongue-in-cheek manner, everything he lists will, in fact, result in getting your plugin removed from the wordpress.org plugin repository. Here are two such offenses (#4-6):

  • #4. Insert SEO spam links into people’s blogs (like <a href=”http://example.com/”>video poker</a>).
  • #5. Insert external links (like a credit link, <a href=”http://example.com/”>My Awesome Plugin by Awesome Sauce</a>) into their blog without explicitly asking their permission, or make the default option be to insert the link.
  • #6. Load portions of the code from an external site for no valid reason, or use any other trick to work around #3, #4 and #5.

Loading portions of code from an external site for no valid reason, Inserting SEO spam links

Taking a look at the JR Protection plugin, Line 449:

add_action('wp_footer', 'protection_footer_plugin_support');

So let's take a look at the protection_footer_plugin_support function, on Line 453:

function protection_footer_plugin_support() {
$linkper=utf8_decode(get_option('jr_protection_link_personal'));
...

The plugin goes through a lot of motions to populate jr_protection_link_personal, including pulling a link from the developers website every so often, based on a defined refresh rate. Here's the link, from Line 468:

$content = curl_get_contents("http://www.jakeruston.co.uk/plugins/links.php?url=".$url."&pname=".$pname);

Currently, that URL pulls the following content:

SEO Spam links injected into wp_footer by JR Protection plugin

SEO Spam links injected into wp_footer by JR Protection plugin

But in case the plugin is unable to pull from the above-specified URL, it conveniently hard-codes some similar, fall-back spam links. See Line 109:

$content = "Powered by <a href='http://arcade.xeromi.com'>Free Online Games</a> and <a href='http://directory.xeromi.com'>General Web Directory</a>.";

And here's some more (including the only legitimate credit link the plugin provides), from Lines 118-130:

if (get_option("jr_protection_link_personal")=="") {
$rand=rand(2,2);

switch ($rand) {
case 1:
$anch="Jake Ruston's <a href="http://www.jakeruston.co.uk">Wordpress Plugins</a>";
break;
case 2:
$anch="<a href="http://www.xeromi.net">Cheap Web Hosting</a>";
break;
}

update_option("jr_protection_link_personal", $anch);

Inserting external links without explicitly asking permission, or setting option to display links to default to enabled

And going even further, these wp_footer links are injected by default. Here are the default settings. The key option is "Show Plugin Support?":

wp_footer spam link injection enabled by default in JR Protection plugin

wp_footer spam link injection enabled by default in JR Protection plugin

And especially annoying, when the user clicks the "No" option for "Show Plugin Support?", the plugin displays a nag dialogue:

nag dialogue displayed when user attempts to disable wp_footer links in JR Protection plugin

nag dialogue displayed when user attempts to disable wp_footer links in JR Protection plugin

Now that's just tacky.

An Ineffective Plugin

And here's the kicker: after putting up with all of that, what do you get? A totally ineffective plugin!

Broken Functionality

First, when testing out the plugin, I noted that, while the right-click disable appeared to be working, the text-highlight disable wasn't. I was able to select text both using the mouse to select specific text, and using CTRL-A to select all text.

Faulty Assumptions

But even more problematic are the plugin's faulty assumptions. The plugin assumes that anyone right-clicking or highlighting text on your site is attempting to steal your content, and that anyone using your RSS feed is attempting to scrape your content. In fact, the majority of people right-clicking and highlighting text on your site are doing so in order to link to/comment on your content, and the majority of users of your RSS are doing so for its intended purpose (to read your content).

Use of this plugin will result in the loss of all of your RSS subscribers, and a sharp reduction in people extending the conversation by writing about/commenting on your blog posts.

Gaping Holes

And it still won't do a think to prevent content theft! Due to two especially glaring holes, anyone intent on stealing your content will still be able to do so, despite use of this plugin:

Reliance on Javascript

The plugin relies on javascript to disable right-clicks and text-highlighting. Any user who browses your site with javascript disabled will be completely unaffected by this plugin, with respect to right-clicking and text-highlighting.

Browser Source

The plugin has absolutely no way to disable website visitors from using the view-source functionality built into every web browser. Even with the plugin enabled, one needs merely to invoke the browser's view-source functionality (in Firefox or Chrome, type "CTRL-U"; in Internet Explorer, select "View" from the menu, then "View Source"), which presents the entire contents in text format.

Conclusion

For all the reasons above, I would recommend against using the JR Protection plugin. Further, unless the plugin is modified to address the plugin repository restriction violations listed above, the plugin should be removed from the wordpress.org plugin repository.

Fixing Math Comment Spam Protection Plugin in WordPress 3.0

Filed in Web DevelopmentTags: Math Comment Spam Protection, Plugins, WordPress

Otto has an even better suggestion: use is_user_logged_in(). I have updated the recommended fix accordingly. Thanks, Otto!

As many people have noticed, the Math Comment Spam Protection plugin no longer works in WordPress 3.0. Unfortunately, the plugin appears to be no longer supported by its author, so some people are searching for an alternative plugin.

However, I have some good news: after a bit of trial-and-error, I found the problem with Math Comment Spam Protection. Even better news: the fix is incredibly simple.

Find Line 211 of math-comment-spam-protection.php:

if (  ( !isset($user_ID) ) && ( $comment_data['comment_type'] == '' ) ) { // Do not check if the user is registered & do not check trackbacks/pingbacks

The offending code is this:

( !isset($user_ID ))

Simply change the above to this (note: see update, above):

( ! $user_ID )

( ! is_user_logged_in() )

So that Line 211 looks like the following:

if (  ( ! $user_ID ) && ( $comment_data['comment_type'] == '' ) ) { // Do not check if the user is registered & do not check trackbacks/pingbacks

Voila! The Math Comment Spam Protection plugin will now work again!

This fix is so simple, I don't see any need to fork the code. I'll try to contact the developer, and see if he will patch the original.

New Plugin: cbnet Manage Plugins Donate Link

Filed in Web DevelopmentTags: Geekery, Plugins, WordPress

Do you use WordPress? Do you use several plugins? Have you ever considered donating to the authors of the plugins you use, but find it rather inconvenient to find each plugin's listing at WordPress.org Extend, just to find the author's Donate link?

If so, then you will find the cbnet Manage Plugins Donate Link plugin to be incredibly useful.

Donate Link

Donate link displayed in list of links beneath the plugin description

Plugin authors define their Donate link in the plugin's readme.txt file. This plugin reads each plugin's readme.txt to find the Donate link, and then, if it finds one, adds that link to the list of links in plugin_row_meta beneath the plugin description on the Manage Plugins page.

With this plugin enabled, you will have quick, convenient, one-stop access to the Donate links for the authors of all your installed plugins.

My hope is that this plugin will encourage more voluntary donations to plugin authors, most of whom put in countless hours of unpaid code contribution to the WordPress community. More and more plugin authors, out of economic necessity, are moving to "freemium", subscription, or other paid models, because they simply do not receive donations from those who use their plugins.

This plugin is but a humble attempt to encourage more users to donate to plugin authors.

Links

Support

Please do not post support questions here. Please post all support questions in the plugin support forum.

Haloscan Discontinued: Why NOT to Use Third-Party Services

Filed in Web DevelopmentTags: Geekery, Haloscan, Plugins, Web Site, WordPress

When I first started blogging many years ago, I used the Blogger platform, which I imported to my own domain using FTP (so that anyone reading my blog would see it as www.chipbennett.net). At the time, Blogger's commenting system was rather rudimentary, so I opted to use a third-party comment management system called Haloscan.

Interestingly, due to developments over the past couple of months, had I stuck with this arrangement, I would be in serious trouble. Recently, Haloscan was bought by Echo, which has announced that Haloscan is being discontinued in a matter of days. Likewise, Blogger has recently announced that it is discontinuing support for FTP importing of Blogger-hosted blogs to third-party domains.

Fortunately, I long ago left Blogger for the infinitely better self-hosted WordPress. Thus, even though Blogger has implemented a replacement service - their Custom Domains feature - and therefore the change would be minor, I don't have to worry about it at all.

More problematic, however, is the announced discontinuation of the Haloscan commenting system. When I first moved to WordPress, I continued to use Haloscan, which I had been using on Blogger. At some point, I decided to make the jump to native WordPress comments - but I still had several hundred comments hosted by Haloscan.

At the time, I was unable to export those comments from Haloscan, as such exports required the purchase of a Haloscan Pro account. So, I modified my blog theme to account for the old Haloscan comments, and kept a hybrid system.

Now, however, with the announced discontinuation of Haloscan, my hand was forced. Fortunately, Echo offered the option of converting to a (paid) Echo account, or exporting Haloscan comments. I quickly exported my comments, as I had no desire to pay for something that I was doing natively from within WordPress.

Thus, the problem became one of how to import several hundred comments into my WordPress database? Fortunately, this script came to the rescue, with a slight modification provided by the script developer. Using the provided script and the export.xml file provided by the Haloscan export, I seamlessly pulled all of my several hundred Haloscan comments into my WordPress database.

If you find yourself facing the same situation, the above script should help you as well.

Importing Blogger-Integrated Haloscan Comments

If you need to import Blogger-integrated Haloscan comments into your WordPress installation, follow the instructions provided by the script author, at the above link.

Importing WordPress-Integrated Haloscan Comments

If you, like I did, integrated your Haloscan comments directly into your WordPress installation, do the following:

  1. Download the script.
  2. Replace lines 77-81 of the script with the following code:

    $meta_records = $wpdb->get_results("select * from $wpdb->posts");

    foreach ($meta_records as $meta_record) {
    $blogger_to_wordpress[$meta_record->ID] = $meta_record->ID;
    }

  3. Upload the modified script file to www.domain.com/wordpress/wp-admin/ (where domain.com is your domain name)
  4. Export your existing Haloscan comments by logging into your Haloscan account and following the instructions provided.
  5. Upload the resulting export.xml file to www.domain.com/wordpress/
  6. Using your browser, go to www.domain.com/wordpress/wp-admin/import-haloscan.php
  7. Click the "OK" button to perform the import.

If your experience is like mine, you may get an error regarding a malformed XML file, due to its encoding. There are various options for rectifying the problem, but in my case, I just went to the line in export.xml indicated by the error message, and replaced the non-UTF-8 characters (in my case, fancy quote marks) with valid characters, and re-ran the script. Everything worked flawlessly at that point.

Auditing WordPress Plugins for License Information

Filed in Web DevelopmentTags: Geekery, GPL, Plugins, WordPress

The wordpress.org Plugin Repository requires adherence to a few simple guidelines in order for plugin authors to have their plugins hosted there:

  1. Your plugin must be GPL Compatible.
  2. The plugin most not do anything illegal, or be morally offensive (that’s subjective, we know).
  3. You have to actually use the subversion repository we give you in order for your plugin to show up on this site. The WordPress Plugins Directory is a hosting site, not a listing site.
  4. The plugin must not embed external links on the public site (like a "powered by" link) without explicitly asking the user's permission.

Lately, however, those guidelines have apparently been interpreted somewhat more strictly (emphasis added):

(13:27:03) KnxDT: By the way: Is the GPL header necesary?
(13:27:18) markr: very.
(13:27:28) KnxDT: because WP didn't mention in the standar readme.txt
(13:27:37) markr: Ideally you would include the gpl in a gpl.txt file
(13:27:57) markr: not including the declaration will get it removed
(13:28:10) markr: users have to know what they can do if they wish

I find the assertion that not including explicit license information with a plugin would result in the plugin being removed from the repository to be at odds with the current state of plugins in the repository. To confirm my suspicion that a significant number of plugins hosted at the wordpress.org Plugin Repository did not conform to this requirement, I did a quick audit of both my own installed plugins, and the current Top Ten Most Popular plugins in the repository. I posted my findings in the WPTavern forum. In short:

  • Almost 2/3 of the plugins I personally have installed don't have GPL information in the plugin
  • 2 of the Top Ten most popular plugins at Extend don't have GPL information in the plugin
  • 1 of the Top Ten most popular plugins at Extend violates the requirement that the entire plugin be distributed under a GPL-compatible license

Based on these findings, I decided to audit a few well-known and influential plugin authors - not to pick on the more high-profile developers per se, but rather to determine the state of license inclusion in plugins developed by those who, ideally, should be leading by example.

Here's what I found:

Matt Mullenweg

Plugins:
Notes:
  • bbPress was originally a stand-alone script, that included a license.txt file.
  • SyntaxHilighter Plus was written by Viper007Bond, but credited to Matt.
  • Top Comments was written by Andrew Ozz.
  • Sympathy For The Devil was written by Jeff Schult
Summary:

(0/19) of Matt Mullenweg's plugins written as a plugin and maintained by him have license notice of some kind. Shockingly, the majority of Matt's plugins lack even a readme.txt file.

Mark Jaquith

Plugins:
Summary:

(13/21) of Mark Jaquith's plugins have license notice of some kind (including one with both a license.txt file and plugin header license notice).

Ozh

Plugins:
Summary:

(0/16) of Ozh' plugins have license notice of some kind.

Peter Westwood (westi)

Plugins:
Summary:

(4/9) of Westi's plugins have license notice of some kind (including one with both a license.txt file and plugin header license notice).

Viper007Bond

Plugins:
Notes:
  • SyntaxHighlighter Evolved includes license.txt file from original SyntaxHighlighter written by Andrew Ozz
  • SyntaxHighlighter Plus includes license.txt file from original SyntaxHighlighter by Alex Gorgatchev
Summary:

(11/33) of Viper007Bond's plugins have license notice of some kind.

Overall Summary

Overall, for the plugin authors listed, only 28 out of 107 plugins (26%) have license notice of some kind (including two plugins that have both a license.txt file and a plugin header license notice). And the number is only that high thanks to Mark Jaquith, without whom the percentage of plugins with license notice of some kind would drop to less than 18%. Only 2 out of 107 plugins (<2%) include both a license.txt file and license information in the plugin header.

I find these numbers to be downright shocking, considering the unwritten rule now being enforced regarding removal from the repository of plugins that lack license disclosure, as well as the assertion that plugins should "ideally" include a license.txt file.

Let me be clear: I fully support the effort to ensure that plugin authors explicitly disclose license information in their plugins, either in the plugin header or in a separate license.txt file. The assertion that users need to be given explicit explanation of their rights to use, modify, and distribute plugins.

That said, perhaps those in the WordPress project leadership, and the plugin developers whom others look up to, should ensure that they are leading by example before a more-strict interpretation of the Plugin Repository guidelines is enforced against plugin developers at large.

Further, since new plugin developers will likely refer to the official wordpress.org Plugin Repository Readme File standard (which currently is silent on the matter of license disclosure) when determining what information needs to be included with their plugins, I would recommend that the standard be modified to include a License section - perhaps something like such:

== 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 2 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.

This way, new plugin authors would have a standard means of disclosing license information in their plugin - and also, users searching Extend for new plugins would have a known means of determining the license of any given plugin.

What are your thoughts?

MaxBlogPress and the WordPress Plugin Repository

Filed in Web DevelopmentTags: Geekery, GPL, Plugins, WordPress

Related Plugins

Automatically activate all MaxBlogPress plugins:

Forks of existing MaxBlogPress plugins, with registration/subscription activation removed:

The So-Called WordPress "Ban Hammer"

The latest WordPress-related minor controversy involves the removal of MaxBlogPress plugins from the WordPress plugin repository. The action appears to be in response to numerous complaints and calls for their removal, due to the behavior of the plugins.

The news of the removal of MBP plugins from the WordPress repository first appeared on the Warrior forum (h/t WPKid), and then on WPTavern, the WPTavern forum, and BloggingPro.

The removal took place presumably for violation of the guidelines for hosting a plugin on the WordPress repository:

  1. Your plugin must be GPL Compatible.
  2. The plugin most not do anything illegal, or be morally offensive (that’s subjective, we know).
  3. You have to actually use the subversion repository we give you in order for your plugin to show up on this site. The WordPress Plugins Directory is a hosting site, not a listing site.
  4. The plugin must not embed external links on the public site (like a "powered by" link) without explicitly asking the user's permission.

The MBP plugins, which offer a range of functionality from basic blog management (Ping Optimizer, Different Posts Per Page, Multi Author Comment Notification, Duplicate Post Checker) to internet-marketing tools (Stripe Ad, Unblockable Popup, Optin Form Adder, SEO Post Link, etc.), exhibit some abnormal behavior for WordPress repository-hosted plugins.

MaxBlogPress Plugin Behavior

All MBP plugins behave as follows:

  • Upon installation of the plugin, a notification message is displayed on the Plugin Management page, indicating that the plugin needs to be registered.
  • The options page, instead of displaying plugin options, first displays a two-part registration form. The first form requires a name and email address.
  • Upon submission of this form, the user receives an email list subscription confirmation email. The user is required to click the link in the email to confirm their subscription. Until the user does so, the plugin options page displays only a message that the email must be responded to.
  • Once the user clicks the link in the email, confirming the double-opt-in of the email list subscription, returning to the options page will, before displaying the plugin options, perform an update check - not through the WordPress SVN that hosts the plugin, but from the MaxBlogPress web site. If an update is available, the plugin updates itself.
  • Only then does the options page (finally) display the plugin options, and enable use of the plugin.

The plugins are problematic in further ways:

  • Not all MBP plugins hosted at the WordPress repository were released under the GPL (or a compatibile license).
  • Some MBP plugins embed external (i.e. public-facing) "powered by MaxBlogPress" links on users' blogs.
  • Many users complained that the email list to which they were forced to subscribe behaved in a "spammy" manner, sending far too many emails (daily or near-daily) with content that only marginally (if at all) had anything to do with the plugin they installed.
  • If you unsubscribe from the email list, existing MBP plugins will continue to work, but if you install any new MBP plugins, you will have to re-register, including re-subscribing to the email list from which you already unsubscribed.

Problems With MBP Plugin Behavior

There are several problems with this behavior for a WordPress repository-hosted plugin:

  • This behavior violates the GPL under which the plugins were released.
  • This behavior uses WordPress repository SVN merely for listing, rather than for hosting.
  • Some have speculated that this behavior may be illegal in some jurisdictions. (Note: this speculation is outside the scope of this blog post.)
  • This behavior violates the guideline against embedding external "powered by" links.
Violates GPL

The GPL defines itself as a free-software license, and defines the term "free software". From the preamble of the GNU GPL v2 [emphasis added]:

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

To make this point even more explicit, from the GNU.org website GPL FAQ:

See the definition of free software. The GPL is a free software license...

And from that linked definition of free software [emphasis added]:

The freedom to run the program means the freedom for any kind of person or organization to use it on any kind of computer system, for any kind of overall job and purpose, without being required to communicate about it with the developer or any other specific entity. In this freedom, it is the user's purpose that matters, not the developer's purpose; you as a user are free to run the program for your purposes, and if you distribute it to someone else, she is then free to run it for her purposes, but you are not entitled to impose your purposes on her.

Comparing the behavior of MBP plugins to this definition of "free software":

  • Requiring "registration" or "activation" in order to use an MBP plugin is clearly a violation of this principle.
  • Requiring email-list subscription (forced opt-in) in order to use an MBP plugin is clearly a violation of this principle.

So, compelling the user to register/"activate" software is a violation of the GPL - much less, requiring the user to opt-in to an email list. Such functionality clearly violates the user's freedom of use of the software without being required to communicate about it with the developer or any other specific entity. Thus, as written and distributed by MBP, the plugins in question do not conform to the GPL, which by its own definition is a free software license.

Released Under GPL-Incompatible License

Further, some of the MBP plugins hosted by the WordPress repository were released under licenses that are not compatible with the GPL.

For example, MaxBlogPress Stripe Ad is released under the following license:

MaxBlogPress Stripe Ad Library
End User License Agreement
Copyright (c) 2008, Pawan Agrawal
All rights reserved.

By using the software, you agree to be bound by the terms of this license.

1. You may install and use the software on as many computers and websites/blogs as you wish. You may make back-up copies of the software for archival purposes.

2. You can distribute this software in its original form with any other products or stand alone.

3. You are not allowed to use this script library for creating any other software or plugin without expressed permission from us.

4. The software is protected by the copyright laws of the U.S. and other countries, and we retain all intellectual property rights in the software. You may not separately publish, sell, market, distribute, lend, lease, rent, modify, reverse engineer or sublicense the software code.

5. You must not make any modification to the software without express permission from us. If there is a feature you want included or a bug you want fixed, let us know.

Such a license is clearly incompatible with the GPL, and any plugin released under such a license should never be allowed in the WordPress repository.

Using WordPress SVN to List Rather Than to Host

The MBP plugins circumvent the built-in plugin-update functionality of WordPress, and instead query the MBP web site for updates. Consider the following update function from the Multi Author Comment Notification plugin:


function mcnExtractUpdateData() {
$arr = array();
$version_chk_file = "http://www.maxblogpress.com/plugin-updates/multi-author-comment-notification.php?v=".MCN_VERSION;

The plugin then uses this version check to determine if an update is available, downloads the update, and installs it - entirely circumventing the WordPress repository.

All MBP plugins have this same functionality. Thus, essentially, the plugins are using the WordPress repository merely as a listing site - a means to allow users to search for their plugins (or tags/keywords for their plugins) in order to get more exposure and more users installing their plugins.

Such exposure is intended to be a benefit of hosting a plugin on the WordPress repository, not the sole purpose - which is why the guidelines explicitly state that the repository is for hosting, not merely for listing.

Embedding External "Powered By" Links

But to go even further, some MBP plugins - including Stripe Ad - place a publicly visible "powered by MaxBlogPress" text/link, which explicitly violates WordPress repository guideline #4. And to make matters worse, the author actively seeks to enforce his non-GPL license to prevent users from removing such text from the front end of their blogs:

No it’s NOT licensed under GNU GPL. We are using that format of readme.txt as most people are familiar with that.

It’s illegal to remove the powered by link without notifying me about that.

My Response

While the response from WordPress was the removal of the MBP plugins from the WordPress repository, I decided to respond on my own.

When the news broke, several people suggested that the plugins should be forked, to remove the offending code. I thought that this suggestion would make for a good challenge, so I undertook it.

As it turns out, removing the offending code from the plugins proved to be incredibly easy. Thus, forks of Favicon, Ping Optimizer, Multi Author Comment Notification, and Different Posts Per Page are now available from the WordPress repository.

Further, as pointed out by Blogging Pro (linked above), making the MBP plugins think that they are activated is as easy as updating a single database option for each plugin. It seemed like it would be fairly easy to loop through each plugin's option, and set it to the appropriate value, and I thought that some enterprising plugin author should whip up a plugin to do so.

As it turns out, I ended up being that plugin author (with a great deal of help from my friends at the WPTavern forum). As a result, I have also released cbnet MBP Auto-Activate, which, when installed, will determine which MBP plugins are installed, and automatically activate them, without needing to register or subscribe to the email list. The plugin will auto-activate any MBP plugins subsequently installed, also.

If you use MBP plugins, please let me know if you find any of these plugins to be useful. And if I can improve them, please let me know that, as well.