<?xml version="1.0"?>
<News hasArchived="true" page="8756" pageCount="10850" pageSize="10" timestamp="Sun, 04 Oct 2026 06:12:17 -0400" url="https://my3.my.umbc.edu/posts.xml?mode=activity&amp;page=8756&amp;range=30">
<NewsItem contentIssues="true" id="31244" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31244">
<Title>Digital NHS needs to take the lead from design</Title>
<Body>
<![CDATA[
    <div class="html-content">Mark Curtis, chief client officer at service design consultancy Fjord, discusses why design should be top of the agenda when creating digital healthcare products<div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fwww.netmagazine.com%2Fopinions%2Fdigital-nhs-needs-take-lead-design&amp;t=Digital+NHS+needs+to+take+the+lead+from+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/twitter.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/facebook/?u=http%3A%2F%2Fwww.netmagazine.com%2Fopinions%2Fdigital-nhs-needs-take-lead-design&amp;t=Digital+NHS+needs+to+take+the+lead+from+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/facebook.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/linkedin/?u=http%3A%2F%2Fwww.netmagazine.com%2Fopinions%2Fdigital-nhs-needs-take-lead-design&amp;t=Digital+NHS+needs+to+take+the+lead+from+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/linkedin.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/gplus/?u=http%3A%2F%2Fwww.netmagazine.com%2Fopinions%2Fdigital-nhs-needs-take-lead-design&amp;t=Digital+NHS+needs+to+take+the+lead+from+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/googleplus.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/email/?u=http%3A%2F%2Fwww.netmagazine.com%2Fopinions%2Fdigital-nhs-needs-take-lead-design&amp;t=Digital+NHS+needs+to+take+the+lead+from+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/email.png" style="max-width: 100%; height: auto;"></a>
    </td></tr></tbody></table></div>
    <br><br><a href="http://da.feedsportal.com/r/165664960510/u/49/f/502346/c/32632/s/2d3c6cf9/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/165664960510/u/49/f/502346/c/32632/s/2d3c6cf9/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>Mark Curtis, chief client officer at service design consultancy Fjord, discusses why design should be top of the agenda when creating digital healthcare products     </Summary>
<Website>http://feedproxy.google.com/~r/net/topstories/~3/dUrny_7opf4/story01.htm</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31244/guest@my.umbc.edu/8823271fc7fc7ee06dedee73cdcc37de/api/pixel</TrackingUrl>
<Tag>css</Tag>
<Tag>development</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>net</Tag>
<Tag>php</Tag>
<Tag>sql</Tag>
<Tag>web</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Thu, 13 Jun 2013 06:10:15 -0400</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="31240" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31240">
<Title>Building An App In 45 Minutes With Meteor</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <table width="650">
    <tbody>
    <tr>
    <td>
    <div>
    <img src="http://statisches.auslieferung.commindo-media-ressourcen.de/advertisement.gif" alt="" style="max-width: 100%; height: auto;"><br><a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=1" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=1" alt="" style="max-width: 100%; height: auto;"></a> <a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=2" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=2" alt="" style="max-width: 100%; height: auto;"></a> <a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=3" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=3" alt="" style="max-width: 100%; height: auto;"></a>
    </div>
    </td>
    </tr>
    </tbody>
    </table>
    <p>The other day, I finally accomplished one of my long-standing goals: to go from one of those “Wouldn’t it be cool…” ideas to a working, live app in less than 1 hour. 45 minutes, actually.</p>
    <p>It all started with a <a href="http://designerpotluck.eventbrite.com/" rel="nofollow external" class="bo">design meet-up in San Francisco</a>. I can honestly say this was the best meet-up I’ve ever been to: Even though it was announced only two days in advance, more than 200 people RSVPed, and a good number of them showed up. It was a great chance to put faces to familiar names, as well as to make new friends.</p>
    <p>But I got to talking with so many people that I didn’t have a chance to get contact info for everybody. So, the next day, I asked the organizers about it and they suggested that everyone who attended leave a link to their Twitter account in a shared Google Doc.</p>
    <p>That would work, but I was afraid it would prove to be too much effort. <strong>If I’ve learned one thing in my years as a designer, it’s that people are lazy</strong>. Instead, what if I built an app that lets the user add their Twitter account to a list in a single click?</p>
    <p>The app would work something like this:</p>
    <ol>
    <li>The user signs into Twitter,</li>
    <li>A link to their Twitter profile appears on the page,</li>
    <li>That’s pretty much it!</li>
    </ol>
    <p>With my list of requirements complete, I set to work to see how fast I could build this, and I thought it’d be interesting to walk you through the process.</p>
    <p>At first, take a peek at how the final app looked like:</p>
    <p><a href="http://media.smashingmagazine.com/wp-content/uploads/2013/05/whowasthere-ss-500.png" rel="nofollow external" class="bo"><img alt="Our very bare-bones (but working!) app." src="http://media.smashingmagazine.com/wp-content/uploads/2013/05/whowasthere-ss-500.png" width="500" height="516" style="max-width: 100%; height: auto;"></a><br>
    <em>Our final bare-bones (but working!) app.</em></p>
    <p>You can also see a <a href="http://smashingdemo.meteor.com" rel="nofollow external" class="bo">demo of the finished product</a>, and find the <a href="https://github.com/DiscoverMeteor/twitterList" rel="nofollow external" class="bo">code on GitHub</a>. (<strong>Note:</strong> Give it some time to load. Apps hosted on Meteor’s free hosting service often slow down under a lot of traffic.)</p>
    <p>A word of warning: This won’t be a traditional tutorial. Instead, it will be a play-by-play walkthrough of how I coded the app in one hour, including the usual dumb mistakes and wrong turns.</p>
    <h3>Introducing Meteor</h3>
    <p>I decided to build the app with <a href="http://meteor.com" rel="nofollow external" class="bo">Meteor</a>. Meteor is a fairly young JavaScript framework that works on top of Node and has a few interesting characteristics.</p>
    <p><a href="http://meteor.com/" rel="nofollow external" class="bo"><img alt="The Meteor homepage" src="http://media.smashingmagazine.com/wp-content/uploads/2013/05/meteor-ss-500.png" width="500" height="288" style="max-width: 100%; height: auto;"></a><br>
    <em>Meteor’s home page</em></p>
    <p>First, it’s <strong>all JavaScript</strong>, so you don’t need to deal with one language in the browser and another on the server. That’s right: the same language you use to set up jQuery slider plugins can also be used to query your app’s database! The added benefit of this is that your app now has only a single code base — meaning you can make the same code accessible from both the client and server if you need to.</p>
    <p>Meteor is also <strong>reactive</strong>, meaning that any change to your data is automatically reflected everywhere throughout the app (including the user interface) without the need for callbacks. This is a powerful feature. Imagine adding a task to a to-do list. With reactivity, you don’t need a callback to insert the new HTML element into the list. As soon as Meteor receives the new item, it automatically propagates the change to the user interface, without any intervention on your part!</p>
    <p>What’s more, Meteor is <strong>real time</strong>, so both your changes and the changes made by other users are instantly reflected in the UI.</p>
    <p>Like many other modern frameworks, Meteor also speeds up your Web app by transforming it into a single-page Web app. This means that instead of refreshing the whole browser window every time the user changes the page or performs an action, Meteor modifies only the part of the app that actually changes without reloading the rest, and then it uses the HTML5 pushState API to change the URL appropriately and make the back button work.</p>
    <p>Not having to update the whole page enables another very powerful feature. Instead of sending HTML code over the network, Meteor sends the raw data and lets the client decide how to render it.</p>
    <p>Finally, one of my favorite features of Meteor is simply that it <strong>automates a lot of boring tasks</strong>, such as linking up and minifying style sheets and JavaScript code. It also takes care of routine stuff for you on the back end, letting you add user accounts to the app with a single line of code.</p>
    <p>I’ve been experimenting with Meteor for the past six months, using it first to build <a href="http://telesc.pe" rel="nofollow external" class="bo">Telescope</a> (an open-source social news app), and then in turn using Telescope as a base to create <a href="http://sidebar.io" rel="nofollow external" class="bo">Sidebar</a> (a design links website), and I’ve just <a href="http://www.discovermeteor.com" rel="nofollow external" class="bo">released a book</a> about it. I believe that, more than any other framework, Meteor helps you get from idea to app in the shortest possible amount of time. So, if all of this has made you curious, I recommend you give it a try and follow along this short walkthrough.</p>
    <h4>Step 0: Install Meteor (5 Minutes)</h4>
    <p>First, let’s install Meteor. If you’re on Mac or Linux, simply open a Terminal window and type:</p>
    <pre><code>&#x000A;    curl <a href="https://install.meteor.com">https://install.meteor.com</a> | /bin/sh&#x000A;    </code></pre>
    <p>Installing Meteor on Windows is a little trickier; you can refer to <a href="http://win.meteor.com/" rel="nofollow external" class="bo">this handy guide</a> to get started.</p>
    <h4>Step 1: Create The App (1 Minute)</h4>
    <p>Creating a Meteor app is pretty easy. Once you’ve installed Meteor, all you need to do is go back to the Terminal and type this:</p>
    <pre><code>&#x000A;    meteor create myApp&#x000A;    </code></pre>
    <p>You’ll then be able to run your brand new app locally with this:</p>
    <pre><code>&#x000A;    cd myApp&#x000A;    meteor myApp&#x000A;    </code></pre>
    <p>In my case, I decided to call my app twitterList, but you can call yours whatever you want!</p>
    <p>Once you run the app, it will be accessible at <code><a href="http://localhost:3000/">http://localhost:3000/</a></code> in your browser.</p>
    <h4>Step 2: Add Packages (1 Minute)</h4>
    <p>Because I want users to be able to log in with Twitter, the first step is to set up user accounts. Thankfully, Meteor makes this trivially easy as well. First, add the required Meteor packages, <code>accounts-ui</code> and (since we want users to log in with Twitter) <code>accounts-twitter</code>.</p>
    <p>Open up a new Terminal window (since your app is already running in the first one) and enter:</p>
    <pre><code>&#x000A;    meteor add accounts-ui&#x000A;    meteor add accounts-twitter</code></pre>
    <p>You’ll now be able to display a log-in button just by inserting <code>{{loginButtons}}</code> anywhere in your Handlebars code.</p>
    <p><a href="http://telesc.pe/" rel="nofollow external" class="bo"><img alt="A more complex version of the accounts-ui widget, as seen in Telescope" src="http://media.smashingmagazine.com/wp-content/uploads/2013/05/accounts-ui-ss-500.png" width="500" height="378" style="max-width: 100%; height: auto;"></a><br>
    <em>A more complex version of the <code>accounts-ui</code> widget, as seen in <a href="http://telesc.pe" rel="nofollow external" class="bo">Telescope</a>.</em></p>
    <p>I didn’t want to have to bother with styling, so I decided to also include Twitter Bootstrap with my app.</p>
    <p>I went to the Twitter Bootstrap website, downloaded the framework, extracted the ZIP file, copied it to my app’s Meteor directory, and then hooked up the required CSS files in the head of my app’s main file.</p>
    <p>Ha ha, not really. What is this, 2012? That’s not how it works with Meteor. Instead, we just go back to the Terminal and type:</p>
    <pre><code>&#x000A;    meteor add bootstrap&#x000A;    </code></pre>
    <h3>Client Vs. Server</h3>
    <p>I guess at this point I should briefly tell you more about how Meteor apps work. First, we’ve already established that a Meteor app’s code is all JavaScript. This JavaScript can be executed in the browser like regular JavaScript code (think a jQuery plugin or an <code>alert()</code> message), but can additionally be executed on the server (like PHP or Ruby code). What’s more, the same code can even be executed in both environments!</p>
    <p>So, how do you keep track of all this? It turns out Meteor has two mechanisms to keep client and server code separate: the <code>Meteor.isClient</code> and <code>Meteor.isServer</code> booleans, and the <code>/client</code> and <code>/server</code> directories.</p>
    <p>I like to keep things clean; so, unlike the default Meteor app that gets generated with <code>meteor create</code> (which uses the booleans), I’d rather use separate directories.</p>
    <p>Also, note that anything that <em>isn’t</em> in the <code>/client</code> or <code>/server</code> directories will be executed in both environments by default.</p>
    <p>Since our app is pretty simple, we won’t actually have any custom server-side code (meaning that Meteor will take care of that part for us). So you can go ahead and create a new <code>/client</code> directory, and  move <code>twitterList.html</code> and <code>twitterList.js</code> (or however your files are called) to it now.</p>
    <h4>Step 3: Create the Markup (10 Minutes)</h4>
    <p>I like to start from a static template and then fill in the holes with dynamic data, so that’s what I did. Just write your template as if it were static HTML, except replace every “moving part” with <a href="http://handlebarsjs.com" rel="nofollow external" class="bo">Handlebars</a> tags. So, something like this…</p>
    <pre><code>&#x000A;     &lt;a href="<a href="http://twitter.com/SachaGreif%22&gt;Sacha">http://twitter.com/SachaGreif"&gt;Sacha</a> Greif&lt;/a&gt;&lt;/p&gt;&#x000A;    </code></pre>
    <p>… becomes this:</p>
    <pre><code>&#x000A;     &lt;a href="<a href="http://twitter.com/%7B%7BuserName%7D%7D%22&gt;%7B%7BfullName%7D%7D&lt;/a&amp;gt">http://twitter.com/{{userName}}"&gt;{{fullName}}&lt;/a&amp;gt</a>;&#x000A;    </code></pre>
    <p>Of course, those tags won’t do anything yet and will appear blank. But we’ll match them up with real data pretty soon. Next, I deleted the contents of <code>twitterlist.html</code> and got to work on my HTML. This is the code I had after this step:</p>
    <pre><code>&#x000A;    &lt;head&gt;&#x000A;      &lt;title&gt;Who Was There?&lt;/title&gt;&#x000A;    &lt;/head&gt;&#x000A;    &lt;body&gt;&#x000A;      &lt;div class="container"&gt;&#x000A;        &lt;div class="row"&gt;&#x000A;        &lt;div class="span6"&gt;&#x000A;          &lt;div class="well"&gt;&#x000A;            &lt;h4&gt;Did you go to the &lt;a href="<a href="http://www.meetup.com/Meteor-SFBay/events/115875132/%22&gt;Designer">http://www.meetup.com/Meteor-SFBay/events/115875132/"&gt;Designer</a> Potluck&lt;/a&gt;? Sign in with Twitter to add your name.&lt;/h4&gt;&#x000A;            {{loginButtons}}&#x000A;          &lt;/div&gt;&#x000A;          &lt;table class="table"&gt;&#x000A;             &lt;tr&gt;&#x000A;    &lt;td&gt;&#x000A;      &lt;a target="_blank" href="<a href="http://twitter.com/%7B%7BuserName%7D%7D%22&gt;&lt;img">http://twitter.com/{{userName}}"&gt;&lt;img</a> src="{{image}}"/&gt; {{fullName}}&lt;/a&gt;&#x000A;    &#x000A;    &lt;/td&gt;&#x000A;    &lt;/tr&gt; &#x000A;          &lt;/table&gt;&#x000A;        &lt;/div&gt;&#x000A;      &lt;/div&gt;&#x000A;    &lt;/div&gt;&#x000A;    &lt;/body&gt;&#x000A;    </code></pre>
    <h4>Step 4: Configure Twitter Sign-In (3 Minutes)</h4>
    <p>You’ll have noticed the <code>{{loginButtons}}</code> Handlebars tag, which inserts a log-in button on your page. If you try to click it right now, it won’t work, and Meteor will ask you for additional information.</p>
    <p><a href="http://media.smashingmagazine.com/wp-content/uploads/2013/06/sm_meteor_app_1_tiny.png" rel="nofollow external" class="bo"><img alt="You need to fill in your app's Twitter credentials." src="http://media.smashingmagazine.com/wp-content/uploads/2013/05/twitter-error-ss-500.png" width="500" height="273" style="max-width: 100%; height: auto;"></a><br>
    <em>You’ll need to fill in your app’s Twitter credentials. <a href="http://media.smashingmagazine.com/wp-content/uploads/2013/06/sm_meteor_app_1_tiny.png" rel="nofollow external" class="bo">Larger view</a>.</em></p>
    <p>To get this information, we first need to tell Twitter about our app. Follow the steps on the screen and <a href="https://dev.twitter.com/apps/new" rel="nofollow external" class="bo">create a new Twitter app</a>; once you’re done, try logging in. If everything has worked right, you should now have a user account in the app!</p>
    <p><a href="http://media.smashingmagazine.com/wp-content/uploads/2013/06/sm_meteor_app_2_tiny.png" rel="nofollow external" class="bo"><img alt="Creating a new Twitter app." src="http://media.smashingmagazine.com/wp-content/uploads/2013/05/twitter-new-app-ss-500.png" width="500" height="324" style="max-width: 100%; height: auto;"></a><br>
    <em>Creating a new Twitter app. <a href="http://media.smashingmagazine.com/wp-content/uploads/2013/06/sm_meteor_app_2_tiny.png" rel="nofollow external" class="bo">Larger view</a>.</em></p>
    <p>To test this out, open your browser’s console (in the WebKit inspector or in Firebug) and type this:</p>
    <pre><code>&#x000A;    Meteor.user()&#x000A;    </code></pre>
    <p>This will retrieve the currently logged-in user, and, if everything has gone right, it will give you your own user object in return (something like <code>Object {_id: "8ijhgK5icGrLjYTS7", profile: Object, services: Object}</code>).</p>
    <h4>Step 5: Split It Into Templates (5 Minutes)</h4>
    <p>You’ll have noticed that our HTML has room to display only a single user. We’ll need some kind of loop to iterate over the whole list. Thankfully, Handlebars provides us with the <code>{{#each xyz}}</code> … <code>{{/each}}</code> helper (where <code>xyz</code> are the objects you want to iterate on, usually an array), which does just that.</p>
    <p>We’ll also split the code into a few templates to keep things organized. The result is something like this:</p>
    <pre><code>&#x000A;    &lt;head&gt;&#x000A;      &lt;title&gt;Who Was There?&lt;/title&gt;&#x000A;    &lt;/head&gt;&#x000A;    &#x000A;    &lt;body&gt;&#x000A;      &lt;div class="container"&gt;&#x000A;        {{&gt; content}}&#x000A;      &lt;/div&gt;&#x000A;    &lt;/div&gt;&#x000A;    &lt;/body&gt;&#x000A;    &#x000A;    &lt;template name="content"&gt;&#x000A;      &lt;div class="row"&gt;&#x000A;        &lt;div class="span6"&gt;&#x000A;          &lt;div class="well"&gt;&#x000A;            {{loginButtons}}&#x000A;          &lt;/div&gt;&#x000A;          &lt;table class="table"&gt;&#x000A;          {{#each users}}&#x000A;            {{&gt; user}}&#x000A;          {{/each}}&#x000A;          &lt;/table&gt;&#x000A;        &lt;/div&gt;&#x000A;    &lt;/template&gt;&#x000A;    &#x000A;    &lt;template name="user"&gt;&#x000A;     &lt;tr&gt;&#x000A;    &lt;td&gt;&#x000A;      &lt;a target="_blank" href="<a href="http://twitter.com/%7B%7BuserName%7D%7D%22&gt;&lt;img">http://twitter.com/{{userName}}"&gt;&lt;img</a> src="{{image}}"/&gt; {{fullName}}&lt;/a&gt;&#x000A;    &lt;/td&gt;&#x000A;    &lt;/tr&gt; &#x000A;    &lt;/template&gt;&#x000A;    </code></pre>
    <h4>Step 6: Hook Up Our Template (5 Minutes)</h4>
    <p>Our template is all set up, but it’s iterating over empty air. We need to tell it what exactly this <code>users</code> variable in the <code>{{#each users}}</code> block is. This block is contained in the <code>content</code> template, so we’ll give that template a <a href="http://docs.meteor.com/#template_helpers" rel="nofollow external" class="bo">template helper</a>.</p>
    <p>Delete the contents of <code>twitterlist.js</code>, and write this instead:</p>
    <pre><code>&#x000A;    Template.content.users = function () {&#x000A;      return Meteor.users.find();&#x000A;    };&#x000A;    </code></pre>
    <p>What we’re doing here is defining <code>Template.content.users</code> as a function that returns <code>Meteor.users.find()</code>.</p>
    <p><code>Meteor.users</code> is a special collection created for us by Meteor. Collections are Meteor’s equivalent of MySQL tables. In other words, they’re a list of items of the same type (such as users, blog posts or invoices). And <code>find()</code> simply returns all documents in the collection.</p>
    <p>We’ve now told Meteor where to find that list of users, but nothing’s happening yet. What’s going on?</p>
    <h4>Step 7: Fix Our Tags (5 Minutes)</h4>
    <p>Remember when we typed this?</p>
    <pre><code>&#x000A;    &lt;a target="_blank" href="<a href="http://twitter.com/%7B%7BuserName%7D%7D%22&gt;&lt;img">http://twitter.com/{{userName}}"&gt;&lt;img</a> src="{{image}}"/&gt; {{fullName}}&lt;/a&gt;&#x000A;    </code></pre>
    <p>The <code>{{userName}}</code>, <code>{{image}}</code> and <code>{{fullName}}</code> are just random placeholders that I picked for the sake of convenience. We’d be pretty lucky if they corresponded to actual properties of our user object! (Hint: they don’t.)</p>
    <p>Let’s find out the “real” properties with the help of our friend, the browser console. Open it up, and once more type this:</p>
    <pre><code>&#x000A;    Meteor.user()&#x000A;    </code></pre>
    <p>The object returned has all of the fields we need. By exploring it, we can quickly find out that the real properties are actually these:</p>
    <ul>
    <li><code>{{services.twitter.screenName}}</code></li>
    <li><code>{{services.twitter.profile_image_url}}</code></li>
    <li><code>{{profile.name}}</code></li>
    </ul>
    <p>Let’s make the substitutions in our template and see what happens.</p>
    <p>It works! Our first and only user (you!) should now appear in the list. We’re still missing some fields, though, and only the user’s full name appears. We need to dig deeper into Meteor to understand why.</p>
    <h3>A Database On The Client</h3>
    <p>We haven’t really touched on what Meteor does behind the scenes yet. Unlike, say, PHP and MySQL, with which your data lives only on the server (and stays there unless you extract it from the database), Meteor replicates your server-side data in the client and automatically syncs both copies.</p>
    <p>This accomplishes two things. First, reading data becomes very fast because you’re reading from the browser’s own memory, and not from a database somewhere in a data center.</p>
    <p>Secondly, modifying data is extremely fast as well, because you can just modify the local copy of the data, and Meteor will replicate the changes for you server-side in the background. But this new paradigm comes with a caveat: We have to be more careful with data security.</p>
    <h4>Step 8: Make the App Secure (1 Minute)</h4>
    <p>We’ll address data security in terms of both writing and reading. First, let’s prevent people from writing whatever they want to our database. This is simple enough because all we need to do is remove Meteor’s <code>insecure</code> package:</p>
    <pre><code>&#x000A;    meteor remove insecure&#x000A;    </code></pre>
    <p>This package comes bundled with every new Meteor app to speed up development (letting you insert data client-side without having to set up all of the necessary checks and balances first), but it is obviously not meant for production. And because <strong>our app won’t need to write to the database at all</strong> (except for creating new users — but that’s a special case that Meteor already takes care of), we’re pretty much done!</p>
    <h3>More On Security</h3>
    <p>While we’re on the topic of security, Meteor apps also come with a second default package, <code>autopublish</code>, which takes care of sending all of the data contained in your server-side collections to the client.</p>
    <p>Of course, for a larger app, you probably won’t want to do that. After all, some of the information in your database is supposed to remain private, and even if all your data is public, sending <em>all</em> of it to the client might not be good for performance.</p>
    <p>In our case, this doesn’t really matter because we do want to “publish” (i.e. send from the server to the client) all of our users. Don’t worry, though — Meteor is still smart enough not to publish sensitive information, such as passwords and authentication tokens, even with <code>autopublish</code> on.</p>
    <h4>Step 9: Add Follow Buttons (8 Minutes)</h4>
    <p>While visitors can now click on a name to go to their Twitter profile, simply displaying follow buttons for each user would be much better. This step took a little tinkering to get right. It turns out that Twitter’s default follow button code doesn’t play nice with Meteor.</p>
    <p>After 15 minutes of unsuccessful attempts, I turned to the Google and quickly found that for single-page apps, Twitter suggests using an iframe instead.</p>
    <p>This worked great:</p>
    <pre><code>&#x000A;    &lt;iframe style="width: 300px; height: 20px;" src="//platform.twitter.com/widgets/follow_button.html?screen_name={{services.twitter.screenName}}" height="240" width="320" frameborder="0" scrolling="no"&gt;&lt;/iframe&gt;</code></pre>
    <h4>Step 10: Deploy (1 Minute)</h4>
    <p>The last step is to deploy our app and test it in production. Once again, Meteor makes this easy. No need to find a hosting service, register, launch an instance, and do a Git push. All you need to do is go back to the Terminal and type this:</p>
    <pre><code>&#x000A;    meteor deploy myApp&#x000A;    </code></pre>
    <p>Here, <code>myApp</code> is a unique subdomain that you pick (it doesn’t have to be the same as the app’s name). Once you’ve deployed, your app will live at <code><a href="http://myapp.meteor.com">http://myapp.meteor.com</a></code>. Go ahead and ask a few people to register: You’ll see their Twitter profiles added to the list in real time!</p>
    <h3>Going Further</h3>
    <p>Of course, I had to gloss over a lot of key Meteor concepts to keep this tutorial light. I barely mentioned collections and publications, and I didn’t even really talk about Meteor’s most important concept, reactivity. To learn more about Meteor, here are a few good resources:</p>
    <ul>
    <li>
    <a href="http://docs.meteor.com" rel="nofollow external" class="bo">Documentation</a>, Meteor<br>
    This is a required reference for any Meteor developer. And it’s cached, meaning you can even access it offline.</li>
    <li>
    <a href="http://eventedmind.com" rel="nofollow external" class="bo">EventedMind</a><br>
    Chris Mather puts out two Meteor screencasts every Friday. They’re a great help when you want to tackle Meteor’s more advanced features.</li>
    <li>
    <a href="http://www.discovermeteor.com" rel="nofollow external" class="bo"><em>Discover Meteor</em></a><br>
    I’m obviously biased, but I think our book is one of the best resources to get started with Meteor. It takes you through building a real-time social news app (think <a href="http://reddit.com" rel="nofollow external" class="bo">Reddit</a> or <a href="http://news.ycombinator.com" rel="nofollow external" class="bo">Hacker News</a>) step by step.</li>
    <li>
    <a href="http://www.discovermeteor.com/blog" rel="nofollow external" class="bo">Blog</a>, Discover Meteor<br>
    We also make a lot of information available for free on our blog. We suggest looking at “<a href="http://www.discovermeteor.com/2013/01/30/getting-started-with-meteor/" rel="nofollow external" class="bo">Getting Started With Meteor</a>” and “<a href="http://www.discovermeteor.com/2013/02/10/useful-meteor-resources/" rel="nofollow external" class="bo">Useful Meteor Resources</a>.”</li>
    <li>“<a href="http://net.tutsplus.com/tutorials/javascript-ajax/prototyping-with-meteor/" rel="nofollow external" class="bo">Prototyping With Meteor</a>”<br>
    A tutorial we wrote for NetTuts that takes you through building a simple chat app.</li>
    </ul>
    <p>I truly believe Meteor is one of the best frameworks out there for quickly building apps, and it’s only going to get better. Personally, I’m really excited to see how the framework evolves in the next couple of months. I hope this short tutorial has given you a taste of what Meteor’s all about and has made you curious to learn more!</p>
    <p><em>(il) (ea) (al)</em></p>
    <hr>
    <p><small>© Sacha G for <a href="http://www.smashingmagazine.com" rel="nofollow external" class="bo">Smashing Magazine</a>, 2013.</small></p>
    </div>
]]>
</Body>
<Summary>        The other day, I finally accomplished one of my long-standing goals: to go from one of those “Wouldn’t it be cool…” ideas to a working, live app in less than 1 hour. 45 minutes, actually....</Summary>
<Website>http://www.smashingmagazine.com/2013/06/13/build-app-45-minutes-meteor/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31240/guest@my.umbc.edu/f253d4930033c7f1a6bd169e6d3e8fbd/api/pixel</TrackingUrl>
<Tag>apps</Tag>
<Tag>coding</Tag>
<Tag>css</Tag>
<Tag>design</Tag>
<Tag>developers-toolbox</Tag>
<Tag>development</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>php</Tag>
<Tag>sql</Tag>
<Tag>tutorials</Tag>
<Tag>web</Tag>
<Tag>web-design</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Thu, 13 Jun 2013 05:17:53 -0400</PostedAt>
<EditAt>Thu, 13 Jun 2013 05:17:53 -0400</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="31241" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31241">
<Title>Must-know facts about responsive design</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <p><img alt="thumbnail" src="http://netdna.webdesignerdepot.com/uploads/2013/06/thumbnail4.jpg" width="200" height="160" style="max-width: 100%; height: auto;">Responsive design is a relatively new term in web design. It was only coined three years ago in May of 2010, when web designer Ethan Marcotte used the term in <a href="http://alistapart.com/article/responsive-web-design" rel="nofollow external" class="bo">his article</a> for A List Apart.</p> <p>Today, there’s even a mini debate going on regarding whether responsive design is here to stay or whether it’s just a flash in the pan. Only time will tell, but for now, it’s clear that responsive design strives to make the user experience as comfortable as possible.</p> <p>Responsive design is a web design philosophy that focuses on creating sites that give users an enhanced viewing experience. This includes features such as effortless navigation and reading, and a minimum of browser resizing, scrolling and panning. All of this takes place across a range of different devices, from desktops to smartphones.</p> <p>Since this web design approach is still in a fledgling state, you may not be totally clear on what responsive design is all about. Is it more about seamlessly displaying content across multiple platforms, or is it mainly about helping businesses build more attractive sites to increase their sales through a better user experience?</p> <p>Mashable has already gone out on a limb and <a href="http://mashable.com/2012/12/11/responsive-web-design/" rel="nofollow external" class="bo">called</a> 2013 the year of responsive design. While that remains to be seen, some basic aspects of responsive design are standards that will never go away. Here are the most important responsive design facts to familiarize yourself with.</p> <p> </p> <h1>There’s a difference between responsive design and mobile design</h1> <p>You’d be forgiven for thinking that responsive design and mobile design are one and the same — but they’re not. Sure, responsive design does create websites so that they both respond to a browser’s size and are mobile-friendly, but responsive design really is whole web design. The problem with referring to this web design approach as mobile design is that it’s inherently limiting, which does a disservice to the approach itself.</p> <p>The most effective responsive websites may be viewed as they were meant to be, across a range of resolutions. This includes everything from the usual 1024×768 pixels to the 1920×1080 displays and everything in between. Sites like these also look splendid on tablets (both retina and standard displays), as well as on smartphones. If a web designer looks at responsive design solely through the context of mobile, then he’s potentially missing out on a broader user experience. </p> <p>At the same time, mobile is a really opportune starting point for the entire responsive design discussion. It’s been the norm to begin with a mobile scheme and then expand this design to additional sizes as a responsive website is developed. Lots of designers believe it’s simpler to grow visuals rather than to minimize them.</p> <p> </p> <h1>Quality and image size are priorities </h1> <p>If there’s a rule that web designers ought to follow, it’s that image quality is a whole lot more vital than the actual number of images. The reason is that a low-quality image simply doesn’t look attractive in any size. The time it takes a site to load an image is almost as important as the size. Mobile users will agree with this because they have limited bandwidth with which to contend.</p> <p>What’s a web designer to do? Simply reach a smart balance between load time and quality. This includes scaling images with CSS height and width properties, steering clear of loading full-size images, and optimizing images for the Internet. Prior to uploading, it’s highly advised to crop any images and save each picture at the smallest size possible, as long as it still maintains sharp, visual quality.</p> <p>Check out <a href="http://www.sony.com/index.php" rel="nofollow external" class="bo">Sony USA’s website</a>. Note how all the images are super sharp in quality, no matter what size they are. Once you visit the site or refresh the homepage, also note how fast the images load. You don’t have to wait more than a second for everything to come into focus extremely sharply.</p> <p>Designers have many choices when incorporating images in a responsive setting. They can use only a few images; lessen the use of images within mobile-sized schemes; permit images to mask themselves in mobile surroundings; or utilize various file sizes and versions. These choices will work effectively, although some developers are against hiding images, because the user will still have to load the images in spite of them being unseen.</p> <p> </p> <h1>Let’s talk about responsive type</h1> <p>Type shouldn’t be one size fits all. One kind of font that looks appealing to the eye on your desktop may be horrid on your smartphone. Typography must follow the same rules as other aspects of responsive design.</p> <p>The most important aspect of responsive typography is the line length. For smooth readability, type ought to be optimized based on the width of the screen. The rule of thumb, for desktop websites, is that between 50 and 75 characters a line is ideal; for mobile devices, just between 35 to 50 characters is ideal.</p> <p>Type must also be easily read vertically. Lots of sites utilize a line space that’s up to 140 percent of the screen’s point size for bigger blocks of text. If the screen is smaller, more space should be added.</p> <p>Even the specific typeface that’s utilized is significant. Fancy fonts and novelty typefaces have the ability to look visually appealing on bigger screens, yet they’re hard to read if the point size is small. These sorts of fonts should have lots of space between them. When you work with smaller sizes, it’s easiest to utilize normal sans serif styles and even strokes.</p> <p>On <a href="http://hardboiledwebdesign.com/" rel="nofollow external" class="bo">Hardboiled Web Design’s site</a>, you can see a lot of these principles being followed, making for good responsiveness. Note how its line length of text on a desktop — while on average greater than the ideal recommendation of 50 to 75 characters — is comprised of a typeface that’s clean and easy to read. In addition, the line space is also greater than the point size of the font. On mobile devices, the site’s responsiveness performs even better: On an iPhone 5 display, the number of characters per line was approximately 67, which is just a bit over the ideal rule of between 35 and 50 characters.</p> <p> </p> <h1>Don’t forget about navigation</h1> <p>When it comes to the user experience — which is one of the most important factors that web designers should think about — navigation is right at the top of priorities. Navigation has to be smooth and efficient to ensure a comfortable user experience.</p> <p>Effective responsive design must ensure this by paying extra attention to the specific width of a given browser. A site that uses responsive design well will lay out its site navigation in different areas, all dependent on the browser’s width. One of the best examples of this is <a href="http://foodsense.is/" rel="nofollow external" class="bo">Food Sense</a>’s site navigation.</p> <p>It would be a mistake for responsive design to scale the site navigation to larger proportions on devices with bigger screens.</p> <p> </p> <h1>The takeaway of responsive design</h1> <p>So now, when you hear web designers talking about responsive design, you’ll know that it isn’t just about making a website look good and run smoothly on smaller, mobile screens. You’ll know that this design approach is based on the principle of making websites of all sizes provide the most optimal experience to the user — no matter what they’re using to view the site.</p> <p>Responsive design is still a relatively new concept, at least to most people who just view websites on the internet. That’s why so many people still can’t agree on what makes responsive design…responsive design. Is it about seeing everything properly on mobile screens? Is it just about load times and high-quality images that will please the eye? Is it about a clean design and easy-to-read typefaces? </p> <p>It’s all of that and more. Those are just the fundamentals of this web design approach, but responsive design is still evolving and changing, so chances are good that additional elements will be considered, too. In the end, it’s about enhancing the user experience, because no one wants to deal with a website that’s slow, blurry, hard to read, cluttered or difficult to navigate.</p> <p> </p> <p><em><strong>Is responsive design just a trend? What are the key aspects of responsive design? Let us know your thoughts in the comments.</strong></em></p> <p><em>Featured image/thumbnail, <a href="http://www.flickr.com/photos/s58y/4415406714/sizes/o/in/photostream/" rel="nofollow external" class="bo">via s58y</a></em></p> <p><br><br> </p>
    <table width="100%"> <tbody>
    <tr> <td> <a href="http://www.mightydeals.com/deal/vandelay-mobile-design-bundle.html?ref=inwidget" rel="nofollow external" class="bo"><strong>The Ultimate Mobile Design Resource Bundle – only $29!</strong></a> </td> <td> <a href="http://www.mightydeals.com/?ref=inwidget" rel="nofollow external" class="bo"><br> <img src="http://mightydeals.com/web/images/widget-logo.png" height="40" width="90" alt="Must know facts about responsive design" style="max-width: 100%; height: auto;"><br> </a> </td> </tr> </tbody>
    </table> <p><br> </p> <a href="http://www.webdesignerdepot.com/2013/06/must-know-facts-about-responsive-design/" rel="nofollow external" class="bo">Source</a> <div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2013%2F06%2Fmust-know-facts-about-responsive-design%2F&amp;t=Must-know+facts+about+responsive+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/twitter.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/facebook/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2013%2F06%2Fmust-know-facts-about-responsive-design%2F&amp;t=Must-know+facts+about+responsive+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/facebook.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/linkedin/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2013%2F06%2Fmust-know-facts-about-responsive-design%2F&amp;t=Must-know+facts+about+responsive+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/linkedin.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/gplus/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2013%2F06%2Fmust-know-facts-about-responsive-design%2F&amp;t=Must-know+facts+about+responsive+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/googleplus.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/email/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2013%2F06%2Fmust-know-facts-about-responsive-design%2F&amp;t=Must-know+facts+about+responsive+design" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/email.png" style="max-width: 100%; height: auto;"></a>
    </td></tr></tbody></table></div>
    <br><br><a href="http://da.feedsportal.com/r/165665491367/u/49/f/661066/c/35285/s/2d3ae10f/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/165665491367/u/49/f/661066/c/35285/s/2d3ae10f/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>Responsive design is a relatively new term in web design. It was only coined three years ago in May of 2010, when web designer Ethan Marcotte used the term in his article for A List Apart....</Summary>
<Website>http://rss.feedsportal.com/c/35285/f/661066/s/2d3ae10f/l/0L0Swebdesignerdepot0N0C20A130C0A60Cmust0Eknow0Efacts0Eabout0Eresponsive0Edesign0C/story01.htm</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31241/guest@my.umbc.edu/b8e36170e6e6bde13648d01d17423c37/api/pixel</TrackingUrl>
<Tag>art</Tag>
<Tag>css</Tag>
<Tag>design</Tag>
<Tag>design-priorities</Tag>
<Tag>designing-for-mobile</Tag>
<Tag>development</Tag>
<Tag>how-to-produce-responsive-design</Tag>
<Tag>html</Tag>
<Tag>html5</Tag>
<Tag>illustrator</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>oracle</Tag>
<Tag>photoshop</Tag>
<Tag>php</Tag>
<Tag>responsive-design</Tag>
<Tag>responsive-typography</Tag>
<Tag>rules-of-responsive-design</Tag>
<Tag>sql</Tag>
<Tag>user-interface</Tag>
<Tag>web-design</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Thu, 13 Jun 2013 05:15:00 -0400</PostedAt>
<EditAt>Thu, 13 Jun 2013 05:15:00 -0400</EditAt>
</NewsItem>

<NewsItem contentIssues="false" id="31289" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31289">
<Title>Incoming Freshman Swimmer Andrew McKissick Wins Great Chesapeake Bay Swim</Title>
<Body>
<![CDATA[
    <div class="html-content">BALTIMORE � Incoming UMBC freshman swimmer Andrew McKissick won the 2013 Great Chesapeake Bay Swim (GCBS), Sunday afternoon near Annapolis.</div>
]]>
</Body>
<Summary>BALTIMORE � Incoming UMBC freshman swimmer Andrew McKissick won the 2013 Great Chesapeake Bay Swim (GCBS), Sunday afternoon near Annapolis.</Summary>
<Website>http://www.umbcretrievers.com/release.asp?RELEASE_ID=8052</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31289/guest@my.umbc.edu/04a8c1517bc27b259a6634fda8e50d7f/api/pixel</TrackingUrl>
<Group token="athletics">UMBC Athletics</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/athletics</GroupUrl>
<AvatarUrl>https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/xsmall.png?1709304849</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/original.jpg?1709304849</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/xxlarge.png?1709304849</AvatarUrl>
<AvatarUrl size="xlarge">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/xlarge.png?1709304849</AvatarUrl>
<AvatarUrl size="large">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/large.png?1709304849</AvatarUrl>
<AvatarUrl size="medium">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/medium.png?1709304849</AvatarUrl>
<AvatarUrl size="small">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/small.png?1709304849</AvatarUrl>
<AvatarUrl size="xsmall">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/xsmall.png?1709304849</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/009/69595c9b99f609d75fbb8232d9bd73d3/xxsmall.png?1709304849</AvatarUrl>
<Sponsor>UMBC Athletics</Sponsor>
<PawCount>36</PawCount>
<CommentCount>3</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Thu, 13 Jun 2013 01:00:00 -0400</PostedAt>
</NewsItem>

<NewsItem contentIssues="false" id="31238" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31238">
<Title>Review: WPTouch for WordPress</Title>
<Body>
<![CDATA[
    <div class="html-content"><p>In this review we look at WPTouch Pro 3, a WordPress plugin which allows you to create a customized mobile site from within WordPress, using your existing layout as the source material.</p></div>
]]>
</Body>
<Summary>In this review we look at WPTouch Pro 3, a WordPress plugin which allows you to create a customized mobile site from within WordPress, using your existing layout as the source material.</Summary>
<Website>http://www.htmlgoodies.com/beyond/cms/review-wptouch-for-wordpress.html</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31238/guest@my.umbc.edu/b94f81934dcd6e5159f5cf20ce55fe5a/api/pixel</TrackingUrl>
<Tag>html</Tag>
<Tag>htmlgoodies</Tag>
<Tag>learning</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Wed, 12 Jun 2013 22:27:00 -0400</PostedAt>
</NewsItem>

<NewsItem contentIssues="false" id="31235" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31235">
<Title>PAID Summer Internship - Geographic Information Systems</Title>
<Tagline>Great Opportunity at Catholic Relief Services</Tagline>
<Body>
<![CDATA[
    <div class="html-content"><table border="0" width="100%"><tbody>
    <tr>
    					<td colspan="1">
    						Internship - Geographic Information Systems – Server Integration 
    					</td>
    					
    				</tr>
    				
    				
    				
    				
    				
    				<tr>
    				
    					
    					<td colspan="1">
    						Full Time<br>
    								
    					</td>
    					
    				</tr>
    				
    				<tr>
    				
    					
    					<td colspan="1">
    						United States/Baltimore<br>
    								
    					</td>
    					
    				</tr>
    				
    				<tr>
    				
    					
    					<td colspan="1">
    						<p><strong><span>Job Title:<span>         </span><span>           </span></span></strong><span>Intern, Geographic Information Systems<strong> – </strong>ServerIntegration<strong><span>   </span></strong></span><strong><span><span>           </span></span></strong></p>
    <p><strong><span>Department: <span>             </span></span></strong><span>Global Knowledge and Information Management (GKIM)</span></p>
    <p><strong><span>Work Location:<span>  </span><span>       </span></span></strong><span>Baltimore, MD, USA</span></p>
    <p><strong><span>Duration:<span>  </span><span>                 </span></span></strong><span>8 weeks, summer 2013</span></p>
    <p> </p>
    <p><strong><span>Job Summary:</span></strong></p>
    <p><span>The
     geographic information systems intern will analyze and configure 
    applications and integration as part of a larger geospatial analytics 
    initiative for Catholic Relief Services (CRS).<span>  </span>This geospatial analytics service will improve efficacy of and have a decisive impact on CRS’ overseas programs.</span></p>
    <p> </p>
    <p><strong><span>Job Responsibilities:</span></strong></p>
    <p><span><span>·<span>         </span></span></span><span>Analyze
     ArcGIS server security model and configuration options; recommend and 
    implement configuration including user groups and permissions</span></p>
    <p><span><span>·<span>         </span></span></span><span>Establish connector between ArcGIS server and ArcGIS online</span></p>
    <p><span><span>·<span>         </span></span></span><span>Establish connector between SQL and ArcGIS server and demonstrate using sample geodatabase</span></p>
    <p><span><span>·<span>         </span></span></span><span><span> </span></span><span>Share recommendations with information technology team and incorporate feedback received</span></p>
    <p><span><span>·<span>         </span></span></span><span><span> </span></span><span>Create documentation for all work products</span></p>
    <p><span><span>·<span>         </span></span></span><span>Report status and escalate issues</span></p>
    <p> </p>
    <p> </p>
    <p><strong><span>Qualifications:</span></strong></p>
    <p><span><span>·<span>         </span></span></span><span>Demonstrable experience or formal training using ArcGIS</span></p>
    <p><span><span>·<span>         </span></span></span><span>Pursuing degree in Computer Science or related field</span></p>
    <p> </p>
    <p><strong><span>Agency-wide Competencies (for all CRS Staff) </span></strong><em><span>These
     are rooted in the mission, values, and principles of CRS and used by 
    each staff member to fulfill his or her responsibilities and to achieve 
    the desired results</span></em><span>· </span></p>
    <ul>
    <li><span>Promotes learning </span></li>
    <li><span>Develops constructive relationships </span></li>
    <li><span>Models stewardship </span></li>
    <li><span>Serves with integrity</span></li>
    </ul>
    </td>
    </tr>
    </tbody></table></div>
]]>
</Body>
<Summary>Internship - Geographic Information Systems – Server Integration                                                                        Full Time...</Summary>
<Website>https://www5.apply2jobs.com/CRS/ProfExt/?fuseaction=mExternal.showJob&amp;RID=1311&amp;CurrentPage=1</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31235/guest@my.umbc.edu/dff37c6fc4b0686c685504a7d0f9b8f1/api/pixel</TrackingUrl>
<Group token="shriver">The Shriver Center</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/shriver</GroupUrl>
<AvatarUrl>https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/xsmall.png?1441293069</AvatarUrl>
<AvatarUrl size="original">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/original.jpg?1441293069</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/xxlarge.png?1441293069</AvatarUrl>
<AvatarUrl size="xlarge">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/xlarge.png?1441293069</AvatarUrl>
<AvatarUrl size="large">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/large.png?1441293069</AvatarUrl>
<AvatarUrl size="medium">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/medium.png?1441293069</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/small.png?1441293069</AvatarUrl>
<AvatarUrl size="xsmall">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/xsmall.png?1441293069</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/008/0bfad113286cf6b1bc6dedbdbfc7e5ef/xxsmall.png?1441293069</AvatarUrl>
<Sponsor>Shriver Center: Intern, Co-op, Research &amp; Service-Learning</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Wed, 12 Jun 2013 21:03:38 -0400</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="31236" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31236">
<Title>Gatekeepers of Cable TV Try to Stop Intel</Title>
<Body>
<![CDATA[
    <div class="html-content">Intel’s plan to create a virtual cable service is running up against a barricade being guarded by cable and satellite distributors.<div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fbusiness%2Fmedia%2Fgatekeepers-of-cable-tv-try-to-stop-intel.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Gatekeepers+of+Cable+TV+Try+to+Stop+Intel" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/twitter.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/facebook/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fbusiness%2Fmedia%2Fgatekeepers-of-cable-tv-try-to-stop-intel.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Gatekeepers+of+Cable+TV+Try+to+Stop+Intel" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/facebook.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/linkedin/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fbusiness%2Fmedia%2Fgatekeepers-of-cable-tv-try-to-stop-intel.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Gatekeepers+of+Cable+TV+Try+to+Stop+Intel" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/linkedin.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/gplus/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fbusiness%2Fmedia%2Fgatekeepers-of-cable-tv-try-to-stop-intel.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Gatekeepers+of+Cable+TV+Try+to+Stop+Intel" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/googleplus.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/email/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fbusiness%2Fmedia%2Fgatekeepers-of-cable-tv-try-to-stop-intel.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Gatekeepers+of+Cable+TV+Try+to+Stop+Intel" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/email.png" style="max-width: 100%; height: auto;"></a>
    </td></tr></tbody></table></div>
    </div>
]]>
</Body>
<Summary>Intel’s plan to create a virtual cable service is running up against a barricade being guarded by cable and satellite distributors.     </Summary>
<Website>http://www.nytimes.com/2013/06/13/business/media/gatekeepers-of-cable-tv-try-to-stop-intel.html?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31236/guest@my.umbc.edu/56ac9f6cec894516cc7c4cb0f5a15a2e/api/pixel</TrackingUrl>
<Tag>antitrust-laws-and-competition-issues</Tag>
<Tag>apple-inc-aapl-nasdaq</Tag>
<Tag>cable-television</Tag>
<Tag>comcast-corporation-cmcsa-nasdaq</Tag>
<Tag>computers-and-the-internet</Tag>
<Tag>intel-corporation-intc-nasdaq</Tag>
<Tag>microsoft-corporation-msft-nasdaq</Tag>
<Tag>netflix-inc-nflx-nasdaq</Tag>
<Tag>new</Tag>
<Tag>sony-corporation-sne-nyse</Tag>
<Tag>technology</Tag>
<Tag>television</Tag>
<Tag>time-warner-cable-inc-twc-nyse</Tag>
<Tag>york</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Wed, 12 Jun 2013 20:48:45 -0400</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="31237" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31237">
<Title>In Battle Over Malibu Beaches, an App Unlocks Access</Title>
<Body>
<![CDATA[
    <div class="html-content">The app, which has maps that point the way to hidden entry gates, escalates the war between homeowners and a less-privileged public that wants to share the California coastline.<div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fus%2Fin-battle-over-malibu-beaches-an-app-unlocks-access.html%3Fpartner%3Drss%26emc%3Drss&amp;t=In+Battle+Over+Malibu+Beaches%2C+an+App+Unlocks+Access" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/twitter.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/facebook/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fus%2Fin-battle-over-malibu-beaches-an-app-unlocks-access.html%3Fpartner%3Drss%26emc%3Drss&amp;t=In+Battle+Over+Malibu+Beaches%2C+an+App+Unlocks+Access" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/facebook.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/linkedin/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fus%2Fin-battle-over-malibu-beaches-an-app-unlocks-access.html%3Fpartner%3Drss%26emc%3Drss&amp;t=In+Battle+Over+Malibu+Beaches%2C+an+App+Unlocks+Access" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/linkedin.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/gplus/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fus%2Fin-battle-over-malibu-beaches-an-app-unlocks-access.html%3Fpartner%3Drss%26emc%3Drss&amp;t=In+Battle+Over+Malibu+Beaches%2C+an+App+Unlocks+Access" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/googleplus.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/email/?u=http%3A%2F%2Fwww.nytimes.com%2F2013%2F06%2F13%2Fus%2Fin-battle-over-malibu-beaches-an-app-unlocks-access.html%3Fpartner%3Drss%26emc%3Drss&amp;t=In+Battle+Over+Malibu+Beaches%2C+an+App+Unlocks+Access" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/email.png" style="max-width: 100%; height: auto;"></a>
    </td></tr></tbody></table></div>
    </div>
]]>
</Body>
<Summary>The app, which has maps that point the way to hidden entry gates, escalates the war between homeowners and a less-privileged public that wants to share the California coastline.     </Summary>
<Website>http://www.nytimes.com/2013/06/13/us/in-battle-over-malibu-beaches-an-app-unlocks-access.html?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31237/guest@my.umbc.edu/9041073561d8dd8e3536671dd9408632/api/pixel</TrackingUrl>
<Tag>beaches</Tag>
<Tag>malibu-calif</Tag>
<Tag>mobile-applications</Tag>
<Tag>new</Tag>
<Tag>price-jennifer</Tag>
<Tag>technology</Tag>
<Tag>york</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Wed, 12 Jun 2013 20:40:34 -0400</PostedAt>
</NewsItem>

<NewsItem contentIssues="false" id="31239" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31239">
<Title>Appshark Partners with Cosentry to Develop a More Robust HTML5 Application Tool for Mobile Migration</Title>
<Body>
<![CDATA[
    <div class="html-content"><p>Cosentry expands their cloud based offerings to now include application migration services, cloud deployment and HTML5 mobile enablement with the help of their new partnership with AppShark.</p></div>
]]>
</Body>
<Summary>Cosentry expands their cloud based offerings to now include application migration services, cloud deployment and HTML5 mobile enablement with the help of their new partnership with AppShark.</Summary>
<Website>http://www.htmlgoodies.com/daily_news/appshark-partners-with-cosentry-to-develop-a-more-robust-html5-application-tool-for-mobile-migration.html</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31239/guest@my.umbc.edu/c05d62009eff9152b6667b8525aac7d3/api/pixel</TrackingUrl>
<Tag>html</Tag>
<Tag>htmlgoodies</Tag>
<Tag>learning</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Wed, 12 Jun 2013 20:02:00 -0400</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="31231" important="false" status="posted" url="https://my3.my.umbc.edu/posts/31231">
<Title>Intro to Flask: Signing In and Out</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <a href="http://rss.buysellads.com/click.php?z=1260013&amp;k=d754f1e9ba63a736ba8ff5ece958f7dd&amp;a=29982&amp;c=120943108" rel="nofollow external" class="bo"><img src="http://rss.buysellads.com/img.php?z=1260013&amp;k=d754f1e9ba63a736ba8ff5ece958f7dd&amp;a=29982&amp;c=120943108" alt="" style="max-width: 100%; height: auto;"></a><p>Many web applications require users to sign in and out in order to perform important tasks (like administration duties). In this article, we’ll create an authentication system for our application.</p>
    <p></p>
    <p>In the <a href="http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-adding-a-contact-page/" rel="nofollow external" class="bo">previous article</a>, we built a contact page using the <a href="http://packages.python.org/Flask-WTF/" rel="nofollow external" class="bo">Flask-WTF</a> and <a href="http://packages.python.org/Flask-Mail/" rel="nofollow external" class="bo">Flask-Mail</a> extensions. We’ll use <a href="http://packages.python.org/Flask-WTF/" rel="nofollow external" class="bo">Flask-WTF</a>, once again, this time to validate a user’s username and password. We’ll save these credentials into a database using yet another extension called <a href="http://packages.python.org/Flask-SQLAlchemy/" rel="nofollow external" class="bo">Flask-SQLAlchemy</a>.</p>
    <p>You can find the source code for this tutorial on <a href="https://github.com/NETTUTS/intro-to-flask" rel="nofollow external" class="bo">GitHub</a>. While following along with this tutorial, when you see a caption, such as <code>Checkpoint: 13_packaged_app</code>, it means that you can switch to the GIT branch named “13_packaged_app” and review the code at that point in the article.</p>
    <hr>
    <h2>Growing the Application</h2>
    <p>So far, our <a href="http://flask.pocoo.org/" rel="nofollow external" class="bo">Flask</a> app is a fairly simple application. It consists of mostly static pages; so, we’ve been able to organize it as a Python module. But now, we need to reorganize our application to make it easier to maintain and grow. <a href="http://flask.pocoo.org/docs/patterns/packages/" rel="nofollow external" class="bo">The Flask documentation recommends</a> that we reorganize the app as a Python package, so let’s start there.</p>
    <p>Our app is currently organized like this:</p>
    <pre>flaskapp/&#x000A;    └── app/&#x000A;            ├── static/&#x000A;            ├── templates/&#x000A;            ├── forms.py&#x000A;            ├── routes.py&#x000A;            └── README.md</pre>
    <p>To restructure it as a package, let’s first create a new folder inside <code>app/</code> named <code>intro_to_flask/</code>. Then move <code>static/</code>, <code>templates/</code>, <code>forms.py</code> and <code>routes.py</code> into <code>intro_to_flask/</code>. Also, delete any .pyc files that are hanging around.</p>
    <pre>flaskapp/&#x000A;    └── app/&#x000A;            ├── intro_to_flask/&#x000A;            │      ├── static/&#x000A;            │      ├── templates/&#x000A;            │      ├── forms.py&#x000A;            │      ├── routes.py&#x000A;            └── README.md</pre>
    <p>Next, create a new file named <code>__init__.py</code> and place it inside <code>intro_to_flask/</code>. This file is required to make Python treat the <code>intro_to_flask/</code> folder as a package.</p>
    <pre>flaskapp/&#x000A;    └── app/&#x000A;            ├── intro_to_flask/&#x000A;            │      ├── __init__.py&#x000A;            │      ├── static/&#x000A;            │      ├── templates/&#x000A;            │      ├── forms.py&#x000A;            │      ├── routes.py&#x000A;            └── README.md</pre>
    <p>When our app was a Python module, the application-wide imports and configurations were specified in <code>routes.py</code>. Now that the app is a Python package, we’ll move these settings from <code>routes.py</code> into <code>__init__.py</code>.</p>
    <p><code>app/intro_to_flask/__init__.py</code></p>
    <pre>from flask import Flask&#x000A;    &#x000A;    app = Flask(__name__)&#x000A;    &#x000A;    app.secret_key = 'development key'&#x000A;    &#x000A;    app.config["MAIL_SERVER"] = "smtp.gmail.com"&#x000A;    app.config["MAIL_PORT"] = 465&#x000A;    app.config["MAIL_USE_SSL"] = True&#x000A;    app.config["MAIL_USERNAME"] = '<a href="mailto:contact@example.com">contact@example.com</a>'&#x000A;    app.config["MAIL_PASSWORD"] = 'your-password'&#x000A;    &#x000A;    from routes import mail&#x000A;    mail.init_app(app)&#x000A;    &#x000A;    import intro_to_flask.routes</pre>
    <p>The top of <code>routes.py</code> now looks like this:</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>from intro_to_flask import app&#x000A;    from flask import render_template, request, flash&#x000A;    from forms import ContactForm&#x000A;    from flask.ext.mail import Message, Mail&#x000A;    &#x000A;    mail = Mail()&#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    # @app.route() mappings start here</pre>
    <p>We previously had <code>app.run()</code> inside of <code>routes.py</code>, which allowed us to type <code>$ python routes.py</code> to run the application. Since the app is now organized as a package, we need to employ a different strategy. <a href="http://flask.pocoo.org/docs/patterns/packages/" rel="nofollow external" class="bo">The Flask docs recommend</a> adding a new file named <code>runserver.py</code> and placing it inside <code>app/</code>. Let’s do that now:</p>
    <pre>flaskapp/&#x000A;    └── app/&#x000A;            ├── intro_to_flask/&#x000A;            │      ├── __init__.py&#x000A;            │      ├── static/&#x000A;            │      ├── templates/&#x000A;            │      ├── forms.py&#x000A;            │      ├── routes.py&#x000A;            ├── runserver.py        &#x000A;            └── README.md</pre>
    <p>Now take the <code>app.run()</code> call from <code>routes.py</code> and place it inside of <code>runserver.py</code>.</p>
    <p><code>app/runserver.py</code></p>
    <pre>from intro_to_flask import app&#x000A;    &#x000A;    app.run(debug=True)</pre>
    <p>Now you can type <code>$ python runserver.py</code> and view the app in the browser. From the top, here’s how you’ll enter your development environment and run the app:</p>
    <pre>$ cd flaskapp/&#x000A;    $ . bin/activate&#x000A;    $ cd app/&#x000A;    $ python runserver.py</pre>
    <p>The app is now organized as a package, we’re ready to move on and install a database to manage user credentials.</p>
    <p>— <code>Checkpoint: 13_packaged_app</code> —</p>
    <hr>
    <h2>Flask-SQLAlchemy</h2>
    <p>We’ll use MySQL for our database engine and the <a href="http://packages.python.org/Flask-SQLAlchemy/" rel="nofollow external" class="bo">Flask-SQLAlchemy</a> extension to manage all of the database interaction.</p>
    <p>Flask-SQLAlchemy uses Python objects instead of SQL statements to query the database. For example, instead of writing <code>SELECT * FROM users WHERE firstname = "lalith"</code>, you would write <code>User.query.filter_by(username="lalith").first()</code>.</p>
    <blockquote><p> The moral of this aside is to not completely rely on, or abandon a database abstraction layer like Flask-SQLAlchemy, but to be aware of it, so that you can determine when it’s useful for your needs.</p></blockquote>
    <p>But why can’t we just write raw SQL statements? What’s the point of using this weird syntax? As with most things, using Flask-SQLAlchemy, or any database abstraction layer, depends on your needs and preferences. Using Flask-SQLAlchemy allows you to work with your database by writing Python code instead of SQL. This way you don’t have SQL statements scattered amidst your Python code, and that’s a good thing, from a code quality perspective.</p>
    <p>Also, if implemented correctly, using Flask-SQLAlchemy will help make your application to be database-agnostic. If you start building your app on top of MySQL and then decide to switch to another database engine, you shouldn’t have to rewrite massive chunks of sensitive database code. You could simply switch out Flask-SQLAlchemy with your new database abstraction layer without much of an issue. Being able to easily replace components is called modularity, and it’s a sign of a well designed application.</p>
    <p>On the other hand, it might be more intuitive and readable if you write raw SQL statements instead of learning how to translate it into Flask-SQLAlchemy’s Expression Language. Fortunately, it’s possible to write raw SQL statements in Flask-SQLAlchemy too, if that’s what you need.</p>
    <p>The moral of this aside is to not completely rely on, or abandon a database abstraction layer like Flask-SQLAlchemy, but to be aware of it, so that you can determine when it’s useful for your needs. For the database queries in this article, I’ll show you both the Expression Language version and the equivalent SQL statement.</p>
    <h3>Installing MySQL</h3>
    <p>Check to see if your system already has MySQL by running the following command in your terminal:</p>
    <pre>$ mysql --version</pre>
    <p>If you see a version number, you can skip to the “Creating a Database” section. If the command was not found, you’ll need to install MySQL. With the large variety of different operating systems out there, I’ll defer to Google to provide installation instructions that work for your OS. The installation usually consists of running a command or an executable. For example, the Linux command is:</p>
    <pre>$ sudo apt-get install mysql-server mysql-client</pre>
    <h3>Creating a Database</h3>
    <p>Once MySQL is installed, create a database for your app called ‘<code>development</code>‘. You can do this from a web interface like <a href="http://www.phpmyadmin.net/home_page/index.php" rel="nofollow external" class="bo">phpMyAdmin</a> or from the command line, as shown below:</p>
    <pre>$ mysql -u username -p&#x000A;    Enter password:&#x000A;    &#x000A;    mysql&gt; CREATE DATABASE development;</pre>
    <h3>Installing Flask-SQLAlchemy</h3>
    <p>Inside the isolated development environment, install Flask-SQLAlchemy.</p>
    <pre>$ pip install flask-sqlalchemy</pre>
    <p>When I tried to install Flask-SQLAlchemy, I received an error stating that the installation had failed. I searched the error and found that others had resolved the problem by installing <code>libmysqlclient15-dev</code>, which installs MySQL’s development files. If your Flask-SQLAlchemy installation fails, Google the error for solutions or leave a comment and we’ll try to help you figure it out.</p>
    <h3>Configuring Flask-SQLAlchemy</h3>
    <p>Just as we did with Flask-Mail, we need to configure Flask-SQLAlchemy so that it knows where the <code>development</code> database lives. First, create a new file named <code>models.py</code>, along with adding in the following code</p>
    <p><code>app/intro_to_flask/models.py</code></p>
    <pre>from flask.ext.sqlalchemy import SQLAlchemy&#x000A;    &#x000A;    db = SQLAlchemy()</pre>
    <p>Here we import the <code>SQLAlchemy</code> class from Flask-SQLAlchemy (line one) and create a variable named <code>db</code>, containing a usable instance of the <code>SQLAlchemy</code> class (line three).</p>
    <p>Next, open <code>__init__.py</code> and add the following lines after <code>mail.init_app(app)</code> and before <code>import intro_to_flask.routes</code>.</p>
    <p><code>app/intro_to_flask/__init__.py</code></p>
    <pre>app.config['SQLALCHEMY_DATABASE_URI'] = '<a href="mysql://your-username:your-password@localhost/development">mysql://your-username:your-password@localhost/development</a>'&#x000A;    &#x000A;    from models import db&#x000A;    db.init_app(app)</pre>
    <p>Let’s go over this:</p>
    <ol>
    <li>Line one tells the Flask app to use the ‘<code>development</code>‘ database. We specify this through a data URI which follows the pattern of: <code><a href="mysql://username:password@server/database">mysql://username:password@server/database</a></code>. The server is ‘localhost’ because we’re developing locally. Make sure to fill in your MySQL <code>username</code> and <code>password</code>.</li>
    <li>
    <code>db</code>, the usable instance of the <code>SQLAlchemy</code> class we created in <code>models.py</code>, still doesn’t know what database to use. So we import it from <code>models.py</code> (line three) and bind it to our app (line four), so that it also knows to use the ‘<code>development</code>‘ database. We can now query the ‘<code>development</code>‘ database through our <code>db</code> object.</li>
    </ol>
    <p>Now that our configuration is complete, let’s ensure that everything works. Open <code>routes.py</code> and create a temporary URL mapping so that we can perform a test query.</p>
    <p><code>app/intro-to-flask/routes.py</code></p>
    <pre>from intro_to_flask import app&#x000A;    from flask import Flask, render_template, request, flash, session, redirect, url_for&#x000A;    from forms import ContactForm, SignupForm, SigninForm&#x000A;    from flask.ext.mail import Message, Mail&#x000A;    from models import db&#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    @app.route('/testdb')&#x000A;    def testdb():&#x000A;      if db.session.query("1").from_statement("SELECT 1").all():&#x000A;        return 'It works.'&#x000A;      else:&#x000A;        return 'Something is broken.'</pre>
    <p>First we import the database object (<code>db</code>) from <code>models.py</code> (line five). We then create a temporary URL mapping (lines 9-14) wherein we issue a test query to ensure that the Flask app is connected to the ‘<code>development</code>‘ database. Now when we visit the URL <code>/testdb</code>, a test query will be issued (line 11); this is equivalent to the SQL statement <code>SELECT 1;</code>. If all goes well, we’ll see “<strong>It works</strong>” in the browser. Otherwise, we’ll see an error message stating what went wrong.</p>
    <p>I received an error when I visited the <code>/testdb</code> URL: <code>ImportError: No module named MySQLdb</code>. This meant that I didn’t have the mysql-python library installed, so I tried to install it by typing the following:</p>
    <pre>$ pip install mysql-python</pre>
    <p>That installation failed, too. The new error message suggested that I first run <code>easy_install -U distribute</code> and then try the mysql-python installation again. So I did, just like below:</p>
    <pre>$ easy_install -U distribute&#x000A;    $ pip install mysql-python</pre>
    <p>This time the mysql-python installation succeeded, and then I received the “<strong>It works</strong>” success message in the browser. Now the reason I’m recounting the errors I’ve received and what I did to solve them is because installing and connecting to databases can be a tricky process. If you get an error message, please don’t get discouraged. Google the error message or leave a comment, and we’ll figure it out.</p>
    <p>Once the test query works, delete the temporary URL mapping from <code>routes.py</code>. Make sure to retain the “<code>from models import db</code>“” part, because we’ll need it next.</p>
    <p>— <code>Checkpoint: 14_db_config</code> —</p>
    <hr>
    <h2>Create a User Model</h2>
    <blockquote><p>It’s not a good idea to store passwords in plain text, for security reasons.</p></blockquote>
    <p>Inside the ‘<code>development</code>‘ database, we need to create a users table where we can store each user’s information. The information we want to collect and store are the user’s first name, last name, email, and password.</p>
    <p>It’s not a good idea to store passwords in plain text, for security reasons. If an attacker gains access to your database, they would be able to see each user’s login credentials. One way to defend against such an attack is to encrypt passwords with a hash function and a <code>salt</code> (some random data), and store that encrypted value in the database instead of the plain text password. When a user signs in again, we’ll collect the password that was submitted, hash it, and check if it matches the hash in the database. Werkzeug, the utility library on which Flask is built, provides the functions <code>generate_password_hash</code> and <code>check_password_hash</code> for these two tasks, respectively.</p>
    <p>With this in mind, here are the columns we’ll need for the users table:</p>
    <table>
    <thead><tr>
    <td><strong>Column</strong></td>
    <td><strong>Type</strong></td>
    <td><strong>Constraints</strong></td>
    </tr></thead>
    <tbody>
    <tr>
    <td>uid</td>
    <td>int</td>
    <td>Primary Key, Auto Increment</td>
    </tr>
    <tr>
    <td>firstname</td>
    <td>varchar(100)</td>
    </tr>
    <tr>
    <td>lastname</td>
    <td>varchar(100)</td>
    </tr>
    <tr>
    <td>email</td>
    <td>varchar(120)</td>
    <td>Unique</td>
    </tr>
    <tr>
    <td>password</td>
    <td>varchar(54)</td>
    </tr>
    </tbody>
    </table>
    <p>Just like before, you can create this table from a web interface such as phpMyAdmin or from the command line, as shown below:</p>
    <pre>mysql&gt; CREATE TABLE users (&#x000A;    uid INT NOT NULL PRIMARY KEY AUTO_INCREMENT,&#x000A;    firstname VARCHAR(100) NOT NULL,&#x000A;    lastname VARCHAR(100) NOT NULL,&#x000A;    email VARCHAR(120) NOT NULL UNIQUE,&#x000A;    pwdhash VARCHAR(100) NOT NULL&#x000A;    );</pre>
    <p>Next, in <code>models.py</code>, let’s create a class to model a user with attributes for a user’s first name, last name, email, and password.</p>
    <p><code>app/intro_to_flask/models.py</code></p>
    <pre>from flask.ext.sqlalchemy import SQLAlchemy&#x000A;    from werkzeug import generate_password_hash, check_password_hash&#x000A;    &#x000A;    db = SQLAlchemy()&#x000A;    &#x000A;    class User(db.Model):&#x000A;      __tablename__ = 'users'&#x000A;      uid = db.Column(db.Integer, primary_key = True)&#x000A;      firstname = db.Column(db.String(100))&#x000A;      lastname = db.Column(db.String(100))&#x000A;      email = db.Column(db.String(120), unique=True)&#x000A;      pwdhash = db.Column(db.String(54))&#x000A;      &#x000A;      def __init__(self, firstname, lastname, email, password):&#x000A;        self.firstname = firstname.title()&#x000A;        self.lastname = lastname.title()&#x000A;        self.email = email.lower()&#x000A;        self.set_password(password)&#x000A;        &#x000A;      def set_password(self, password):&#x000A;        self.pwdhash = generate_password_hash(password)&#x000A;      &#x000A;      def check_password(self, password):&#x000A;        return check_password_hash(self.pwdhash, password)</pre>
    <blockquote><p>We use the set_password() function to set a salted hash of the password, instead of using the plain text password itself.</p></blockquote>
    <p>Lines one and four already existed in <code>models.py</code>, so we’ll start on line two by importing the <code>generate_password_hash</code> and <code>check_password_hash</code> security functions from Werkzeug. Next, we create a new class named <code>User</code>, inheriting from the database object <code>db</code>‘s <code>Model</code> class (line six.)</p>
    <p>Inside of our <code>User</code> class, we create attributes for the table’s name, primary key, and the user’s first name, last name, email, and password (lines 10-14). We then write a constructor which sets the class attributes (lines 17-20). We save names in title case and email addresses in lowercase to ensure a match regardless of how a user types in his credentials on subsequent sign ins.</p>
    <p>We use the <code>set_password</code> function (lines 22-23) to set a salted hash of the password, instead of using the plain text password itself. Lastly, we have a function named <code>check_password</code> that uses <code>check_password_hash</code>, to check a user’s credentials on any subsequent sign ins (lines 25-26).</p>
    <p>— <code>Checkpoint: 15_user_model</code> —</p>
    <p>Sweet! We’ve created a users table and a user model, thereby laying down the foundation of our authentication system. Now let’s build the first user-facing component of the authentication system: the signup page.</p>
    <hr>
    <h2>Building a Signup Page</h2>
    <h3>Planning</h3>
    <p>Take a look at <code>Fig. 1</code> below, to see how everything will fit together.</p>  <img src="http://cdn.tutsplus.com/net.tutsplus.com/authors/lalith-polepeddi/intro-to-flask-p3-fig1.png" alt="The Sign up process." style="max-width: 100%; height: auto;"><p><code>Fig. 1</code></p> <blockquote><p>Implement SSL site-wide so that passwords and session tokens cannot be intercepted.</p></blockquote>
    <p>Let’s go over the figure from above:</p>
    <ol>
    <li>A user visits the URL <code>/signup</code> to create a new account. The page is retrieved through an HTTP GET request and loads in the browser.</li>
    <li>The user fills in the form fields with his first name, last name, email, and password.</li>
    <li>The user clicks the “Create account” button, and the form submits to the server with an HTTP POST request.</li>
    <li>On the server, a function validates the form data.</li>
    <li>If one or more fields do not pass validation, the signup page reloads with a helpful error message, prompting the the user to try again.</li>
    <li>If all fields are valid, a new <code>User</code> object will be created and saved into the database. The user will then be signed in and redirected to a profile page.</li>
    </ol>
    <p>This sequence of steps should look familiar, as it’s identical to the sequence of steps we took to create a contact form. Here, instead of sending an email at the end, we save a user’s credentials to the database. The <a href="http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-adding-a-contact-page/" rel="nofollow external" class="bo">previous article</a> already explained creating a form in detail, I’ll move more quickly in this section so that we can get to the more exciting parts, faster.</p>
    <h3>Creating a Signup Form</h3>
    <p>We installed Flask-WTF in the <a href="http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-adding-a-contact-page/" rel="nofollow external" class="bo">previous article</a>, so let’s proceed with creating a new form inside <code>forms.py</code>.</p>
    <p><code>app/intro_to_flask/forms.py</code></p>
    <pre>from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError, PasswordField&#x000A;    from models import db, User&#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    class SignupForm(Form):&#x000A;      firstname = TextField("First name",  [validators.Required("Please enter your first name.")])&#x000A;      lastname = TextField("Last name",  [validators.Required("Please enter your last name.")])&#x000A;      email = TextField("Email",  [validators.Required("Please enter your email address."), validators.Email("Please enter your email address.")])&#x000A;      password = PasswordField('Password', [validators.Required("Please enter a password.")])&#x000A;      submit = SubmitField("Create account")&#x000A;    &#x000A;      def __init__(self, *args, **kwargs):&#x000A;        Form.__init__(self, *args, **kwargs)&#x000A;    &#x000A;      def validate(self):&#x000A;        if not Form.validate(self):&#x000A;          return False&#x000A;        &#x000A;        user = User.query.filter_by(email = self.email.data.lower()).first()&#x000A;        if user:&#x000A;          self.email.errors.append("That email is already taken")&#x000A;          return False&#x000A;        else:&#x000A;          return True</pre>
    <p>We start by importing one more Flask-WTF class named <code>PasswordField</code> (line one), which is like <code>TextField</code> except that it generates a password textbox. We’ll need the <code>db</code> database object and the <code>User</code> model to handle some custom validation logic inside the <code>SignupForm</code> class; so we import them too (line two).</p>
    <p>Then we create a new class named <code>SignupForm</code> containing a field for each piece of user information we wish to collect (lines 7-11). There’s a presence validator on each field to ensure it’s filled in, and a format validator which requires that email addresses match the pattern: <code><a href="mailto:user@example.com">user@example.com</a></code>.</p>
    <p>Next, we write a simple constructor for the class that just calls the base class’ constructor (lines 13-14).</p>
    <p>So we’ve added some presence and format validators to our form fields, but we need an additional validator that ensures an account does not already exist with the user’s email address. To do this we hook into Flask-WTF’s validation process (lines 16-25).</p>
    <p>Now inside of the <code>validate()</code> function, we first ensure the presence and format validators run by calling the base class’ <code>validate()</code> method; if the form is not filled in properly, <code>validate()</code> returns <code>False</code> (lines 16-17).</p>
    <p>Next we define the custom validator. We start by querying the database with the email that the user submitted (line 18). If you remember from our <code>models.py</code> file, the email address is converted to lowercase to ensure a match regardless of how it was typed in. This Flask-SQLAlchemy expression corresponds to the following SQL statement:</p>
    <pre>SELECT * FROM users &#x000A;      WHERE email = self.email.data.lower() &#x000A;      LIMIT 1</pre>
    <p>If a user record already exists with the submitted email, validation fails giving the following error message: “<strong>That email is already taken</strong>” (lines 21-22).</p>
    <h3>Using the Signup Form</h3>
    <p>Let’s now create a new URL mapping and a new web template for the signup form. Open <code>routes.py</code> and import the newly created signup form so that we can use it.</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>from intro_to_flask import app&#x000A;    from flask import render_template, request, flash&#x000A;    from forms import ContactForm, SignupForm</pre>
    <p>Next, create a new URL mapping.</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>@app.route('/signup', methods=['GET', 'POST'])&#x000A;    def signup():&#x000A;      form = SignupForm()&#x000A;      &#x000A;      if request.method == 'POST':&#x000A;        if form.validate() == False:&#x000A;          return render_template('signup.html', form=form)&#x000A;        else:   &#x000A;          return "[1] Create a new user [2] sign in the user [3] redirect to the user's profile"&#x000A;      &#x000A;      elif request.method == 'GET':&#x000A;        return render_template('signup.html', form=form)</pre>
    <p>Inside the <code>signup()</code> function, we create a variable named <code>form</code> that contains a usable instance of the <code>SignupForm</code> class. If a GET request has been issued, we’ll return the <code>signup.html</code> web template containing the signup form for the user to fill out.</p>
    <p>Otherwise, we’ll see just a temporary placeholder string. For now, the temp string lists the three actions that should take place when the form has been successfully submitted. We’ll come back and replace this string with real code in “The First Signup” section below.</p>
    <p>Now that we’ve created a URL mapping, the next step is to create the web template <code>signup.html</code> and place it inside the <code>templates/</code> folder.</p>
    <p><code>app/intro_to_flask/templates/signup.html</code></p>
    <pre>{% extends "layout.html" %}&#x000A;    &#x000A;    {% block content %}&#x000A;      &lt;h2&gt;Sign up&lt;/h2&gt;&#x000A;    &#x000A;      {% for message in form.firstname.errors %}&#x000A;        &lt;div class="flash"&gt;{{ message }}&lt;/div&gt;&#x000A;      {% endfor %}&#x000A;      &#x000A;      {% for message in form.lastname.errors %}&#x000A;        &lt;div class="flash"&gt;{{ message }}&lt;/div&gt;&#x000A;      {% endfor %}&#x000A;      &#x000A;      {% for message in form.email.errors %}&#x000A;        &lt;div class="flash"&gt;{{ message }}&lt;/div&gt;&#x000A;      {% endfor %}&#x000A;      &#x000A;      {% for message in form.password.errors %}&#x000A;        &lt;div class="flash"&gt;{{ message }}&lt;/div&gt;&#x000A;      {% endfor %}&#x000A;      &#x000A;      &lt;form action="{{ url_for('signup') }}" method=post&gt;&#x000A;        {{ form.hidden_tag() }}&#x000A;        &#x000A;        {{ form.firstname.label }}&#x000A;        {{ form.firstname }}&#x000A;        &#x000A;        {{ form.lastname.label }}&#x000A;        {{ form.lastname }}&#x000A;        &#x000A;        {{ form.email.label }}&#x000A;        {{ form.email }}&#x000A;        &#x000A;        {{ form.password.label }}&#x000A;        {{ form.password }}&#x000A;        &#x000A;        {{ form.submit }}&#x000A;      &lt;/form&gt;&#x000A;        &#x000A;    {% endblock %}</pre>
    <p>This template looks just like <code>contact.html</code>. We first loop through and display any error messages if necessary. We then let Jinja2 generate most of the HTML form for us. Remember how in the <code>Signup</code> form class we appended the error message “<strong>That email is already taken</strong>” to <code>self.email.errors</code>? That’s the same object that Jinja2 loops through in this template.</p>
    <p>The one difference from the <code>contact.html</code> template is the omission of the <code>if...else</code> logic.</p>
    <p>In this template, we want to register and sign in the user on a successful form submission. This takes place on the back-end, so the <code>if...else</code> statement is not needed here.</p>
    <p>Finally, add in these CSS rules to your <code>main.css</code> file so that the signup form looks nice and pretty.</p>
    <p><code>app/intro_to_flask/static/css/main.css</code></p>
    <pre>/* Signup form */&#x000A;    form input#firstname,&#x000A;    form input#lastname,&#x000A;    form input#password {&#x000A;      width: 400px;&#x000A;      background-color: #fafafa;&#x000A;      -webkit-border-radius: 3px;&#x000A;         -moz-border-radius: 3px;&#x000A;              border-radius: 3px;&#x000A;      border: 1px solid #cccccc;&#x000A;      padding: 5px;&#x000A;      font-size: 1.1em;&#x000A;    }&#x000A;    &#x000A;    form input#password {&#x000A;      margin-bottom: 10px;&#x000A;    }</pre>
    <p>Let’s check out the newly created signup page by typing:</p>
    <pre>$ cd app/&#x000A;    $ python runserver.py</pre>
    <p>And browse to <a href="http://localhost:5000/signup" rel="nofollow external" class="bo">http://localhost:5000/signup</a> in your favorite web browser.</p>  <img src="http://cdn.tutsplus.com/net.tutsplus.com/authors/lalith-polepeddi/intro-to-flask-p3-signup.png" alt="The sign up page." style="max-width: 100%; height: auto;"><br> <p>Excellent! We just created a signup form from scratch, handled complex validation, and created a good looking signup page with helpful error messages.</p>
    <p>— <code>Checkpoint: 16_signup_form</code> —</p>
    <p>If any of these steps were unclear, please take a moment to review the <a href="http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-adding-a-contact-page/" rel="nofollow external" class="bo">previous article</a>. It covers each step in greater detail, and I followed the same steps from that article, to create this signup form.</p>
    <hr>
    <h2>The First Signup</h2>
    <p>Let’s start by replacing the temporary placeholder string in <code>routes.py</code>‘s <code>signup()</code> function with some real code. Upon a successful form submission, we need to create a new <code>User</code> object, save it to the database, sign the user in, and redirect to the user’s profile page. Let’s take this step by step, starting with creating a new <code>User</code> object and saving it to the database.</p>
    <h3>Saving a New User Object</h3>
    <p>Add in lines five and 17-19 to</p>
    <p><code>routes.py.</code></p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>from intro_to_flask import app&#x000A;    from flask import render_template, request, flash, session, url_for, redirect&#x000A;    from forms import ContactForm, SignupForm&#x000A;    from flask.ext.mail import Message, Mail&#x000A;    from models import db, User&#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    @app.route('/signup', methods=['GET', 'POST'])&#x000A;    def signup():&#x000A;      form = SignupForm()&#x000A;      &#x000A;      if request.method == 'POST':&#x000A;        if form.validate() == False:&#x000A;          return render_template('signup.html', form=form)&#x000A;        else:&#x000A;          newuser = User(form.firstname.data, form.lastname.data, form.email.data, form.password.data)&#x000A;          db.session.add(newuser)&#x000A;          db.session.commit()&#x000A;          &#x000A;          return "[1] Create a new user [2] sign in the user [3] redirect to the user's profile"&#x000A;      &#x000A;      elif request.method == 'GET':&#x000A;        return render_template('signup.html', form=form)</pre>
    <p>First, we import the <code>User</code> class from models.py so that we can use it in the <code>signup()</code> function (line five). Then we create a new <code>User</code> object called <code>newuser</code> and populate it with the signup form's field data (line 17).</p>
    <p>Next, we add <code>newuser</code> to the database object's session (line 18), which is Flask-SQLAlchemy's version of a regular database transaction. The <code>add()</code> function generates an <code>INSERT</code> statement using the <code>User</code> object's attributes. The equivalent SQL for this Flask-SQLAlchemy expression is:</p>
    <pre>  INSERT INTO users (firstname, lastname, email, pwdhash)&#x000A;      VALUES (form.firstname.data, form.lastname.data, form.email.data, form.password.data)  </pre>
    <p>Lastly, we update the database with the new user record by committing the transaction (line 19).</p>
    <h3>Signing in the User</h3>
    <p>Next, we need to sign in the user. The Flask app needs to know that subsequent page requests are coming from the browser of the user who has successfully signed up. We can accomplish this by setting a cookie in the user's browser containing some sort of ID and associating that key with the user's credentials in the Flask app.</p>
    <p>This way, the ID in the browser's cookie will be passed to the app on each subsequent page request, and the app will look up the ID to determine whether it maps to valid user credentials.</p>
    <p>If it does, the app allows access to the parts of the website that you need to be signed in for. This combination of having a key stored on the client and a value stored on the server is called a session.</p>
    <p>Flask has a <code>session</code> object that accomplishes this functionality. It stores the session key in a secure cookie on the client and the session value in the app. Let's use it in our <code>signup()</code> function.</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>from flask import render_template, request, flash, session&#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    @app.route('/signup', methods=['GET', 'POST'])&#x000A;    def signup():&#x000A;      form = SignupForm()&#x000A;      &#x000A;      if request.method == 'POST':&#x000A;        if form.validate() == False:&#x000A;          return render_template('signup.html', form=form)&#x000A;        else:&#x000A;          newuser = User(form.firstname.data, form.lastname.data, form.email.data, form.password.data)&#x000A;          db.session.add(newuser)&#x000A;          db.session.commit()&#x000A;          &#x000A;          session['email'] = newuser.email&#x000A;          &#x000A;          return "[1] Create a new user [2] sign in the user [3] redirect to the user's profile"&#x000A;      &#x000A;      elif request.method == 'GET':&#x000A;        return render_template('signup.html', form=form)</pre>
    <p>We start by importing Flask's <code>session</code> object on line one. Next, we associate the key '<code>email</code>' with the value of the newly registered user's email (line 17). The <code>session</code> object will take care of hashing '<code>email</code>' into an excrypted ID and storing it in a cookie on the user's browser. At this point, the user is signed in to our app.</p>
    <h3>Redirecting to a Profile page</h3>
    <p>The last step is to redirect the user to a Profile page after signing in. We'll use the <code>url_for</code> function (which we've seen in <code>layout.html</code> and <code>contact.html</code>) in conjunction with Flask's <code>redirect()</code> function.</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>from intro_to_flask import app&#x000A;    from flask import render_template, request, flash, session, url_for, redirect&#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    @app.route('/signup', methods=['GET', 'POST'])&#x000A;    def signup():&#x000A;      form = SignupForm()&#x000A;      &#x000A;      if request.method == 'POST':&#x000A;        if form.validate() == False:&#x000A;          return render_template('signup.html', form=form)&#x000A;        else:&#x000A;          newuser = User(form.firstname.data, form.lastname.data, form.email.data, form.password.data)&#x000A;          db.session.add(newuser)&#x000A;          db.session.commit()&#x000A;          &#x000A;          session['email'] = newuser.email&#x000A;          return redirect(url_for('profile'))&#x000A;      &#x000A;      elif request.method == 'GET':&#x000A;        return render_template('signup.html', form=form)&#x000A;    </pre>
    <p>on line two, we import Flask's <code>url_for()</code> and <code>redirect()</code> functions. Then on line 19, we replace our temporary placeholder string with a redirect to the URL <code>/profile</code>. We don't have a URL mapping for <code>/profile</code> yet, so let's create that next.</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>@app.route('/profile')&#x000A;    def profile():&#x000A;    &#x000A;      if 'email' not in session:&#x000A;        return redirect(url_for('signin'))&#x000A;    &#x000A;      user = User.query.filter_by(email = session['email']).first()&#x000A;    &#x000A;      if user is None:&#x000A;        return redirect(url_for('signin'))&#x000A;      else:&#x000A;        return render_template('profile.html')&#x000A;    </pre>
    <p>Here we can finally see sessions in action. We start on line four by fetching the browser's cookie and checking if it contains a key named '<code>email</code>'. If it doesn't exist, that means the user is not authenticated, so we redirect the user to a signin page (we'll create this in the next section).</p>
    <p>If the '<code>email</code>' key does exist, we look up the server-side user email value associated with the key using <code>session['email']</code>, and then query the database for a registered user with this same email address (line seven). The equivalent SQL for this Flask-SQLAlchemy expression is:</p>
    <pre>SELECT * FROM users WHERE email = session['email'];</pre>
    <p>If no registered user exists, we'll redirect to the signup page. Otherwise, we render the <code>profile.html</code> template. Let's create <code>profile.html</code> now.</p>
    <p><code>app/intro_to_flask/templates/profile.html</code></p>
    <pre>{% extends "layout.html" %}&#x000A;    {% block content %}&#x000A;      &lt;div class="jumbo"&gt;&#x000A;        &lt;h2&gt;Profile&lt;h2&gt;&#x000A;        &lt;h3&gt;This is {{ session['email'] }}'s profile page&lt;h3&gt;&#x000A;      &lt;/div&gt;&#x000A;    {% endblock %}</pre>
    <p>I've kept this profile template simple. If we focus in on line five — you'll see that we can use Flask's <code>session</code> object inside Jinja2 templates. Here, I've used it to create a user-specific string, but you could use this ability to pull other types of user-specific information instead.</p>
    <p>We're finally ready to see the result of all our hard work. Type the following into your terminal:</p>
    <pre>$ python runserver.py</pre>
    <p>Go to <a href="http://localhost:5000/signup" rel="nofollow external" class="bo">http://localhost:5000/</a> in your favorite web browser, and complete the sign up process. You should be greeted with a profile page that looks like the following screenshot:</p>  <img src="http://cdn.tutsplus.com/net.tutsplus.com/authors/lalith-polepeddi/intro-to-flask-p3-profile.png" alt="A successful sign up!" style="max-width: 100%; height: auto;"><br> <p>Signing up users is a huge milestone for our app. We can adapt the code in our <code>/signup()</code> function and round out our authentication system by allowing users to sign in and out of the app.</p>
    <p>— <code>Checkpoint: 17_profile_page</code> —</p>
    <hr>
    <h2>Building a Signin Page</h2>
    <p>Creating a signin page is similar to creating a signup page — we'll need to create a signin form, a URL mapping, and a web template. Let's start by creating the <code>SigninForm</code> class in <code>forms.py</code>.</p>
    <p><code>app/intro_to_flask/forms.py</code></p>
    <pre>class SigninForm(Form):&#x000A;      email = TextField("Email",  [validators.Required("Please enter your email address."), validators.Email("Please enter your email address.")])&#x000A;      password = PasswordField('Password', [validators.Required("Please enter a password.")])&#x000A;      submit = SubmitField("Sign In")&#x000A;      &#x000A;      def __init__(self, *args, **kwargs):&#x000A;        Form.__init__(self, *args, **kwargs)&#x000A;    &#x000A;      def validate(self):&#x000A;        if not Form.validate(self):&#x000A;          return False&#x000A;        &#x000A;        user = User.query.filter_by(email = self.email.data.lower()).first()&#x000A;        if user and user.check_password(self.password.data):&#x000A;          return True&#x000A;        else:&#x000A;          self.email.errors.append("Invalid e-mail or password")&#x000A;          return False</pre>
    <p>The <code>SigninForm</code> class is similar to the <code>SignupForm</code> class. To sign a user in, we need to capture their email and password, so we create those two fields with presence and format validators (lines 2-3). Then we define our custom validator inside the <code>validate()</code> function (lines 10-15). This time the validator needs to make sure the user exists in the database and has the correct password. If a record does exist with the supplied information, we check to see if the password matches (line 14). If it does, the validation check passes (line 15), otherwise they get an error message.</p>
    <p>Next, let's create a URL mapping in <code>routes.py</code>.</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>...&#x000A;    from forms import ContactForm, SignupForm, SigninForm&#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    @app.route('/signin', methods=['GET', 'POST'])&#x000A;    def signin():&#x000A;      form = SigninForm()&#x000A;      &#x000A;      if request.method == 'POST':&#x000A;        if form.validate() == False:&#x000A;          return render_template('signin.html', form=form)&#x000A;        else:&#x000A;          session['email'] = form.email.data&#x000A;          return redirect(url_for('profile'))&#x000A;                    &#x000A;      elif request.method == 'GET':&#x000A;        return render_template('signin.html', form=form) &#x000A;    </pre>
    <p>Once again, the <code>signin()</code> function is similar to the <code>signup()</code> function. We import <code>SigninForm</code> (line two), so that we can use it in the <code>signin()</code> function. Then in <code>signin()</code>, we return the <code>signin.html</code> template for GET requests (lines 17-18).</p>
    <p>If the form has been POSTed and any validation check fails, the signin form reloads with a helpful error message (lines 11-12). Otherwise, we sign in the user by creating a new session and redirecting to their profile page (lines 14-15).</p>
    <p>Lastly, let's create the web template <code>signin.html</code>.</p>
    <p><code>app/intro_to_flask/templates/signin.html</code></p>
    <pre>{% extends "layout.html" %}&#x000A;    &#x000A;    {% block content %}&#x000A;      &lt;h2&gt;Sign In&lt;/h2&gt;&#x000A;    &#x000A;      {% for message in form.email.errors %}&#x000A;        &lt;div class="flash"&gt;{{ message }}&lt;/div&gt;&#x000A;      {% endfor %}&#x000A;      &#x000A;      {% for message in form.password.errors %}&#x000A;        &lt;div class="flash"&gt;{{ message }}&lt;/div&gt;&#x000A;      {% endfor %}&#x000A;      &#x000A;      &lt;form action="{{ url_for('signin') }}" method=post&gt;&#x000A;        {{ form.hidden_tag() }}&#x000A;        &#x000A;        {{ form.email.label }}&#x000A;        {{ form.email }}&#x000A;        &#x000A;        {{ form.password.label }}&#x000A;        {{ form.password }}&#x000A;        &#x000A;        {{ form.submit }}&#x000A;      &lt;/form&gt;&#x000A;        &#x000A;    {% endblock %}</pre>
    <p>Similar to <code>signup.html</code> template, we first loop through and display any error messages, then we let Jinja2 generate the form for us.</p>
    <p>And that does it for the signin page. Visit <a href="http://localhost:5000/signin" rel="nofollow external" class="bo">http://localhost:5000/signin</a> to check it out. Go ahead and sign in, you should get redirected to your profile page.</p>  <img src="http://cdn.tutsplus.com/net.tutsplus.com/authors/lalith-polepeddi/intro-to-flask-p3-signin.png" alt="A successful sign in." style="max-width: 100%; height: auto;"><br> <p>— <code>Checkpoint: 18_signin_form</code> —</p>
    <hr>
    <h2>Signing Out</h2>
    <p>In "The First Signup" section above, we saw that "signing in" meant setting a cookie in the user's browser containing an ID and associating that ID with the user's data in the Flask app. Therefore, "signing out" means clearing the cookie in the browser and dissociating the user data.</p>
    <p>This can be accomplished in one line: <code>session.pop('email', None)</code>.</p>
    <p>We don't need a form or even a web template to sign out. All we need is a URL mapping in <code>routes.py</code>, which terminates the session and redirects to the Home page. The mapping, therefore, is short and sweet:</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>@app.route('/signout')&#x000A;    def signout():&#x000A;    &#x000A;      if 'email' not in session:&#x000A;        return redirect(url_for('signin'))&#x000A;        &#x000A;      session.pop('email', None)&#x000A;      return redirect(url_for('home'))</pre>
    <p>The user is not authenticated if the browser's cookie does not contain a key named '<code>email</code>', in that case, we just redirect to the signin page (lines 4-5). Otherwise, we terminate the session (line seven) and redirect back to the home page (line eight).</p>
    <p>You can test the sign out functionality by visiting <a href="http://localhost:5000/signout" rel="nofollow external" class="bo">http://localhost:5000/signout</a>. If you're signed in, the app will sign you out and redirect to <a href="http://localhost:5000/" rel="nofollow external" class="bo">http://localhost:5000/</a>. Once you've signed out, try visiting the profile page <a href="http://localhost:5000/profile" rel="nofollow external" class="bo">http://localhost:5000/profile</a>. You shouldn't be allowed to see the profile page if you're not signed in, and the app should redirect you back to the Signin page.</p>
    <p>— <code>Checkpoint: 19_signout</code> —</p>
    <hr>
    <h2>Tidying Up</h2>
    <p>Now we need to update the site header with navigation links for "Sign Up", "Sign In", "Profile", and "Sign Out". The links should change based on whether the user is signed in or not. If the user is signed out, links for "Sign Up" and "Sign In" should be visible. When the user is signed in, we want links for "Profile" and "Sign Out" to appear, while hiding the "Sign Up" and "Sign In" links.</p>
    <p>So how can we do this? Think back to the <code>profile.html</code> template where we used Flask's <code>session</code> object. We can use the <code>session</code> object to show navigation links based on the user's authentication status. Let's open <code>layout.html</code> and make the following changes:</p>
    <p><code>app/intro_to_flask/templates/layout.html</code></p>
    <pre>&lt;!DOCTYPE html&gt;&#x000A;    &lt;html&gt;&#x000A;      &lt;head&gt;&#x000A;        &lt;title&gt;Flask App&lt;/title&gt;&#x000A;        &lt;link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}"&gt;&#x000A;      &lt;/head&gt;&#x000A;      &lt;body&gt;&#x000A;    &#x000A;      &lt;header&gt;&#x000A;        &lt;div class="container"&gt;&#x000A;          &lt;h1 class="logo"&gt;Flask App&lt;/h1&gt;&#x000A;          &lt;nav&gt;&#x000A;            &lt;ul class="menu"&gt;&#x000A;              &lt;li&gt;&lt;a href="{{ url_for('home') }}"&gt;Home&lt;/a&gt;&lt;/li&gt;&#x000A;              &lt;li&gt;&lt;a href="{{ url_for('about') }}"&gt;About&lt;/a&gt;&lt;/li&gt;&#x000A;              &lt;li&gt;&lt;a href="{{ url_for('contact') }}"&gt;Contact&lt;/a&gt;&lt;/li&gt;&#x000A;              {% if 'email' in session %}&#x000A;              &lt;li&gt;&lt;a href="{{ url_for('profile') }}"&gt;Profile&lt;/a&gt;&lt;/li&gt;&#x000A;              &lt;li&gt;&lt;a href="{{ url_for('signout') }}"&gt;Sign Out&lt;/a&gt;&lt;/li&gt;&#x000A;              {% else %}&#x000A;              &lt;li&gt;&lt;a href="{{ url_for('signup') }}"&gt;Sign Up&lt;/a&gt;&lt;/li&gt;&#x000A;              &lt;li&gt;&lt;a href="{{ url_for('signin') }}"&gt;Sign In&lt;/a&gt;&lt;/li&gt;&#x000A;              {% endif %}&#x000A;            &lt;/ul&gt;&#x000A;          &lt;/nav&gt;&#x000A;        &lt;/div&gt;&#x000A;      &lt;/header&gt;&#x000A;    &#x000A;        &lt;div class="container"&gt;&#x000A;          {% block content %}&#x000A;          {% endblock %}&#x000A;        &lt;/div&gt;&#x000A;    &#x000A;      &lt;/body&gt;&#x000A;    &lt;/html&gt;</pre>
    <p>Starting on line 17, we use Jinja2's <code>if...else</code> syntax and the <code>session()</code> object to check if the browser's cookie contains the '<code>email</code>' key. If it does, then the user is signed in and therefore should see the "Profile" and "Sign Out" navigation links. Otherwise, the user is signed out and should see links to "Sign Up" and "Sign In".</p>
    <p>Now give it a try! Check out how the navigation links appear and disappear by signing in and out of the app.</p>
    <p>The last task remaining is a similar issue: when a user is signed in, we don't want him to be able to visit the signup and signin pages. It makes no sense for a signed in user to authenticate themselves again. If signed in users try to visit these pages, they should instead be redirected to their profile page. Open <code>routes.py</code> and add the following piece of code to the beginning of the <code>signup()</code> and <code>signin()</code> functions:</p>
    <pre>if 'email' in session:&#x000A;      return redirect(url_for('profile'))&#x000A;    </pre>
    <p>Here's what your <code>routes.py</code> file will look like after adding in that snippet of code:</p>
    <p><code>app/intro_to_flask/routes.py</code></p>
    <pre>@app.route('/signup', methods=['GET', 'POST'])&#x000A;    def signup():&#x000A;      form = SignupForm()&#x000A;    &#x000A;      if 'email' in session:&#x000A;        return redirect(url_for('profile')) &#x000A;    .&#x000A;    .&#x000A;    .&#x000A;    @app.route('/signin', methods=['GET', 'POST'])&#x000A;    def signin():&#x000A;      form = SigninForm()&#x000A;    &#x000A;      if 'email' in session:&#x000A;        return redirect(url_for('profile'))  &#x000A;      ...&#x000A;    </pre>
    <p>And with that we're finished! Try visiting the the "signup" or "signin" pages while you are currently signed in, to test it out.</p>
    <p>— <code>Checkpoint: 20_visibility_control</code> —</p>
    <hr>
    <h2>Conclusion</h2>
    <p>We've accomplished a lot in this article. We've taken our Flask app from being a simple Python module and turned it into a well organized application, capable of handling user authentication.</p>
    <p>There are several directions in which you can take this app from here. Here are some ideas:</p>
    <ul>
    <li>Let users sign in with an existing account, such as their Google account, by adding support for OpenID.</li>
    <li>Give users the ability to update their account information, as well as delete their account.</li>
    <li>Let users reset their password if they forget it.</li>
    <li>Implement an authorization system.</li>
    <li>Deploy to a production server. Note that when you deploy this app to production, you will need to implement SSL site-wide so that passwords and session tokens cannot be intercepted. If you deploy to Heroku, you can use their SSL certificate.</li>
    </ul>
    <p>So go forth and continue to <a href="http://flask.pocoo.org/docs/" rel="nofollow external" class="bo">explore Flask</a>, and build your next killer app! Thanks for reading.</p>
    </div>
]]>
</Body>
<Summary>Many web applications require users to sign in and out in order to perform important tasks (like administration duties). In this article, we’ll create an authentication system for our application....</Summary>
<Website>http://feedproxy.google.com/~r/nettuts/~3/E4cf3iGOp7M/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/31231/guest@my.umbc.edu/389681dc0188bb2681eee0273ca5dd04/api/pixel</TrackingUrl>
<Tag>css</Tag>
<Tag>development</Tag>
<Tag>flask</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>php</Tag>
<Tag>python</Tag>
<Tag>sql</Tag>
<Tag>wed</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Wed, 12 Jun 2013 19:53:13 -0400</PostedAt>
<EditAt>Wed, 12 Jun 2013 19:53:13 -0400</EditAt>
</NewsItem>

</News>
