Minggu, 24 Juli 2011

Google Blogger Page Elements Tags For Layouts

MenuHome: How Blogger Data Tags Work
Page Elements Tags For Layouts in Blogger
Available Data Tags in Blogger
HTML Template Syntax in Blogger

The <body> section of a Layouts template is made up primarily of sections and widgets. Sections mark out areas of your page, such as the sidebar, footer, etc. A widget is an individual page element such as a picture, a blogroll, or anything else you can add from the Page Elements tab. You can include any HTML you like around the sections in your template. Questions?

Each section in your template has an opening and a closing tag, looking something like this:

<b:section id='header' class='header' maxwidgets="1" showaddelement="no">
</b:section>

A <b:section> tag can have the following attributes:

  • id - (Required) A unique name, with letters and numbers only.
  • class - (Optional) Common class names are 'navbar,' 'header,' 'main,' 'sidebar,' and 'footer.' If you switch templates later, these names help Blogger determine how best to transfer over your content. However, you can use different names, if you like.
  • maxwidgets - (Optional) The maximum number of widgets to allow in this section. If you don't specify a limit, there won't be one.
  • showaddelement - (Optional) Can be 'yes' or 'no,' with 'yes' as the default. This determines whether the Page Elements tab will show the 'Add a Page Element' link in this section.
  • growth - (Optional) Can be 'horizontal' or 'vertical,' with 'vertical' as the default. This determines whether widgets within this section are arranged side-by-side or stacked.

A section can contain widgets; it can't contain other sections or other code. If you need to insert extra code between or around certain widgets within a section, you'll need to split the section into two or more new sections.

In its simplest form, a widget is represented by a single tag, which is basically just a placeholder indicating how the widget should be handled in the Page Elements tab. The actual data for any given widget is stored in the Blogger database and only accessed when the widget needs to be displayed. Some examples of widgets (one for a page header and one for a list) are:

<b:widget id="header" type='HeaderView' locked="yes"/>
<b:widget id="myList" type='ListView' locked="no" title="My Favorite Things"/>

A widget may have the following attributes:

  • id - (Required) May contain letters and numbers only, and each widget ID in your template should be unique. A widget's ID cannot be changed without deleting the widget and creating a new one.
  • type - (Required) Indicates what kind of a widget it is, and should be one of the valid widget types listed below.
  • locked - (Optional) Can be 'yes' or 'no,' with 'no' as the default. A locked widget cannot be moved or deleted from the Page Elements tab.
  • title - (Optional) A display title for the widget. If none is specified, a default title such as 'List1' will be used.
  • pageType - (Optional) Can be 'all,' 'archive,' 'main,' or 'item,' with 'all' as the default. The widget will display only on the designated pages of your blog. (All widgets display on the Page Elements tab, regardless of thier pageType.)

The types of widgets you can specify are:
  • BlogArchive
  • Blog
  • Feed
  • Header
  • HTML
  • SingleImage
  • LinkList
  • List
  • Logo
  • BlogProfile
  • Navbar
  • VideoBar
  • NewsBar

Each widget can also be written out in an expanded form, detailing the complete layout and contents for that widget. This is what you'll see, for instance, if you download your template from the Edit HTML tab to make a backup. Usually, you won't need to work with widgets in this mode, since it's simpler to just modify them from the Page Elements tab. However, if you want to know more, you can read about the HTML Template Syntax in Google Blogger.

Note: In your published blog, all <b:section> and <b:widget> tags will be replaced with <div> tags, which will have the specified ID. So, you're welcome to refer to, for example, div#header or div#myList in your CSS if you want to. Questions? Let me know!


◀ How Blogger Data Tags WorkAvailable Data Tags ▶

Google Blogger HTML Template Syntax

MenuHome: How Blogger Data Tags Work
Page Elements Tags For Layouts in Blogger
Available Data Tags in Blogger
HTML Template Syntax in Blogger

The basic <b:widget> tags for creating widgets are described in Page Elements Tags For Layouts in Google Blogger. If you just want to use the Page Elements tab to work with everything, then that's all you need to know. However, if you want more fine-grained control, this article describes what you can put inside a widget, if you're working in the "Expand Widget Templates" mode of the Edit HTML page. Questions?

The first thing to do is to add a closing tag. So this:

<b:widget [...attributes...] />
becomes this:

<b:widget [...attributes...]>
</b:widget>

Now with that out of the way, let's talk about what you can put between those tags.

Includes

Widget content is contained in "includable" sections, which have this format:

<b:includable id='main' var='thiswidget'>
    [insert whatever content you want here]
</b:includable>

The attributes are as follows:

  • id: (Required) A unique identifier made up of letters and numbers.
  • var: (Optional) An identifier made up of letters and numbers, for referencing data within this section. (See the data section below.)

Each widget must have one includable with id='main'. This will usually contain most or all of the content that will display for this widget, and in many cases it will be all you need.

If you make more includables with different IDs, they will not be displayed automatically. However, if you make an includable with id='new', then you can reference it in your main includable with <b:include name='new' /> and it will display that way.

The attributes for the b:include tag are as follows:

  • name: (Required) An identifier made up of letters and numbers. It must match the ID of an existing b:includable in the same widget.
  • data: (Optional) An expression or peice of data to pass on to the includable section. This will become the value of the var attribute in the includable.

Here is a simple example demonstrating the use of b:includable and b:include. Loops and data are described later in this article. The main thing to understand here is how the "main" section includes the "post" section within it. It passes along a post that it calls "i" and the included section references it as its var "p", then prints the title.

<b:includable id='main'>
   <b:loop var='i' values='posts'>
      <b:include name='post' data='i'/>
   </b:loop>
</b:includable>

<b:includable id='post' var='p'>
   Title: <data:p.title/>
</b:includable>

Includes are most useful if you have a section of code that you want to repeat multiple times in different places. You can just write the code once, put it inside a b:includable, then use b:include wherever you want it to appear. If you don't need to do that, then you can just stick with the single main includable and not worry about the rest. (Note that the main includable is included automically -- <b:include name='main'/> is unnecessary.)

Data

The data: tag is arguably one of the most important ones, since it's the avenue that brings in all of your actual content. Some examples of this tag are:

<data:title/>
or

<data:photo.url/>

The first example is simplest, and will work in most widgets, since most widgets have titles. All it does is print out the title of the widget. The second example shows a more complex variable, from which we select a particular component. A photo, say in the context of a profile widget, may have components such as url, height, and width. Using the "." notation indicates that we want the URL for this photo, rather than a URL from something else.

There is a great deal of data that you can access with the data: tag, and it varies depending on which widget you're working with. We've got a All Available Data Tags in Google Blogger to help you find the data you need.

Loops

The b:loop tag lets you repeat a section of content multiple times. This is most commonly used for printing out each post in a list of posts for a given page, or each comment, or each label, etc. The general format for using loops is this:

<b:loop var='identifier' values='set-of-data'>
   [repeated content goes here]
</b:loop>

The 'identifier' part can be any name you choose, and will be used to stand in for each new item in the list, each time through the loop. A common convention is to simply call this "i". The set of data you specify for the values can be any piece of data described in the Available Data Tags in Google Blogger as being a list of items. For instance, in the blog posts widget, posts is a list. Code like the following will loop through each post, printing out the title for each one, with header tags around it.

<b:loop var='i' values='data:posts'>
   <h2><data:i.title/></h2>
</b:loop>

Notice how "i" takes on the value of each post in turn, so you can get the title from each one.

If / Else

You can use the b:if and b:else tags to display content in some places but not others. The general format is this:

<b:if cond='condition'>
   [content to display if condition is true]
<b:else/>
   [content to display if condition is false]
</b:if>

The b:else tag is optional. Without it, the result will be either the content listed in the b:if section or nothing. The closing </b:if> is required in each case, however.

For "condition" you can put in anything that evaluates to either true or false. Some data tags are simply true/false values on their own, e.g. allowComments on a post. With other pieces of data, you can compare them with specific values to get a true or false. Here are some examples:

  • <b:if cond='data:post.showBacklinks'> True if the current post is set to show backlinks.
  • <b:if cond='data:blog.pageType == "item"'> True if the current page is an item page (post page).
  • <b:if cond='data:displayname != "Fred"'> True if this is not Fred's display name.
  • <b:if cond='data:post.numComments > 1'> True if the current post has more than one comment.
Unfortunately Blogger template does NOT support declaring and using custom variables. This is surprising and inconvenient to many of us. Let's keep our fingers crossed that Google will add this feature in the future..
Now in Google Blogger's HTML template, you know how the layout tags work, you know all the available tags that give you data regarding this post, and you know the logic syntax to manipulate those data. You should be able to modify the template to do whatever you need within the capabilities of the language! Questions?

◀ Available Data Tags

Sabtu, 23 Juli 2011

Google Blogger Available Data Tags

MenuHome: How Blogger Data Tags Work
Page Elements Tags For Layouts in Blogger
Available Data Tags in Blogger
HTML Template Syntax in Blogger

As mentioned in the HTML Template Syntax in Google Blogger post, there are many different tags you can use to include specific pieces of data in your template. They will all be formatted as <data:name/> or <data:name1.name2/>, where name is the name of the particular piece of data you want to use. In the name1.name2 example, name2 is a particular item within a set of data called name1, e.g. photo.url. Questions?

This is a master list of all such available data. It is divided into sections by page element, because different types of widgets use different data.

Globally Available Data

This information applies to the entire page, so you can use it anywhere, unlike other data which can only be used in a specific widget. These should be referenced as part of the overall "blog" data, e.g. as <data:blog.title/>, etc.

  • title: The blog's title.
  • pageType: The type of the current page. One of 'item', 'archive', or 'index'.
  • url: The URL of the current page.
  • homepageUrl: The homepage of the blog.
  • pageTitle: The title of the current page. This is often the blog title, but may contain additional information on archive or post pages.
  • encoding: The encoding to use for the blog, e.g. UTF-8.
  • languageDirection: Either "ltr" or "rtl" for left-to-right and right-to-left languages, respectively.
  • feedLinks: The autodiscovery feed links for the page header.

Page Header

This is a simple widget with just two pieces of data. They can be referenced simply as <data:title/> and <data:description/>.

  • title: The blog's title.
  • description: The blog's description.

Blog Posts

This is the central part of any blog, and the most complex. You should definitely consider simply making modifications to one of the default templates before writing a blog posts widget from scratch. But however you want to do it, here's all the data available in this widget.

  • feedLinks: A list of feeds for this page. On the main page, this will contain the main blog feeds; on item pages, this will also contain comments feeds. Each item in this list contains the following:
    • url: The feed URL.
    • name: The feed name (i.e. 'Posts' or 'Comments').
    • feedType: The type of feed (Atom or RSS).
    • mimeType: The mime type of the feed.
  • olderPageUrl: If there are older posts than the ones on the current page, this is a URL to those posts. Context-sensitive for page type. (Not all pages will have this link.)
  • olderPageTitle: Title of the link to the older page of posts.
  • newerPageUrl: The newer equivalent of olderPageUrl.
  • newerPageTitle: The newer equivalent of olderPageTitle.
  • commentLabel: The phrase to use to show the number of comments, e.g. "comments."
  • authorLabel: The phrase to use to indicate who wrote the post, e.g. "posted by."
  • timestampLabel: The phrase to use to indicate when the post was written, e.g. "posted at."
  • postLabelsLabel: Phrase to introduce the list of post labels, e.g. "labels for this post."
  • backlinksLabel: Phrase to describe backlinks to this post, e.g. "links to this post."
  • posts: A list of all posts for this page. Each post contains the following:
    • dateHeader: The date of this post, only present if this is the first post in the list that was posted on this day.
    • id: The numeric post ID.
    • title: The post's title.
    • body: The content of the post.
    • author: The display name of the post author.
    • url: The permalink of this post.
    • timestamp: The post's timestamp. Unlike dateHeader, this exists for every post.
    • labels: The list of the post's labels. Each label contains the following:
      • name: The label text.
      • url: The URL of the page that lists all posts in this blog with this label.
      • isLast: True or false. Whether this label is the last one in the list (useful for placing commas).
    • allowComments: 'True' if this post allows comments.
    • numComments: The number of comments on this post.
    • showBacklinks: Whether to show backlinks for this post.
    • numBacklinks: Number of backlinks for this post.
    • addCommentUrl: The URL of the 'add a comment' form for this post.
    • emailPostUrl: The URL of the 'email this post' form for this post.
    • editUrl: The URL of the edit form for this post.
    • feedLinks: A list of feeds specific to this post. (This is different from the overall blog feedLinks, as it may contain a feed for the post's comments, for instance.) Each contains the following:
      • url: The feed URL.
      • name: The feed name (e.g. 'Posts' or 'Comments').
      • feedType: The type of feed (Atom or RSS).
      • mimeType: The mime type of the feed.
    • comments: A list of all comments for this post (on item pages only). Each contains the following:
      • id: The numeric ID of the comment.
      • body: The body of the comment.
      • timestamp: The time the comment was created.
      • author: The display name of the comment's author, or 'Anonymous'.
      • authorUrl: URL of the comment author's profile, if the comment is not anonymous.
      • deleteUrl: The URL for deleting this comment.
      • isDeleted: Whether this comment has been deleted. (The text of deleted comments is replaced with a placeholder.)

Blog Archives

The different styles provided here are for the different default options on the Page Elements tab. If you're designing a new version, it's easiest to use 'FLAT' as the style, and then manipulate the rest of the data as desired.

  • title: The title of the widget.
  • style: One of 'MENU', 'FLAT', or 'HIERARCHY'.
  • data: A list of each archive unit, each of which contains:
    • name: The name of this archive interval, e.g. "August 2006."
    • url: The link to the page containing posts from this interval.
    • post-count: How many posts there are in this interval.

Profile Widget

For a blog with a single author, the profile widget contains the following information. Note that to access different parts of the photo data, you'll use notation such as <data:photo.url/>.

  • title: The title of the widget.
  • userUrl: The author's profile URL.
  • location: The location from the author's profile.
  • aboutme: The "About Me" information from the profile.
  • displayname: The author's display name.
  • photo: The user's profile photo, made up of the following:
    • url: The photo URL.
    • width: The photo's width, in pixels.
    • height: The photo's height, in pixels.
    • alt: The "alt" text for the photo.

On team blogs, the profile widget contains less information about more authors, as follows.

  • title: The title of the widget.
  • authors: The list of all authors, each of which contains the following:
    • displayname: The author's display name.
    • userURL: The author's profile URL.

If you want to design your template to handle both single- and multiple-author blogs, you can use the data:team variable to distinguish between the two cases. E.g. <b:if cond='data:team=="true"'> (display multiple authors) </b:if>

Text / HTML / JavaScript Widget

The Text widget and the HTML/JavaScript widget work the same way and have the same two pieces of data.

  • title: The widget's title.
  • content: The content of the widget.

Feed Widget

A feed widget's content is dynamically loaded using Google AJAX API after blog is rendered in a browser. It can be styled only using CSS.

  • title: The widget's title.
  • feedUrl: The URL of the feed.

Picture Widget

A picture widget contains a single image, and provides all the relevant data for that image.

  • title: The title of the widget.
  • sourceUrl: The URL of the image.
  • width: The image's width, in pixels.
  • height: The image's height, in pixels.
  • caption: The image caption.

Labels Widget

The labels widget includes a list of all labels that are in use on the blog.

  • title: The widget title.
  • labels: The list of labels, each of which contains:
    • name: The text of the label.
    • count: How many posts have this label.
    • url: A link to a page displaying posts with this label.

List Widget

The simplest form of a list. Each item is just a single piece of text, without any different types of data within it.

  • title: The widget title.
  • items: The list of items.

Link List Widget

A slightly fancier list, where each item has two parts to it: the text and the link.

  • title: The widget title.
  • links: The list of links, each of which contains:
    • name: The link's text.
    • target: The link's URL.

Logo Widget

It doesn't get any simpler than this one. Just one piece of data here.

  • fullButton: The URL of the Blogger button you've selected.
Questions? Let me know!

◀ Page Elements Tags For LayoutsHTML Template Syntax ▶

How Google Blogger Data Tags Work

MenuHome: How Blogger Data Tags Work
Page Elements Tags For Layouts in Blogger
Available Data Tags in Blogger
HTML Template Syntax in Blogger
Google Blogger is one of the most popular blogs. You sign up for an account and get a blog going within minutes. It offers much flexibility in making the blog the way you want it to be but not without some knowledge and finesse. Read on to know exactly how to access data related to the current post and how to manipulate them with loops and conditionals.

When you log into your Blogger account and go to Design -> Edit HTML and check 'Expand Widget Template you see a bunch of Blogger specific tags interspersed with HTML code. Here's an example:
...
<b:loop values='data:post.comments' var='comment'>
<dt expr:class='"comment-author " + data:comment.authorClass' expr:id='data:comment.anchorName'>
<b:if cond='data:comment.favicon'>
<img expr:src='data:comment.favicon' height='16px' style='margin-bottom:-2px;' width='16px'/>
</b:if>
<a expr:name='data:comment.anchorName'/>
<b:if cond='data:blog.enabledCommentProfileImages'>
<data:comment.authorAvatarImage/>
</b:if>
<b:if cond='data:comment.authorUrl'>
<a expr:href='data:comment.authorUrl' rel='nofollow'><data:comment.author/></a>
<b:else/>
<data:comment.author/>
</b:if>
<data:commentPostedByMsg/>
</dt>
<dd class='comment-footer'>
<span class='comment-timestamp'>
<a expr:href='data:comment.url' title='comment permalink'>
<data:comment.timestamp/>
</a>
<b:include data='comment' name='commentDeleteIcon'/>
</span>
</dd>
<dd class='comment-body' expr:id='data:widget.instanceId + data:comment.cmtBodyIdPostfix'>
<b:if cond='data:comment.isDeleted'>
<span class='deleted-comment'><data:comment.body/></span>
<b:else/>
<p>
<data:comment.body/>
</p>
</b:if>
</dd>
</b:loop>
...
In the Blogger HTML template we see regular HTML tags such as <p> and CSS styling class inside HTML tags. But what about other tags that are NOT HTML tags? What is "data:post.comments"? What is "b:loop" tag? What is "b:if" tag? How does the syntax work?

Basically they are Google Blogger's way of showing each post's data and manipulating them with some simple logic. For example "data:post.comments" store all the comments of this post, and b:loop loops through them one by one. You can identify basic data of a post including the post's title, URL, date and time of the post, etc. However without a comprehensive tutorial that goes through all the available data tags and programming logic syntax how do we know what data are available and how we can manipulate them?
To my dismay I cannot find much help on Google with keywords like "blogger html template tutorial", "blogger data tags help", "blogger widget tags syntax", "blogger tags language tutorial". I finally found some help but they are so hidden on the web that I decided to write a post on them to bring more visibility to where people can find help!

Basically you need to understand three things: element tags for layout in Blogger, available data tags in Blogger, and syntax to manipulate them in Blogger's template language.

Without further ado let's look at the available data tags in Blogger! Questions?

Page Elements Tags For Layouts ▶

Reverse the Order Of Comments In Google Blogger

Q: How can I reverse the order of comments of each blog in Google Blogger?

By default Google Blogger orders the comments of each post in the ascending order of recentness, meaning that the most recent comment shows up last. How uncool! Did it ever occur to them that people would like to see the most recent comment first? How do I show the most recent comments first? How do I show the comments in the descending order of recentness? This post will first describe the issue and then recommend a fix that works!

The Problem
Let's see if there's a way to fix this issue natively in Blogger's widget tag language.

Log into your Blogger account and go to Design -> Edit HTML and check 'Expand Widget Templates' you'll see the following (I put the 'comment-footer' section above 'comment-body' to fit my blog's style):
...
<b:loop values='data:post.comments' var='comment'>
<dt expr:class='"comment-author " + data:comment.authorClass' expr:id='data:comment.anchorName'>
<b:if cond='data:comment.favicon'>
<img expr:src='data:comment.favicon' height='16px' style='margin-bottom:-2px;' width='16px'/>
</b:if>
<a expr:name='data:comment.anchorName'/>
<b:if cond='data:blog.enabledCommentProfileImages'>
<data:comment.authorAvatarImage/>
</b:if>
<b:if cond='data:comment.authorUrl'>
<a expr:href='data:comment.authorUrl' rel='nofollow'><data:comment.author/></a>
<b:else/>
<data:comment.author/>
</b:if>
<data:commentPostedByMsg/>
</dt>
<dd class='comment-footer'>
<span class='comment-timestamp'>
<a expr:href='data:comment.url' title='comment permalink'>
<data:comment.timestamp/>
</a>
<b:include data='comment' name='commentDeleteIcon'/>
</span>
</dd>
<dd class='comment-body' expr:id='data:widget.instanceId + data:comment.cmtBodyIdPostfix'>
<b:if cond='data:comment.isDeleted'>
<span class='deleted-comment'><data:comment.body/></span>
<b:else/>
<p>
<data:comment.body/>
</p>
</b:if>
</dd>
</b:loop>
...
The key line is this:

<b:loop values='data:post.comments' var='comment'>
This line basically loops through each comment for this post. To reverse the order of these comments you'll need a way to indicate you want to loop starting at the most recent comment and work your way through the older comments. But HOW? Unfortunately there is NO WAY to do it natively in Blogger's widget tags for layout! How frustrating!

Luckily you can use the almighty Javascript to manipulate the DOM and reverse the order of the comments! Below we'll discuss this solution. Questions? Let me know!

Solution: Use Javascript
To use the Javascript solution here are the steps you need to do. I assume you've already created an external Javascript file (mine is called my.js) and referenced it within <head> section in your HTML template. Your Javascript file should contain the Javascript code I'll be discussing in this post.

In STEP 1 wrap all components of a comment in a DIV. Each comment has three components: author, body, footer. Once they are wrapped inside one DIV, you can manipulate the comments much more easily. This is not a necessary step but it helps organize comments.

In STEP 2 you write a Javascript function to collect all the comment nodes and insert them into the DOM in the reverse order.

Finally, in STEP 3, call the Javascript function and see that the comments are reversed! Obviously this solution only works if Javascript is enabled, but in today's world of browsers you can pretty much assume that this is the case.


Next let's look at each step in detail below!

STEP 1: Group each comment into one DIV block
Log into your Blogger account and go to Design -> Edit HTML and check 'Expand Widget Templates' and locate the following block of code:
<b:loop values='data:post.comments' var='comment'> 
<dt expr:class='"comment-author " + data:comment.authorClass' expr:id='data:comment.anchorName'>
...
</dt>
<dd class='comment-footer'>
...
</dd>
<dd class='comment-body' expr:id='data:widget.instanceId + data:comment.cmtBodyIdPostfix'>
...
</dd>
</b:loop>
As you can see each comment consists of the author, footer (date and time, etc.), and body. Again I've put the 'comment-footer' section above 'comment-body' to fit my blog's style. Change it to the following:
<b:loop values='data:post.comments' var='comment'> 
<div class='comment-block'>
<dt expr:class='"comment-author " + data:comment.authorClass' expr:id='data:comment.anchorName'>
...
</dt>
<dd class='comment-footer'>
...
</dd>
<dd class='comment-body' expr:id='data:widget.instanceId + data:comment.cmtBodyIdPostfix'>
...
</dd>
</div>
</b:loop>
Now you can see that we've grouped the 'comment-author' block, 'comment-footer' block, and 'comment-body' block inside one DIV block so that we can easily manipulate each comment!

STEP 2: Write a Javascript Function
Now let's open up the external Javascript file referenced by your blog's HTML template. In it we need to write a Javascript function to manipulate the order of comments. Here's the code:
function reverseComments() {
var commentList = document.querySelectorAll("div.comment-block");

for(var i=0;i< commentList.length;i++) {
var node = commentList[i];
node.parentNode.insertBefore(node, node.parentNode.firstChild);
}
}
The way the function works is simple. It selects all the DIV blocks with class set to 'comment-block'; then it loops through each comment and insert it again in the reverse order. Once it's done you get the same comments in the reverse order!In fact this is Javascript and as such you can manipulate the comments any way you want. You can do things like show only the first 3 comments and have the rest hidden which become visible upon clicking a button that says "See more comments". The sky is the limit!

STEP 3: Call the Javascript functionNow you simply call the function 'reverseComments()' where appropriate! You MUST call it when the DOM is ready! I use jQuery, so I call reverseComments() inside the block of $(document).ready(function() { ... }); in my external Javascript file. If you don't use jQuery simply call the function in the following manner:
window.onload=function(){
reverseComments();
}
Questions? Let me know!

Minggu, 17 Juli 2011

Putty Session Times Out Too Fast! Let's NEVER time out!

When I use Putty to run an SSH session it always times out too quickly. How do I make it so that the session never times out?
Putty is one of the most popular Windows based network connection clients. The protocols it supports include SSH, Telnet, Rlogin, Serial, Raw. Whenever I ssh into my ssh server and wait a bit the session times out! The reason is my SSH server sets a timeout value and when my inactivity exceeds that value the session times out. When it happens and when I type something the following dialog window pops up:

Putty Session Timeout Error Window

Isn't it annoying? How the hell do I tell the session to NEVER time out? One way is to configure your remote server to never time out. However it may be complicated and you may not have the permission to do so. Luckily there's a way for Putty to help you achieve this goal!

Solution
You can actually tell Putty to keep sending packets to the remote server so that the server thinks the session is alive and will not time out. Isn't that nice? Simply follow the following steps.

1. Create a session profile!
Fill in the session credentials of the session you regularly access by filling out the following fields: Host Name (or IP address), Port, Connection type. A sample screen shot looks like this:

Putty Session Configuration Window

2. Configure your session profile!
Go to Connection on the left tree and you should see the following screen shot:

Putty Connection Configuration Window

The red rectangle highlights what you need to change. The value of 'Seconds between keepalives (0 to turn off)' is the number of seconds between each two null packets sent to the destination server to keep the session alive. Simply change the value to something like 10. Keep in mind the value depends on how quickly your remote server times out your session when you are inactive. If you find that your session still times out set the value lower.

3. Save your session profile!
Click on Session on the left to go back to the session window which looks like:

Putty Session Configuration Window

Give it a name in the 'Saved Sessions' field. In the example the value of that field is 'daltrac'. Then click on Save button. Now you've saved this session profile. Whenever you want to access a session created by this profile simply click on it in the saved session list and click on Open button!

Now you should be able to run the session and keep the session indefinitely without it timing out. Again this is because Putty keeps sending packets to the remote server to make it think that the session is alive.

If you have any questions please let me know and I will do my best to help you!

Subclipse "SVN Cannot Create Tunnel" Error

When I check out SVN project in Eclipse via Subclipse plugin it fails and displays an error dialog "SVN: can't create tunnel". How do I fix it?
A few definitions first. Eclipse is a popular IDE for developing software, particularly Java, PHP, and C++. Subclipse is a plugin that can be installed on Eclipse making SVN tools available to the IDE's user. SVN is Subversion which is one of the most popular free source control software applications.

When I installed Subclipse on Eclipse on Windows 7 platform I didn't have trouble checking out and manipulating my SVN project which is hosted somewhere remotely. However when my friend did he saw the following error when trying to check out an SVN project:

Subclipse SVN Cannot Create Tunnel

If you are using SVN command line then the SVN command to check out a project looks like 'svn co svn+ssh://...'

How do I fix this error?

The Issue
When you check out an SVN project two protocols are needed: SSH, SVN. That's they the SVN project URL is prefixed with 'svn+ssh://'. This combination ensures that your communication with the SVN server is encrypted and secure. This means that Eclipse needs to know how the SSH protocol works. Below we'll discuss the solution.

Solution
Simply download any SSH client! Such tools include Putty and TortoiseSVN. Download Putty and reinstall Subclipse and see if the problem goes away. It should because when I installed Eclipse and Subclipse I already had Putty installed, and I never had this issue.

If your problem still exists for whatever reason, install TortoiseSVN and tell Subversion how to run SSH protocol by following these steps:

  • Install TortoiseSVN. Download the latest TortoiseSVN from their official website and install it.
  • Locate Subversion's configuration file. If you are using Windows 7 or Windows Vista it should be located at C:\Users\[User]\AppData\Roaming\Subversion\config. If you are using Windows XP it should be located at C:\Documents and Settings\%USERID%\Application Data\Subversion\config.
  • Edit Subversion's configuration file. Open it and locate the line where 'ssh' is commented out as follows "# ssh = $SVN_SSH ssh". This is where you need to let SVN know where the SSH executable file is. If you are using TortoiseSVN then change the line to 'ssh = C:/Program Files/TortoiseSVN/bin/TortoisePlink.exe'. If you don't want to enter user name and password every time you use SVN then change the line to 'ssh = C:/Program Files/TortoiseSVN/bin/TortoisePlink.exe -l user -pw password'
  • Check out your SVN project in Eclipse again

If you have any questions please let me know and I will do my best to help you!

Where Can I Find Multi Language CD or DVD?

Background
When my cousin's wife's baby boy was just born I was brainstorming on what gift I should get them. They had absolutely everything - baby clothes, diapers, baby stroller, cradle, infant car seat, ... You name it and they got it!

Baby's brain
It is scientifically proven that when an infant is born they have up to 6 months for their brain to be fully developed. During this time whatever information their brain receives becomes part of their brain for eternity! Imagine how cool it is. What they learn during this period of time becomes part of them for the rest of their lives. The bad thing is if they learn something bad or get scared by something they may be scared by it for the rest of their lives..

I know what to get the baby boy!
Then I suddenly realized that I should take the direction where the gift would be educational and help the baby intellectually. Why not get them a set of multi language CDs or DVDs so that when he grows up he'll be able to speak 8 languages?

So where do I find CDs that speak multiple languages? More importantly how do I make sure the speaker does NOT speak improper content in those languages? It turns out it's extremely DIFFICULT to locate such CDs or DVDs! Do a search on Google and you'll see what I mean.

Solution - 1st part
One idea suddenly struck me - online radio stations. There are many English online radio stations that air popular channels' content 24/7! If such radio stations exist for English channels they must exist for Chinese, German, Japanese, Spanish, French, Russian channels!

Then I found this wonderful online radio website - http://tunein.com/. "TuneIn gives you access to over 50,000 of the world's radio stations so you can find and listen to unlimited music, sports and talk radio." You simply search for the language you want to find radios for and you see a list of relevant results! For example if you are looking for Spanish radios type 'Spanish' in the search box and Search and the URL will become http://tunein.com/search/?query=Spanish in which you'll see all the Spanish channels! Here's a list of radios of various languages all over the world (burn them on a CD or a DVD and you'll get a multi language CD or DVD):

  • English: http://tunein.com/search/?query=English
  • Chinese: http://tunein.com/search/?query=Chinese
  • Mandarin: http://tunein.com/search/?query=Mandarin
  • Spanish: http://tunein.com/search/?query=Spanish
  • Portuguese: http://tunein.com/search/?query=Portuguese
  • Hindi: http://tunein.com/search/?query=Hindi
  • Arabic: http://tunein.com/search/?query=Arabic
  • Japanese: http://tunein.com/search/?query=Japanese
  • German: http://tunein.com/search/?query=German
  • French: http://tunein.com/search/?query=French
  • Italian: http://tunein.com/search/?query=Italian
  • Russian: http://tunein.com/search/?query=Russian
  • Malaysian: http://tunein.com/search/?query=Malaysian
  • Taiwanese: http://tunein.com/search/?query=Taiwanese
  • Korean: http://tunein.com/search/?query=Korean
  • Turkish: http://tunein.com/search/?query=Turkish
  • Vietnamese: http://tunein.com/search/?query=Vietnamese
  • Polish: http://tunein.com/search/?query=Polish

Solution - 2nd part
However if I just email my cousin and his wife this list of online radio station URLs of different languages in the world it seems too shabby doesn't it? As I suggested earlier you can burn them on a CD or a DVD and you'll get a multiple language CD or DVD! The problem is there is no audio file that contains the radio station's content. Here are two ways you can solve it:

  • Play the radio and direct audio output to audio input of your computer via a wire while recording the audio content with such software as Audacity. Once you are done burn the audio content onto a CD.
  • Buy the audio content directly from Amazon and burn it onto a CD. Simply go to Amazon and select 'MP3 Downloads' in the Search drop down menu and search for the language you are interested in such as 'Spanish'.

When my cousin and his wife got my present they were ecstatic! Let's hope their baby will pick up all these languages when he grows up...

Minggu, 29 Mei 2011

How Does Apache Server Work With PHP Module?

Q: How the HECK does Apache server work with PHP module? What is the flow in which an HTTP request makes through a PHP-enabled Apache web server?

Problem
I have WAMP (Version 2.0) installed on my Windows 7 operating system. For those who don't know, WAMP is an acronym that stands for Windows Apache MySQL PHP. Just so you know this versino of WAMP comes with the following versions of Apache, MySQL, PHP:

- Apache version 2.2.11
- PHP version 5.3.0
- MySQL version 5.1.36

I need to enable the rewrite module of Apache to process URL rewrite rules, so I have the following line in httpd.conf:

LoadModule rewrite_module modules/mod_rewrite.so
I enable rewrite logging at "C:\wamp\logs\rewrite.log" so I can see how rewriting is done via adding the following line in httpd.conf right after Loadmodule:

RewriteLog "C:\wamp\logs\rewrite.log"
RewriteLogLevel 9

I also have the following line in httpd.conf to enable PHP processing:

LoadModule php5_module "c:/wamp/bin/php/php5.3.0/php5apache2_2.dll"
Now an HTTP request goes through Apache, which hands it to the rewrite module to process URL rewrite handling, and has the resulting URL go through PHP engine. But how does Apache rewrite module supposed to know when it should stop rewriting the URL and hand it to PHP module? Is there any way we can control when the processing goes to PHP engine through rewrite rules? Let's answer these questions below.

By the way if you need to know how Apache server variables including %{DOCUMENT_ROOT}, %{REQUEST_URI}, %{REQUEST_FILENAME} work refer to Test Whether a Server Variable is Empty in Popular Web Servers.

Questions?

My Answers
Simply by having the line 'LoadModule php5_module "c:/wamp/bin/php/php5.3.0/php5apache2_2.dll"' in httpd.conf, PHP is enabled to handle pages with extension 'php' (e.g. black-jacket.php) after Apache passes the page through to PHP module. By the way if you'd like PHP to also be able to handle pages with other extensions add the following line after LoadModule in httpd.conf (so PHP handles .html too):

AddType application/x-httpd-php .html
Now here's the interesting part. How the HECK does Apache rewrite module know when to pass through the request to PHP module? The answer is ONLY if and ONLY when the request is NOT matched by ANY rewrite rule, the request is passed through to PHP handler. Even funnier is that there is absolutely NO control you have in the rewrite rules to tell Apache to pass through to PHP handler right away! If you don't agree let me know.

I've tried [L] and [PT] on the RewriteRule directive but when the request matches that particular directive, the request is "internally redirected" to rewrite module again and it's processed by the rewrite module again! In rewrite log it looks like this:

127.0.0.1 - - [29/May/2011:22:26:46 +0800] [localhost/sid#d33140][rid#2254c80/initial/redir#1] (4) [perdir C:/repository/trunk-php/] RewriteCond: input='C:/repository/trunk-php/cache/index.php' pattern='-f' => matched
127.0.0.1 - - [29/May/2011:22:26:46 +0800] [localhost/sid#d33140][rid#2254c80/initial/redir#1] (2) [perdir C:/repository/trunk-php/] rewrite 'index.php' -> '/cache/index.php' # this rule has [L] but it's still internally redirected to rewrite module as the following log statement suggests
127.0.0.1 - - [29/May/2011:22:26:46 +0800] [localhost/sid#d33140][rid#2254c80/initial/redir#1] (1) [perdir C:/repository/trunk-php/] internal redirect with /cache/index.php [INTERNAL REDIRECT]
127.0.0.1 - - [29/May/2011:22:26:46 +0800] [localhost/sid#d33140][rid#2351398/initial/redir#2] (3) [perdir C:/repository/trunk-php/] strip per-dir prefix: C:/repository/trunk-php/cache/index.php -> cache/index.php
127.0.0.1 - - [29/May/2011:22:26:46 +0800] [localhost/sid#d33140][rid#2351398/initial/redir#2] (3) [perdir C:/repository/trunk-php/] applying pattern '(.+)$' to uri 'cache/index.php'
...

The log statement in RED tells you the request is internally redirected to Apache rewrite module for handling again. Only when no rewrite rules have been matched by the current request will you see the following line in the rewrite log:

127.0.0.1 - - [29/May/2011:22:27:52 +0800] [localhost/sid#d33140][rid#228b510/initial/redir#2] (1) [perdir C:/repository/trunk-php/] pass through C:/repository/trunk-php/cache/index.php
Meaning that the Apache rewrite module is finally done with handling the request, and it's the next handler's job to handle it. In this case PHP engine will simply render C:/repository/trunk-php/cache/index.php.

So how do you add a handler and control the order of the handlers? To my disappointment I have NOT found any document online that answers this question. There is "AddHandler" directive in httpd.conf that's supposed to do that but loading the PHP module does that implicitly already.

Conclusion
So the conclusion is that ONLY if and ONLY when the request is NOT matched by ANY rewrite rule, the request is passed through to PHP handler. There is absolutely NO control you have in the rewrite rules to tell Apache to pass through to PHP handler right away! Not by using 'last' option [L] or 'pass through' option [PT] at the end of RewriteRule directive. If you don't agree let me know.

Knowing this fact you may find it impossible to handle the rewrite logic you have in mind. Think deeper and the solution will come. For example you may have a rewrite rule that you'd like the rewritten URL to go straight to PHP engine right away, and you add [L] to that rule which immediately internally redirects the rewritten URL to rewrite engine again (and therefore all the server variables such as %{QUERY_STRING}, %{REQUEST_FILENAME} and %{REQUEST_URI} are updated accordingly) and make the request go through each rule again. In that case you'll have to make sure the request is NOT matched by any RewriteRule and therefore passed through to PHP handler.

If you have any questions please let me know and I will do my best to help you!

Test Whether a Server Variable is Empty in Popular Web Servers

Q: How to test whether a variable is empty in server configurations of popular web servers such as Apache and Nginx?

Problem
It is AMAZING how unfriendly server configuration syntax can be. Even tasks as small as testing whether a value is empty can be confusing. Since I've personally used Apache and Nginx for a long time allow me to unravel the mysteries of how to evaluate whether a servervariable (e.g. document root, query string, etc.) is empty in server's configuration file.

Solution for Apache
In your httpd.conf or .htaccess simply use ="" to evaluate whether an Apache server variable is empty. Use !="" to test whether an Apache server variable is NOT empty. Here's an example:

RewriteEngine on
...
RewriteCond %{DOCUMENT_ROOT}/cache%{REQUEST_URI} -f
RewriteCond %{QUERY_STRING} =""
RewriteRule (.+)$ /cache/$1 [L]
...

This block of rules basically checks whether %{DOCUMENT_ROOT}/cache%{REQUEST_URI} exists as a file and whether the HTTP request has no URL parameters. If both are true rewrite current request to /cache/{current request uri}.

Common Apache Server Variables: By the way the following is some comments that tell you how common server variables such as %{DOCUMENT_ROOT}, %{REQUEST_URI} and %{REQUEST_FILENAME} work as they are often confusing as hell:

#
# Suppose your website is http://www.mensfashionforless.com/
# and document root is set to /usr/repository/trunk (via DocumentRoot directive in httpd.conf).
#
# Now someone issues a request for
# http://www.mensfashionforless.com/2010/10/g-by-guess-grey-low-boot-cut-jeans.html
# then the following is a list of common Apache server variables and their values:
#
# %{DOCUMENT_ROOT} = /usr/repository/trunk
# %{REQUEST_URI} = /2010/10/g-by-guess-grey-low-boot-cut-jeans.html
# %{REQUEST_FILENAME} = /usr/repository/trunk/2010/10/g-by-guess-grey-low-boot-cut-jeans.html
#

Questions? Let me know!

Solution for Nginx
Nginx is a popular web server for its speed and efficiency. Simply use ='' to check whether an Nginx server variable is empty. use !='' to check whether an Nginx server variable is not empty. Here's an example:
server {
listen 80;
server_name www.mensfashionforless.com;
rewrite_log on;
...
location / {
# test whether $document_root/cache$request_uri exists
# in the file system
if (-f $document_root/cache$request_uri) {
set $test P;
}
# test whether there's no URL argument to this request
# = '' tests whether the value is empty!
if ($args = ''){
set $test "${test}C";
}
# if both of the above tests are true, do the rewrite
if ($test = PC){
rewrite ^/(.+)$ /cache/$1 last;
break;
}
...
}
...
}
This block of code checks whether $document_root/cache$request_uri exists as a file in the file system AND whether there is no URL parameter to this HTTP request. If both are true rewrite current request to /cache/{current request uri}. Refer to the post on How do I test multiple conditions in Nginx server configuration? if you are confused by the syntax of testing multiple conditions.

If you have any questions please let me know and I will do my best to help you!

In Nginx Rewrite How To Test Multiple "if" Conditions

Q: How the HECK do I test multiple conditions in "if" statement in Nginx server configuration file?

Problem
It it AMAZING that Nginx server configuration does NOT support multiple conditions natively, meaning no such thing as the following:

if ($request_method = POST && -f $request_filename) {
...
}

In fact it does NOT even support nested conditions, meaning no such syntax as the following:
if ($request_method = POST) {
if (-f $request_filename) {
...
}
}
Why?? I am just as confused as you are. The fact that common conditionals like AND and OR are NOT supported is a serious inconvenience to webmasters especially those who are converting from Apache to Nginx. Below we'll see how to get around the issue of Nginx not supporting multiple conditions in "if" block.

Solution
When there's a will there's a way. You can use a hack by setting a variable in each tested-true condition and when the variable reflects that both conditions are true, do what you need to do. Here's an example:
server {
listen 80;
server_name www.mensfashionforless.com;
rewrite_log on;
...
location / {
# test whether $document_root/cache$request_uri exists
# in the file system
if (-f $document_root/cache$request_uri) {
set $test P;
}
# test whether there's no URL argument to this request
# = '' tests whether the value is empty!
# for more info refer to how to test whether a server variable is empty
if ($args = ''){
set $test "${test}C";
}
# if both of the above tests are true, do the rewrite
if ($test = PC){
rewrite ^/(.+)$ /cache/$1 last;
break;
}
...
}
...
}

Incidentally if you are confused by the test-empty-variable syntax refer to how to test whether a server variable is empty. You need to pay extra attention to Nginx's syntax! For example there MUST be a space between if and ( according to the syntax. The following will fail:
...
if($test = PC){
rewrite ^/(.+)$ /cache/$1 last;
break;
}
...
Nginx's server configuration syntax is very unforgiving. So make sure you check the correctness of syntax before you restart Nginx server. The way to do is it use Nginx's command line tool with "-t" option. Assume it's installed at /usr/sbin/nginx you run the following command:

/usr/sbin/nginx -t -c {location to nginx configuration file or no -c to check the default location}
If you see the following then it means your syntax is correct:

$ /usr/sbin/nginx -t
2011/05/29 11:43:04 [info] 29803#0: the configuration file /etc/nginx/nginx.conf syntax is ok
2011/05/29 11:43:04 [info] 29803#0: the configuration file /etc/nginx/nginx.conf was tested successfully
$

If your nginx config has syntax errors you'll see something like the following:

$ /usr/sbin/nginx -t
2011/05/29 11:52:08 [emerg] 30258#0: unknown directive "abc" in /etc/nginx/nginx.conf:2
2011/05/29 11:52:08 [emerg] 30258#0: the configuration file /etc/nginx/nginx.conf test failed
$

Simply correct the errors and run the command again until you see the success message. By the way this command will check your nginx configuration RECURSIVELY! Suppose your nginx.conf contains such statements as the following:

include /etc/nginx/sites-enabled/*;
Now you run "nginx -t" to check syntax, and it'll check syntax of every configuration file located in /etc/nginx/sites-enabled/!

If you have any questions please let me know and I will do my best to help you!

Jumat, 27 Mei 2011

A QUICK Unix Shell Script To Crawl an XML Sitemap or sitemap.xml

Background
I'd like to quickly crawl every URL of my XML sitemap because doing so triggers caching of each page and better user experience. An XML sitemap is usually named sitemap.xml and contains URLs for crawlers to crawl. It looks something like this:

<?xml version="1.0" encoding="UTF-8" ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.mensfashionforless.com/</loc>
<priority>1.000</priority>
</url>
...
<url>
<loc>http://www.mensfashionforless.com/black-jacket.html</loc>
<priority>0.5000</priority>
</url>
</urlset>

<loc> is the tag you use to indicate an URL. They are the URLs I'd like to spider.

Here is the Unix shell script!
To achieve this purpose I first extract all the URLs; then I issue an HTTP request to them one by one. Keep in mind I don't need to see the content at all; I just need to issue the request so that the server receives the request and does what it's supposed to do. A good use case is that my server caches webpages on demand. So I use this crawler to make my server cache all the webpages specified in sitemap.xml so that later when someone visits my website they'll see the webpage more quickly.

# spider.sh: use awk to get URLs from an XML sitemap 
# and use wget to spider every one of them
ff()
{
while read line1; do
wget --spider $line1
done
}
awk '{if(match($0,"<loc>")) {sub(/<\/loc>.*$/,"",$0);
sub(/<loc>/,"",$0); print $0}}' sitemap.xml | ff

The above script should run successfully in C shell, Bourne shell, Korne shell. If not let me know! In the script above I use 'awk' to extract URLs and use 'wget' to spider each of the URLs without downloading the contents (done via --spider option). Save it as 'spider.sh' and run 'chmod 700 spider.sh' and run './spider.sh' to spider your sitemap.xml!

If you have any questions please let me know and I will do my best to help you!

Unix Command 'nohup' Does Not Work

Q: I am trying to use Unix command 'nohup' to run a process in the background even when I log out. However 'nohup' does NOT work. Why?

Introduction
'nohup' stands for 'no hang up' and allows you to run a process continually until it ends during which you can log out and close your terminal. This is because 'nohup' suppresses or ignores HUP (also known as hangup) Unix signal allowing the process to still run even after the user who issued it logs out. This is useful when for example you'd like to start running a big process, shut down your computer, go home. When you get home you'd like to log in and see that the process is still running.

Tutorial
Suppose you have a script called 'shell-script.sh'. You run the following to run the script in the background persistently:

$ nohup shell-script.sh &
In the same directory a file called 'nohup.out' will be created if it hasn't been created yet. The output of running shell-script.sh goes into nohup.out. Therefore you can run the following to see the output of running shell-script.sh as it rolls:

$ tail -f nohup.out
Problem
The problem is sometimes 'nohup' just doesn't work even though I can run the script fine! Recently I wrote a script to crawl my website and I call it spider.sh. When I run './spider.sh' in the directory where spider.sh exists it works perfectly. However when I run 'nohup ./spider.sh &' it doesn't work. Here's the command prompt trace:

$ nohup ./spider.sh &
[1] 21724
$ nohup: ignoring input and appending output to `nohup.out'

[1]+ Exit 2 nohup ./spider.sh
$
$ cat nohup.out
./spider.sh: 1: Syntax error: "(" unexpected
$

I know there's a problem because when I press Enter after I run 'nohup ./spider.sh &' my shell says 'Exit 2' meaning 'nohup' process has ended. Then in nohup.out I see the syntax error. The weird thing is that I can run 'spider.sh' successfully if I simply run it. How come 'nohup' complains that spider.sh has syntax errors? This is because the shell 'nohup' uses to run the process is different from the shell that you account uses to run the process. The syntax of each shell (e.g. C Shell, Bash Shell, Korn Shell) is different but is mostly minor.

Solution
Script spider.sh begins with:

function ff() {
And 'nohup' complains about "(" (however shell does NOT complain about it). When I changed it to:

ff() {
It works for both 'nohup' and shell! This is because the shell 'nohup' uses to run the process is different from the shell that you account uses to run the process. The syntax of each shell (e.g. C Shell, Bash Shell, Korn Shell) is different but should be minor. Fix the syntax errors and 'nohup' will work! Any feedback feel free to share with us!

Kamis, 05 Mei 2011

Combine Multiple Javascript Files Into One

How come while combining multiple JavaScript files into one I get errors?
SCENARIO
Here's what I am trying to do. I have several Javascript files and I combine them into one via 'cat' command or something (refer to Insert Newlines With Unix 'cat' Command To Combine Multiple Files for how to combine several files into one). Now I run the website and the browser is complaining about the Javascript saying it contains errors! But WHY??

PROBLEM
Don't worry I'll get to the solution soon, but you need to know why this is happening first. The problem is when you combine the js files WITHOUT ending each js file properly you run the risk of violating Javascript syntax! At the end of the js source you have the leniency of not ending it properly but still having it work fine. When it's followed by another Javascript statement however you'll run into errors.

SOLUTION
Simple. Just make sure before you combine the Javascript sources every Javascript source ends with a semicolon (;). I suggest that you add newlines or spaces at the end of the source to make it more readable. Again refer to Insert Newlines With Unix 'cat' Command To Combine Multiple Files for how to combine several files into one!

If you have any questions please let me know and I will do my best to help you!

Insert Newlines With Unix Cat Command

Q: In Unix how do you use 'cat' command to combine multiple files into one with a newline (or any other character) inserted between each pair of files?

A: What I am trying to do is simple: I have multiple files and I'd like to run a Unix command to combine them into one big file, with newlines or breaks inserted following the content of each file. This is useful to for example combine many css files into one so that your website can reference only one css file but you can use many css files during development for easier understanding and modularization purposes.

It turns out that it's not so simple. You CANNOT use newline operators like \n in the shell command because it'll be interpreted incorrectly by the shell. After some trial and error I finally arrived at the following solution.

SOLUTION
1. Create a file called 'separator.txt' that contains one newline. If you use 'vi' program simply type 'i', 'Enter', 'ESC' or 'Escape', then 'ZZ'. If you want to insert other characters such as 'XXX' simply put 'XXX' in that file.

2. Use the 'cat' Unix command in the following manner, assuming you have three files you'd like to combine, fileA, fileB, fileC:

cat fileA separator.txt fileB separator.txt fileC > all.txt

Now open all.txt and it should contain the content of fileA, fileB, fileC with a newline (or whatever characters you put in the file 'separator.txt') inserted between each of them. If you'd like to insert 2 breaks between each two files simply add 2 newlines in the file 'separator.txt'.

Questions? Let me know!

Minggu, 17 April 2011

Fix 403 Error When You Go To Your Website's Web or Document Root

Q: How do you fix 403 error when you go to your website's homepage?

For example suppose your website is http://www.mensfashionforless.com/ but when you go there you see something like the following if your web server is Nginx:

403 Forbidden



nginx/0.6.32


And you see the following if your web server is Apache:

Forbidden

You don't have permission to access /
on this server.


SOLUTION
And you wonder WHY?? The answer is easy. You need to configure your web server to do two things:

1. Allow access to the document root.
2. Specify what file or script to run when document root is accessed.


If you are using Apache and the web root is C:\repository\trunk-php, then the following configuration is an example of allowing access to your document root (placed in httpd.conf):

<directory "C:\repository\trunk-php">
...
Allow from all
...
</Directory>

<IfModule dir_module>
DirectoryIndex index.php
</IfModule>


You'll have to go through Apache's tutorial to find exactly how the configuration works, but the above configuration basically says everyone is allowed to access C:\repository\trunk-php via whatever reachable name. For example if your website can be reached by 127.0.0.1, 192.168.0.33, www.mensfashionforless.com this configuration makes your site accessible by all of them. When you do go to www.mensfashionforless.com you'll see C:\repository\trunk-php\index.php as the 2nd part of the configuration suggests.

If you are using Nginx the configuration may look something like this:

server {
listen 80;
server_name www.mensfashionforless.com;
...
location / {
root /home/repository/trunk-php/;
index index.php
allow all;
}
}


This configuration means when you go to www.mensfashionforless.com you'll see /home/repository/trunk-php/index.php served and 'allow all' means it's accessible from all reachable names.

Error Reflected In The Log
By the way you can also see the 403 error reflected in your log. In nginx log you'd see:

2011/03/08 14:40:59 [error] 7659#0: *1 directory index of "/home/repository/trunk-php/" is forbidden, client: 61.218.81.34, server: www.mensfashionforless.com, request: "GET / HTTP/1.1", host: "www.mensfashionforless.com"

In Aache log you'd see:

[Tue Mar 08 23:17:25 2011] [error] [client 127.0.0.1] Directory index forbidden by Options directive: C:/repository/trunk-php/

Questions? Let me know!

Issues with Sphinx Search Engine Wordforms

Q: How come sometimes Sphinx search engine's index does not obey the rules I put in wordforms configurations?

To give you more context Sphinx is an open-source SQL full-text search engine. Wordforms is a functionality it provides where you can specify synonyms so that your index covers them. For example suppose your index contains documents that contain the word 't-shirt' and you also want them to be matched when the search keyword is 'tshirt'. Without setting up such synonym mapping in Sphinx, Sphinx wouldn't know 't-shirt' and 'tshirt' mean the same thing. Once you set the configuration accordingly Sphinx will get you the desired search results.

Sometimes I run into the problem where I define the mapping, rebuild Sphinx index, rerun Sphinx search daemon, but still fail to have Sphinx recognize the new mapping I put in. If this happens to you simply insert a new line at the end of the configuration file, or reorder the mappings. For example if your original mapping is the following:

mockneck > mock neck
ae > american eagle
peacoat > pea coat
slitneck > slit neck


Reorder them to the following:

mockneck > mock neck
slitneck > slit neck
ae > american eagle
peacoat > pea coat


Rebuilt Sphinx index, rerun search daemon, and try querying your index again. Hope this helps! By the way this fix applies to other configuration for Sphinx as well such as Stopwords. If you run into similar problems with other kinds of configuration try the same fix! Questions? Let me know!

Minggu, 27 Maret 2011

How to Get Prompted To Enter MySQL Password in MySQL Command?

QUESTION
In entering the MySQL command to connect to a MySQL database server in Unix how do I enter the command and then get prompted to enter the password so that it's not displayed on the screen?

ANSWER
The MySQL command to connect to a MySQL server is simple. You can use '-p' flag followed by the password like the following (assuming user is root and password is MyPassWord and you want to connect to localhost):

mysql -uroot -pMyPassWord
Let's say you'd like to enter the password passively meaning you'd like to get prompted to enter the password so that while you do it the password is not shown on the screen. The following is a failed attempt:

savior@myUnixBox:~$ mysql -uroot
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

This would work if 'root' does NOT have a password. This is not true because it does have a password. The correct command is the following:

savior@myUnixBox:~$ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 808754
Server version: 5.0.67-0ubuntu6 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>

Any questions?

Install MySQL Include Files On Unix

Q: While installing Sphinx search engine I get an error saying I have to install MySQL include files. How do I do it?

My Unix version is Ubuntu 9.10. Here's what happens. When I run ./configure tool to install Sphinx I got the following error:

configure: error: invalid MySQL root directory '/var/lib/mysql'; neither bin/mysql_config, nor include/ and lib/ were found there
To give you some context into why this is needed, if you install any application that needs to communicate with your MySQL server you'll have to let that application know the MySQL protocol via MySQL libraries in the form of MySQL include files. Note this problem is NOT limited to Sphinx search engine only; whatever Unix application software that needs to talk to MySQL server also applies!

To solve this error I needed to use 'apt-get' to install libmysql++-dev. However when I run 'sudo apt-get install libmysql++-dev' it says it cannot find the library and asks me to run 'apt-get update', but when I do that I get a bunch of 404 errors:

Ign http://security.ubuntu.com intrepid-security Release.gpg
Ign http://security.ubuntu.com intrepid-security/main Translation-en_US
Ign http://security.ubuntu.com intrepid-security/restricted Translation-en_US
Ign http://archive.ubuntu.com intrepid Release.gpg
...
Ign http://archive.ubuntu.com intrepid-updates/restricted Packages
Ign http://archive.ubuntu.com intrepid-updates/universe Packages
Ign http://archive.ubuntu.com intrepid-updates/main Sources
Err http://security.ubuntu.com intrepid-security/main Packages
404 Not Found [IP: 91.189.92.167 80]
Err http://security.ubuntu.com intrepid-security/restricted Packages
404 Not Found [IP: 91.189.92.167 80]
...
W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/intrepid-updates/restricted/source/Sources.gz 404 Not Found [IP: 91.189.92.169 80]

W: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/intrepid-updates/universe/source/Sources.gz 404 Not Found [IP: 91.189.92.169 80]

E: Some index files failed to download, they have been ignored, or old ones used instead.

==== or the following error ====

E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

This happens because my repositories are out of date. So I updated my sources.list (Where is sources.list? It is usually located at /etc/apt/sources.list) to (after backing it up of course) to the default of Ubuntu 9.10 which is the following:

# deb cdrom:[Ubuntu-Server 9.10 _Karmic Koala_ - Release i386 (20091027.2)]/ karmic main restricted

#deb cdrom:[Ubuntu-Server 9.10 _Karmic Koala_ - Release i386 (20091027.2)]/ karmic main restricted
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.

deb http://us.archive.ubuntu.com/ubuntu/ karmic main restricted
deb-src http://us.archive.ubuntu.com/ubuntu/ karmic main restricted

## Major bug fix updates produced after the final release of the
## distribution.
deb http://us.archive.ubuntu.com/ubuntu/ karmic-updates main restricted
deb-src http://us.archive.ubuntu.com/ubuntu/ karmic-updates main restricted

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team. Also, please note that software in universe WILL NOT receive any
## review or updates from the Ubuntu security team.
deb http://us.archive.ubuntu.com/ubuntu/ karmic universe
deb-src http://us.archive.ubuntu.com/ubuntu/ karmic universe
deb http://us.archive.ubuntu.com/ubuntu/ karmic-updates universe
deb-src http://us.archive.ubuntu.com/ubuntu/ karmic-updates universe

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team, and may not be under a free licence. Please satisfy yourself as to
## your rights to use the software. Also, please note that software in
## multiverse WILL NOT receive any review or updates from the Ubuntu
## security team.
deb http://us.archive.ubuntu.com/ubuntu/ karmic multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ karmic multiverse
deb http://us.archive.ubuntu.com/ubuntu/ karmic-updates multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ karmic-updates multiverse

## Uncomment the following two lines to add software from the 'backports'
## repository.
## N.B. software from this repository may not have been tested as
## extensively as that contained in the main release, although it includes
## newer versions of some applications which may provide useful features.
## Also, please note that software in backports WILL NOT receive any review
## or updates from the Ubuntu security team.
# deb http://us.archive.ubuntu.com/ubuntu/ karmic-backports main restricted universe multiverse
# deb-src http://us.archive.ubuntu.com/ubuntu/ karmic-backports main restricted universe multiverse

## Uncomment the following two lines to add software from Canonical's
## 'partner' repository.
## This software is not part of Ubuntu, but is offered by Canonical and the
## respective vendors as a service to Ubuntu users.
# deb http://archive.canonical.com/ubuntu karmic partner
# deb-src http://archive.canonical.com/ubuntu karmic partner

deb http://security.ubuntu.com/ubuntu karmic-security main restricted
deb-src http://security.ubuntu.com/ubuntu karmic-security main restricted
deb http://security.ubuntu.com/ubuntu karmic-security universe
deb-src http://security.ubuntu.com/ubuntu karmic-security universe
deb http://security.ubuntu.com/ubuntu karmic-security multiverse
deb-src http://security.ubuntu.com/ubuntu karmic-security multiverse

Now I run 'apt-get update' and 'apt-get install libmysql++-dev' successfully! This time when I run configure for Sphinx it successfully recognizes the MySQL includes and is installed correctly!

Kamis, 17 Maret 2011

How Do You Escape Special Characters In Sphinx Configuration?

Q: My database password contains bang character (!) and when I specify the password in sphinx config I keep getting errors. Why?

SOLUTION
It goes without saying that it must have something to do with escaping special characters! How nasty is that? And the Sphinx user manual does not really mention how to deal with special characters. Anyway the solution is that you simply use backslash (\) to escape special characters. Suppose my host is localhost and user name is root and password is crack!t, then here's a typical 'source' block in sphinx configuration:

source someSource
{
type = mysql
sql_host = localhost
sql_user = root
sql_pass = crack\!t
sql_db = mffl
sql_port = 3306

sql_query = \
select store_id+1000,store_id as id,'store' as type,title,title as data,body from store union \
select brand_id+2000,brand_id as id,'brand' as type,title,title as data,body from brand union \
select style_id+3000,style_id as id,'keyword' as type,title,title as data,body from keyword

sql_attr_uint = id
sql_attr_uint = sort
sql_attr_string = type
sql_field_string = title
sql_field_string = data
}


Note how I escape the exclamation mark in sql_pass with a backslash (\) haha!

How Do You 301 Redirect a Naked Domain To a Subdomain such as www

QUESTION
How do you 301 redirect a naked domain to some sub domain, most commonly www, so that when someone goes to yourDomain.com they'll be 301 redirected to www.yourDomain.com?

ANSWER
For those who don't understand what 301 redirect is here's a quick answer. When your webpage has been moved to a new place you usually want to set your web server to tell whoever wanting to access your old webpage that it's been moved to a new place permanently. This behavior is realized with a 301 redirect. If the client is a search engine they'll know your web page has been moved permanently.

There are at least 2 ways to 301 redirect a naked domain to a subdomain:

1: In your IP host setting add an 'A record' with host @ and configure your web server accordingly.

2: Have your domain name registry do that for you.


Below we'll look at each method in more detail and also look at the pros and cons of each.

1. In your web host setting add an 'A record' with Host set to @ that points to your machine's IP and configure your web server accordingly.
In your web host setting add an 'A record' with Host set to @ that points to your machine's IP.

If you use GoDaddy.com here's a screen shot of that setting:

GoDaddy DNS Manager A Host Setting For Naked Domain

Once you finish the setting you may need to wait for up to 1 hour for the setting to take effect. Then configure your web server to recognize the naked domain and 301 redirect it to any subdomain you want such as www. This depends on the capabilities of your web server but it should have this feature. For example if you use Nginx and your domain is mensfashionforless.com then here's what you put in your web server's configuration file:

server {
listen 80;
server_name mensfashionforless.com;
rewrite ^/(.*) http://www.mensfashionforless.com/$1 permanent;
}


This rule simply means that the web server listens on port 80 and it responds to domain name 'mensfashionforless.com' by 301 redirecting it to 'www.mensfashionforless.com' (what 'permanent' means).

In fact, this rule exhibits a great benefit that this way offers: You have total control over how the redirect happens. In this case you'll be able to redirect http://mensfashionforless.com/some-page.html to http://www.mensfashionforless.com/some-page.html. Again you have total control.

2. Have your domain name registry do that for you.
Have your domain name registry (e.g. GoDaddy) do that for you (read http://www.printfection.com/help/article.php?articleid=41 if you use GoDaddy). Basically you'll be able to pick 'Forward Domain' option to forward your naked domain, or non-www domain, to any domain you'd like via 301 redirect. Typically you'd forward your naked domain to your www domain but you can actually forward it to any domain you want.

This depends on the abilities of the registry you registered your domain with but they usually have this functionality.

This way is very limited because you might not be able to forward your old path to your new path. For example you might want to redirect http://mensfashionforless.com/some-page.html to http://www.mensfashionforless.com/some-page.html but whether you can do this totally depends on your web host. In addition your web host, although unlikely, can alter their functionality any time they want. You are basically at their mercy.

Questions? Shoot me an email.

How To Skip Password Prompt In 'Sudo' Command In Unix

QUESTION
Whenever I execute a command with 'sudo' in Unix I always get a prompt for entering my password. How do I skip entering my password?

SOLUTION
When you are writing a script and in that script would like to execute a command that requires elevated privilege, you'd have issues because you'd need to enter the password afterwords. Suppose the command you want to execute is:

sudo rm *

And your password is 'myPassWord' without quotes, then here's what you do:

echo myPassWord | sudo -S rm *

'-S' is the option provided by 'sudo' command that means it'll read the password from the standard input instead of the terminal device. That's why you can pipe your password via 'echo' and you'll be able to skip the password prompt this way.

Enjoy!

Escape Command Line Args In Unix Shell

Q: Recently I am trying to use 'mysql' to connect to my MySQL server and I'd like to include the password in the command line. My password is "Crack!t" without quotes. However when I enter the command I get an error. What should I do?

A: Escaping special characters in the command line in any Unix shell can be confusing and frustrating. For me I am merely trying to connect to a MySQL database specifying the user name and password as the parameters but I don't realize it's that difficult! Here's the complete command for me to connect to my local MySQL server with user name set to 'owner' and database set to 'mffl':

mysql -uowner mffl -pCrack!t

Obviously it doesn't work. The problem is that the bang character (!) is a special character in shells that can be used to re execute a previously entered command. Anyway the output of executing the above command looks like:

saviorsage@frankwxn:~$ mysql -uowner mffl -pCrack!t
mysql -uowner mffl -pCracktouch a
mysql Ver 14.12 Distrib 5.0.67, for debian-linux-gnu (x86_64) using readline 5.2
Copyright (C) 2000-2008 MySQL AB
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL license
Usage: mysql [OPTIONS] [database]
-?, --help Display this help and exit.
-I, --help Synonym for -?
--auto-rehash Enable automatic rehashing. One doesn't need to use
'rehash' to get table and field completion, but startup
and reconnecting may take a longer time. Disable with
--disable-auto-rehash.
...
...


It sucks doesn't it. Look below to find the solution.

Solution
To solve it, simply use \ to escape it:

mysql -uowner mffl -pCrack\!t

It should work now. If not shoot me an email. Now whenever you need to escape special characters in a command line use a backslash (\) to do it!

Jumat, 11 Maret 2011

Install Komodo Edit 6 on Natty

Just wanted to give a quick update on how to install the latest Komodo Edit on the latest Ubuntu. Its been a while since I posted the last article about Komodo Edit and it needed a quick refresh.

  • The packages for Linux can be found here. Download the appropriate package for whether you have 32-bit or 64-bit.
  • Use either the GUI to extract the contents or
tar -xvzf Komodo-version-platform.tar.gz
  • In your terminal, or open a terminal if you haven't already, change to the new directory
cd Komodo-version-platform
  • Run the install script. I had to use sudo before the script to get it to install.
sudo ./install.sh
  • Go through and answer all of the installer prompts
  • Once the installer finishes you will need to add Komodo Edit to your PATH. Personally I like the symlink method, but it does require root/sudo access.
ln -s /bin/komodo /usr/bin/komodo
Two caveats that I have found is that you probably should not install to the default because it will only work for root or if you sudo. I went ahead and installed to /opt/komodo, but you could install wherever you like to install your non-package maintained software. Second, the installer actually tells you to make the symbolic link in /usr/local/bin but I have found that is not in the default PATH of the users (though it could be added). Other than that it is pretty straight forward. Let me know if you all find any other pain points.

Sabtu, 05 Maret 2011

Replace Unity Interface

I know I am not alone when I say that I don't like the new unity interface. I am using the Ubuntu Netbook Edition on my Asus EEEpc and my biggest complaint is that it is too slow. I like the idea of a better interface, but they have a long way to go to make it usable. Until then I will be sticking with the standard GNOME interface which is still included to use. I am going to show you how to change the default session to be the regular desktop session.
  • Open the Login Screen Settings dialog box.
  • Change the bottom dropdown box to be Ubuntu Desktop Edition as the default session (you can see I already have mine changed). You may need to press the Unlock button and supply your password to be able to change this setting.
  •  You should just be able to log out and back in and then you will get the familiar GNOME desktop.
This will not actually remove Unity from your installation so at what time you think you might want to use it again you could. I've read a few posts that take you through the steps to remove it, but I don't see it necessary since it can be easily disabled through the login screen.

      Kamis, 24 Februari 2011

      How To Install Internet Explorer 6 so You Can Test Your Website On IE6

      Q: I am running a website on my local machine and I'd like to validate it on Internet Explorer 6 but I am not able to install IE6 on my Windows. What can I do?
      If you've read Guide on Cross Browser Compatibility you'd know the importance of testing IE6 as it accounts for a significant percentage of website traffic. If you have trouble installing IE6 on your Windows OS follow the following steps:

      * Go through Install IE6 on Windows: Although this guide talks about installing IE6 on Windows 7 it should apply to other Windows OS as well. Install a virtual PC with Windows XP on it so you can run IE6 in that Windows XP. After you install it run the Windows XP profile.

      * Access your local machine through internal IP: Find out your internal IP through 'ipconfig' via command line in your local machine (NOT virtual PC!). It should be something like 192.168.xxx.xxx. Then make sure your web server allows a client to access it via this IP. This depends on the web server you use. If you use Apache make sure you have the following in your httpd.conf (assuming "C:\repository\trunk-php" is your document root):

      <Directory "C:\repository\trunk-php">
      Allow from all
      </Directory>

      instead of the default configuration

      <Directory "C:\repository\trunk-php">
      Options Indexes FollowSymLinks
      AllowOverride all
      Order Deny,Allow
      Deny from all
      </Directory>

      Now open an IE6 browser in your virtual PC and go to http://192.168.0.56 or whatever your local IP is!


      Now you should be able test your website on Internet Explorer 6! Any question let me know!
       
      support by: infomediaku.com