Comment/Trackback Display

I’ve made some changes on my blog template so Trackbacks are handled a little bit nicer: specifically, Trackbacks/Pingbacks now also get the “owner” class. I’ve also turned off Gravatars for Trackbacks/Pingbacks, since they don’t make that much sense. I’d like to use a favicon or something in their place, but I’m not yet sure how to do this. At this stage, I’ve left in code so that the Gravatar for the Author Email will appear for owner trackbacks/pingbacks. Here is the code I am using as of now for my Comment Loop:

    {* Variables that don't change (post/blog) *}
    {capture name=author_email}{the_author_email}{/capture}
    {capture name=author}{the_author echo=false}{/capture}
    {capture name=blog_title}{bloginfo show='title'}{/capture}
    
    
    {if $comments != ''}
      <ol id="commentlist">
      {foreach from=$comments key=key item=comment}
            {globalvar var='comment' value=$comment}
    
            {* Setup for Owner Comments, including Pingbacks & Trackbacks *}
            {capture name=comment_type}{comment_type}{/capture}
            {capture name=self_ping}{comment_type}@{bloginfo show='url'}{/capture}
            {if $comment->comment_author_email == $smarty.capture.author_email or $comment->comment_author_email == $smarty.capture.self_ping|lower|replace:"http://":"" or $comment->comment_author == $smarty.capture.blog_title}
                {assign var=owner value=owner}
                {assign var=comment_author_email value=$smarty.capture.author_email}
            {elseif $smarty.capture.comment_type == "Comment"}
                {assign var=comment_author_email value=$comment->comment_author_email}
            {else}
                {* Off-site Trackback/Pingback.  Get an image from their site? *}
            {/if}
            
            {* Gravatar Allocation.  Uses variable $comment_author_email from above. *}
            {if $comment_author_email != ""}
                {assign var=gravatar value=$comment_author_email|gravatar:"http://schinckel.net/images/NoGravatar.gif":"40":"":"0"}
                {assign var=comment_author_email value=""} {*Reset for next comment*}
            {/if}
    
            {* Set up for Alternating Comment Styles *}
            {if $alt != 'altcomment'}
                {assign var='alt' value='altcomment'}
            {else}
                {assign var='alt' value=''}
            {/if}
    
            <li class="{$owner} {comment_type} {$alt}" id="comment-{comment_ID}">
                <div class="right">{$gravatar}</div>
                {comment_author_link} said: 
    
                {if $comment->comment_approved == "0"}{*Not implemented yet.*}
                    <p><em>Your Comment is awaiting Moderation.</em></p>
                {/if}
    
                {comment_text}
    
                <!-- Admin Tools -->
                {capture name=edit_comment}{edit_comment_link link="&#9997; Edit" before='<span class="button">' after="</span>"}{/capture}
                {if $smarty.capture.edit_comment != ""}
                    <div class="right">
                        {$smarty.capture.edit_comment}
                        <span class="button"><a href="http://schinckel.net/wp-admin/post.php?action=confirmdeletecomment&comment={comment_ID}">&#10007; Delete</a></span>
                    </div>
                {/if}
        
                <a href="#comment-{comment_ID}"><span class="timesince" id="c{the_time d="U"}-{comment_date d='U'}">on {comment_date} at {comment_time}.</span></a>
        </li>
      {/foreach}</ol>
    
    {else} <!-- this is displayed if there are no comments so far -->
        <p class='indented'>{_e text="No responses yet."}</p>
    {/if}

Cosmic Girl • Jamiroquai • Travelling Without Moving ★★★

Hosting Scripts on Blogsome

Blogsome is great. You can blog, upload up to 25Mb of images, but unfortunately, you can’t upload your own JavaScripts and use them from the Blogsome server. Or can you? It is actually possible to host a JavaScript on your Blogsome blog, you just need to be a bit tricky. If you have a script called myscript.js, and you try to Upload it using the standard Upload menu item, it will fail. Blogsome only allows files with the extensions .jpg, .gif and .png. Note well that they do not need to be that type of file, but only have that extension. I think you know what I’m going to do next, and that’s rename my file to myscript.js.jpg: leaving in the .js reminds me that this is a JavaScript file, and the .jpg allows me to upload it. Which I did. When you include the script in your template, all you need to use is: <script type="text/javascript" src="/images/myscript.js.jpg"></script> Browsers are smart enough to allow this to happen.

999 Forum Posts

I just noticed I’ve written 999 posts on the Blogsome Forums: Whoa, that’s a lot of helping! (And a little bit of telling off, too!) And in less than one year!

First Comment Spam in Ages

Well, I got a Comment Spam tonight, and as the title indicates, it’s the first Comment Spam I’ve had since I implemented my Captcha. I know it’s a Comment, not a Trackback, as I have code that discriminates between the two. If I continue to get them, I’ll have to either modify my Comment Spam protection, or look into using the Blogsome one, which I don’t really want to do, as it doesn’t ’look’ like my site when the page is loaded.

Edit/Delete Comment

At times, it’s annoying to have to go into the Dashboard, just to delete a Comment that has appeared, or to Edit a comment I have made (or fix the text of someone else’s comment). Luckily, you can create links or buttons to allow you to do these two jobs while viewing a Post page: ✍ Edit ✗ Delete In your Comments.html template, find the location you want to put the links. To just have the Edit Comment link (you can delete a comment from it’s editing page), insert the following:

1
    {edit_comment_link}

To have both an Edit and a Delete link, try the following:

    {capture name=edit_comment}{edit_comment_link link="&#9997; Edit"}{/capture}
    {if $smarty.capture.edit_comment != ""}
        {$smarty.capture.edit_comment}
        <a href="http://schinckel.net/wp-admin/post.php?action=confirmdeletecomment&comment={comment_ID}">Delete</a>
    {/if}

I have mine all nicely styled, using the following (and some CSS):

    {capture name=edit_comment}{edit_comment_link link="&#9997; Edit" before='<span class="button">' after="</span>"}{/capture}
    {if $smarty.capture.edit_comment != ""}
        <div class="right">
            {$smarty.capture.edit_comment}
            <span class="button"><a href="http://schinckel.net/wp-admin/post.php?action=confirmdeletecomment&comment={comment_ID}">&#10007; Delete</a></span>
        </div>
    {/if}

Displaying Comments awaiting Moderation

It’s a neato feature to be able to show a reader the comment they have just posted, but which is awaiting moderation before it it displayed to everyone else. Newer versions of Wordpress can do this, and many templates also have this feature built in, but it does not work on Blogsome. The code that grabs the Comment data is found, in Blogsome, in wp-inst/wp-comments.php:

1
2
        $query = "SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND comment_approved = '1' ORDER BY comment_date";
        $comments = $wpdb->get_results( $query );

This code was fairly easy to find, but the equivalent code in Wordpress was a bit tricker. Eventually, I found it in wp-includes/comment-functions.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
        if ( is_single() || is_page() || $withcomments ) :
            $req = get_settings('require_name_email');
            $comment_author = isset($_COOKIE['comment_author_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_'.COOKIEHASH])) : '';
            $comment_author_email = isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_email_'.COOKIEHASH])) : '';
            $comment_author_url = isset($_COOKIE['comment_author_url_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_url_'.COOKIEHASH])) : '';
        if ( empty($comment_author) ) {
            $comments = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND comment_approved = '1' ORDER BY comment_date");
        } else {
            $author_db = addslashes($comment_author);
            $email_db  = addslashes($comment_author_email);
            $comments = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND ( comment_approved = '1' OR ( comment_author = '$author_db' AND comment_author_email = '$email_db' AND comment_approved = '0' ) ) ORDER BY comment_date");
        }

I had to modify the code somewhat, but here is the code I have put on the private Blogsome test server:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
        $comment_author = isset($_COOKIE['comment_author_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_'.COOKIEHASH])) : '';
        $comment_author_email = isset($_COOKIE['comment_author_email_'.COOKIEHASH]) ? trim(stripslashes($_COOKIE['comment_author_email_'.COOKIEHASH])) : '';
        $author_db = addslashes($comment_author);
        $email_db  = addslashes($comment_author_email);
        if ( empty($comment_author) ) {
            $query = "SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND comment_approved = '1' ORDER BY comment_date";
            $comments = $wpdb->get_results( $query );
        } else {
            $comments = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post->ID' AND ( comment_approved = '1' OR ( comment_author = '$author_db' AND comment_author_email = '$email_db' AND comment_approved = '0' ) ) ORDER BY comment_date");
            $comments = $wpdb->get_results( $query );
        }

It could probably be tweaked (for instance, moving the addslashes() calls to within the else block would save a couple of CPU cycles when no author is set), but it works!. You need to have the following in your Comments Template:

    {if $comment->comment_approved == "0"}
        <p>Your Comment is awaiting Moderation.</p>
    {/if}

I’m also going to style the comment box a little differently for unmoderated comments.

Fancy Archive Page

Ages ago, I wrote about how to (a) pass Pages through the Post template, and (b) use Smarty Tags in special Pages. I had an Archives page, that had a list of every Post I’d ever made. It’s getting kind of long, so I wrote a script that parses this list, and creates a structured list, dividing the posts up according to the month they were published. It then makes this list so that you can hide/show a whole month’s worth of posts in one go. In the Posts Template, I have the following lines:

    {elseif $smarty.server.REQUEST_URI == '/archives/' || $smarty.server.REQUEST_URI == '/archives'}
        <ul>{get_archives type='postbypost' limit='' format='html'}</ul>{$content}

Note that this is out of context: see my whole Posts template file for details. This is in the actual Page:

    <script type="text/javascript">
    var months = new Array("","January","February","March","April","May","June","July","August","September","October","November","December")
    function getYear(li){
        return li.innerHTML.split("/")[3]*1;
    }
    function getMonth(li){
        return li.innerHTML.split("/")[4]*1;
    }
    
    function showKids(id){
        var unit = getById(id);
        for (var i=1;i<unit.childNodes.length;i++){
            unit.childNodes[i].style.display="block";
        }
    }
    function hideKids(id){
        var unit = getById(id);
        for (var i=1;i<unit.childNodes.length;i++){
            unit.childNodes[i].style.display="none";
        }
    }
    function toggleKids(id){
        var unit = getById(id);
        if (unit.childNodes[1].style.display == "none")
            showKids(id)
        else
            hideKids(id)
    }
       
    function Restructure(){
        var pc = getByClass("post-content")[0];
        var items = pc.getElementsByTagName("li");
        var list = items[0].parentNode;
        var month_nodes = document.createElement("ul");
        
        var month_node = document.createElement("li");
        month_node.className = "month";
        month_node.innerHTML = months[getMonth(items[0])] + " " + getYear(items[0]);
        month_node.id = month_node.innerHTML.replace(" ","_");
        month_node.innerHTML = "<h3><a onclick='toggleKids(\""+ month_node.id +"\");'>" + month_node.innerHTML + "</a></h3>";
        month_nodes.appendChild(month_node);
        
        var this_month;
        var next_month;
        
        while (items.length) {
            this_month = items[0];
            next_month = items[1];
            month_node.appendChild(this_month);
            if (next_month && (getMonth(this_month) != getMonth(next_month))){
                month_node = document.createElement("li");
                month_node.className = "month";
                month_node.innerHTML = months[getMonth(next_month)] + " " + getYear(next_month);
                month_node.id = month_node.innerHTML.replace(" ","_");
                month_node.innerHTML = "<h3><a onclick='toggleKids(\""+ month_node.id +"\");'>" + month_node.innerHTML + "</a></h3>";
                month_nodes.appendChild(month_node);
            }
        }
        
        pc.replaceChild(month_nodes,list);
        
        var all = getByClass("month")
        for (var i=0;i<all.length;i++) hideKids(all[i].id);
    }
    
    Restructure();
    </script>

You’ll notice a couple of strange comments, and what looks to be a closing all tag: these are because the Blogsome post editor kindly closes tags for me, even when they aren’t tags!

Movin’ Out (Anthony’s Song) • Billy Joel • Greatest Hits ★★

I’m off to Karaoke now, I might have to sing that song!

Better Display of HTML as Source

I spent hours last night working on this, and a fair bit of time today. I think I’ve got it pretty good, now. See my Templates page for an example. Now, instead of trying to get the data to appear immediately, which I could not do, I have four buttons that the user can select between the various template files, and it shows them in the <textarea> underneath.

 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
    <script type="text/javascript">
        var req1 = new XMLHttpRequest(); 
        req1.open("GET", "/templates/index.html",true); 
        req1.send(""); 
        var req2 = new XMLHttpRequest(); 
        req2.open("GET", "/templates/post.html",true); 
        req2.send(""); 
        var req3 = new XMLHttpRequest(); 
        req3.open("GET", "/templates/comments.html",true); 
        req3.send(""); 
        var req4 = new XMLHttpRequest(); 
        req4.open("GET", "/templates/wp-layout.css",true); 
        req4.send(""); 
    
    function Display(text){
        document.getElementById('template').innerHTML = text;
    }
    </script>
    
    <p>This is the current, up to date version of my conversion of Patricia Müller's Connections Template for Blogsome.</p>
    
    <p>There are four files that Blogsome uses, you need to ensure that the correct data goes into each.  Each one also has, by default some features enabled.  There is commenting throughout that will enable you to disable that which you do not want.</p>
    
    <p><a class="button" onclick="Display(req1.responseText);">Main Page</a> 
    <a class="button" onclick="Display(req2.responseText);">Post</a> 
    <a class="button" onclick="Display(req3.responseText);">Comments</a> 
    <a class="button" onclick="Display(req4.responseText);">Style Sheet</a></p>
    
    <p><textarea id="template" wrap="off"  style='width:95%;height:500px;'>Click on a button above to display the template contents.</textarea></p>

I probably could have saved the data to variables, and reused the XMLHttpRequest item, but then I would have had to find some way to wait for the response code, and that kept failing for me.

Kissing You • Des’ree • ★★★½

Displaying HTML as Source in a Web Page

Now, I’m not talking here about using the “Show Source” feature of a Web Browser. Anyone worth even a quarter of a pinch of salt knows how to do that. What I’m trying to do is display the source of a web page as text/plain within another web page. This is so I can have a current version of my template available on a page of my blog. I’ve put a version up that I need to update, and I make so many changes to the template this is unsustainable.

Under Blogsome, the template files can be found in the /templates/ directory, and there are four of them: index.html, wp-layout.css, post.html and comments.html. Of these, only wp-layout.css is a plain text document, the rest are (naturally) text/html.

Now, HTML has a nice little feature called <object>, which should be able to display anything the browser can render. For example:

<object data="/templates/index.html"> </object>

Will display a rendering of the Main Page template. It looks somewhat ugly, since it’s not been pre-processed by the Smarty Templating engine, so is somewhat useless - it’s neither the source nor the full result:

This element also has an attribute that should allow you to choose the content type: codetype.

<object data="/templates/index.html" codetype=“text/plain”> </object>

This to me should be now rendering the object as a plain text document. But no, it still renders the HTML:

1
<object data="/templates/index.html"codetype="text/plain"></object>

I fiddled around with this for about an hour or so last night, trying to get it to work. The DOM tells me it’s text/plain, but the browser won’t render it as such.

So, after much more rooting around, I finally figured out a way to get the source to display: use XMLHttpRequest() to grab the data, and DOM manipulation to put it into the required location. This is what my page looks like:

 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
46
47
48
49
50
    <h3 class="comments">Main Page (index.html)</h3>
    <textarea id="index.html" wrap="off"  style='width:95%;height:200px;'>
    </textarea>

    <h3 class="comments">Style Sheet (wp-layout.css)</h3>
    <textarea id="wp-layout.css" wrap="off" style='width:95%;height:200px;'>
    </textarea>

    <h3 class="comments">Post (post.html)</h3>
    <textarea id="post.html" wrap="off" style='width:95%;height:200px;'>
    </textarea>

    <h3 class="comments">Comments (comments.html)</h3>
    <textarea id="comments.html" wrap="off" style='width:95%;height:200px;'>
    </textarea>

    <script type="text/javascript">

    var div1 = document.getElementById("index.html");
    var req1 = new XMLHttpRequest();
    req1.open("GET", "/templates/index.html",true);
    req1.send(""); 

    var div2 = document.getElementById("post.html");
    var req2 = new XMLHttpRequest();
    req2.open("GET", "/templates/post.html",true);
    req2.send(""); 

    var div3 = document.getElementById("comments.html");
    var req3 = new XMLHttpRequest();
    req3.open("GET", "/templates/comments.html",true);
    req3.send(""); 

    var div4 = document.getElementById("wp-layout.css");
    var req4 = new XMLHttpRequest();
    req4.open("GET", "/templates/wp-layout.css",true);
    req4.send(""); 

    function Source(){
        if (req1.status+req2.status+req3.status+req4.status == "800"){
            div1.innerHTML = req1.responseText
            div2.innerHTML = req2.responseText
            div3.innerHTML = req3.responseText
            div4.innerHTML = req4.responseText
        } else
            setTimeout("Source();",500);
    }

    Source();
    </script>

It’s not quite perfect: there are still some issues with the timings, but I’ll get there. At least it works, now. You may need to type javascript:Source() into the address bar to get it to work.

All I need to do now is some syntax styling!

Fixing Generated Invalid XHTML

I had a standard Smarty function, {list_cats}, that under certain circumstances generates invalid XHTML. Specifically, when a feed_image is supplied, not alt tag is generated. This annoys me, as I have worked hard to make my template generate “nice” code. Smarty to the rescue. Whilst you cannot pass the results of a Smarty function through a Smarty filter, like replace, you can capture the results, and then pass this through the filter:

    <h2><a href="/categories/">Main Categories</a></h2>
    <ul>
        {capture name=cats}{list_cats optionall='1' all='All' sort_column='name' optioncount='0' children='0' hierarchical='1' feed_image='/images/feedicon10x10.gif'}{/capture}
        {$smarty.capture.cats|replace:"/></a>":"alt='Live Bookmark (RSS Feed)' /></a>"}
    </ul>

By the way, I used a similar technique to convert a list of (list) items into a list of selects:

    <h2>Links Dropdown</h2>
    <ul>
        <li><form name="linksform" action="" method="post" style="margin: 0px; padding: 0px;">
            <select id="link_list">
            <option value=''>Select Link to View</option>
            {capture name="linklist"}{get_links}{/capture}
            {$smarty.capture.linklist|replace:'<a':'<option'|replace:'</a><br />':'</option>'|replace:'href':'value'}
            </select>
            <input type="button" class="button" value="Go" onclick="window.location = (document.forms.linksform.link_list[document.forms.linksform.link_list.selectedIndex].value);" />
        </form></li>
    </ul>