Blogsome Templates

Say you find a Blogsome site, with a template you really like. You ask the author for the data, but they don’t reply to you. Never fear. You can always get the data from a Template, as this data is world-readable:

You may need to right-click and Save As…, or view source to see them properly. Obviously, for blogs other than mine, replace _schinckel _with the relevant word… I’m working on getting this so the source of the current templates can be viewed in another web page, but there are some issues with this. The iframe/object will only display the rendered value, and CSS files don’t display at all.

{todayayearago}

Another Blogsome user was wanting to use {todayayearago} to get posts from a week ago, which reminded me I do it with a month ago. Looking through the source to see if a weekly one exists (it doesn’t, as yet), I came across this little nugget:

1    function todayayearago( $when, $wpblog, $spacer ='<br /' )

Now, ignoring the apparent typo (I think there should in fact be a closing tag to that there BR tag…), what do you see that is notable? That’s right, you can choose a different blog. So I tried this with my test blog:

 1    {todayayearago when='month' wpblog='schinckel'}
 2    {if $todayayearago != ''}
 3        <h2>A Month Ago</h2>
 4            <ul>
 5                {foreach from=$todayayearago key=id item=details}
 6                    <li>
 7                        <a href="{get_permalink id=$id}" title="{$details.content|truncate:25:"..."}">
 8                            {$details.title|truncate:20:"..."}
 9                        </a>
10                    </li>
11                {/foreach}
12            </ul>
13    {/if}

Now, this gives some very interesting results. Firstly, it works, except for the part that tries to get the permalink:

href="{get_permalink id=$id}"

This fails, because it (naturally) tries to grab the permalink from the current blog, not the other one. My next step was to see if {get_permalink} can handle an argument like wp_blog. The sad news is it can’t. So, the next question is: does the URL get returned in the data structure along with the content and title. I suspect so, but need to see how. Looking through the source of wp-db.php doesn’t throw much light on the topic, so it’s time for trial and error. That’s what a test blog is for, after all! Aha! You can see most of what happens from the todayayearago file itself:

1    foreach( $reqhistory as $row )
2    {
3     $todayayearago[ $row->ID ] = array( "title" => strip_tags( stripslashes($row->post_title) ),
4      "content" => strip_tags( stripslashes( $row->post_content ) ) );
5    }

Which clearly shows that only the title and content are ‘grabbed’. So, until I rewrite this function (so it includes the URL, and perhaps even so it allows for other intervals), it’s use is still limited to posts within your own blog, from either a year or a month ago. Unless I can find a way to get post data from another blog’s database…

Interesting WordPress Smarty Functions

Here are some interesting Smarty equivalents of WordPress functions I hadn’t come across before:

  • {get_lastpostdate} or {get_lastpostmodified} - returns a timestamp, such as 2005-10-16 07:10:51 that contains the last post date. {get_lastcommentmodified} is identical, but for the last comment.
  • {human_time_diff from=n to=n} - similar to a script I wrote about, and implemented. Limited in that it only does days, not any larger unit. Advantage: done by the server, doesn’t ‘flash’ changing text via JavaScript, neater than my Smarty version.
  • {get_day_link} - much neater than the version I was using! Generates the URI as described. Also {get_page_link}, {get_year_link}, {get_month_link} and {get_feed_link}.
  • {globalvar var=varname value=value} - makes a value a global PHP variable. Very interesting…

Gravatar Smarty Modifier

I wrote a Gravatar modifier for Smarty: {$comment->comment_author_email|gravatar:"default":     size:"rating":border} For example, to use no default image, and limit the size to 40x40 pixels, you’d use: {$comment->comment_author_email|gravatar:"":"40":"":""} Notice the double quotes where the argument is missing, and that (in this version) you actually need to include all of the arguments. (I’ve updated this, but haven’t tested it fully yet). Anyway, here’s the code:

 1    <?php
 2    /**
 3     * Smarty plugin
 4     * @package Smarty
 5     * @subpackage plugins
 6     */
 7    
 8    
 9    /**
10     * Smarty gravatar plugin
11     *
12     * Type:     modifier<br>
13     * Name:     gravatar<br>
14     * Author:   Matt Schinckel<br>
15     *           mailto:matt@schinckel.net<br>
16     *           aim:mschinckel<br>
17     *           http://schinckel.net<br>
18     * Purpose:  convert email address to gravatar
19     * @param string
20     * @return string
21     */
22    function smarty_modifier_gravatar($email, $default=false, $size=false, $rating=false, $border=false)
23    {
24        $gravurl = "<img src='http://www.gravatar.com/avatar.php?gravatar_id=".md5($email);
25        if ($default)
26        {
27            $gravurl = $gravurl."&amp;default=".urlencode($default);
28        }
29        if ($size)
30        {
31            $gravurl = $gravurl."&amp;size=".$size;
32        }
33        if ($rating)
34        {
35            $gravurl = $gravurl."&amp;rating=".$rating;
36        }
37        if ($border)
38        {
39            $gravurl = $gravurl."&amp;border=".$border;
40        }
41        return $gravurl."' alt='Gravatar Image' />";
42    }
43    
44    ?>

Smarty Hacks

While trying to solve someone elses problems, I came across the following: If you call {have_posts}, this will return a number as long as you are not on the last post to be displayed on the page. This is a standard WP function, but it is interesting to see it works under WP-MU. {the_post} will increment the post counter: basically skip a post, but will cause SQL errors. • My biggest hassle so far is that it doesn’t seem to be possible to assign stuff created by template tags to variables, other than by the very ugly {capture name=foo}{bar}{/capture} ; {$smarty.capture.foo} trick, which doesn’t work for Arrays: it just sets $smarty.capture.foo to ‘Array’, not very much use at all. {section} might be promising, but I doubt it: {foreach value=get_the_category item=each}{$each}{/foreach} just returns ‘get_the_category’.

Limiting Smarty {foreach}

A user on the Blogsome forums wanted to be able to reduce the number of items that appeared in a Smarty generated list. Since the original code was using {foreach}, and I know {section} has a max=n attribute, I thought I’d try that. The problem was that the Array in question: {popularposts} generates $pposts; is not an array that can be accessed by indices. It’s key instead is the number of hits on the pages, so that’s not much good to us. However, it’s possible to use a simple {if} clause, and the {counter} function, to _effectively _limit the number of iterations.

1    {counter assign=idx print=0}
2    {foreach from=$array key=key item=item}
3        {counter}
4        {if $idx < = n}
5            Data to be repeated goes in here.
6        {/if}
7    {/foreach}

Replacing n with a number will cause n-1 iterations to be displayed. I said effectively above, as the other iterations will actually occur, but no data will actually be printed on the screen, as there is no {else} clause.

Random Post Link

Another Blogsome user wanted a Random Post to be displayed on the home page. I haven’t quite figured out how to get the actual post displayed, but a link to a random post should be pretty easy. Luckily, WP-MU uses post ids. If we can get the latest post id, then we should be able to get a random number between 1 and this. We can then check to see if the post exists (it may have been deleted: WP does not reuse post ids), and if it doesn’t, get another random number, and keep doing this until we find a post id that is still valid. It’s then a simple step to create the permalink. The first problem arises when we try to get the latest post. We could use {the_author_posts}, but this will give us the number of posts, which means if the user has deleted n posts, the last n posts will not be in the selection of random posts. This is not necessarily such a bad thing, as it will mean that older posts are likely to be used. The next problem of {the_author_posts} is that it must be used within The Loop. If we are planning to put the link before we have been through ‘The Loop’, then we should be alright. The third issue of {the_author_posts} is that with a multi-author blog, the random number will be much lower than the total number of posts, meaning even older posts are only likely to be selected from. Another option would be to say “Okay, I want a random post lower in id (ie, older than) every other post on the page.” This makes things much easier, but is limited to the same problem of not being able to be placed before the loop, as we’ll set a variable to the post id on each post as we come to it, and since most blogs are ordered in descending order, the last post visited will (or should) have the lowest id. The problem here is that a category view may have a very low post count, and it’s oldest visible post may be a very early post. The third option would be to say “I want a post that is older than the newest post on the page.” Again, the limit as to where this must appear is present. However, an additional problem is that we may get a link to a post that’s already visible. This third one to me seems to be the best compromise. So how do we get this post id? We’ll need to modify our post.html file under Blogsome. {capture name=max_id}{the_ID}{/capture} {if $smarty.capture.max_id > $max_id}{assign var=max_id value=$smarty.capture.max_id}{/if} This code can go anywhere: I stuck it right at the bottom, out of the way. The next part should only be done once: {math} is quite processor intensive, so do it just where you want the object to appear: {math equation="rand(1,n)" assign=rand_post n=$max_id} Our final code is to grab the permalink and title for this post. However, I cannot yet find a way to get the title, so we are stuck not knowing the post title: Random Post I’ve tried using {get_post} and {get_postdata}, but since the latter returns an Array, it’s not much use. Returned Arrays are no good - the value needs to be assigned to a variable for us to be able to access it. It’s probably possible to alter the query, or rerun a new query, but I’m getting to tired to do this right now.

Seperating Trackbacks and Comments

I can’t recall if this came up on the Blogsome forums or not, but some people are keen to seperate Trackbacks and Comments in the comment list. Sometimes, and especially for people new to blogs, Trackbacks don’t seem to make that much sense, so I thought I would try doing this on Blogsome. Googling seperate trackbacks comments wordpress turned up several sites, the first one I visited was from way back in July 2004, and was way to complicated to bother with.

The second one was better, and gave me the idea of how to do it. Then I remembered that there is a Template Tag called comment_type. I duplicated the main comment: the commentlist and the {foreach} that cycles through each comment, and used the following bit of code to get the {comment_type} into a variable (so I can test it):

1    {capture name=comment_type}{comment_type}{/capture}

It’s a simple matter to test this against a known value:

1    {if $smarty.capture.comment_type != 'Comment'}

And you know that you are working with a Trackback or Pingback. The first iteration of the commentlist only displays non-comments, the second only displays comments (it uses == 'Comment' instead). The last tricky bit was to figure out how to show Trackbacks from my own site in the class='owner' format (on here, in a reddish box rather than green). This required a little trick I had discovered earlier today:

1    {capture name=author_trackback}trackback@{bloginfo show='url'}{/capture}

And then the test is:

1    {if $smarty.capture.reader == $smarty.capture.author_trackback|replace:'http://':''}

(This assumes you have used my previous method for styling your own comments differently). The full text of my comments.html template then:

  1    <div id="commentarea">
  2    {if $post->ping_status}
  3        <p class='indented'>{_e text='The <acronym title="Uniform Resource Identifier">URI</acronym> to TrackBack this entry is:'} <em>{trackback_url}</em></p>
  4    {/if}
  5    
  6    <p class='indented'>{comments_rss_link link_text='<span class="rss"><abbr title="Really Simple Syndication">RSS</abbr></span> feed for comments on this post.'}</p>
  7    
  8    <h3 class="comments">{comments_number zero="Responses" one="1 Response" more="% Responses"} to &#8220;{the_title}&#8221;
  9    {if $post->comment_status == 'open'}
 10    <a href="#postcomment" title="{_e text='Leave a comment';}">&raquo;</a>
 11    {/if}
 12    </h3>
 13    
 14    {if $comments != ''}
 15      {capture name=author}{the_author_email}{/capture}
 16      {capture name=author_trackback}trackback@{bloginfo show='url'}{/capture}
 17    
 18      <h3 class="comments">Trackbacks / Pingbacks</h3>
 19      <ol class="commentlist">
 20      {foreach from=$comments key=key item=comment}
 21        {globalvar var='comment' value=$comment}
 22        {capture name=comment_type}{comment_type}{/capture}
 23        {if $smarty.capture.comment_type != 'Comment'}
 24            {capture name=reader}{comment_author_email}{/capture}
 25            {if $smarty.capture.reader == $smarty.capture.author_trackback|replace:'http://':''}
 26            <li class="owner" id="comment-{comment_ID}">
 27            {else}
 28            <li class="alt" id="comment-{comment_ID}">
 29            {/if}
 30            From the site {comment_author_link}:
 31        {comment_text}
 32            Posted on {comment_date} at {comment_time}.
 33            {edit_comment_link text="Edit this trackback", before='<span class="button">' after="</span>"}
 34        </li>
 35        {/if}
 36      {/foreach}
 37      </ol>
 38    
 39      <h3 class="comments">Comments</h3>
 40      <ol class="commentlist">
 41      {foreach from=$comments key=key item=comment}
 42        {globalvar var='comment' value=$comment}
 43        {capture name=comment_type}{comment_type}{/capture}
 44        {if $smarty.capture.comment_type == 'Comment'}
 45            {capture name=reader}{comment_author_email}{/capture}
 46            {if $smarty.capture.reader == $smarty.capture.author}
 47        <li class="owner" id="comment-{comment_ID}">
 48            {else}
 49        <li class="alt" id="comment-{comment_ID}">
 50            {/if}
 51                <script type="text/javascript">document.write('<div class="right"><img src="http://www.gravatar.com/avatar.php?gravatar_id='); document.write(hex_md5("{comment_author_email}")); document.write('&size=50" /><\/div>'); </script>
 52                On <a href="#comment-{comment_ID}">{comment_date} at {comment_time}</a>, {comment_author_link} said: 
 53              {comment_text}
 54                {edit_comment_link text="Edit this comment", before='<span class="button">' after="</span>"}
 55        </li>
 56         {/if}
 57      {/foreach}
 58      </ol>
 59    
 60    {else} _<!-- this is displayed if there are no comments so far -->_
 61        <p class='indented'>{_e text="No comments yet."}</p>
 62    {/if}
 63    
 64    <h3  class="comments">{_e text="Leave a Reply"}</h3>
 65    {if 'open' == $post->comment_status}
 66         <form action="{$siteurl}/wp-comments-post.php" method="post" id="commentform">
 67        <p>
 68          <input type="text" name="author" id="author" class="textarea" value="{insert name=var var=comment_author}" size="28" tabindex="1" />
 69           <label for="author">{_e text="Name"}</label> {if $req != ''} {_e text='(required)'} {/if}
 70        <input type="hidden" name="comment_post_ID" value="{$post->ID}" />
 71        <input type="hidden" name="redirect_to" value="{$redirect_to}" />
 72        </p>
 73    
 74        <p>
 75          <input type="text" name="email" id="email" value="{insert name=var var=comment_author_email}" size="28" tabindex="2" />
 76           <label for="email">{_e text="E-mail"}</label> {if $req != ''} {_e text='(required)'}{/if}
 77        </p>
 78    
 79        <p>
 80          <input type="text" name="url" id="url" value="{insert name=var var=comment_author_url}" size="28" tabindex="3" />
 81           <label for="url">{_e text="<acronym title='Uniform Resource Identifier'>URI</acronym>"}</label>
 82        </p>
 83    
 84        <p>
 85          <label for="comment">{_e text="Your Comment"}</label>
 86        <br />
 87          <textarea name="comment" id="comment" cols="60" rows="12" tabindex="4"></textarea>
 88              <input type="hidden" id="textarea_next_time" name="textarea_next_time" value="60,12" />
 89        </p>
 90    
 91        <p>
 92          _<!--input disabled name="preview" type="image" tabindex="5" src="/images/preview.png" size="75%" /-->_
 93          <input name="submit" type="image" tabindex="6" src="/images/post_comment.png" size="75%" />
 94        </p>
 95    
 96    <p>{_e text="Line and paragraph breaks automatic, e-mail address never displayed, <acronym title='Hypertext Markup Language'>HTML</acronym> allowed:"} <code>{allowed_tags}</code>, 'Smart' Smileys.</p>
 97    
 98    
 99    </form>
100    {else} _<!-- comments are closed -->_
101    <p>{_e text="Sorry, the comment form is closed at this time."}</p>
102    {/if}
103    </div>

Archive and Links Pages

It would be nice to be able to have a links and/or archive page under blogsome: that just had a list of all links, or a list of all posts/months/days (whatever). The problem is that Pages cannot use template tags, and it is not much fun to update these pages by hand. A solution presented itself to me today, and it goes like this:

1    {if $smarty.server.REQUEST_URI == '/links/'}
2        {get_links_list}
3    {elseif $smarty.server.REQUEST_URI == '/archives/'}
4        {get_archives type='postbypost' limit='' format='html'}
5    {/if}

This code can go into the Main Page template, just after {content}, or into the post.html template if you’ve used my previous hack to get Pages processed by the post template. You’ll also need to create Pages: Links and Archives (if you use different names, be sure to ensure the page-slug is the one that is listed in the code above). I had to put a <br /> into each Page in order to get rid of all errors. You can see the results here: Archives, Links. It’s more than likely that a similar solution exists for categories: I’ll do that too. Update: Categories, Pages. Note: Make sure you have the slash following the URL when you try to access them. It may be possible to have a double {if} clause (with slash and without), but I’m not sure yet… later works a treat:

1     {if $smarty.server.REQUEST_URI == '/links/' || $smarty.server.REQUEST_URI == '/links'}
2        {get_links_list}

And so on…

Non-attribution

I’ve commented before on how people use my template conversion, and remove the attribution to myself, and more importantly to Patricia, the original person who did all of the work on the template for vanilla wordpress. What is quite funny is that they remove the tags that attribute me, but usually leave in the AdSense code. Which means anyone clicking on ads on their blog will be doing me a favour. Not that I’ve ever got anything from AdSense. I can’t even log in, since there is a clash between my original AdSense login and my new hosted domain google account. Which they haven’t fixed, last time I checked. Perhaps I should write into the code that attribution needs to be left in. I must check…