Google indexes the content inside an iframe

Perhaps you already know if Google does not index the content inside an iframe, so the comments from Disqus or Facebook is not indexed.

But now Google has started indexing the content inside an iframe, I realized this today when I checked the HTMLy blog demo, Danlogs.

The blogs using Disqus comments, and it turns out all the comments on the blog indexed by Google. A certainly good news for Disqus users.

For Facebook comments, I have not yet figured out its already indexed or not, but it should also being indexed because it uses an iframe as well.

HTMLy: Databaseless Blogging Platform (Flat CMS)

There are times when we just want to write without having to think about site management, for example upgrading to the latest version (core and plugin), for this purpose I developed a blogging platform that does not use a database at all, so just write and write.

Although prioritizing simplicity and speed, but the platform also includes standard features available on a blog in general, for example:
  • Admin panel
  • Markdown editor with live preview
  • Categorization with tags (multi tags support)
  • Static pages Eg. for contact page
  • Meta canonical, description, and rich snippets for SEO
  • Pagination
  • Author page
  • Multi author support
  • Social links
  • Disqus Comments (optional)
  • Facebook Comments (optional)
  • Google Analytics
  • Built-in search
  • Related posts
  • Per post navigation (previous and next post)
  • Body class for easy theming
  • Breadcrumb
  • Archive page (by year, year-month, or year-month-day)
  • JSON API
  • OPML
  • RSS Feed
  • RSS 2.0 Importer (basic)
  • Sitemap.xml
  • Archive and tag cloud widget
  • SEO friendly URLs
  • Teaser thumbnail for images and Youtube videos
  • Responsive design
  • Lightbox
  • User role
  • Online backup
This platform is a kind of static web page (Flat CMS) in general but using markdown file to store all its posts. Creation date, category, and the URL is taken from the name of the file, example:

2013-12-25_general_url-of-the-post.md

Here's the explanation (separated by an underscore):
  • 2013-12-25 is the published date
  • general is the tag/category
  • url-of-the-post.md is forming the main url
So we only need to give the name of the file with above format and then upload them to a specified folder, in this case is inside content/author/blog folder for the blog post and content/static for static page.

For static pages I use the following format:

about.md

This means that about as the URL.

Why use a file name?

We believe if the performance will be faster by read the file name, filters it, then read the file content that are matched. Although this may change in the future.

What is the minimum requirements?

HTMLy only require PHP 5.3+, yes no database needed to run HTMLy.

So how do I get it?

You can download it on GitHub. Visit a real blog powered by HTMLy on Danlogs, or visit the official homepage at HTMLy.

Updating Discourse on CentOS

I have one forum using Discourse, as a test for this new platform, for OS I use CentOS. Discourse official documentation to updating the site is using Ubuntu so there are a few changes need to be done for CentOS.

My SSH login name is Discourse and the installation folder is inside the Discourse folder, example url
/home/discourse/discourse

Here's how to update Discourse to the latest version:

# Run these commands as the discourse user
bluepill stop
bluepill quit

# Back up your install
DATESTAMP=$(TZ=UTC date +%F-%T)

pg_dump --no-owner --clean discourse_prod | gzip -c > ~/discourse-db-$DATESTAMP.sql.gz

tar cfz ~/discourse-dir-$DATESTAMP.tar.gz -C /home/discourse discourse

# Get the latest Discourse code
cd discourse
git checkout master
git pull
git fetch --tags

# To run on the latest numbered release instead of bleeding-edge:
# git checkout latest-release

# Merge the sample configuration just in case if there is an update. Run these commands as the discourse user

diff -u config/discourse_defaults.conf config/discourse.conf

diff -u config/discourse.pill.sample config/discourse.pill

diff -u config/nginx.sample.conf /etc/nginx/conf.d/discourse.conf

# Begin upgrade
bundle install --without test --deployment

RUBY_GC_MALLOC_LIMIT=90000000 RAILS_ENV=production bundle exec rake db:migrate

RUBY_GC_MALLOC_LIMIT=90000000 RAILS_ENV=production bundle exec rake assets:precompile

RUBY_GC_MALLOC_LIMIT=90000000 RAILS_ROOT=/home/discourse/discourse RAILS_ENV=production NUM_WEBS=2 bluepill --no-privileged -c ~/.bluepill load /home/discourse/discourse/config/discourse.pill

# Restart bluepill
crontab -l

Google Penguin 2.1 is active

Google has released an update to the Google Penguin algorithm , the Penguin version 2.1. Matt Cutts himself who announced via Twitter, the following is Matt's tweet:


I do not see a significant effect with the 2.1 release of Google Penguin in all of my website/blog.

Now Adsense support HTTPS site

After a long-awaited, Adsense now supports HTTPS sites, the following is the official announcement from Google Adsense blog:

Inside AdSense: Use AdSense on your HTTPS sites: Today, we're happy to announce that AdSense publishers can begin monetizing their HTTPS pages. Many websites, like e-commerce sites and social networking sites, use the HTTPS protocol to protect their users' sensitive data. If you have a HTTPS website you’ll be pleased with how easy it is to monetize using AdSense.

Good news if your site is using HTTPS protocol.

Blogger JSON Feed API

Maybe sometimes we need a widget for certain features, but the widgets are not available. Blogger already provides an APIs to overcome this, so we can create our own widget by reading the blog feed using the JSON and JavaScript.

Here is the JSON feed API:

ObjectDescriptionExample
json.feed.id.$tShow blog IDtag:blogger.com,1999:blog-12345
json.feed.updated.$tLast update of a blog2013-07-08T18:21:57.051+07:00
json.feed.category[]Categories / label array of a blog
json.feed.category[i].termShow the i-th categoryBlogger
json.feed.title.$tShow blog nameDanlogs
json.feed.subtitle.$tShow description of a blogDan's Weblog
json.feed.author[]Array of blog authorsDanang Probo Sayekti, Matt Cutts
json.feed.author[i].name.$tShow the i-th blog author nameDanang Pobo Sayekti
json.feed.author[i].uri.$tShow the i-th profile author urihttps://profiles.google.com/123456789
json.feed.openSearch$totalResults.$tShow total posts777
json.feed.entry[]Posts array of a blog
json.feed.entry[i].id.$tShow the i-th post IDtag:blogger.com,1999:blog-8508.post-12345678
json.feed.entry[i].title.$tShow the i-th post titleBlogger JSON Feed API
json.feed.entry[i].published.$tShow time published of the i-th post2013-07-07T12:56:00.000+07:00
json.feed.entry[i].updated.$tShow when the i-th post is updated2013-07-07T12:56:47.089+07:00
json.feed.entry[i].category[]Show array of post categories
json.feed.entry[i].category[x].termShow the x-th category of the i-th postBlogger API
json.feed.entry[i].summary.$tShow post summaryMaybe sometimes we need a widget ...
json.feed.entry[i].content.$tShow post contentMaybe sometimes we need a widget for certain features, but the widgets are not available ...
json.feed.entry[i].link[]Links array of a post
json.feed.entry[i].link[x].hrefShow the x-th link of the i-th posthttp://www.danpros.com/2013/08/blogger-api.html
json.feed.entry[i].author[]Array of post authors
json.feed.entry[i].author[x].name.$tName of the x-th author on the i-th postDanang Probo Sayekti
json.feed.entry[i].author[x].uri.$tShow uri author profilehttps://profiles.google.com/123456789
json.feed.entry[i].author[x].gd$image.srcImage uri of the x-th author profile on the i-th post//lh4.googleusercontent.com/photo.jpg
json.feed.entry[i].media$thumbnail.urlShow image on the i-th posthttp://3.bp.blogspot.com/danlogs.jpg
json.feed.entry[i].thr$total.$tShow total threaded comments7

Here is an example implementation of the above code:

Suppose I need 5 recent posts by a certain label, the label I want to display is "Blogger". I took the title and summary of the post.

<script type="text/javascript">
  function mycallback(json) {
    for (var i = 0; i < json.feed.entry.length; i++) {
      for (var j = 0; j < json.feed.entry[i].link.length; j++) {
        if (json.feed.entry[i].link[j].rel == 'alternate') {
          var postUrl = json.feed.entry[i].link[j].href;
          break;
        }
      }
      var postTitle = json.feed.entry[i].title.$t;
      var postSummary = json.feed.entry[i].summary.$t;
      var item = '<div class="wrapper"><h3><a href=' + postUrl + '>' + postTitle + '</h3></a><p>' + postSummary + '</p></div>';
      document.write(item);
    }
  }
</script>
<script src="http://www.danpros.com/feeds/posts/summary/-/Blogger?max-results=5&alt=json-in-script&callback=mycallback"></script>

Note: we need to understand that json.feed.entry[i].summary.$t only available if we grab the feed URL using http://www.danpros.com/feeds/posts/summary instead of using http://www.danpros.com/feeds/posts/default.

Now how can we create a widget without unsorted recent post by a certain label? This widget also only displays 90 characters in the summary. The following is the example:

<script type="text/javascript">
  function mycallback(json) {
    for (var i = 0; i < json.feed.entry.length; i++) {
      for (var j = 0; j < json.feed.entry[i].link.length; j++) {
        if (json.feed.entry[i].link[j].rel == 'alternate') {
          var postUrl = json.feed.entry[i].link[j].href;
          break;
        }
      }
      var postTitle = json.feed.entry[i].title.$t;
      var postAuthor = json.feed.entry[i].author[0].name.$t;
      var postSummary = json.feed.entry[i].summary.$t;
      var entryShort = postSummary.substring(0, 90);
      var entryEnd = entryShort.lastIndexOf(" ");
      var postContent = entryShort.substring(0, entryEnd) + '...';
      var item = '<div class="wrapper"><h3><a href=' + postUrl + '>' + postTitle + '</h3></a><span>'+ postAuthor + '</span><p>' + postContent + '</p></div>';
      document.write(item);
    }
  }
</script>
<script src="http://www.danpros.com/feeds/posts/summary?orderby=published&max-results=5&alt=json-in-script&callback=mycallback"></script>

Add rel nofollow to all external links in the node body and comment body

For for node body field use below code:

/* Add nofollow */
if(isset($variables['content']['body'][0]['#markup'])) {
  $html_dom = filter_dom_load($variables['content']['body'][0]['#markup']);
  $variables['content']['body'][0]['#markup'] = nofollowed($variables['content']['body'][0]['#markup'], $html_dom->getElementsByTagName('a'), $html_dom);
}

/* Function to add rel nofollow to external link only */
function nofollowed($str, $tags, $html_dom) {
  if(!is_array($tags)) {
    foreach ($tags as $tag) {
      $this_tag = $tag->getAttribute('href');
      if (0 !== stripos($this_tag, '/') && 0 !== stripos($this_tag, url('', array('absolute' => TRUE)))) {
        $tag->setAttribute('rel', 'nofollow');
      }
    }
  }
  $str = filter_dom_serialize($html_dom);
  return $str;
}

And for comment_body field use below code:

/* Add nofollow */
if(isset($variables['content']['comment_body'][0]['#markup'])) {
  $html_dom = filter_dom_load($variables['content']['comment_body'][0]['#markup']);
  $variables['content']['comment_body'][0]['#markup'] = nofollowed_comment($variables['content']['comment_body'][0]['#markup'], $html_dom->getElementsByTagName('a'), $html_dom);
}

/* Function to add rel nofollow to external link only */
function nofollowed_comment($str, $tags, $html_dom) {
  if(!is_array($tags)) {
    foreach ($tags as $tag) {
      $this_tag = $tag->getAttribute('href');
      if (0 !== stripos($this_tag, '/') && 0 !== stripos($this_tag, url('', array('absolute' => TRUE)))) {
        $tag->setAttribute('rel', 'nofollow');
      }
    }
  }
  $str = filter_dom_serialize($html_dom);
  return $str;
}

This is useful for example, we only allow dofollow links only on signature. I use this solutions on Replr.