Rockablepress.com Envato.com Š Rockable Press 2010 All rights reserved. No part of this publication may be reproduced or redistributed in any form without the prior written permission of the publishers.
3
Table of Contents
An Introduction
8
What This Book Includes What this Book Assumes
8 9
Tumblr Overview
11
The Origins of  Tumblr 11 Why Use it? 12 Advantages 13 Disadvantages 15
Blocks and Variables
17
Home Base 17 Blocks 18 Site Variables 20 Common Site Variables 20 Uploading Static Assets 21
Post Types
23
Post-Specific Blocks Post Variables All Posts Variables Post-Type-Specific Variables Text Posts Variables Quote Posts Variables Link Posts Variables Video Posts Variables Photo Posts Variables Photoset Posts Variables Chat Posts Variables Audio Posts Variables Putting it All Together Date Formatting Date Variables
24 26 26 28 28 29 30 31 32 33 35 37 39 42 43
4
Table of Contents
Creating Pages
47
The Browser’s Source Code
49
Customization Options
51
Meta Types 51 Meta-Color 52 Meta-Text 53 Meta-Image 54 Meta-Font 56 Meta-Boolean 57
Enabling Comments
59
Not All Bloggers Want Commenting Our Logic 1. Updating the Appearance Tab 2. Custom Blocks Post Notes Post Notes Variables
61 61 62 62 66 66
Twitter Integration
69
Trigger Support Referencing Tweets.js The Data Object
Enabling Search
69 69 71
75
Creating a Search Page Template Search Variables and Blocks
76 77
Tumblr and AJAX
81
The Game Plan 1. Adding a Loading Icon 2. Writing the JavaScript
81 81 83
5
Table of Contents
The Tumblr API
89
Optional GET Parameters 1. Retrieve only Video Posts 2. Retrieve 10 Most Recent Posts 3. Only Retrieve Posts that Match a Search Query of “man”, and which are Quote Posts 4. Read Dashboard Posts 5. Only Fetch “Liked” Posts 6. Read Pages 7. Create New Posts 8. Authenticate Users Practical Application: Filtering Posts by Type 1. Create the Markup 2. Writing the JavaScript
90 90 91
Miscellaneous Recipes
91 91 92 92 92 94 96 97 98
103
Custom Background Images 103 The Logic 103 1. Set the Default Background Image 103 2. Add a Meta-Image Tag to the <head> 104 3. Add a “Repeat” Meta-Boolean 105 4. If Set, Use the Custom Image 105 5. Save Bandwidth 108 6. The Final Code 108 Let Readers Drill you with Questions: Answer Posts 109 Enabling the Ask Feature 109 Answer Posts Variables 110 Variable Transformations 112 Transformations 113 Show a User’s Social Networking Links 114 Show Who I’m Following 117 Following Variables 117 1. Sample Markup 118 2. One Problem 119 3. Wrapping Boolean 119
6 Feedburner Integration 1. Sign up for Feedburner 2. Insert the FeedBurner URL Enable Pagination Navigation Variables Display Tags Tag Variables
Tools And Icons 1. Pictos 2. Tag Clouds 3. Mac Dashboard Widget 4. Embed Posts with JavaScript 5. 1,540 Simple White Social Media Icons 6. Bookmark Icons 7. Tumblr Post: Firefox Add-On 9. Sell Tumblr Themes on ThemeForest.net 10. Use Tumblrâ&#x20AC;&#x2122;s Hosting
Table of Contents 120 120 121 122 123 124 124
127 127 127 128 128 130 130 131 132 133
Conclusion 135 About The Author
136
Your Download Links
137
INTRO
8
Introduction
An Introduction In the last few years, Tumblr has sky-rocketed in popularity. How come? In a word: simplicity. In the past, the standard approach with frameworks and libraries was to add more, more, more over time. However, this invariably leads to bloat, bloat, bloat. While, granted, more features are made available to you, this also comes at the expense of an increasingly difficult and confusing platform. Especially in the blogging world, this begs the question: how much power do we really need? Do we need a plugin architecture? Do we need a dozen template pages in order to render our pages appropriately? I suppose the answer to this question will vary, depending upon the project. However, chances are, if you only require an attractive blog, Tumblr’s ease-of-use and simplicity will come as a breath of fresh air. With new CMSs and frameworks seemingly being released every day, how much time do we really have to learn yet another one? If you’re like me, the answer is: very little. Thankfully, I can honestly tell you that Tumblr theme design is a cinch! Assuming that you have a modest understanding of HTML and CSS, you’ll have your tailor-made blog up and running in less than a day.
What This Book Includes Packaged with this book, you will find: 1. Photoshop and Tumblr theme files made available for you to use as you work your way through the book. (These files may not be redistributed or resold in any way.) 2. A series of screencasts corresponding with each chapter, which cover the whole process from beginning to end.
9
Introduction
To access these files, please use the links provided on the last page of this book.
What this Book Assumes I’m going to assume that because you’ve purchased a Rockable Press book, you have a solid understanding of the web. As such, you’re not going to be spoon-fed how to create new posts, the difference between post types, how to access the Dashboard, etc. It will be assumed that you’re reasonably intelligent, and can easily figure these things out for yourself! If you’re like me, and have purchased other tech books before — perhaps one on WordPress — you probably skip over the introductory chapters anyhow. No, in this book, we’re going to dive right into the bulk of Tumblr design. With that said, if you’ve never touched Tumblr before, take an hour or so to acquaint yourself with the platform, the dashboard, adding new themes, etc. Once you have a solid grasp of the basics, return, and let’s continue forward.
11
Tumblr Overview
Tumblr Overview The Origins of Tumblr Tumblr, created by David Karp at the ripe old age of twenty, was first made available in 2007. Quickly, it generated a significant amount of buzz, as thousands upon thousands of bloggers, impressed by its simplicity and unique “post-type” blogging format, switched over.
Fig 1-1. Tumblr’s start page couldn’t be simpler.
If we jump forward a few years to today, Tumblr averages two million posts and roughly fifteen thousand new members per day. Now that’s impressive! It’s used by such celebrities as John Mayer,
12
Tumblr Overview
Katy Perry, and a variety of other influential developers, artists and bloggers. I was running a consulting company in 2006, and one month we had two weeks between contracts where we were just sitting around, and I said “Hey, let’s go for it. Let’s see if we can build this thing.” It took [Marco and I] two weeks to build, and it became the first version of Tumblr. We launched it and showed it to the tumblelog community, and overnight we had like 30,000 registered users in this community who were following those things, who didn’t have the knowhow to create it themselves. —David Karp http://www.mediabistro.com/articles/cache/a10281.asp
Fig 1-2. Tumblr’s simplicity doesn’t get in the way of its use for highprofile subjects.
Why Use it? Tumblr founder David Karp explains: The magic of Tumblr is we let you put anything in and get it out any way you want. We want you to be able
13
Tumblr Overview to post anything. Tumblr takes care of formatting content nicely and making it look good on your blog. –David Karp http://www.mediabistro.com/articles/cache/a10281.asp
Fig 1-3. Tumblr’s custom post types.
Need more reasons to choose Tumblr over its competitors?
Advantages Hopefully, the advantages to using a platform like Tumblr are becoming apparent. But if you need more incentive, here are a handful: • Tumblr has over seven million users. • As of July 2010, Tumblr has made its way into the top fifty sites in the United States, in terms of traffic. • Page views are growing by 300 million a month. • It’s expected to surpass two billion pageviews by September 2010. • Roughly 25,000 new users sign up every day. • There are hundreds of themes available, via the Theme Garden ( http://www.tumblr.com/themes/ ), both free and premium.
14
Tumblr Overview • By allowing for custom post types, Tumblr’s theme designers handle the work of styling your postings. Need a decorative quotation mark for quote posts? Easy. Need custom styling and formatting for posting videos? Not a problem! • Tumblr recently released its handy-dandy iPhone app, to make blogging on the go even easier. • Creating a custom theme is particularly easy, once you learn the fundamentals. • A commonly requested feature is to tweet one’s latest blog postings. Tumblr has Twitter functionality built-in. • Tumblr uses template tags to insert dynamic information. This is particularly beneficial for designers who are familiar with a scripting language like PHP. Those familiar with WordPress will know about the Loop. Tumblr instead uses a template system, similar to {block:Posts}, to allocate and render postings. • Despite its simplicity, Tumblr still offers a variety of customization features, assuming that the theme designers implement them into their themes. • Themes are automatically optimized for mobile viewing. Should the theme designer require more control of the mobile layout, a customized layout can alternatively be created. • It’s 100% free — both the content of your Tumblr blog and the hosting of associated assets: JavaScripts, CSS, images, etc. ( http://www.tumblr.com/themes/upload_static_file ). While this is not required, it’s a best practice to utilize Tumblr’s hosting when developing themes.
15
Tumblr Overview
Disadvantages No framework is without its faults, though, many of the “disadvantages” listed below were done on purpose by the Tumblr team. Their intention is to keep this blogging software as simple and usable as possible. • A commenting system isn’t built-in by default. Theme designers invariably have to utilize third party tools to allow for this, namely with Disqus, which we’ll review in a future chapter. • Some feel that Tumblr is too simple, and should be extended considerably. However, it can be argued that, if the team were to do so, they would be going against the platform’s initial purpose. • The code for a Tumblr theme can quickly become rather messy. You essentially place all of the code for your theme, perhaps excluding any JavaScript, into one page. Though the CSS can technically be referenced externally, it’s generally not the best practice, due to the fact that it limits some of the conveniences offered to the theme’s user. For example, if as a theme designer you wish to offer a way for the blogger to change the text color, there isn’t an easy way to do so if your stylesheet is referenced externally. Instead, this information is generally placed within the <head> tags of your theme. • Beyond the base post types that Tumblr offers — video, audio, photo, text, quote, chat — the platform doesn’t allow for additional post types. Convinced? If so, sit back, grab a beverage, and by the time you finish this book, you’ll be able to officially classify yourself as a Tumblr master!
17
Blocks and Variables
Blocks and Variables Home Base Those familiar with WordPress will be aware of its hugely impressive, and massive “Codex” ( http://codex.wordpress.org/ Main_Page ). Tumblr, on the other hand, in the fashion that it’s accustomed to, illustrates everything a new theme designer needs to know in one single page. One page. Your first thought might be: “Well the documentation sounds like it’s lacking!” But the truth is, Tumblr is so intuitive to the point that this is all you really need to
Fig. 2-1. Home Base: http://www.tumblr.com/docs/en/custom_themes
18
Blocks and Variables
get started. I refer to Tumblr’s “Creating a Custom HTML Theme” page as the “home base,” because you’ll invariably return to it multiple times, as you work your way through your first project.
see: Chapter 2 – Blocks and Variables/ 1_Introduction.mp4
Blocks If you were to create a blog from scratch, you might need to: a. Create tables in your database, which will contain your post data. b. Write a SQL query to retrieve that data — probably using JOIN to combine multiple tables. c. Filter through the result set, and echo out the desired information for each row. Tumblr takes care of this for us; that’s the beauty of it! All that’s required on our part is to create placeholders for our desired data by using what is referred to as blocks. For instance, one of Tumblr’s post types is designated for plain old text. To retrieve all instances of these post types and display them on the page, we could write: {block:Text} {block:Title} <h2><a href="{Permalink}">{Title}</a></h2> {/block:Title} {Body} {/block:Text}
19
Blocks and Variables
Let’s start from the inside, and work our way out, as we decipher these template tags. Within the <h2> tag, note how, rather than using a language like PHP to echo out the requested information — such, as <?php the_title(); ?> — we instead use keywords wrapped within curly braces, like {Title}. The same is true for {Permalink}, which, once rendered, will yield a path to the permanent link of that specific posting.
Fig. 2-2. The title of a text post is optional.
In the example above, did you notice how the <h2> tag is wrapped within an opening and closing {block:Title} ? Why is this necessary? The reason is because it offers more flexibility to the user of the Tumblr theme. When creating a new text post, a title isn’t required (Fig 2-2). It can be omitted, if the author wishes.
If the {block:Title} and {/block:Title} blocks were omitted, our theme would display an <h2> tag, regardless of whether or not the author added one. This is lazy and should be avoided. Always code in a fashion which allows for the most flexibility on the blogger’s part. These wrapping {block:Title} tags literally mean, “Only display the following information IF this specific posting contains a title. Otherwise, skip over it.” In layman’s Blocks are used to render Tumblr data (posts) terms: if the blogger added a title to as HTML — sometimes the posting, place it within an <h2> tag, conditionally. otherwise skip it and move on.
20
Blocks and Variables
Site Variables Much like most blogging applications, Tumblr offers a handful of basic variables, which can be used throughout your projects: things like the title of your blog, its description, and a path to your uploaded avatar or portrait. This information can be inserted or updated from within the Customize section of your Dashboard (Fig. 2-3).
Fig. 2-3. The customize link on the Dashboard and the resulting Info section that can be accessed from it.
Within our theme, if we wish to echo (excuse the PHP terminology habit) the values of the fields from the image above, we use {Title}, {Description}, and {PortraitURL-64}, respectively. In fact, there are roughly a dozen of these sorts of variables. Here’s a handful of the ones you’ll use most often:
Common Site Variables • {Title} : The title of your blog. • {Description} : The description of your blog. • {RSS} : The path to your blog’s respective RSS feed (created by Tumblr, of course).
21
Blocks and Variables • {Favicon} : The URL to your desired favicon (the little icon in the address bar). • {PortraitURL-64} : The URL to the uploaded portrait photo. “64” represents the dimensions, or 64px by 64px. This number can be changed to the themer’s needs, accordingly. Possible sizes are 16, 24, 30, 40, 48, 64, 96 and 128px.
• {CustomCSS} : By including this block directly to the bottom of your CSS, the blogger then has the option — assuming he has the know-how — of overriding your styling with his own custom CSS. This option is available via the Advanced tab of the Dashboard.
Fig. 2-4. Custom CSS field available in the Advanced tab.
Uploading Static Assets One of the benefits of Tumblr is that they will store all the files of your theme for free. We’ll be using this feature throughout this book. If you want to try it out as you work through the book, log in to your Tumblr account and go to http://www. tumblr.com/themes/upload_static_file. See the screencast for a detailed see: Chapter 2 – Blocks walkthrough of this process. and Variables/ 1_Static_Assets.mp4
23
Post Types
Post Types By removing any need for server-side scripting, like how WordPress uses PHP, Tumblr becomes a particularly friendly tool for designers. Instead of requiring something along the lines of the WordPress Loop, with Tumblr we only need to utilize template tags. No scary code necessary!
Fig. 3-1. The main Tumblr post types.
To designate our main posts section in our HTML markup, we must wrap this content within a {block:Posts} {/block:Posts} block. As a first step, letâ&#x20AC;&#x2122;s say that, within this main content, we only want to display the URL of the permalink for each posting. Thatâ&#x20AC;&#x2122;s easy: {block:Posts} <li>{Permalink}</li> {/block:Posts}
The code above will render the following, for my particular demo theme:
24
Post Types
Okay, that works reasonably well. However, what if, instead of the yellow bullet, we wanted to display a tiny icon which references the post type for that specific item — such as video, article, audio, etc. In that case, with our current markup, there really isn’t an easy method to do so. To compensate, we must utilize post-specific blocks, like {block:Quote} {/block:Quote} — which essentially tell the Tumblr engine: only render the markup between these opening and closing blocks if the type of post is “Quote.” Helpful isn’t it? There are a handful of these sorts of conditional blocks for posts.
Post-Specific Blocks These are the post-specific blocks that make up the core of Tumblr themes: • {block:Text} {/block:Text} • {block:Quote} {/block:Quote} • {block:Video} {/block:Video} • {block:Audio} {/block:Audio} • {block:Photo} {/block:Photo} • {block:Photoset} {/block:Photoset} • {block:Link} {/block:Link} • {block:Chat} {/block:Chat} With this information in hand, now we can create post-specific markup. Referring back to our icon example, let’s replace the list item’s yellow bullet with a tiny post icon. For the sake of brevity, we’ll only reference Text, Photo and Link posts. A real-world Tumblr theme will utilize all of these post blocks.
25
Post Types
{block:Posts} {block:Text} <li><img src="http://static.tumblr.com/taio0pf/ aX1l4qwei/article-icon.png" />{Permalink}</li> {/block:Text} {block:Photo} <li><img src="http://static.tumblr.com/taio0pf/ WuHl4qwg3/photo-icon.png" />{Permalink}</li> {/block:Photo} {block:Link} <li><img src="http://static.tumblr.com/taio0pf/ TpTl4qwge/link-icon.png" />{Permalink}</li> {/block:Link} {/block:Posts}
Okay, okay — displaying a non-clickable URL path is hardly useful; but hopefully, you now understand the concept of post-specific markup. Beyond these unique blocks, each post type has its own set of custom variables as well. For instance, a photo posting will make available variables which reference the path to the uploaded photo: {PhotoURL-500}. When a user creates a new “photo” post, this variable will be equal to the path to this uploaded photo — scaled to 500px wide. {block:Photo} <li><img src="{PhotoURL-500}" alt="{PhotoAlt}" /></li> {/block:Photo}
26
Post Types
Naturally, a “video” post will have no need for such a variable. As such, within {block:Video}, this variable does not exist.
Post Variables
see: Chapter 3 – Post Types/ 0_Post_Types_Overview. mp4
As referenced in Tumblr’s getting started guide, here’s a list of each post type, and its respective variables and blocks. Be sure to bookmark this section, as you’ll return to it often.
All Posts Variables • {Permalink} : The permanent link to the posting. • {ShortURL} : A minified link to the post (think TinyURL). • {PostID} : The unique id for each posting. • {block:Odd} {/block:Odd} : Rendered for each odd numbered posting. This can be used to style every other posting on the page. • {block:Even} {/block:Even} : Same as above, but for even numbered postings. • {TagsAsClasses} : Will fill the class attribute of the tag with a space-separated list of the post’s tags. • {block:Post [n] } {/block:Post [n] } : Allows you to target a specific posting, according to the offset. If I wanted to add an advertisement in the middle of the page, I could place it just before {block:Post7}. • {block:More} {/block:More} : If a post has a “Read More” break, the markup within this block will render.
27
Post Types
Let’s decipher these values, and display their real values on the page, using the following test markup. <h2>Actual Values</h2> {block:Posts} <ul> <li><strong>Permalink:</strong>{Permalink}</li> <li><strong>ShortURL:</strong>{ShortURL}</li> <li><strong>PostID:</strong>{PostID}</li> <li><strong>TagsAsClasses:</strong>{TagsAsClasses}</li> </ul> {block:Post2}<p>This is the 3rd posting. (Zero based)</p> {/block:Post2} {block:Odd}<p>This is an odd posting.</p>{/block:Odd} {block:Even}<p>This is an even posting.</p>{/block:Even} {/block:Posts}
Notice that we’ve placed the heading, “Actual Values”, outside of the {block:Posts} block. If it was inside, this value would be repeated for each posting, resulting in multiple “Actual Values” headings. The markup above will render:
28
Post Types
Post-Type-Specific Variables Next, we’ll review the variables that are specific to each post-type.
Text Posts Variables • {block:Text} {/block:Text} : Rendered for text postings. • {block:Title} {/block:Title} : Only rendered if the blogger has added a title to the article. • {Title} : The title of the posting. • {Body} : The content of the posting, wrapped in a paragraph tag. Example: {block:Text} <div class="text"> {block:Title} <h2><a href="{Permalink}">{Title}</a></h2> {/block:Title} {Body} </div> {/block:Text}
see: Chapter 3 – Post Types/ 1_Text_Posts.mp4
29
Post Types
Quote Posts Variables • {block:Quote} {/block:Quote} : Rendered for quote posts. • {Quote} : The contents (quote) of the posting. • {block:Source} {/block:Source} : Content within this block will only be rendered if the blogger included a source for the quote. • {Source} : The source of the posting. • {Length} : Will display “short,” “medium,” or “long,” depending upon the size of the post. Example: {block:Quote} <div class="quote"> <blockquote> <p>{Quote}</p> </blockquote> {block:Source}<cite>&#8212;{Source}</cite> {/block:Source} </div> {/block:Quote}
see: Chapter 3 – Post Types/ 2_Quote_Posts.mp4
30
Post Types
Link Posts Variables • {block:Link} {/block:Link} : Rendered for link posts. • {URL} : The URL of the post. • {Name} : The name of the post. If one is not set, this will default to the URL. • {Target} : Whether to open links in a new tab or new window. • {block:Description} {/block:Description} : If a description has been added, the markup within this posting will render. • {Description} : Description of the link. Example: {block:Link} <div class="link"> <h2><a href="{URL}">{Name}</a></h2> {block:Description} {Description} {/block:Description} </div> {/block:Link}
see: Chapter 3 – Post Types/ 3_Link_Posts.mp4
31
Post Types
Video Posts Variables • {block:Video} {/block:Video} : Rendered for video posts. • {block:Caption} {/block:Caption} : Rendered if a caption has been added for this post type. • {Caption} : The caption for the post. • {Video-500} : Contains the embed code for the video, which will be set to 500px wide. • {Video-400} : Same as above, but 400px wide. • {Video-250} : Same as above, but 250px wide. Example: {block:Video} <div class="video"> {Video-250} {block:Caption}<p>{Caption}</p>{/block:Caption} </div> {/block:Video}
see: Chapter 3 – Post Types/ 4_Video_Posts.mp4
32
Post Types
Photo Posts Variables • {block:Photo} {/block:Photo} : Rendered for photo posts. • {block:Caption} {/block:Caption} : Rendered if a caption has been added for this post type. • {Caption} : The caption for the post. • {LinkURL} : A click-through link to the source, if set. • {PhotoAlt} : The alternate text of the image, for the alt attribute of the image tag. • {PhotoURL-500} : The path to the photo, but no wider than 500px. • {PhotoURL-400} : Same as above, but no wider than 400px. • {PhotoURL-250} : Same as above, but no wider than 250px. • {PhotoURL-100} : Same as above, but no wider than 100px. • {PhotoURL-75sq} : 75px square version of the image. • {LinkOpenTag} : An opening anchor tag linking to the clickthrough URL. • {LinkCloseTag} : A closing anchor tag, if {LinkURL} is set. These two would be used to wrap an image. • {PhotoURL-HighRes} : Path to the high-resolution version of the photo. • {block:HighRes} {/block:HighRes} : Rendered if a highresolution photo is available for the post.
33
Post Types
Example: {block:Photo} <div class="photo"> {LinkOpenTag} <img src="{PhotoURL-500}" alt="{PhotoAlt}" /> {LinkCloseTag} {Caption} </div> {/block:Photo}
see: Chapter 3 – Post Types/ 5_Photo_Posts.mp4
Photoset Posts Variables A photoset post type allows you to upload multiple images, and then have them rendered on the page as a collection, by using a built-in Flash image slider. To create this type of post, you still need to choose the “Photo” type when adding a new post. However, there will be an option to “Add an Additional Photo” (see Fig. 3-2). Once you do, this will trigger the photoset post type, in which case the {block:Photoset} block will take precedence over {block:Photo}. • {block:Photoset} {/block:Photoset} : Rendered for photoset posts. • {block:Caption} {/block:Caption} : Rendered if a caption has been added for this post type.
34
Post Types • {Caption} : The caption for the post. • {Photoset-500} : Embed code for the photoset, set to 500px wide. Flash is used to display the slideshow. • {Photoset-400} : Same as above, but 400px wide. • {Photoset-250} : Same as above, but 250px wide.
Fig. 3-2. The photoset upload dialog.
Example: {block:Photoset} <div class="photoset"> {Photoset-500} {Caption} </div> {/block:Photoset}
see: Chapter 3 – Post Types/ 6_Photoset_Posts.mp4
35
Post Types
Chat Posts Variables • {block:Chat} {/block:Chat} : Rendered for chat posts. • {Title} : The title of the post. • {block:Lines} {/block:Lines} : Markup within this block renders for each line of the post. • {block:Label} {/block:Label} : Will render if Tumblr can extract a label from the current line. • {Label} : The label for the current line. • {Name} : The username for the current line. • {Line} : Current line of the post. • {UserNumber} : Unique id for the user of the current line. • {Alt} : Will render “even” or “odd” depending upon the current line. Can be used for alternating row styling.
see: Chapter 3 – Post Types/ 7_Chat_Posts.mp4
36
Post Types
Example: {block:Chat} <div class="chat"> {block:Title} <h2><a href="{Permalink}">{Title}</a></h2> {/block:Title} <dl> {block:Lines} <dt class="{Alt}"><strong>{block:Label}{Label} {/block:Label}</strong></dt> <dd>{Line}</dd> {/block:Lines} </dl> </div> {/block:Chat}
Chat posts can definitely prove to be somewhat confusing the first time around. Just remember, {block:Lines} will render for each line (or statement) of your chat post. Secondly, Tumblr does the brunt of the work by figuring out what the “label” of your post is. When you create a new chat post, the text will generally take the form of: Me: Hello World! You: Hello Universe! Me: And more… You: And even more… In this case above, Tumblr would extract “Me” and “You” and assign each to the {Label} variable. Beyond that, {Line} will contain the actual dialog. Because we’re using an author: remark type of system, it makes semantic sense to use a definition list (<dl>) for chat posts.
37
Post Types
Audio Posts Variables • {block:Caption} {/block:Caption} : Rendered if a caption has been added for this post type. • {Caption} : The caption for the post. • {AudioPlayer} : The default audio player. • {AudioPlayerWhite} : Same as above, but with a white skin. • {AudioPlayerGrey} : Same as above, but with a grey skin. • {AudioPlayerBlack} : Same as above, but with a black skin. • {PlayCount} : The number of times the audio clip has been played (“20542”). • {FormattedPlayCount} : Same as above, but formatted with commas (“20,542”). • {PlayCountWithLabel} : The number of times the audio clip has been played, formatted with commas and a label (“20,542 plays”). • {block:ExternalAudio} {/block:ExternalAudio} : Rendered if a post uses an externally hosted MP3. This can be used conditionally to add a download link.
38
Post Types • {ExternalAudioURL} : The external MP3 URL. • {block:AlbumArt} {/block:AlbumArt} : Rendered if the audio file’s ID3 information contains album art. • {AlbumArtURL} : The path the album art image. • {block:Artist} {/block:Artist} : Rendered if the ID3 information contains the artist name. • {Artist} : The artist’s name. • {block:Album} {/block:Album} : Rendered if the ID3 information contains the album’s title. • {Album} : The name of the album. • {block:TrackName} {/block:TrackName} : Rendered if the ID3 information contains the track name. • {TrackName} : The track name.
It’s important to note that, just because Tumblr makes all of these variables available to you, doesn’t mean that you’re obligated to use each and every one. If you don’t have a specific need to include the ID3 information, then don’t worry about it. Example: {block:Audio} <div class="audio"> {block:Caption} <h2><a href="{Permalink}">{Caption}</a></h2> {/block:Caption} {AudioPlayerGrey} {block:ExternalAudio} <p><a href="{ExternalAudioURL}">Download this audio clip.</a></p>
39
Post Types
{/block:ExternalAudio} <p>{PlayCountWithLabel}</p> </div> {/block:Audio}
see: Chapter 3 â&#x20AC;&#x201C; Post Types/ 8_Audio_Posts.mp4
Putting it All Together If we combine all of these segments, we might end up with something along the lines of: <!DOCTYPE html> <html lang="en"> <head> <title>{Title}</title> <link rel="shortcut icon" href="{Favicon}"> <link rel="alternate" type="application/rss+xml" href="{RSS}"> {block:Description} <meta name="description" content="{MetaDescription}" /> {/block:Description} </head> <body> <h1>{Title}</h1> {block:Description}
40
Post Types
<p id="desc">{Description}</p> {/block:Description} <div id="posts"> {block:Posts} <!-- TEXT --> {block:Text} <div class="text"> {block:Title} <h2><a href="{Permalink}">{Title}</a></h2> {/block:Title} {Body} </div> {/block:Text} <!-- QUOTE --> {block:Quote} <div class="quote"> <blockquote> <p>{Quote}</p> </blockquote> {block:Source}<cite>&#8212;{Source}</cite> {/block:Source} </div> {/block:Quote} <!-- LINK --> {block:Link} <div class="link"> <h2><a href="{URL}">{Name}</a></h2> {block:Description} {Description} {/block:Description} </div> {/block:Link}
41
Post Types
<!-- VIDEO --> {block:Video} <div class="video"> {Video-250} {block:Caption}<p>{Caption}</p>{/block:Caption} </div> {/block:Video} <!-- PHOTO --> {block:Photo} <div class="photo"> {LinkOpenTag} <img src="{PhotoURL-500}" alt="{PhotoAlt}" /> {LinkCloseTag} {Caption} </div> {/block:Photo} <!-- PHOTOSET --> {block:Photoset} <div class="photoset"> {Photoset-500}
Use HTML comments to label each post type: <!-- Post Type -->
{Caption} </div> {/block:Photoset} <!-- CHAT --> {block:Chat} <div class="chat"> {block:Title} <h2><a href="{Permalink}">{Title}</a></h2> {/block:Title} <dl> {block:Lines}
42
Post Types
<dt class="{Alt}"><strong>{block:Label}{Label} {/block:Label}</strong></dt> <dd>{Line}</dd> {/block:Lines} </dl> </div> {/block:Chat} <!-- AUDIO --> {block:Audio} <div class="audio"> {block:Caption} <h2><a href="{Permalink}">{Caption}</a></h2> {/block:Caption} {AudioPlayerGrey} {block:ExternalAudio} <p><a href="{ExternalAudioURL}">Download this audio clip.</a></p> {/block:ExternalAudio} <p>{PlayCountWithLabel}</p> </div> {/block:Audio} {block:Posts} </div><!-- end posts --> </body> </html>
Date Formatting Itâ&#x20AC;&#x2122;s not uncommon to stamp each posting with the date when it was created. To do so with Tumblr, we use the {block:Date} block, like so:
43
Post Types
{block:Date} <div class="meta"> <span class="day">{DayOfMonth}</span> <span class="month">{ShortMonth}</span> </div> {/block:Date}
Date Variables As with post types, we have a plethora of formatting options available for displaying dates. • {block:Date} {/block:Date} : Rendered for all posts, but not pages. Always use this block. • {block:NewDayDate} {/block:NewDayDate} : Renders for each post that’s the first entry for a given day. • {block:SameDayDate} {/block:SameDayDate} : Rendered for subsequent postings in a given day. • {DayOfMonth} : “1” to “31” • {DayOfMonthWithZero} : “01” to “31” • {DayOfWeek} : “Monday,” “Tuesday,” “Wednesday,” etc. • {ShortDayOfWeek} : “Mon,” “Tues,” “Wed” • {DayOfWeekNumber} : “1” to “7” • {DayOfMonthSuffix} : “st,” “rd,” “nd,” and “th” (think: 1st, 2nd, 3rd… 5th). • {DayOfYear} : “1” to “365” • {WeekOfYear} : “1” to “52” • {Month} : “January” to “December”
44
Post Types • {ShortMonth} : “Jan” to “Dec” • {MonthNumber} : “1” to “12” • {MonthNumberWithZero} : “01” to “12” • {Year} : “2010” • {ShortYear} : “10” • {AmPm} : “am” or “pm” • {CapitalAmPm} : “AM” or “PM” • {12Hour} : “1” to “12” • {12HourWithZero} : “01” to “12” • {24Hour} : “0” to “23” • {24HourWithZero} : “00” to “23”
• {Minutes} : “00” to “59” • {Seconds} : “00” to “59” • {Beats} : “000” to “999” • {TimeStamp} : Unix time, that is, seconds elapsed since Jan. 1, 1970, e.g. “1278254283” • {TimeAgo} : “10 minutes ago,” or “2 weeks ago,” etc. Example: {block:Posts} <!-- TEXT --> {block:Text} <div class="text">
see: Chapter 3 – Post Types/ 9_Dates.mp4
45
Post Types
{block:Title} <h2><a href="{Permalink}">{Title}</a></h2> {/block:Title} {Body} </div> {/block:Text} <!-- other posts... --> {block:Date} <div class="date"> <p>This posting was published on {DayOfWeek}, {Month} {DayOfMonth}, {Year}.</p> </div> {/block:Date} {/block:Posts}
If you require more flexibility with the positioning of your date element, you can also place the {block:Date} block within the post-type blocks. However, if you do so, youâ&#x20AC;&#x2122;ll need to repeat the date portion of your code for every post type. By placing it outside of them, you only need to add it once. Either method will do just fine.
47
Creating Pages
Creating Pages It’s a common misconception that Tumblr only allows for blog posts, and doesn’t contain the logic for static pages. Luckily, this isn’t the case, though, admittedly, it’s a relatively new addition to the platform. The ability to create new pages is available from within the customization section of your Dashboard (Fig. 4-1).
Fig. 4-1. The Pages tab and Add Page dialog.
Once a few pages have been created within the Dashboard, we can create a menu, with links to each of these pages, by using a structure similar to the following: {block:HasPages} <nav> <ul> <li><a href="/">Home</a></li> {block:Pages}<li><a href="{URL}">{Label}</a></li> {/block:Pages}
48
Creating Pages
</ul> </nav> {/block:HasPages}
Again, notice how we wrap all of our code within the {block:HasPages} block, meaning “if there are additional pages associated with this blog.” That way, if the user hasn’t created any pages, we won’t display any empty markup. Why would we? Because we’re cutting edge, we’ll also use the new HTML5 <nav> tag to be as semantic as possible. However, we also have an additional wrapping block: {block: Pages}. What’s the purpose of this one? At this point in the markup, we’ve already established that the blog “has pages,” so why do we seemingly check again? {block:Pages} means something a bit different; think of it as a for statement. “For each page, render the following code. For you coders, {block: Pages} might be akin to something along the lines of: <?php for ($i = 0; $i < count($pages); $i++) : ?> <li><a href="<?php echo $url; ?>"><?php echo $label; ?> </a></li> <?php endfor; ?>
Beyond that, {URL} and {Label} are rather self explanatory: the path to the page and the title of the page, respectively. With a bit of styling, and assuming that three pages have been created, we’ll end up with:
49
Creating Pages
The Browser’s Source Code Should we view the source of the page, our template tags will be replaced with the appropriate HTML. <nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/portfolio">Portfolio</a></li> <li><a href="/contact">Contact</a></li> </ul> </nav>
Because the {block:Pages} block only renders the information for each new page that has been created from within the Tumblr Dashboard, you must also remember to manually include a link back to the “Home” page. This is quite common with blogging applications. <li><a href="/">Home</a></li>
see: Chapter 4 – Pages/ 1_Pages.mp4
51
Customization Options
Customization Options The key to a successful theme is flexibility. Thankfully, Tumblr makes the process of adding custom attributes laughably easy. Add a few <meta> tags to the <head> of your document, and you’re all set. It truly is as easy as that! The basic format for adding a new option is as follows: <meta name="type:Value" content="contentType" />
Mostly important is the “type” that you specify. Tumblr offers a handful of options to choose from, all of which render a different view: maybe a textbox, a checkbox, a color selector, etc.
Meta Types • color: Creates a color selector. This is used to specify the user’s desired colors for the Tumblog. contentType: Can be equal to the color’s default hex value. • if: Used as a boolean. Creates a checkbox. contentType: “0” or “1”, for false and true, respectively. • text: Creates a textbox. contentType: Can be any string, though it is generally left blank. • image: Creates an upload input. Can be used, for example, to upload a custom header image. contentType: Path to the default image. • font: Creates a select box, for choosing from a variety of system fonts. contentType: The default font, with optional fall-backs. Let’s examine these options in a bit more detail.
52
Customization Options
Meta-Color Let’s say that, as a theme creator, you want to give the user an easy way to change the color of the main text. This isn’t an uncommon request, so it makes perfect sense to offer this option. Within the <head> tag, add the following: <meta name="color:Text" content="#292929" />
This tag alone will create a new option within the Appearance tab of the Tumblr Dashboard (Fig. 5-1). By specifying “color” as the meta name, this directs Tumblr also to make a color selector available when you click on the square color icon. Implementation To retrieve this user-set value, we can use {color:Text} in our CSS file:
Fig. 5-1. The Appearance tab and color picker dialog.
body { color: {color:Text}; }
By declaring {color:Text}, this directs the Tumblr engine to search for the <meta> tag with a meta-name of “color” and a value of “Text”. It then either retrieves the default hex value, or whatever the user has declared. In this
see: Chapter 5 – Customization Options/ 1_Meta_Color.mp4
53
Customization Options
example, if we view the source of the page after the browser has finished rendering it, we’ll see: body { color: #292929; }
Meta-Text There will be instances when you require a unique value from the user. For example, should you offer the ability to display the user’s recent tweets, you’ll need their username, accordingly. In these cases, meta-text tags become a tremendous help. <meta name="text:Twitter Username" content="" />
Again, with only that small bit of markup, a new attribute will be added to the Appearances tab. Typically, these fields will have their content initially set to nothing. At that point, the user can then input their Twitter username — in my case, “nettuts”. I’m the editor of Nettuts+ ( http://net.tutsplus.com ), if you didn’t know! Implementation To grab this value, add this to your theme template:
Meta-text tags can additionally be used as booleans, for if statements. As an example, if the user enters a Twitter username, then display a Twitter div on the page; otherwise, don’t. The word “If” must be prefixed: {block:IfTwitterUsername} {/block:IfTwitterUsername}. It’s a nice trick to know!
54
Customization Options
<p>My Twitter username is: {text:Twitter Username}</p>
* Note that we’ll cover a full example of how to import tweets into a Tumblr project in a later chapter.
see: Chapter 5 – Customization Options/ 2_Meta_Text.mp4
Meta-Image Let’s imagine that your theme uses a large banner image for the header of the site. It would be helpful to allow the user to replace this image with anything he wishes, right? Well that’s easy. <meta name="image:Header" content="http://static.tumblr.com/ path/to/header/image.jpg" />
This generates an upload box. The content attribute, by default, can be set to the path to the default banner image. Implementation You’ll either choose to embed the image tag into your markup, or place it as a background image with CSS. This will depend on the specific project. <div id="header"> <img src="{image:Header}" alt="My Site" /> </div>
Or… #header { background: url('{image:Header}') no-repeat; }
55
Customization Options
Remember: if you set the content attribute of the meta-image tag to blank, you must then be careful about assuming that an image has been set by the user. Like meta-text tags, we can also use meta-image tags as a boolean. In the case of a large banner graphic, chances are that you’ll absolutely set one by default; however, what about different circumstances, such as allowing the user to replace the <h1> text of the title with a graphic? <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="image:Logo" content="" /> <title>Example</title> </head> <body> <div id="header"> {block:IfLogoImage} <img src="{image:Logo}" alt="{Title}" /> {/block:IfLogoImage} {block:IfNotLogoImage} <h1>{Title}</h1> {/block:IfNotLogoImage} </div> </body> </html>
1. Again, we prefix our custom block with “If” to designate that this is a true or false statement. 2. We must also append the word “Image” to the block. So: {block:IfLogoImage}
To mimic the if-else effect, use “IfNot”.
56
Customization Options 3. Tumblr doesn’t inherently have a {block:else} block; however we can instead use “IfNot”: {block:IfNotLogoImage}.
Please remember that {block: IfLogoImage} isn’t a block that’s automatically available to you when you create a new Tumblr theme. You created it when you added the meta-image type to your <head> tag, and applied a value of “Logo”.
see: Chapter 5 – Customization Options/ 3_Meta_Image.mp4
Meta-Font Should you need to afford the user the ability to change the default fonts for the theme, Tumblr has you covered! <meta name="font:Title" content="Helvetica, Arial, sans-serif" />
This tag will render a drop down, allowing the user to specify his or her desired system font. Implementation To retrieve this value, we can add this to our CSS: h1, h2, h3 { font-family: {font:Title}; } see: Chapter 5 – Customization Options/ 4_Meta_Font.mp4
57
Customization Options
Meta-Boolean If I wanted to provide the user of my theme the option of removing the search bar, I could wrap the #search div with a boolean tag. Convenient! <meta name="if:Show Search Bar" content="1" />
Note that, for booleans, acceptable values for the content attribute are “0” and “1”, for false and true respectively. This then generates a checkbox field within the Appearance tab. Because we’ve set the content attribute to “1”, the default state of the checkbox is checked. Implementation As meta-boolean tags only allow for two outcomes, true or false, we need to prefix the word “If” inside of our block. {block:IfShowSearchBar} <div id="search">...</div> {/block:IfShowSearchBar}
If the field is checked, the #search div will display on the page. If unchecked, that section will be skipped entirely.
see: Chapter 5 – Customization Options/ 5_Meta_Boolean.mp4
59
Enabling Comments
Enabling Comments Enabling comments within Tumblr is, unfortunately, an unnecessarily tedious task. While I personally understand the reason for the Tumblr team not including it, the fact of the matter is that bloggers do want it. Without a native solution, themers must then refer to third party sources, namely Disqus, to enable commenting systems (Fig. 6-1). Disqus can actually be integrated into any blogging software, including WordPress, Moveable Type, Blogger, Fig. 6-1. Disqusâ&#x20AC;&#x2122; comenting and of course, Tumblr. The blogger system as integrated into only needs to create a free Disqus Tumblr. account (Fig. 6-2). The coder of the Tumblr theme, on the other hand, has to perform a bit of work to get things up and running. (For a crash-course on enabling
Fig. 6-2. Disqus home page.
60
Enabling Comments
comments with Disqus, refer to: http://disqus.com/comments/ tumblr/ ). Before you move on to implementing Disqus comments, you must first visit their site, and sign up for a free account. I’m about to throw a sizeable chunk of JavaScript at you, so hold your breath. One… two… {block:IfDisqusShortname} {block:Permalink} <div id="disqus_thread"></div> <script> (function() { var dsq = document.createElement('script'); dsq.async = true; dsq.src = 'http://{text:Disqus Shortname}.disqus.com/ embed.js'; (document.getElementsByTagName('head')[0] || document. getElementsByTagName('body')[0]).appendChild(dsq); })(); </script> <noscript> Please enable JavaScript to view the <a href="http:// disqus.com/?ref_noscript={text:Disqus Shortname}"> comments powered by Disqus.</a> </noscript> {/block:Permalink} <script> (function() { var links = document.getElementsByTagName('a'), query = '?', len = links.length; for (var i = 0; i < len; i++) { if(links[i].href.indexOf('#disqus_thread') >= 0) { query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&'; }
61
Enabling Comments
} document.write('<script src="http://disqus.com/forums/ {text:Disqus Shortname}/get_num_replies.js' + query + '"></' + 'script>'); })(); </script> {/block:IfDisqusShortname}
Truthfully, I don’t want you to worry about this code too much. If JavaScript isn’t your thing, no big deal. It’s supplied by Disqus ( http://disqus.com/comments/tumblr/ ), and should be placed just before your closing main content div. 99% of the time, you can copy the code and paste it into your theme, accordingly. However, let’s quickly go over each block in detail. {block:IfDisqusShortname}
Not All Bloggers Want Commenting Remember the importance of providing flexibility? Just because a theme supports commenting doesn’t necessarily mean that the user wants comments. As such, we need to implement a method which allows the user to toggle this functionality on or off. The solution is to provide a “Disqus Shortname” textbox within the Appearance tab of the Dashboard.
Our Logic • If this textbox is blank, we won’t display comments. • If the user enters their Disqus shortname (username), we’ll assume that he does, in fact, wish to enable commenting.
62
Enabling Comments • Should he decide at a future date that he no longer wants a commenting system, he need only delete the shortname from this textbox.
1. Updating the Appearance Tab Our first step is to add this new option (textbox) to the Appearance tab within the Tumblr Dashboard. If you read the previous chapter, this should be a cinch! If not, then go back and read it, fool! Within the <head> tag, we need to add a new <meta> tag: <meta name="text:Disqus Shortname" content="" />
This simple piece of code designates that: 1. A textbox should be added to the Appearance tab, as specified by the use of “text:”. This essentially means that the type of attribute that should be added to the panel should be a textbox. 2. The label of this textbox should be “Disqus Shortname”. 3. The default content, or value, of the input should be blank. Next, we need to determine the value of this textbox from within our theme.
2. Custom Blocks Though we’re not setting a boolean, we can still use the value, or lack thereof, in the same fashion. If the user entered a shortname, that will behind the scenes be equal to “1” or true. If they didn’t, this results in “0” or false. With this knowledge, we can create a new custom block, which correlates to the meta tag above.
63
Enabling Comments
{block:IfDisqusShortname} <!-This code will only run if a shortname has been entered... The value of the “Disqus Shortname” textbox can be accessed with {text:Disqus Shortname}. --> {/block:IfDisqusShortname}
Pay attention to a few things here: 1. While the value of the meta tag is “Disqus Shortname”, we must remove any spaces from our block. 2. The word “If” must be prepended to this value: IfDisqusShortname. This translates the value of the input into a boolean: “0” or “1”. 3. If you change the value of the meta tag to “Your Disqus Username”, your block must then be updated, to {block:IfYourDisqusUsername}. 4. To retrieve the value of the textbox, we use {text:Disqus Shortname}. 5. Though we could have created a boolean meta tag instead, such as <meta name="if:Show Comments" content= "1">, we’d still need to add an additional textbox to allow the user to enter his Disqus username. This way is better, and more efficient! Let’s go back to the Disqus JavaScript snippet and continue on with the JavaScript explanation. <div id="disqus_thread"></div>
This empty div, with an id of “disqus_thread”, is a placeholder for the comments.
64
Enabling Comments <script> (function() { var dsq = document.createElement('script'); dsq.async = true; dsq.src = 'http://{text:Disqus Shortname}.disqus.com/ embed.js'; (document.getElementsByTagName('head')[0] || document. getElementsByTagName('body')[0]).appendChild(dsq); })(); </script>
Within a self-invoking anonymous function, we create a <script> tag, and set its src attribute equal to a path to the JavaScript file, hosted on Disqusâ&#x20AC;&#x2122; server, which embeds the comments. This path requires the userâ&#x20AC;&#x2122;s Disqus shortname. We can use {text:Disqus Shortname} to insert this value. Lastly, this script tag is appended to the <head> tag. <noscript> Please enable JavaScript to view the <a href="http:// disqus.com/?ref_noscript={text:Disqus Shortname}"> comments powered by Disqus.</a> </noscript>
If the viewer has disabled JavaScript in their browser, the contents of the <noscript> tag will display on the page.
Remember: Disqus comments are dependent upon JavaScript being enabled.
65
Enabling Comments
<script> (function() { var links = document.getElementsByTagName('a'), query = '?'; for (var i = 0; i < links.length; i++) { if(links[i].href.indexOf('#disqus_thread') >= 0) { query += 'url' + i + '=' + encodeURIComponent(links[i].href) + '&'; } } document.write('<script src="http://disqus.com/forums/ {text:Disqus Shortname}/get_num_replies.js' + query + '"></' + 'script>'); })(); </script>
Fig. 6-3. Disqus commenting as enabled on our Tumblr example site.
66
Enabling Comments
Within another auto-executing function, we grab all anchor tags on the page and assign the array to the variable links. We next cycle through all of the anchor tags, and determine if the href attribute of each contains the string, “#disqus_thread”. If it does, we update the variable query and url-encode the href of the anchor tag. This block of code is used to display the number of comments for each posting. see:
That’s all there is to it! As I mentioned a Chapter 6 – Commenting System/ few pages ago, if JavaScript isn’t your 1_Commenting_with_ thing, don’t worry about it too much. Disqus.mp4 Feel free to copy and paste this snippet of code just before your closing main content div. For each theme, you’ll probably need to play around with the placement of this code. Because of the unique layout for each theme, sometimes it requires a bit of trial and error.
Post Notes Think of notes as a different form of a streamlined commenting system for your followers. Rather than manually visiting the post page and leaving a comment, your Tumblr followers can instead directly reply from their Dashboard. They have the option of either hearting (liking) the post when leaving a specific comment, similar to what you might be familiar with on Facebook… or not.
Post Notes Variables • {block:PostNotes} {/block:PostNotes} : Rendered if a post contains notes. • {PostNotes} : HTML output of the post’s notes. Will only display on a permalink page (when viewing one single posting).
67
Enabling Comments • {block:NoteCount} {/block:NoteCount} : Rendered if the post has at least one note. • {NoteCount} : The number of notes for the post. • {NoteCountWithLabel} : Same as above, but with a pluralized label: “15 notes.”
Example: {block:Posts} {block:Text} <div class="text"> {block:Title} <h2><a href="{Permalink}">{Title}</a></h2> {/block:Title} {Body} </div> {/block:Text} <ul class="post-meta"> {block:PostNotes} <li>{NoteCountWithLabel}</li> {/block:PostNotes} </ul> {/block:Posts}
69
Twitter Integration
Twitter Integration Because of its popularity, Tumblr has built-in Twitter support — at least somewhat. It’s important to remember that, because we’re not using any kind of serverside solution, JavaScript will be our language of choice for retrieving tweets.
Trigger Support
see: Chapter 7 – Twitter Integration/ 1_Pre_Design.mp4
The first step is to trigger support from the Services tab of the Dashboard (Fig. 7-1). As required by Twitter, Tumblr uses O-Auth to sign you in. Once you’ve signed in with your Twitter username and password, this then makes two template tags available to us: • {TwitterUsername} : This variable is equal to the Twitter username.
Fig. 7-1. Twitter integration can be found in the Services tab.
• {block:Twitter} {/block:Twitter} : The code within this block will only run if Twitter integration has been enabled, as detailed above.
Referencing Tweets.js The step above simply enables support for Twitter. However, we must still implement the functionality. Just before the closing <body> tag, reference Tumblr’s tweets.js file.
70
Twitter Integration
{block:Twitter} <script src="/tweets.js"></script> {/block:Twitter}
We wrap the script tag within {block:Twitter} because there’s no use in adding an additional HTTP request if the user hasn’t triggered Twitter support. That’s wasteful. When this script completes, it’ll run recent_tweets as a call-back function, and pass the returned data array as a parameter. We must manually create this function, filter through the returned tweets, and display them on the page, as list items.
see: Chapter 7 – Twitter Integration/ 2_Twitter_Integration.mp4
{block:Twitter} <div id="twitter" style="display: none;"> <ul id="tweets"></ul> </div> <script> function recent_tweets(data) { var frag = document.createDocumentFragment(), i = data.length, li, a; while ( i-- ) { li = document.createElement('li'); a = document.createElement('a'); a.href = 'http://twitter.com/{TwitterUsername}/status/' + data[i].id; a.appendChild(document.createTextNode(data[i].text)); li.appendChild(a); frag.appendChild(li); }
71
Twitter Integration
document.getElementById('tweets').appendChild(frag); document.getElementById('twitter').style.display = 'block'; } </script> {/block:Twitter}
Again, we wrap this code within {block:Twitter} to save time if Twitter support hasn’t been triggered. The recent_tweets function simply filters through the returned data, and appends a new <li> to the #tweets unordered list, containing the tweet — as represented by data[i].text. Note: we initially set the display of the #twitter div to “hidden” because we should only display this div if the recent_tweets function has been called. If it hasn’t, there must have been an error retrieving the tweets, in which case we shouldn’t render the #twitter div.
The Data Object The data object contains a variety of information that you’re free to peruse by using Firebug. The properties that you’ll be most interested in are: • text: The tweet itself. • id: The id associated with the tweet. • in_reply_to_user_id: The id of the user the tweet replied to, if applicable. • created_at: The time stamp of the tweet’s creation. • source: The program used to create the tweet — for example: Echofon, Tweetie, etc.
72
Twitter Integration • user.screen_name: The username of the tweet’s author. • user.friends_count: The number of friends of the tweeter.
To view the entire list of options available via the data object, install Firebug (which every web developer should have: http://getfirebug.com ), access the Net tab, and toggle the “GET tweets.js” item. You’ll see a large string of data:
Okay, okay — that’s impossible to read. If the returned result isn’t JSON, the response will look like a never-ending string of characters. Luckily, there are several utilities available which will format objects like this. A popular choice in the community is http://jsbeautifier.org/. Copy and paste this code, and you’ll end up with something far more readable:
73
Twitter Integration
You can place this block of code anywhere you wish in your theme. For the sample project, Iâ&#x20AC;&#x2122;ve included it in the sidebar:
75
Enabling Search
Enabling Search Tumblr, as expected, has a search functionality built in. To enable it, we only need to create an applicable form for our theme. <section class="search"> <form action="/search" method="get"> <h3>Search</h3> <input type="text" name="q" value="{SearchQuery}" /> <input type="submit" value="Search" /> </form> </section>
This form requires a few specific commands: • The action attribute must be set to “/search”. This is equivalent to the base URL of your blog, plus “/search” — so: “http://rockabletumblr.tumblr.com/search”. If you’re unfamiliar with this attribute, it’s essentially the URL that the form should post the contained information to when the user clicks “Submit”. • The method attribute should be set to “get”. This designates whether the search value is passed through the querystring (visibly in the address bar), or if it posts the value (hidden from the user). • The name attribute of the search textbox must be “q”. • Though not required, you’re encouraged to set the value of the textbox to {SearchQuery}. After you press the “Submit” button, and the page posts back, the search textbox will contain the value you last entered, for convenience.
76
Enabling Search
Placed anywhere in your theme, this simple form itself will enable the functionality. Try it out: search for anything, click the submit button, and within the URL you’ll see something along the lines of “http://yourSite.tumblr.com/search/yourSearchQuery”. However, though it works, this still isn’t very convenient for the readers. Assuming that the search query returns X number of rows, you’ll see the requested postings, as expected. However, if you search for something, such as “bla”, which does not occur in any of the postings, you’ll be greeted with a blank content section:
Creating a Search Page Template Luckily, Tumblr offers a handful of blocks, which we can use to provide a bit more feedback for the readers. Let’s first diagram exactly how we want our search results “page” to display, ideally.
77
Enabling Search 1. After the user clicks the search submit button, there should be a heading at the top of the page, called “Search Results”. 2. This heading should also display the value you searched for. 3. Below the heading, let’s also display the number of results returned from the database. 4. If no rows were returned, we should inform the reader that nothing was found. 5. We should also, for convenience, add another search form at the top, so that the reader can search for something else.
Search Variables and Blocks While other blogging platforms offer a search template, which will be imported automatically for search results, this functionality works a bit differently with Tumblr. Instead, we use the {block:SearchPage} block. Anything within these opening and closing tags will only display on the page if the user has searched for some string of text. Here are the relevant tags: • {block:SearchPage} {/block:SearchPage} : Only rendered on search pages. • {SearchQuery} : The value of what the user searched for. For example, <p>You searched for: {SearchQuery}. </p> becomes “You searched for: lorem.” • {SearchResultCount} : The number of results returned from the search query. • {block:NoSearchResults} {/block:NoSearchResults} : Markup within this block will only display if no search results were returned from the search query. • {URLSafeSearchQuery} : URL-safe format of the search query.
78
Enabling Search
With these variables in hand, at the top of your main content section, add a new section of markup: {block:SearchPage} <h2 id="searchItemsFound"> Search results for: &#8220;{SearchQuery}.&#8221; <small>{SearchResultCount} items found.</small> </h2> {block:NoSearchResults} <div id="searchResults"> <section class="search"> <form action="/search" method="get"> <h5>Search for something else... </h5> <input type="text" name="q" value="{SearchQuery}" /> <input type="submit" value="Search" /> </form> </section> </div> {/block:NoSearchResults} {/block:SearchPage}
Fig. 8-1. A revised version of the Search code that returns the last text searched for, giving the user the opportunity to search for something else.
79
Enabling Search
Fig. 8-2. Search results when the text is found.
Remember, the reader will only see this markup if he searched for something. Further, the second search form will only be loaded if no results were returned from the search query, as represented by the {block:NoSearchResults} block.
see: Chapter 8 â&#x20AC;&#x201C; Search/ 1_Search.mp4
81
Tumblr and AJAX
Tumblr and AJAX Just because we’re using a hosted blog, like Tumblr, doesn’t mean that we can’t use as much fun AJAX as we want! Strangely however, you don’t see it used much in Tumblogs around the web. As a proof of concept, let’s take the search form that we created in the previous chapter and AJAXify it. To simplify the amount of code we must write, we’ll use the extremely popular see: Chapter 9 – jQuery library ( http://www.jquery.com ) Search and AJAX/ to handle the AJAX requests. 1_Search_and_AJAX.mp4
The Game Plan • When the user enters a search query, and clicks the submit button, we’ll first display an animated loading GIF to signal that work is being done behind the scenes. • We next make a jQuery $.get request, passing in the user’s search query. • Thirdly, we replace the main content section with the information that is returned from the AJAX request. • Finally, we fade out the loading icon, as our operations have been completed and it’s no longer necessary.
1. Adding a Loading Icon We have two options when it comes to add a loading icon to our page. We could dynamically insert the icon with JavaScript, after the submit button has been pressed, and then remove it after the operation has been completed. However, if the user searches multiple times, this task will be repeated again and again. A more
82
Tumblr and AJAX
efficient method is to add the icon directly into our markup, and its display attribute to “none”. Then we can simply toggle the display value accordingly. Let’s do that. Return to the search form that we previously created: <section class="search"> <form action="/search" method="get"> <h3>Search</h3> <input type="text" name="q" id="query" value="{SearchQuery}" /> <input type="submit" value="Search" /> </form> </section>
Let’s place the loading icon directly to the left of the “Search” heading. To do so, we can insert an <img> tag into the <h3> tag. <h3> <img id="loading" src="http://static.tumblr.com/udyyidq/ TR9l4ov0g/loadingicon.gif" alt="Please Wait" /> Search </h3>
Another, perhaps smarter, option would be to apply the loading icon as a background image of the <h3> tag. Choose whichever method you prefer. For the sake of simplicity, I’ll stick with the first option.
83
Tumblr and AJAX
The icon will require a touch of styling to float it to the left of the heading, and provide a bit of breathing room. #loading { float: left;
http://ajaxload.info/ offers a very helpful GIF loading icon generator.
margin-right: 5px; margin-bottom: 5px; }
Of course, we don’t want this icon to remain there permanently. In fact, it should only display after the “Search” button has been clicked, while we’re retrieving the requested posts. So let’s hide the icon by default. #loading { display: none; float: left; margin-right: 5px; margin-bottom: 5px; }
2. Writing the JavaScript Just before the closing </body> tag, within the <script> tags, append the following code:
84
Tumblr and AJAX
(function() { // Search AJAX var search = $('section.search')[0]; $('form', search).submit(function() { var mainContent = $('#main'), query = $('#query', this).val(), loadingIcon = $('#loading', this); loadingIcon.fadeIn(400); $.get('/search/' + query, function(results) { results = $(results).find('#main').html(); mainContent.html(results); loadingIcon.fadeOut(400); }); return false; }); })();
Hopefully, you’re mildly familiar with jQuery. If not, feel free to either copy and paste this code into your project, or better yet, take some time to learn the library. It’ll save you from massive headaches in the future! Let’s go over each section: (function() { })();
This is referred to as a self-invoking anonymous function. It’s a function that calls itself, as represented by the () at the very end. This is a recommended practice, as it limits the number of global variables that are created. var search = $('section.search')[0];
Here we’re creating a new variable, search, and making it equal to the <section> with an id of “search”, wrapped in the jQuery
85
Tumblr and AJAX
object. The [0] on the end turns it into a DOM element. I’ll explain the reason for this shortly. $('form', search).submit(function() {
We’re wrapping the form element, within the #search section element in jQuery and are then attaching the submit event, which listens for when the form is submitted — essentially, when the “Submit” button is clicked. var mainContent = $('#main'), query = $('#query', this).val(), loadingIcon = $('#loading', this);
Now, we create three variables at the top of our function. It’s considered a best practice to place all of your variable declarations at the top of your functions. • mainContent : The main content section of my theme. This may be different for you. • query : The textbox that the user enters his search query into. We use .val() to retrieve the value of the input. • loadingIcon : Caching the location of the loading icon. A context of this is passed as the second parameter. This is helpful because rather than having to traverse the entire page for the #loading element, we can first start with the form, and then search from that point. loadingIcon.fadeIn(400);
We use the fadeIn(400) method to change the display of the loading icon from “none” to “inline”. The image will fade in over the course of about a half-second.
86
Tumblr and AJAX
$.get('/search/' + query, function(results) {
jQuery offers a helpful AJAX method, called $.get. The first parameter will be the path to the page that we’re requesting, and the second is the callback function that will run when the request has been completed successfully. Because we know from the previous chapter that searches can be executed by accessing “http://yourSite.tumblr.com/search/mySearchQuery”, all we need to do is append the user’s search query to the first parameter. Luckily, we’ve already stored the value of the input in the query variable; so we can simply append it to “/search/”. The callback function will receive a variable — that we can name how we wish — which contains the data returned from the GET request. results = $(results).find('#main').html(); mainContent.html(results); loadingIcon.fadeOut(400);
Regardless of whether we use $.get or $(element).load(), the request will return the entire contents of the requested page. However, we only need the contents of the #main div — nothing else. As such, we find() the #main div, and store its html() contents in the results variable. Finally, we exchange the current contents of the #main div (mainContent) with the results variable, and fade out the loading icon. return false;
The default browser action, when a submit button is clicked, is to post the contents of form to whatever has been specified as the action of the form. Because we’re using AJAX, we need to override this functionality. return false will do the trick. And if JavaScript is disabled, the form will simply work as it did before. This is only a helpful enhancement!
87
Fig. 9-1. Our Search: now AJAXified.
Tumblr and AJAX
89
The Tumblr API
The Tumblr API Because the Tumblr API is implemented over standard HTTP requests, it is laughably simple to integrate into your application. You can access your data as either structured XML or JSON. I generally prefer the latter. To read this data, browse to “http://yourSite.tumblr.com/api/read/”. This will return the XML version. For example, here’s the data for this book’s demo site.
To fetch the data as JSON instead, change the URL to: “http:// yourSite.tumblr.com/api/read/json”.
90
The Tumblr API
Optional GET Parameters To modify the returned data, you can pass a variety of GET parameters to this URL.
Pass “debug=1” as a GET parameter to format the results of your XML fetch. This works similarly to PHP’s print_r function, e.g.: http://yourSite.tumblr. com/api/read/?type=video &debug=1
• type : The post type to return. Needs to be either text, chat, photo, quote, link, audio, or video. • id : A specific post’s id that you want to return. • num : The number of items (posts) to return. By default, this is set to 20. • start : The post offset to begin from. • search : Only returns posts which match this search value. • tagged : Return posts with a specific tag. • debug : If passed a value of “1”, this will prettify the XML feed. • callback : JSON only. It should be equal to the name of the function to call with the JSON object as its parameter.
1. Retrieve only Video Posts http://yourSite.tumblr.com/api/read/json?type=video
91
The Tumblr API
2. Retrieve 10 Most Recent Posts http://yourSite.tumblr.com/api/read/json?num=10
3. Only Retrieve Posts that Match a Search Query of “man”, and which are Quote Posts http://yourSite.tumblr.com/api/read/json?search=man &type=quote
4. Read Dashboard Posts To retrieve all of the posts available on your Dashboard, you must perform an “authenticated read.” This consists of accessing http://www.tumblr.com/api/dashboard and then passing in a few GET parameters. • email : Your account’s email address. • password : The password associated with your Tumblr account. • start, num, filter (optional) : Same as with /api/read (see above). • debug (optional) : If passed a value of “1”, this will prettify the XML feed • likes (optional) : Can be set to “0” or “1”. As such, your request might look like: http://www.tumblr.com/api/dashboard?email=myEmail @example.com&password=myPassword&debug=1
92
The Tumblr API
5. Only Fetch “Liked” Posts To fetch only your “liked” postings, request http://www.tumblr.com/ api/likes and pass in the same parameters as listed above. http://www.tumblr.com/api/likes?email=myEmail @example.com&password=myPassword
6. Read Pages To fetch the contents of your Tumblog’s pages, access: “http:// yourSite.tumblr.com/api/pages”. This will return an XML version of each of your site’s pages.
7. Create New Posts If we combine the Tumblr API with a server-side language like PHP, we can quite easily create new postings without ever visiting the Dashboard. To create a post, fetch http://www.tumblr.com/api/ write and pass in your user credentials, along with the type of post and any other required parameters. • email : Your account’s email address. • password : The password associated with your Tumblr account. • type : The type of post. Can be set to regular, photo, quote, link, conversation, video, or audio. • tags (optional) : Comma-separated list of tags for the post. <?php // Authorization info $tumblr_email
= 'myEmail@example.com';
$tumblr_password = 'myPassword';
93
The Tumblr API
// Data for new record $post_type
= 'regular';
$post_title = 'The post title'; $post_body
= 'This is the body of the post.';
// Prepare POST request $request_data = http_build_query( array( 'email'
=> $tumblr_email,
'password' => $tumblr_password, 'type'
=> $post_type,
'title'
=> $post_title,
'body'
=> $post_body
) ); // Send the POST request (with cURL) $c = curl_init('http://www.tumblr.com/api/write'); curl_setopt($c, CURLOPT_POST, true); curl_setopt($c, CURLOPT_POSTFIELDS, $request_data); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($c); $status = curl_getinfo($c, CURLINFO_HTTP_CODE); curl_close($c); // Check for success if ($status == 201) { echo "Success! The new post ID is $result.\n"; } else if ($status == 403) { echo 'Bad email or password'; } else { echo "Error: $result\n"; } ?>
( http://www.tumblr.com/docs/en/api )
94
The Tumblr API
8. Authenticate Users To validate a Tumblr user’s credentials, send a POST request to http://www.tumblr.com/api/authenticate, and pass an email and password parameters to obtain the applicable information, if any. http://www.tumblr.com/api/authenticate?email=myEmail @example.com&password=myPassword
This will return a block of XML containing two tags, and various other attributes. <user> • can-upload-audio • can-upload-aiff • can-upload-video • max-video-bytes-uploaded <Tumblelog> • title • type • name • url • avatar-url • is-primary With PHP, we can quickly and easily grab a user’s Tumblr information with these credentials. Here’s a short example:
95
The Tumblr API
<?php // Authorization info $tumblr_email
= 'myEmail@example.com';
$tumblr_password = 'myPassword'; // Prepare POST request $request_data = http_build_query( array( 'email'
=> $tumblr_email,
'password' => $tumblr_password ) ); // Send the POST request $c = curl_init('http://www.tumblr.com/api/authenticate'); curl_setopt($c, CURLOPT_POST, true); curl_setopt($c, CURLOPT_POSTFIELDS, $request_data); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($c); $status = curl_getinfo($c, CURLINFO_HTTP_CODE); curl_close($c); // Check for success if ($status != 200) { echo "Error!"; } $xml = new SimpleXMLElement($result); ?> <h1>User Information:</h1> <ul> <li>Title: <?php echo $xml->tumblelog['title']; ?></li> <li>Number of Posts: <?php echo $xml->tumblelog['posts']; ?></li>
96
The Tumblr API
<li>Avatar: <br /> <img src="<?php echo $xml->tumblelog['avatar-url'];?>" alt="<?php echo $xml->tumblelog['title'];?>" /></li> </ul>
see: Chapter 10 – The Tumblr API/ 1_The_Tumblr_API.mp4
Practical Application: Filtering Posts by Type As we’ve seen, the Tumblr API can handle a number of common tasks. But one thing that can be frustrating is the fact that, at the time of this writing, there really isn’t an easy way to filter postings according to their post type (video, audio, article, etc.). In fact, before preparing this chapter, I sent a plea out on Twitter asking if anyone knew of some native method that I wasn’t familiar with. In the past, I’ve used a mixture of the Tumblr API and JavaScript to accomplish this task, but surely, I thought, there must be a built-in way to achieve this effect. Sadly, if one is available, it’s undocumented. What this means is that, if we require this functionality, we’ll need to use the API.
97
The Tumblr API
1. Create the Markup As always, the first step is to create the necessary markup, which I’ll place within the sidebar of my theme. You’re free to place yours anywhere you wish. <!-- SIDEBAR: SORT BY CATEGORY --> <section id="categorySort"> <h3>Sort By:</h3> <ul> <li><a href="#" class="regular"><img src="http:// static.tumblr.com/taio0pf/aX1l4qwei/article-icon.png" alt="Articles" /></a></li> <li><a href="#" class="photo"><img src="http:// static.tumblr.com/taio0pf/WuHl4qwg3/photo-icon.png" alt="Photos" /></a></li> <li><a href="#" class="video"><img src="http:// static.tumblr.com/taio0pf/pICl4qwfs/video-icon.png" alt="Videos" /></a></li> <li><a href="#" class="audio"><img src="http:// static.tumblr.com/taio0pf/cI5l4qwdb/audio-icon.png" alt="Audio" /></a></li> <li><a href="#" class="chat"><img src="http:// static.tumblr.com/taio0pf/jCel4qwgn/chat-icon.png" alt="Chats" /></a></li> <li><a href="#" class="link"><img src="http:// static.tumblr.com/taio0pf/TpTl4qwge/link-icon.png" alt="Links" /></a></li> </ul> </section>
Within this section, we’ve added an unordered list, with each list item wrapping an image, which links to the icon which corresponds to a specific post type. Note that we’ve also added a class to each anchor, which refers to the post type associated with its respective icon. After a bit of styling, we end up with:
98
The Tumblr API Good deal. Of course, when we click on the icons, nothing will happen. We have to enable that functionality with JavaScript, specifically jQuery.
2. Writing the JavaScript Next, with jQuery, we need to listen for when one of these anchor tags is clicked. When it is, we’ll call a new function — let’s name it byCategory — that will retrieve a JSON feed from our blog. $('#categorySort').find('a').click(function() { var postType = this.className; byCategory(postType); return false; });
When an anchor tag within the #categorySort element is clicked, we run an anonymous function. Within this function, we first create a new variable, postType, and make it equal to the value of the class for the anchor that was clicked. For instance, if the user clicked this anchor tag: <li><a href="#" class="regular"><img src="http://static. tumblr.com/taio0pf/aX1l4qwei/article-icon.png" alt="Articles" /></a></li>
The postType variable would then be equal to “regular”. Next, we call our byCategory function, that has yet to be created. However, when we call it, we make sure that we pass it the post type. Lastly, we return false to disable the default browser action when an anchor tag is clicked.
99
The Tumblr API
function byCategory(postType, callback) { $.getJSON('http://rockabletumblr.tumblr.com/api/read/ json?type=' + postType + '&callback=?', function(data) { // data is equal to the returned data }); // end getJSON };
Here, we’re using the helpful $.getJSON method to retrieve our site’s JSON feed. The basic format is http://mySite.tumblr. com/api/read/json. Beyond that, we can pass in as many keyvalue pairs as we require. In this case, I’m setting type equal to the postType variable, which, if you remember, is equal to the post type of the icon that the user clicks on. Secondly, we also pass “callback=?” which is required in order to utilize the anonymous callback function. The data returned from this query will then be represented via the data object. Within the callback function of the $.getJSON method, we next need to loop through the returned items, and display the information on the page. var article = []; $.each(data.posts, function(i, item) { // i = index // item = data for a particular post switch(item.type) { case 'photo': article[i] = '<li><a href="' + item.url + '"> <img src="' + item['photo-url-500'] + '" alt="image" /></a></li>'; break; case 'video': article[i] = '<li><a href="' + item.url + '">' + item['video-player'] + '</a></li>'; break;
100
The Tumblr API
case 'audio': article[i] = '<li><a href="' + item.url + '">' + item['audio-player'] + '</a></li>'; break; case 'regular': article[i] = '<li><a href="' + item.url + '">' + item['regular-title'] + '</a></li>'; break; case 'quote': article[i] = '<li><a href="' + item.url + '">' + item['quote-text'] + '</a></li>'; break; case 'chat': article[i] = '<li><a href="' + item.url + '">' + item['conversation-text'] + '</a></li>'; break; case 'link': article[i] = '<li><a href="' + item.url + '">' + item['link-text'] + '</a></li>'; break; default: alert('Something went wrong, yo.'); }; }); // end each
Unfortunately, this becomes more work than would be ideal. Preferably, we could simply loop through the items and reference the contents of each post type with â&#x20AC;&#x153;post-contents.â&#x20AC;? Unfortunately, depending upon the post type, we must use a different property. As such, we need to use a switch statement to first determine what the post type of the item is (audio, video, chat, etc.). Depending upon the outcome, we create a new list item, passing in the applicable key. $('#main').html('<h2>' + postType + '</h2> <ul id="sortPosts">' + article.join('') + '</ul>');
101
The Tumblr API
Lastly, we grab our main content div ( #main ), and append the name of the post type, as well as an unordered list, containing the relevant information. Because the data was stored within an article array, we need to use article.join('') to turn it into a string.
That should do it. Try clicking on each icon, and watch the applicable data load asynchronously.
103
Miscellaneous Recipes
Miscellaneous Recipes Custom Background Images As designers, we can be as creative as we wish with the background of our website — typically set using the background property on the body element. However, theme development requires a bit more flexibility. What if the user wants to use an enormous custom image for the background of his website? If that’s the case, we can easily allow for this option.
The Logic • By default, we’ll use our own background image, whatever that might be. • We need to create a new option within the Appearance tab of the Dashboard, which will allow the user to upload an image. We can use a meta-image tag for this task. • If an image has been uploaded, we must use that particular image, rather than our own. • Lastly, we must allow the user to set a “Repeat” option for their custom background images as well. Some users might upload a large image that will suffice, while others might prefer to use a small PNG that should be repeated, like a texture.
1. Set the Default Background Image body { background: url(http://static.tumblr.com/taio0pf/ G4Rl2tzfc/bg-glow.png) no-repeat 50% 0; }
104
Miscellaneous Recipes
This specifies that we want to set an image, stored on Tumblr’s server, as the background of the body element. As it’s just a PNG radial gradient, I’ll also push it to the right by 50% of the width of the browser window.
Fig. 11-1. The radial gradient applied to our theme.
2. Add a Meta-Image Tag to the <head> Next, we must create a new attribute in the Appearance tab, which will allow the user to upload a different image, should he choose to do so.
105
Miscellaneous Recipes
<meta name="image:Background" content="0" />
… which will render something like this. This alone will enable the author to upload and save an image from his desktop.
3. Add a “Repeat” Meta-Boolean As mentioned previously, we must also allow the user to specify if his uploaded background image should be repeated — though, by default, we’ll assume that he doesn’t. Just below the meta-image tag, add: <meta name="if:Background Image Repeat" content="0" />
Remember, meta-booleans will create checkboxes. By setting content equal to “0”, this designates that by default the checkbox should be unchecked.
4. If Set, Use the Custom Image As you might remember, a handy feature of the meta-image tag is that we can also use it as a boolean of sorts. We’ll take advantage of this to determine if the user has uploaded an image. If he has, we’ll use it! {block:IfBackgroundImage} background: url('{image:Background}') no-repeat; {/block:IfBackgroundImage}
Don’t forget: we must also listen for if the user checked the “Repeat” checkbox. If they have, we’ll add another background-repeat property to our stylesheet.
106
Miscellaneous Recipes
{block:IfBackgroundImage} background: {color:PrimaryColor} url('{image:Background}') no-repeat; {block:IfBackgroundImageRepeat} background-repeat: repeat; {/block:IfBackgroundImageRepeat} {/block:IfBackgroundImage}
In this block, we’re essentially saying, “If the user checked the “Background Image Repeat” checkbox, then set the background-repeat property to “repeat”. That’s it! If the user uploads a large space graphic, and leaves “Background Image Repeat” unchecked, he’ll see:
107
Miscellaneous Recipes
Okay, okay — it doesn’t look great. But isn’t it the norm for clients to take your beautiful designs and ruin them with in-house changes? Ideally, the user would also need to edit the text colors as well, which we can easily allow for. Alternatively, if he were to use a small 25 × 25px black textured background, and check the “Background Image Repeat” checkbox, he’d see:
Should he wish to return to the default background image that we’ve created, he only needs to click the “Clear” button.
108
Miscellaneous Recipes
5. Save Bandwidth There’s one small problem with what we have so far: we begin by using our default background image, and, if the user uploads a custom image, we continue to load that one as well. There’s no need to load two background images, if only one will be used. That makes sense, right? To compensate, we’ll use the “if not” feature of meta-boolean tags. {block:IfNotBackgroundImage} background: url(http://static.tumblr.com/taio0pf/ G4Rl2tzfc/bg-glow.png) no-repeat 50% 0; {/block:IfNotBackgroundImage}
That’s better! Now, we’ll only load this default background image if the user did not upload his own.
6. The Final Code <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>{Title}</title> <meta name="image:Background" content="0" /> <meta name="if:Background Image Repeat" content="0" /> <style> body { /* CUSTOM BACKGROUND IMAGE SUPPORT */ {block:IfBackgroundImage} background: url('{image:Background}') no-repeat; {block:IfBackgroundImageRepeat} background-repeat: repeat; {/block:IfBackgroundImageRepeat} {/block:IfBackgroundImage}
109
Miscellaneous Recipes
{block:IfNotBackgroundImage} background: url(http://static.tumblr.com/taio0pf/ G4Rl2tzfc/bg-glow.png) no-repeat 50% 0; {/block:IfNotBackgroundImage} </style> </head> <body> <!-- content --> </body> </html>
Let Readers Drill you with Questions: Answer Posts Tumblr offers a Q&A post-type, which allows readers to ask the author questions, by visiting “http://yourSite.tumblr.com/ask”.
Enabling the Ask Feature Before readers can begin asking you questions, you must first enable this functionality from within the Dashboard. With this checkbox checked, readers can now visit “yourSite. tumblr.com/ask” to ask you a question — though you should add a link to this page somewhere on your site for convenience, probably in your primary navigation area. Fig. 11-2. The ability for users to ask questions can be enabled under the Community tab.
110
Miscellaneous Recipes
Answer Posts Variables • {block:Answer} {/block:Answer} : The markup within this block will render if the post type is “answer”. • {Question} : The question for the post. • {Answer} : The answer for the post. • {Asker} : An anchor tag with a value equal to the asker’s username. • {AskerPortraitURL-16} : Portrait URL of the asker: 16 × 16px • {AskerPortraitURL-24} : same as above, but 24 × 24px • {AskerPortraitURL-30} : same as above, but 30 × 30px • {AskerPortraitURL-40} : same as above, but 40 × 40px • {AskerPortraitURL-48} : same as above, but 48 × 48px • {AskerPortraitURL-64} : same as above, but 64 × 64px • {AskerPortraitURL-96} : same as above, but 96 × 96px • {AskerPortraitURL-128} : same as above, but: 128 × 128px Example: {block:Answer} <div class="answer"> <h4><a href="{Permalink}">Question and Answers</a></h4> <h2><a href="{Permalink}">{Question}</a></h2> <em>{Answer}</em> <p class="askedBy"><img src="{AskerPortraitURL-24}" alt="Asked By" />Asked By: {Asker}</p>
111
Miscellaneous Recipes
{Body} </div> {/block:Answer}
The “answer” block can be a bit confusing at first. This is because one would think that by visiting “yourSite.tumblr.com/ask” the contents of the {block:Answer} block will load. This actually isn’t the case. When you visit that link, you’re accessing a page, in which case the “Text” block will be used instead. The textbox and “Ask” button will be created for you dynamically by Tumblr:
{block:Answer} on the other hand, is used to display the question and answer data. Once a question has been asked by a reader, it will show up within the “Messages” section of the owner’s Dashboard:
112
Miscellaneous Recipes
At this point, you can either answer the question, delete it, or even queue it to be published at a later date:
Once your answer has been published, it will display on the page in the same way that any other post type would:
Variable Transformations Tumblr makes it incredibly easy to transform values to specific formats, such as for JavaScript. By prefixing short keywords to the site-wide variables, Tumblr will generate an optimized string. For example, to keep things simple, if I wanted to simply alert the title of my Tumblog with JavaScript, just before the closing <body> tag, I could write: <script> var title = {JSTitle};
113
Miscellaneous Recipes
alert(title); </script>
Note that we’re prefixing the general {Title} variable with the “JS” keyword. This instructs Tumblr to generate a JavaScript-friendly string, wrapping the value in quotes accordingly.
Transformations • Plaintext : Use this prefix when you need to strip HTML tags, and convert all necessary characters to HTML entities. • JS : As demonstrated in the previous example, this will output the string, wrapped in quotes. • JSPlaintext : This does the same thing as “Plaintext,” however it wraps the entire string in quotes. • URLEncoded : Should the value need to be passed through a querystring, for example, this will output a URL encoded string. To transfer values to your JavaScript, prepend “JS” to the Tumblr variable.
114
Miscellaneous Recipes
Example: Assuming that {Title} is equal to “John & Kate’s Blog”, the four transformations listed above will generate the following: • {PlaintextTitle} : John &amp; Kate's Blog • {JSTitle} : "John & Kate's Blog" • {JSPlaintextTitle} : "John &amp; Kate's Blog" • {URLEncodedTitle} : John%20%26%20Kate's%20Blog
Show a User’s Social Networking Links Let’s add a method for the user of our theme to display links to his favorite social networking sites within the sidebar. If we wanted, we could get fancy and use a set of custom social networking icons; however, I’ll keep it simple, and use some basic CSS to style our list of links. 1. We begin with the following markup: <section id="social"> <h3>Find Me</h3> <ul> <li><a href="/archive">Archive</a></li> <li><a href="{RSS}">RSS Feed</a></li> </ul> </section>
• Archives Path : “/archive” • RSS Path : {RSS}
115
Miscellaneous Recipes
These two links will display by default, though, we should also create some textboxes that will allow the user to type in their social networking usernames and such for each site. To accomplish this, we can use metatext tags. 2. Within the <head> tag of your HTML, add: <meta name="text:Flickr Username" content="" /> <meta name="text:Twitter Username" content="" /> <meta name="text:MySpace Username" content="" /> <meta name="text:Picasa Username" content="" /> <meta name="text:Facebook Username" content="" /> <meta name="text:StumbleUpon Username" content="" /> <meta name="text:Website" content="" />
Feel free to modify these tags as you see fit; I may have missed some commonly used networks — perhaps Last.fm. Luckily, it only takes a few seconds to add another option. Within the Dashboard ➤ Customize ➤ Appearance tab of Tumblr’s back-end, this “code” will render:
116
Miscellaneous Recipes
By default, the inputs above will be blank. Feel free to insert your own credentials, as I have done. 3. Next, we need to determine if a value has been entered into each of these textboxes, and, if so, create a new list item in our markup to display a link accordingly. <!-- SIDEBAR: SOCIAL NETWORKING LINKS --> <section id="social"> <h3>Find Me</h3> <ul> <li><a href="/archive">Archive</a></li> <li><a href="{RSS}">RSS Feed</a></li> {block:IfTwitterUsername} <li><a href="http://www.twitter.com/{text:Twitter Username}">Twitter</a></li> {/block:IfTwitterUsername} {block:IfFlickrUsername} <li><a href="http://www.flickr.com/photos/ {text:Flickr Username}">Flickr</a></li> {/block:IfFlickrUsername} {block:IfStumbleUponUsername} <li><a href="http://www.stumbleupon.com/profile/ {text:StumbleUpon Username}">StumbleUpon</a></li> {/block:IfStumbleUponUsername} {block:IfFacebookUsername} <li><a href="http://www.facebook.com/{text:Facebook Username}">Facebook</a></li> {/block:IfFacebookUsername} {block:IfWebsite} <li><a href="{text:Website}">My Website</a></li> {/block:IfWebsite} </ul> </section>
117
Miscellaneous Recipes
Meta-text tags allow us to use their values, or lack thereof, as if statements. Above, we first check to see if any text has been entered into the textboxes. If there has, we create a new list, with a nested anchor tag that links to the user profile. We can retrieve the value for each textbox by using the meta-text template tag, for example, {text:Flickr Username}. That’s all there is to it. If you’d like to use specific social networking icons for each item, you could simply add a unique class to each list item, and then set the icon as a background image from within your CSS file.
Show Who I’m Following Integrating a “show who I follow” widget into your theme is mostly a simple task — though there might be a couple of kinks.
Following Variables • {block:Following} {/block:Following} : Code within these tags will display if you’re following other Tumblogs. • {block:Followed} {/block:Followed} : For each blog you’re following. • {FollowedName} : The username of the Tumblog you’re following. • {FollowedTitle} : The title of the Tumblog you’re following. • {FollowedURL} : The URL of the Tumblog you’re following.
118
Miscellaneous Recipes
• {FollowedPortraitURL-16} : Portrait URL of the followed blog, at 16 × 16px. • {FollowedPortraitURL-24} : Same as above, but 24 × 24px. • {FollowedPortraitURL-30} : Same as above, but 30 × 30px. • {FollowedPortraitURL-40} : Same as above, but 40 × 40px. • {FollowedPortraitURL-48} : Same as above, but 48 × 48px. • {FollowedPortraitURL-64} : Same as above, but 64 × 64px. • {FollowedPortraitURL-96} : Same as above, but 96 × 96px. • {FollowedPortraitURL-128} : Same as above, but 128 × 128px.
1. Sample Markup Let’s review the beginning markup, which can be placed anywhere you wish, but within the sidebar of your theme. {block:Following} <section id="following"> <h3>Who I Follow</h3> <ul> {block:Followed} <li> <a href="{FollowedURL}"> <img src="{FollowedPortraitURL-48}" alt="{FollowedName}" /> </a> </li> {/block:Followed} </ul> </section> {/block:Following}
119 With the variables above in mind, we can easily construct a <section> element, which contains an unordered list of each site we follow. As this is a demo Tumblr theme, I’m only following two users, however, yours will undoubtedly be more!
Miscellaneous Recipes
If you’re rendering a list of blogs you follow, use an unordered list for the markup.
2. One Problem This is great; however, always keep flexibility in mind. What if the user of your theme doesn’t want to show this information to their readers? Hmm… what to do? To compensate, let’s add another meta-boolean attribute to the Appearance tab of your Dashboard. Within the <head> tags of your theme, add: <meta name="if:Show Who I Follow" content="1" />
It’s not uncommon by any means to display such a list. With that in mind, we’ll set the content attribute to “1”, or true. This means that, by default, this widget will render — though the user will now have the ability to override this section, should they choose to do so.
3. Wrapping Boolean The final step is to wrap the markup above with our newly created custom block: {block:IfShowWhoIFollow}. <!-- SIDEBAR: FOLLOWING --> {block:IfShowWhoIFollow} {block:Following}
120
Miscellaneous Recipes
<section id="following"> <h3> Who I Follow </h3> <ul> {block:Followed} <li> <a href="{FollowedURL}"> <img src="{FollowedPortraitURL-48}" alt="{FollowedName}" /> </a> </li> {/block:Followed} </ul> </section> {/block:Following} {/block:IfShowWhoIFollow}
An extra few minutes of work will result in a far more flexible Tumblr theme. Always allow for flexibility.
Feedburner Integration Should you or your users require a way to track RSS subscribers, chances are you’ll need to use a tool like Google’s excellent FeedBurner. Luckily, Tumblr not too long ago streamlined this process for us.
1. Sign up for Feedburner Begin by visiting http://www.feedburner.com/, and sign in with your Google credentials. You’ll next need to burn a feed. To do so, simply paste in the URL of your Tumblog, and click “Next”:
121
Miscellaneous Recipes
That’s all Google needs! You’ll be directed to a page similar to this one:
2. Insert the FeedBurner URL Next, return to the Customize section of your Dashboard, click the Services tab, and insert the Feedburner URL that was generated for you in the previous step (Fig. 11-3). That’s all there is to it! Now, after 24–48 hours, you’ll be able to view your site’s statistics, including daily visitors, the number of RSS subscribers, etc (Fig. 11-4). Fig. 11-3. Feedburner integration can be found under the Services tab.
122
Miscellaneous Recipes
Fig. 11-4. Your Feedburner account page collects and displays your Tumblr site’s statistics.
Enable Pagination By default, Tumblr displays ten postings on the page at a time. Beyond that, they expect the theme developer to add pagination support. By doing so, this improves loading times for visitors. This default count of ten can be overridden by visiting the Advanced tab in the Dashboard, and changing the “Post Count” value accordingly.
123
Miscellaneous Recipes
Navigation Variables • {block:Pagination} {/block:Pagination} : Rendered if there is a “Next” or “Previous” page available. • {block:PreviousPage} {/block:PreviousPage} : Rendered if a “Previous” page is available to navigate to. • {block:NextPage} {/block:NextPage} : Rendered if a “Next” page is available to navigate to. • {PreviousPage} : The URL to the previous page. • {NextPage} : The URL to the next page. • {CurrentPage} : The current page number. • {TotalPages} : The total page number count. Example: <!-- PAGINATION --> {block:Pagination} <ul id="pagination"> {block:PreviousPage} <li><a href="{PreviousPage}">Previous</a></li> {/block:PreviousPage} {block:NextPage} <li><a href="{NextPage}">Next</a></li> {/block:NextPage} </ul> {/block:Pagination}
This code must not be placed within {block:Posts}. Remember, if it is, these pagination links will be repeated for every post. No thanks — just once will do!
124
Miscellaneous Recipes
This markup is all that is required. Beyond that, Tumblr will dynamically determine whether or not to render these “Previous” and “Next” links.
Display Tags Similar to most blogging platforms, Tumblr has a tagging taxonomy, which bloggers can optionally use. The advantage is that when a list of these tags is displayed on the page, the reader is then afforded an additional means of traversing through relevant postings. A text field for tagging is available in the sidebar when creating a new post of any type (Fig. 11-5).
Tag Variables • {block:HasTags} {/block:HasTags} : Rendered inside posts that were tagged when created.
Fig. 11-5. Tumblr’s tagging dialog in the sidebar.
• {block:Tags} {/block:Tags} : Rendered for each of a post’s tags. • {Tag} : The name of the tag. • {URLSafeTag} : A URL safe version of the tag name. • {TagURL} : A tag page, which will contain all postings that share this tag name. • {TagURLChrono} : Same as above, but with the posts in chronological order.
125
Miscellaneous Recipes
Example: {block:HasTags} <div id="tags"> <h5>Tagging:</h5> <ul> {block:Tags} <li><a href="{TagURL}">{Tag}</a></li> {/block:Tags} </ul> </div> {/block:HasTags}
By clicking on one of these links, the visitor is then directed to a new page, which displays all postings with that particular tag name:
127
Tools And Icons
Tools And Icons 1. Pictos
Fig. 12-1. http://www.woothemes.com/2010/05/pictos/
Commissioned by WooThemes ( http://www.woothemes.com/ ) and created by Drew Wilson ( http://drewwilson.com/ ) this is a vector set of twenty-one icons which are specifically related to Tumblr. In fact, you might recognize these, because the Rockable Tumblr demo theme uses them in the sidebar.
2. Tag Clouds Tumblr unfortunately lacks any form of tag cloud; however, it can be accomplished on the client-side, with JavaScript. The most popular solution at the time of this writing is called
128
Tools And Icons
“Tumblr Tag Clouds” ( http://tumblrtags.rivers.pro/ ). While there are core JavaScript solutions, this particular component utilizes the jQuery library and the Tumblr API.
3. Mac Dashboard Widget
Fig. 12-2. http://www.tumblr.com/goodies/ (must be logged in)
Intentionally minimal, much like Tumblr, the Mac Dashboard widget allows you to easily create new posts with only the click of a button — without ever having to open a web browser.
4. Embed Posts with JavaScript The Tumblr team provides a helpful script which can be used to import your postings into a different blog or website. All that is required is to reference the script in the desired location in your markup. Even this will work: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8">
129
Tools And Icons
<title>Importing Tumblr Posts with JavaScript</title> </head> <body> <script src="http://yourSite.tumblr.com/js"></script> </body> </html>
Fig. 12-3. Tumblr post information pulled into another site with JavaScript.
130
Tools And Icons
5. 1,540 Simple White Social Media Icons
Fig. 12-4. http://webtreats.mysitemyway.com/1540-infocus-simple-whitesidebar-social-media-icons/
While perhaps not specifically a Tumblr icon-set, these nonetheless fit in perfectly with the clean, minimal aesthetic that Tumblr provides.
6. Bookmark Icons Need some color in your social media icons? This awesome set, created by Benjamin Reid of nouveller.com, should do the trick!
Fig. 12-5. http://www.nouveller.com/general/free-social-media-bookmarkicon-pack-the-ever-growing-icon-set/
131
Tools And Icons
7. Tumblr Post: Firefox Add-On
Fig. 12-6. https://addons.mozilla.org/en-US/firefox/addon/5867/
This Firefox add-on allows you to quickly create new postings by dragging and dropping content onto the extensionâ&#x20AC;&#x2122;s status bar icon. Very helpful!
132
Tools And Icons
8. StreamPad
Fig. 12-7. http://blog.dankantor.com/post/43158314/tumblr-mp3-player
Dan Kantor created a Tumblr widget which allows your readers to easily listen to all of your audio posts via a simple Flash player at the bottom of the page. To use it: 1. Go to your Dashboard 2. Click Customize 3. Click Theme 4. Copy the script code below, just before the closing head tag: <script src="http://o.aolcdn.com/art/merge ?f=/_media/sp/sp-player.js&f=/_media/sp/ sp-player-tumblr.js&expsec=86400&ver=11"></script>
That’s it!
9. Sell Tumblr Themes on ThemeForest.net If you have the necessary design chops, there’s plenty of money to be made by selling premium Tumblr themes. The most popular theme marketplace on the web, ThemeForest.net, has a category specifically for Tumblr themes. Becoming an author is free, and you’ll earn 40–70% of every sale. When you’re ready, definitely check it out!
133
Tools And Icons
Fig. 12-8. Tumblr themes listing on ThemeForest.net
10. Use Tumblr’s Hosting When distributing a theme, the buyer or downloader shouldn’t be expected to store the assets himself. Instead, Tumblr will host your images, JavaScripts and CSS files for free. After you upload an asset, you’ll be presented with a path to the file stored on Tumblr’s server, which you may then use in your theme. Simply replace the relative path to your asset with the new one. Admittedly, editing the URLs of assets in a custom theme is tedious, but it is necessary.
134
Tools And Icons
Fig. 12-9. http://www.tumblr.com/themes/upload_static_file â&#x20AC;&#x192; (must be logged in)
Conclusion So there you have it; once you’ve wrapped your head around the general template structure for Tumblr, creating custom themes becomes as simple as inserting the necessary template tags, perhaps adding some customization options, testing the theme in various browsers, and adding any additional Tumblr API interaction with JavaScript. Beyond that, for each new project, it’s only a matter of “rinse and repeat.” While other blogging platforms, like WordPress, are certainly extremely flexible, powerful and customizable, Tumblr’s core appeal is that it can achieve much of the same, but very simply. Let’s be honest: for 95% of general blogging applications, how much power do we truly need? The Tumblr team have already asked themselves this question for us. And their answer… well, isn’t it obvious?
About The Author Author Jeffrey Way is part of the Envato team and Editor of Nettuts+, a web development tutorials blog with over 70,000 daily readers. He’s also the manager of the popular code marketplace, CodeCanyon.net. He has been in the web industry for over six years, with expertise in HTML, CSS, PHP, JavaScript, jQuery, CodeIgniter, and WordPress. Jeffrey lives in Chattanooga, Tennessee, USA, with his wife-intraining, Allie, and their little dachshund. In addition to Theme Tumblr Like a Pro, he is also the author of From Photoshop to HTML ( http://rockablepress.com/books/photoshop-to-html/ ), and the co-author of Build your own Wicked WordPress Themes, for SitePoint ( http://www.sitepoint.com/books/wordpress1/ ).
Your Download Links Use the link below to download your Photoshop and Tumblr theme files for the Syrup design and theme. http://rockable-extras.s3.amazonaws.com/tumblr-pro/ source-files.zip Use the three links below to download your series of screencasts. Screencasts, chapters 2 and 3: http://rockable-extras.s3.amazonaws.com/tumblr-pro/ chapter-2-and-3.zip Screencasts, chapters 4, 5 and 6: http://rockable-extras.s3.amazonaws.com/tumblr-pro/ chapter-4-5-and-6.zip Screencasts, chapters 7, 8, 9 and 10: http://rockable-extras.s3.amazonaws.com/tumblr-pro/ chapter-7-8-9-and-10.zip