Portal Hero Image

Rolling my own iGoogle Replacement using CraftCMS

I've been trying to figure out what to do for months since Google announced that iGoogle is going to be shut down. I really like having a portal as my homepage, but none of the currently available options are appealing to me. So what I've decided to do is code my own custom portal page.

I've been trying to figure out what to do for months since Google announced that iGoogle is going to be shut down. I really like having a portal as my homepage, but none of the currently available options are appealing to me. So what I've decided to do is code my own custom portal page. I used this opportunity to explore CraftCMS and foundation 4. I've been following the development of Craft since it was released and originally called Blocks CMS, but didn't have an opportunity to use it. Also wanted to try out Foundation 4 since I will not need to support IE8 or any version of IE for that matter, this being a personal project that only I will be using.

If you are looking for an iGoogle replacement and want to roll your own version of it, then read on as this tutorial is perfect for you provide you have basic linux webhosting with a mysql database. You can see a desktop screenshot here and a mobile screenshot here.

Step one: Download CraftCMS and install it on your hosting following the documentation - it's super easy.

Step two: Download Foundation 4, customize if you want, but I went with the default, or your favorite CSS Framework. If you use Foundation 4 and then use the templates below everything should work right out of thebox. Upload your Foundation files to the your public_html or httpdocs folder.

Step three: In your craft/templates folder you'll modify your _layout.html and index.html files to the templates below. This is essentially the wrapper file in case you would like to extend the site to more than one page in the future. The links at the top of the page are edited here at lines 27-40

_layout.html

    
      <!DOCTYPE html>
<!--[if IE 8]> 				 <html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->

<head>

	<link href='http://fonts.googleapis.com/css?family=Roboto:400,300' rel='stylesheet' type='text/css'>

	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width" />
	<title>{{ siteName }}</title>

	<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
	<script src="/js/caffeine.js"></script>


	<link rel="stylesheet" href="/css/foundation.css" />
	<link rel="stylesheet" href="/css/normalize.css" />
	<link rel="stylesheet" href="/css/styles.css" />
  

  <script src="/js/vendor/custom.modernizr.js"></script>

</head>
<body>
  
  <div id="top">
  <div class="row">
    <ul class="hide-for-small small-12 columns inline-list topLinks">
      <li><a href="http://gmail.google.com/gmail">Gmail</a></li>
      <li><a href="http://flickr.com">Flickr</a></li>
      <li><a href="http://facebook.com">Facebook</a></li>
      <li><a href="http://cbc.ca/news">CBC</a></li>
      <li><a href="http://www.thestar.com/">The Star</a></li>
      <li><a href="javascript:(function(){ window.open('http://player.edge.ca','The Edge','status=no,directories=no,location=no,resizable=no,menubar=no,width=820,height=660,toolbar=no'); })();">The Edge</a></li>
      <li><a href="http://devot-ee.com/">Devot:ee</a></li>
      <li><a href="http://expressionengine.stackexchange.com/">EESE</a></li>
    <ul>
  </div>
  </div>

	<div class="row">
		<div class="small-12 columns">
			<h1><a href="/">{{ siteName }}</a></h1>
		</div>
	</div>


		{% block main %}
		{% endblock %}

  <script>
  document.write('<script src=' +
  ('__proto__' in {} ? 'js/vendor/zepto' : 'js/vendor/jquery') +
  '.js><\/script>')
  </script>
  
  <script src="js/foundation.min.js"></script>
  <!--
  
  <script src="js/foundation/foundation.js"></script>
  
  <script src="js/foundation/foundation.interchange.js"></script>
  
  <script src="js/foundation/foundation.dropdown.js"></script>
  
  <script src="js/foundation/foundation.placeholder.js"></script>
  
  <script src="js/foundation/foundation.forms.js"></script>
  
  <script src="js/foundation/foundation.alerts.js"></script>
  
  <script src="js/foundation/foundation.magellan.js"></script>
  
  <script src="js/foundation/foundation.reveal.js"></script>
  
  <script src="js/foundation/foundation.tooltips.js"></script>
  
  <script src="js/foundation/foundation.clearing.js"></script>
  
  <script src="js/foundation/foundation.cookie.js"></script>
  
  <script src="js/foundation/foundation.joyride.js"></script>
  
  <script src="js/foundation/foundation.orbit.js"></script>
  
  <script src="js/foundation/foundation.section.js"></script>
  
  <script src="js/foundation/foundation.topbar.js"></script>
  
  -->
  <script src="/js/foundation/foundation.js"></script>
  <script>
    $(document).foundation();
  </script>
</body>
</html>
    
  

index.html

This is the main content area of the portal. Each of the three columns is commented so that you can easily adjust the code that goes there.

    
      {% extends "_layout" %}

{% block main %}

	<div class="row">
		<div class="small-12 large-4 columns">
			<form id="" name="" method="get" action="http://google.ca/search" target="_blank">
				<div class="row collapse">

					<div class="large-7 small-6 columns">
						<input id="" class=" large-" name="q" type="text" autocomplete="off" value="" placeholder="Enter Search Terms">
					</div>
					<div class="large-5 small-6 columns">
						<input type="submit" value="Google" class="button postfix search">
					</div>

				</div><!--row collapse-->		
			</form>
		</div>

		<div class="small-12 large-4 columns">
			<form id="wikipedia" name="" method="post" action="http://en.wikipedia.org/wiki/Special:Search" target="_blank">
				<div class="row collapse">

					<div class="large-7 small-6 columns">
						<input id="" class=" large-" name="search" type="text" value="" placeholder="Enter Search Terms">
					</div>
					<div class="large-5 small-6 columns">
						<input type="submit" value="Wikipedia" class="button postfix search">
					</div>

				</div><!--row collapse-->		
			</form>
		</div>

			<div class="small-12 large-4 columns">
			<form id="" name="" method="post" action="http://www.youtube.com/results" target="_blank">
				<div class="row collapse">

					<div class="large-7 small-6 columns">
						<input id="" class=" large-" name="search_query" type="text" value="" placeholder="Enter Search Terms">
					</div>
					<div class="large-5 small-6 columns">
						<input type="submit" value="YouTube" class="button postfix search">
					</div>

				</div><!--row collapse-->		
			</form>
		</div>

	</div><!--row-->

	<div class="row">
			
			<!--column one-->
			<div class="large-4 columns no-padding">
				
				
				{#
				<article>
					<h2>Gmail InBox</h2>
					<h3 style="color:purple;">After much research, Embedding Gmail Inbox is Impossible</h3>
					<p>Disappointing, but not unexpected</p> 
				</article>
				#}



				{% for entry in craft.entries.find({section: 'feeds', limit: '200', order: 'postDate desc', search: 'column::1'}) %}
				<article> 
				    <h2>{{ entry.title }}</h2>
				    
				    {% set items = craft.feeds.getFeedItems(entry.rssURL, entry.feedLimit) %}

					<ul>
					{% for item in items %}
					        <li><a href="{{ item.permalink }}">{{ item.title }}</a></li>
					{% endfor %}
					</ul>
				</article>
				{% endfor %}




				<article>
					<h2>Weather</h2>
					<!--weather code from http://yowindow.com/weatherwidget.php -->

					<p style="font-size:13px;background:#e1e1e1;padding:5px;">
						<span style="color:red;">below is temporary</span>: hopefully can build a craft plugin to connect to the API at <a href="http://www.wunderground.com/weather/api">Weather Underground</a> but my php is incredibly weak.
					</p>
					<div style="width:220px; height:150px;margin:0 auto;">
					    <object type="application/x-shockwave-flash" data="http://swf.yowindow.com/yowidget3.swf" width="220" height="150">
					    	<param name="movie" value="http://swf.yowindow.com/yowidget3.swf"/>
					    	<param name="allowfullscreen" value="true"/>
					    	<param name="wmode" value="opaque"/>
					    	<param name="bgcolor" value="#FFFFFF"/>
					    	<param name="flashvars" 
					    	value="location_id=gn:6167865&amp;location_name=Toronto&amp;landscape=airport&amp;time_format=12&amp;unit_system=metric&amp;background=#FFFFFF&amp;copyright_bar=false"
					    />
					        <a href="http://WeatherScreenSaver.com?client=widget&amp;link=copyright"
					        style="width:220px;height:150px;display: block;text-indent: -50000px;font-size: 0px;background:#DDF url(http://yowindow.com/img/logo.png) no-repeat scroll 50% 50%;"
					        >Screen Saver</a>
					    </object>
					</div>
				</article>
			</div><!--column one-->



			<!--column two-->
			<div class="large-4 columns no-padding">
				{% for entry in craft.entries.find({section: 'feeds', limit: '200', order: 'postDate desc', search: 'column::2'}) %}
				<article>
				    <h2>{{ entry.title }}</h2>

				    {% set items = craft.feeds.getFeedItems(entry.rssURL, entry.feedLimit) %}

					<ul>
					{% for item in items %}
					        <li><a href="{{ item.permalink }}">{{ item.title }}</a></li>
					{% endfor %}
					</ul>
				</article>
				{% endfor %}
			</div><!--column two-->




			<!--column three-->
			<div class="large-4 columns no-padding">
				{% for entry in craft.entries.find({section: 'feeds', limit: '200', order: 'postDate desc', search: 'column::3'}) %}
				<article>
				    <h2>{{ entry.title }}</h2>
				    
				    {% set items = craft.feeds.getFeedItems(entry.rssURL, entry.feedLimit) %}

					<ul>
					{% for item in items %}
					        <li><a href="{{ item.permalink }}">{{ item.title }}</a></li>
					{% endfor %}
					</ul>
				</article>
				{% endfor %}
			</div><!--column three-->

	</div><!--row-->
{% endblock %}
    
  

The following javascript is needed to ensure that the wikiepedia search works on both mobile as well as desktop as there is a different action url depending on how which version of wikipedia you are viewing.

caffeine.js

    
      //
// Change wikipedia action url for small screens as it
// uses a different action url for mobile
//
$(document).ready(function() {
    function doneResizing() {
        if(Modernizr.mq('screen and (min-width:768px)')) {
            $('#wikipedia').attr('action', 'http://en.wikipedia.org/wiki/Special:Search');
        }
        else if(Modernizr.mq('screen and (max-width:767px)')) {
            $('#wikipedia').attr('action', 'http://en.m.wikipedia.org/wiki/Special:Search');
        }
    }

    var id;
    $(window).resize(function() {
        clearTimeout(id);
        id = setTimeout(doneResizing, 0);
    });

    doneResizing();
});
    
  

style.css

In this file you'll want to change line 4 to reference you're own image to use as a background. Without the image you'll have a sold blue background which is the default on mobile view as the image wasn't stretching correctly for me. If you have a fix to get the image to stretch in mobile let me know, but it's not overly important to me.

    
      html {
	height: 100%;
	background:#456596;
	background-image: url(/img/13-Penetanguishene-134.jpg);
	background-repeat: no-repeat;
	background-position: center center;
	background-attachment: fixed;
	background-size: cover;
}
body {
	background: none;
}
.row {max-width: 90em;}
h1, h2, h3, h4, h5, h6 {
	font-family: 'Roboto', sans-serif;
	font-weight: 300;
	color:rgb(255,255,255);
}
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
	color:rgb(255,255,255);
}
h1:hover a, h2:hover a, h3:hover a, h4:hover a, h5:hover a, h6:hover a {
	color:rgb(255,255,255);
}
h1 {
	font-weight: bold;
	text-shadow:1px 1px 4px #000000;
	text-align: center;
}
h2 {
	font-size: 1.2em;
	background: rgba(0,0,0, 0.5);
	text-align: center;
	font-weight: 400;
	margin: -1px -1px 0 -1px;
	border-bottom: 2px solid rgba(91,91,91,1)
}

input.search.button {
	background-color: rgb(43, 103, 211);
	background-image:url(/img/search-24.png);
	background-repeat:no-repeat;
	background-position:5px center;
	border-color: rgb(16, 55, 66);
	text-align: left;
	text-indent: 35px;
}

.no-padding {
	padding-left:0;
	padding-right: 0;
}
article {
	margin:15px; 0;
	background: #fff;
	border: 1px solid #f1f1f1;
	/*padding:0 15px;*/
}
ul, p {
	padding-left:15px;
	padding-right: 15px;
}
article li, article p {
	font-size: 0.9em;
}


@media only screen and (max-width: 479px) {
	html {
		background-image: none; /*image doesn't stretch vertically on phones for some reason so hiding*/
	}
}
    
  

The next step is to open craft/config/general.php and add the following line inside the the return array at line 10. By adding this your feeds will show new articles once an hour rather than once a day.

    
      'cacheDuration' => 'PT1H',
    
  

After you've created your templates it's now time to set up Craft so that you can add new entries pulling in the RSS feeds that you are interested in displaying on your personal portal page.

The fields that are referenced inside the craft tags will need to be created in the CP by going to settings > fields > new field.

column: plain text, handle "column", instructions "Which column does this go in?", Placeholder Text "1 or 2 or 3"
Feed Limit: Dropdown, handle "feedLimit", instructions "how many article titles to display", Option type in numbers 1 through 10
Feed URL: plain text, handle "rssURL", instructions "insert rss feed here", Placeholder text "Enter text..."
Now go to Settings > Sections and rename the blog to Feeds with handle feeds. Uncheck "Entries in this section have their own URLS". Then go to the "Field Layout" tab and drag your fields into the layout.

Finally, go publish an entry or three by going to Entries > New entry. Voila your iGoogle portal replacement site is up and running. The only thing missing is being able to display your gmail inbox, but that's something I can accept. Line 86-108 of index.hmtl includes a weather widget that I found online. I posted about a weather plugin on twitter and there is one being built. Once it's built and working, I'll update the code here, provide a download link, and give credit to the author.

While this isn't really a tutorial on how to use CraftCMS, you can build a portal site without using any of the paid modules. The bonus for that is you can see how easy CraftCMS is to use and then when you're ready buy a paid module to expand on your skills. I will definitely be using CraftCMS as I enjoyed working with it and I know the support is awesome as I've used several ExpressionEngine addons by Pixel and Tonic creators of CraftCMS.