Spam

Spam has become the bane of the connected world. Whilst it is simple for a human to be able to decide if an object (email, comment, trackback) is Spam, it appears that computers have a harder time of it. Perhaps the most surprising thing is that Spam exists at all. It relies on some sort of a payoff for the Spammer: for email Spam this must mean that people actually buy the crap they advertise. My Gmail Spam folder is bulging as we speak: I seem to get around 400 Spam emails per month. Almost all of these are automatically picked up by Gmail’s Spam filter - but this filter also picks up several Ham (or legitimate emails) in the average month. The majority of these are bounced emails: almost by default all bounced replies end up as Spam, since a common occurrence seems to be for Spammers to use real (other people’s) email addresses as the From: address, resulting in a whole bunch of Undeliverable Mail messages bouncing back to that person. Usually, this is not a problem, as most people I email I get the address right. However, as I have just taken over administration of a Sporting Organisation’s member list, I now get several bounced emails each time I do a mailout - I’m going through pruning addresses each time, but people’s addresses are often lapsing. Profit from comment and trackback Spam is a little different. Rather than getting people to buy things from them, these Spammers rely on clicks or links to make money. The second has been largely overcome in many circles by the rel=“nofollow” tag, however, unless use of this is universal, Spammers may still make money. The way linking makes money is largely by improving PageRank™, or prestige in Search Engines. The first one relies on users to click on links, and visit sites. If enough users do this, then it could concieveably make a significant amount of money for the Spammers. I’ve had some comment and trackback Spam where the URI isn’t even valid, so I’m not so sure how well this works for Spammers. I seem to have overcome the issues I had with comment Spam - just requiring users to have JavaScript activated seems to be enough for now. And if the Spammers catch on, then I’ll re-implement the catchpa. That leaves trackback Spam. This is the one that is causing me the most grief now. At least it’s not as bad as email Spam (in volume, anyway) on my blog. I’ll get around about 6-10 Spam trackbacks in a given month. I’m not sure how many Ham trackbacks I get - not too many, IIRC. I may turn trackbacks off, if I cannot come up with a decent solution.

Simplest Spam Comment Killer

There are two main tasks that are required to implement a Comment Spam Protection system. The first is to disable comments for all people. The second is to enable it for people who legitimately want to comment. A very simple way to solve the first task is to remove the action from the form tag. In my template, the code looked like this:

    <form action="{$siteurl}/wp-comments-post.php" method="post" 
        id="commentform" name="commentform">
    ...
    </form>

(Obviously, I’ve removed most of the code for brevity.) Deleting the contents of the form action works to a certain extent, except it creates invalid code. Another option is to replace the action with another URI, such as http://www.google.com. The second task is to re-enable it where appropriate. Since most Spammers use an automated system of some sort to generate comments, and these bots don’t use JavaScript, we can just write a JavaScript that puts the right value back in. This will prevent a user who has JavaScript disabled, or who is in an old browser, from leaving a comment. I’m prepared to live with this. Anyway, I don’t usually add script tags directly into a page, but in this case I will, just to make it easier. After the close of the form tag, insert the following:

1
2
3
    <script type="text/javascript">
        document.forms[0].action="{$siteurl}/wp-comments-post.php";
    </script>

Of course, if this isn’t the first form in your document, for example your search form appears in the source before this one, then you’ll need to change the number of forms[_x_] accordingly. You could try using document.getElementById("commentform").action, but I think there’s an issue with this method not being available until the DOM is complete, which it isn’t at this stage.

Interactionless Catchpa

Over on this site, I read about a system that seems to be a bit like mine (only perhaps more complicated). It did give me one idea though: I could automatically populate the value into the box. Or even more simply, have the JavaScript simply fix up the action="" attribute. Then the user just needs to set the action attribute to a whitespace string, and then the JavaScript will make it possible for the reader to submit comments. I’m not sure if this would really be all is needed to prevent Spam comments. Requiring JavaScript to be on… • And trackbacking it gave me another idea: setting the action on a trackback URI to copy the URI to the clipboard. That would be cool. Or making the ‘LinkThis’ bookmarklet get the trackback URI from the page. Should be able to look for a link with rel="trackback", and use the href from this. Okay, this bit works, but again I’ve run into the problem that I’m trying access an ‘off-site’ document. And I cannot do that with JavaScript.

1
2
3
4
5
    var a = document.getElementsByTagName('a');
    var tb = '';
    for (var i=0;i<a.length;i++){
        if (a[i].rel=="trackback") 
            tb=a[i].href;};

That code fragment will get the trackback URI. But there’s no argument I can pass to the bookmarklet.php file that will insert this automatically. Something to add to the Blogsome wishlist, I guess.

New Script Online

Note: there is a newer post you should read instead of/as well as this one: Updated Toolbox Scripts. I’ve finished the Mozilla part of the new script: I think there’s some issues with Internet Explorer, and Safari, but I’ll work on those over the next week. You can use the script by including the following code in the head section of your main page template: <script type="text/javascript" src="http://schinckel.net/images/toolbox.jpg"></script> And then, where you want the following tags, enter them: <!--quicktags--> <!--catchpa--> <!--commentpreview--> <!--resize--> If you put the following inside your comment area, a gravatar will appear for each commenter: {capture name=reader}{comment_author_email}{/capture} <div class="gravatar">{$smarty.capture.reader|encode:"hex"}</div> There’s also the functionality to change post and comment times to a more human readable form: <span class="post_time"> Posted at: 12:34pm</span> Comment times are a bit more difficult: they require a time difference. Getting this on Blogsome is cumbersome at the moment, so I’ll write up a better way of doing it later. At the momement it uses the value obtained through a complicated {Smarty} expression. If you are really desperate, you can find this expression elsewhere on this blog, and put it into a tag as follows: <span class="timesince"><!--{$since}--></span> I’ll write it up so you can just put in <!--{the_time d="U"}-{comment_date d='U'}-->, but that won’t be for a day or so. The other feature I have added is automatic: I’ve got seperate Comments & Trackbacks, and the script will automatically hide whichever one is empty, if the other one isn’t. I will write up a way to seperate Comments & Trackbacks using DOM management, but, again, not right now. Finally, all of the stuff on this page could seriously screw with your template/browser. It is still in beta, and doesn’t work at all in some browsers. I did kill a bug that caused a hang on Firefox Mac, but there may be more of them. Make sure you have a backup of your template, and don’t do this unless you know how to fix it up…

Running JavaScript Before onload.

JavaScript has a pretty neat feature. Lets say I have some functions that need to be run when the page has finished loading. Say, like replacing some encrypted email addresses with the gravatar they are associated with. Since you want all of your JavaScript to remain in the external.js file, and not have to add anything other than the one call to the file in the header of the page, you need a method of knowing when the page has finished loading. By setting up as follows, you can do this:

1
2
3
4
    window.onload = function () {
        // Put code or calls to functions in here
        SetupStuff();
    }

This has a couple of disadvantages:

  1. If another script called after yours also has an assignment to window.onload, only their code will run.
  2. Alternately, if you are running after another script, you will replace any assignments they have made to window.onload.
  3. This will only be called when the whole page has run, including any images that need to be downloaded.

Various solutions for the second problem have surfaced, including testing window.onload, and appending to it instead of replacing it, but this does not fix either of the other two problems. Another solution is to have a function call at the end of the HTML code, but this could also fail, since the DOM is not yet complete. I have found a better solution, located at brothercake. It’s not really clear how to use it on that page, but the source is straightforward. What I did was include the text of the domFunction.js file, and right at the bottom of the script, included this line:

1
    var myStartup = new domFunction(Startup);

Right at the top of my code, I have a function called Startup, which contains all of the function calls I want to happen, in the order they need to. Much better than waiting for all of the images to load, even if most of them are cached.

Enlarge Text Area.

Last update for tonight: <a class='button' onclick='javascript:document.getElementById("the_id").rows+=10;'> Make Text Box Bigger </a> And similar for Make Text Boxes Smaller. It’s working on my comment box as we speak. Try it out. (Thanks to Dunstan).

Show/Hide JavaScript Code

There’s a little one-liner I’ve just learned for showing or hiding the contents of a particular <div> tag. (May also work for other types of tag). The stuff you want to show or hide needs to have an id attribute. I use it to hide some stuff if there are no Comments or Trackbacks, since I’ve seperated them, and this was the best way to hide the title.

1
2
3
    <div id="the_id">
        Text/Data To Hide Goes Gere
    </div>

Then, you can use this code to hide it (in an {if} block, for instance):

1
2
3
    <script type="text/javascript">
        document.getElementById("the_id").style.display = "none";
    </script>

And this to show it:

1
2
3
    <script type="text/Javascript">
        document.getElementById("the_id").style.display = "block";
    </script>

I haven’t tried, but it’s also probably possible to have show/hide links:

1
2
3
4
5
6
7
8
9
    <div id="the_id">
        Text/Data to Hide Goes Here.
    </div>
    <a onclick="javascript:document.getElementById('the_id').style.display = 'none'">
        Hide Text
    </a>
    <a onclick="javascript:document.getElementById('the_id').style.display = 'block'">
        Show Text
    </a>

Of course, if you want to hide text in-place (without rearranging the flow of the remainder of the text), you’ll need:

1
2
3
4
5
6
7
8
9
    <div id="the_id">
        Text/Data to Hide Goes Here.
    </div>
    <a onclick="javascript:document.getElementById('the_id').style.visibility = 'hidden'">
        Hide Text
    </a>
    <a onclick="javascript:document.getElementById('the_id').style.visibility = 'visible'">
        Show Text
    </a>

'Fixed' XHTML Export

Since I need to replace all " in the generated XHTML with ‘, I use the replace function from the previous post.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    (* 
    Bugs:
    
    Does not like no selection: no real way to get the selection from SEE anyway.
    Sometimes does not execute if called from Script Menu.  Intermittant.
    *)
    
    -- If called from Script Menu, need to do this.
    
    tell application "SubEthaEdit"
    	activate
    	tell application "System Events" to keystroke "C" using {command down}
    end tell
    
    set theStart to (the clipboard)
    
    set the clipboard to (my replace(theStart, "\"", "'"))
    
    beep
    
    -- Another way of the Replace Function being called:
    
    -- set the clipboard to (replaceText from theStart to "'" instead of "\"")
    
    on replace(theText, find, replace)
    	set OldDelims to AppleScript's text item delimiters
    	set AppleScript's text item delimiters to find
    	set newText to text items of theText
    	set AppleScript's text item delimiters to replace
    	set theResult to newText as text
    	set AppleScript's text item delimiters to OldDelims
    	return theResult
    end replace
    
    -- Alternate version of replace()
    
    to replaceText from theText to replace instead of find
    	set OldDelims to AppleScript's text item delimiters
    	set AppleScript's text item delimiters to find
    	set theText to text items of theText
    	set AppleScript's text item delimiters to replace
    	set theText to theText as text
    	set AppleScript's text item delimiters to OldDelims
    	theText
    end replaceText

Code Markup

I like to present styled code for my readers, but I’m not totally happy with my methods of getting it: SubEthaEdit’s Export as XHTML is very cool, but I still have to tweak it so it looks nice. And I’ve got a great program for getting the XHTML version of the code Script Editor (AppleScript) is currently displaying. But, I’d like more flexibility. I want the code to be tagged with classes, like it’s possible to do with the Script Editor add-on, rather than using inline styles. I’ve already done this for the Script Editor code, but I just need to remember to remove the inline stylesheet from the start of the generated code. Unfortunately the Script has been saved as a Run Only. I’d love to know a way to decompile one of these Scripts…apparently there isn’t. I’ll have to write my own. Or see if Jon is nice enough to give the Source Code. As for the SubEthaEdit part: I should be able to do it. I’ve learned a couple of tricks that will help.

Browser Incompatibilities

I’ve spent most of the day tweaking stuff, and have come across the following issues:

  • Safari has no ability to find either the cursor position or the selection in a TextArea. This means that Quicktags will not work under Safari. I’m going to file a bug report with Apple over this one, as it’s rather crap.
  • Neither Safari nor Mozilla support onTimer(), which means my plan to have the Comment Preview update every 3 seconds rather than on every keypress will not work. I still need to try to find a way to make the Preview faster on Mozilla. It’s fine on Safari.

I’ve also spent a fair amount of time playing around with trying to get comments to appear on the front page, but I think I need to be able to access the SQL database directly, and run a query in order to do this. And store the result in a variable. But I can’t figure out how (if it’s even possible) to do this.