<?xml version="1.0"?>
<News hasArchived="true" page="7870" pageCount="10751" pageSize="10" timestamp="Sat, 15 Aug 2026 13:16:24 -0400" url="https://my3.my.umbc.edu/posts.xml?mode=activity&amp;page=7870">
<NewsItem contentIssues="false" id="40555" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40555">
<Title>Tom Cronin Guest Editor</Title>
<Tagline>Seeing and doing!</Tagline>
<Body>
<![CDATA[
    <div class="html-content">Tom Cronin was invited to be an editor of a special issue of "Philosophical Transactions of the Royal Society for January, 2014.  Read the article that he and Ronald H. Douglas, Department of Optometry and Visual Science, City University London, Northampton Square, London, UK wrote,  Seeing and doing:  how vision shapes animal behavior.<br>
    </div>
]]>
</Body>
<Summary>Tom Cronin was invited to be an editor of a special issue of "Philosophical Transactions of the Royal Society for January, 2014.  Read the article that he and Ronald H. Douglas, Department of...</Summary>
<Website>http://rstb.royalsocietypublishing.org/content/369/1636.toc</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40555/guest@my.umbc.edu/a0963f08169da113d58fbffd51b4c79b/api/pixel</TrackingUrl>
<Group token="biol">Department of Biological Sciences</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/biol</GroupUrl>
<AvatarUrl>https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xsmall.png?1681829319</AvatarUrl>
<AvatarUrl size="original">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/original.jpg?1681829319</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xxlarge.png?1681829319</AvatarUrl>
<AvatarUrl size="xlarge">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xlarge.png?1681829319</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/large.png?1681829319</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/medium.png?1681829319</AvatarUrl>
<AvatarUrl size="small">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/small.png?1681829319</AvatarUrl>
<AvatarUrl size="xsmall">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xsmall.png?1681829319</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xxsmall.png?1681829319</AvatarUrl>
<Sponsor>Department of Biological Sciences</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Thu, 23 Jan 2014 16:43:32 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="40556" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40556">
<Title>Microsoft Earnings Illustrate Move to Devices and Services From Software</Title>
<Body>
<![CDATA[
    <div class="html-content">The company said its second-quarter profit rose 3 percent, ahead of analysts’ forecasts, with strong sales of its Xbox consoles and Surface tablets.<br><div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fwww.nytimes.com%2F2014%2F01%2F24%2Ftechnology%2Fholiday-sales-help-push-profit-up-at-microsoft.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Microsoft+Earnings+Illustrate+Move+to+Devices+and+Services+From+Software" 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%2F2014%2F01%2F24%2Ftechnology%2Fholiday-sales-help-push-profit-up-at-microsoft.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Microsoft+Earnings+Illustrate+Move+to+Devices+and+Services+From+Software" 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%2F2014%2F01%2F24%2Ftechnology%2Fholiday-sales-help-push-profit-up-at-microsoft.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Microsoft+Earnings+Illustrate+Move+to+Devices+and+Services+From+Software" 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%2F2014%2F01%2F24%2Ftechnology%2Fholiday-sales-help-push-profit-up-at-microsoft.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Microsoft+Earnings+Illustrate+Move+to+Devices+and+Services+From+Software" 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%2F2014%2F01%2F24%2Ftechnology%2Fholiday-sales-help-push-profit-up-at-microsoft.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Microsoft+Earnings+Illustrate+Move+to+Devices+and+Services+From+Software" 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/186529567116/u/0/f/640387/c/34625/s/364746e8/sc/15/rc/1/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529567116/u/0/f/640387/c/34625/s/364746e8/sc/15/rc/1/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529567116/u/0/f/640387/c/34625/s/364746e8/sc/15/rc/2/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529567116/u/0/f/640387/c/34625/s/364746e8/sc/15/rc/2/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529567116/u/0/f/640387/c/34625/s/364746e8/sc/15/rc/3/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529567116/u/0/f/640387/c/34625/s/364746e8/sc/15/rc/3/rc.img" style="max-width: 100%; height: auto;"></a><br><br><a href="http://da.feedsportal.com/r/186529567116/u/0/f/640387/c/34625/s/364746e8/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529567116/u/0/f/640387/c/34625/s/364746e8/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>The company said its second-quarter profit rose 3 percent, ahead of analysts’ forecasts, with strong sales of its Xbox consoles and Surface tablets.      </Summary>
<Website>http://www.nytimes.com/2014/01/24/technology/holiday-sales-help-push-profit-up-at-microsoft.html?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40556/guest@my.umbc.edu/6d7a92f81748a64c5688b877e96515e1/api/pixel</TrackingUrl>
<Tag>company-reports</Tag>
<Tag>microsoft-corporation</Tag>
<Tag>new</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>Thu, 23 Jan 2014 16:41:06 -0500</PostedAt>
<EditAt>Thu, 23 Jan 2014 20:22:22 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="40560" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40560">
<Title>Neiman Marcus Data Breach Worse Than First Said</Title>
<Body>
<![CDATA[
    <div class="html-content">The theft of consumer data from the luxury retailer appears to have involved 1.1 million credit and debit cards.<br><div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fwww.nytimes.com%2F2014%2F01%2F24%2Fbusiness%2Fneiman-marcus-breach-affected-1-1-million-cards.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Neiman+Marcus+Data+Breach+Worse+Than+First+Said" 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%2F2014%2F01%2F24%2Fbusiness%2Fneiman-marcus-breach-affected-1-1-million-cards.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Neiman+Marcus+Data+Breach+Worse+Than+First+Said" 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%2F2014%2F01%2F24%2Fbusiness%2Fneiman-marcus-breach-affected-1-1-million-cards.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Neiman+Marcus+Data+Breach+Worse+Than+First+Said" 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%2F2014%2F01%2F24%2Fbusiness%2Fneiman-marcus-breach-affected-1-1-million-cards.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Neiman+Marcus+Data+Breach+Worse+Than+First+Said" 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%2F2014%2F01%2F24%2Fbusiness%2Fneiman-marcus-breach-affected-1-1-million-cards.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Neiman+Marcus+Data+Breach+Worse+Than+First+Said" 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/186529531978/u/0/f/640387/c/34625/s/3647f106/sc/2/rc/1/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529531978/u/0/f/640387/c/34625/s/3647f106/sc/2/rc/1/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529531978/u/0/f/640387/c/34625/s/3647f106/sc/2/rc/2/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529531978/u/0/f/640387/c/34625/s/3647f106/sc/2/rc/2/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529531978/u/0/f/640387/c/34625/s/3647f106/sc/2/rc/3/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529531978/u/0/f/640387/c/34625/s/3647f106/sc/2/rc/3/rc.img" style="max-width: 100%; height: auto;"></a><br><br><a href="http://da.feedsportal.com/r/186529531978/u/0/f/640387/c/34625/s/3647f106/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529531978/u/0/f/640387/c/34625/s/3647f106/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>The theft of consumer data from the luxury retailer appears to have involved 1.1 million credit and debit cards.      </Summary>
<Website>http://www.nytimes.com/2014/01/24/business/neiman-marcus-breach-affected-1-1-million-cards.html?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40560/guest@my.umbc.edu/8295fa6c3a96c8634ea4605679e66eed/api/pixel</TrackingUrl>
<Tag>bergdorf-goodman</Tag>
<Tag>computer-security</Tag>
<Tag>federal-bureau-of-investigation</Tag>
<Tag>mastercard-inc-ma-nyse</Tag>
<Tag>neiman-marcus-group</Tag>
<Tag>new</Tag>
<Tag>target-corporation-tgt-nyse</Tag>
<Tag>technology</Tag>
<Tag>visa-inc-v-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>Thu, 23 Jan 2014 16:37:21 -0500</PostedAt>
<EditAt>Thu, 23 Jan 2014 20:54:13 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="false" id="40554" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40554">
<Title>Lasse Lindahl &amp; Four Graduate Students Publish Paper in MBoC</Title>
<Tagline>First paper published on new project!</Tagline>
<Body>
<![CDATA[
    <div class="html-content">Congratulations to Mamata Thapa, Ananth Bommakanti, Md Shamsuzzaman, Brian Gregory, Leigh Samsel, Janice M. Zengel and Lasse Lindahl  for a paper published in The American Society for Cell Biology on a new project started several years ago on the requirement for ribosome biogenesis on cell cycle progression.  <br>
    </div>
]]>
</Body>
<Summary>Congratulations to Mamata Thapa, Ananth Bommakanti, Md Shamsuzzaman, Brian Gregory, Leigh Samsel, Janice M. Zengel and Lasse Lindahl  for a paper published in The American Society for Cell Biology...</Summary>
<Website>http://www.molbiolcell.org/content/early/2013/10/07/mbc.E13-02-0097.full.pdf+html</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40554/guest@my.umbc.edu/7b43a1237d79f1a521dd5a62f667cc7d/api/pixel</TrackingUrl>
<Group token="biol">Department of Biological Sciences</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/biol</GroupUrl>
<AvatarUrl>https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xsmall.png?1681829319</AvatarUrl>
<AvatarUrl size="original">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/original.jpg?1681829319</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xxlarge.png?1681829319</AvatarUrl>
<AvatarUrl size="xlarge">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xlarge.png?1681829319</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/large.png?1681829319</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/medium.png?1681829319</AvatarUrl>
<AvatarUrl size="small">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/small.png?1681829319</AvatarUrl>
<AvatarUrl size="xsmall">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xsmall.png?1681829319</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/430/6b175bb0e4c9635bddddedfbeb4f8c38/xxsmall.png?1681829319</AvatarUrl>
<Sponsor>Department of Biological Sciences</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Thu, 23 Jan 2014 16:31:16 -0500</PostedAt>
<EditAt>Thu, 23 Jan 2014 16:34:15 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="40557" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40557">
<Title>Product Analytics 101</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <p>Feedback loops are an essential component in product development because these loops provide regular opportunities to learn and adjust the product to have better fit. Qualitative feedback loops enable you to <a href="http://blog.teamtreehouse.com/make-better-product-decisions" rel="nofollow external" class="bo">better understand your customers</a>. Prototyping is another <a href="http://blog.teamtreehouse.com/prototype-prototypes" rel="nofollow external" class="bo">powerful feedback loop</a> in product development. As effective as these are, both qualitative feedback and prototyping are poorly suited to give feedback at scale—they are best done in one on one scenarios or small groups and often times in-person.</p>
    <p>However, one of the benefits to building products in the age of the internet is that we can do many things at scale that were previously not possible. This includes automated, remote, and statistically significant product feedback loops. This type of product feedback loop depends on having well-implemented data gathering and analytics.</p>
    <h2>What are analytics?</h2>
    <p>Product analytics are the computational analyses of data about a product. This can be passive data that is not explicitly under your control, like number of downloads or support requests. But for the duration of this article we will be looking at data that is generated from your product that you have explicitly decided to capture.</p>
    <h3>Data Events</h3>
    <p>Data events are the basic building block for most product analytics. A data event is any meaningful action or event that you would like to track or factor into some kind of analysis. Events are composed of a name, like <em>created-account</em>, and a date and time of occurrence. Most event tracking tools will also let you add custom attributes to events to capture other relevant state of the product when the event occurred.</p>
    <p>Choosing what data events to capture takes a little bit of foresight (and experience) to know what you might want to track. For this reason, it is often best to start with a small number of events and iteratively add more as you learn about what you want to know. Web analytics tools usually automatically track every URL loaded as a page view event; event tracking for native apps will most likely not have any built-in events.</p>
    <p>Segmentation, funnels, and cohorts are three important analytics concepts. You will probably get a better idea of the events you should capture in your product as we discuss these concepts.</p>
    <h3>Segmentation</h3>
    <p>Splitting event data into groups based on some particular characteristic is known as segmentation. There are two main types of segmentation: event and customer.</p>
    <p><img src="http://blog.teamtreehouse.com/wp-content/uploads/2014/01/event_segmentation.png" alt="Event Segmentation" width="1895" height="786" style="max-width: 100%; height: auto;"></p>
    <p>Event segmentation lets you understand different aspects of a particular event. For example, if you have a “save note” event with a custom attribute of “note length” that counts the number of words, you could break down the event into buckets of length to understand if your customers are typing long or short notes. You can leverage this type of analysis to inform future UI improvements or feature decisions.</p>
    <p><img src="http://blog.teamtreehouse.com/wp-content/uploads/2014/01/customer_segmentation.png" alt="Customer Segmentation" width="1887" height="963" style="max-width: 100%; height: auto;"></p>
    <p>Customer segmentation lets you look at how different groups of customers use your product. For example, you could look at how customers from different regions trigger an event or use a feature (which could be an aggregation of multiple events).</p>
    <h3>Funnels</h3>
    <p><img src="http://blog.teamtreehouse.com/wp-content/uploads/2014/01/funnel.png" alt="Funnel" width="1867" height="1013" style="max-width: 100%; height: auto;"></p>
    <p>Some action paths through a product are important to track and optimize for the highest completion rate possible, like signing up for an account or making a purchase. You can simply track the occurrence of the final action with a single data event, but you need to be able to connect multiple events together to analyze the entire path. This kind of analysis is referred to as funnel analysis.</p>
    <p>Funnels are useful to quickly see where customers are dropping off in a path. For example, are they having issues on the checkout page? You can also use funnels across product releases to ensure a change has not decreased conversions on critical product paths.</p>
    <h3>Cohorts</h3>
    <p>Cohorts are groups of customers that have a common characteristic within a specified time period, usually day, week, or month. Preceding concepts, like segmentation or simple data events, can be used to define cohorts, but a common cohort characteristic is the date a person started using a product.</p>
    <p>Cohort analysis is helpful in tracking two important product metrics at a high-level: engagement and retention. Pairing a useful cohort characteristic, like start date, with one of your key performance indicator metrics is a powerful way to make sure each <em>new</em> cohort group is staying engaged. Similarly, cohorts provide a quick way to see how long customers are sticking around with your product. Here is one way of visualizing a cohort analysis:</p>
    <p><img src="http://blog.teamtreehouse.com/wp-content/uploads/2014/01/cohort.png" alt="Cohort" width="1899" height="1018" style="max-width: 100%; height: auto;"></p>
    <p>It’s important to note that cohort analysis does not show why engagement or retention is worsening or improving, but it will give a heads up that something warrants further investigation.</p>
    <h2>What‘s next?</h2>
    <p>Now that you understand the basics of analytics, you should choose one of the tools out there—like Google Analytics, Mixpanel, or Localytics—and implement it in your product. You should also begin to regularly ask yourself, “What do I want to know? What data do I need to capture to know this? What analysis will I do with the data to give me an answer?”</p>
    <p>Have you implemented analytics in your own products? Leave a comment adding to what we’ve discussed here.</p>
    <p>The post <a href="http://blog.teamtreehouse.com/product-analytics-101" rel="nofollow external" class="bo">Product Analytics 101</a> appeared first on <a href="http://blog.teamtreehouse.com" rel="nofollow external" class="bo">Treehouse Blog</a>.</p>
    </div>
]]>
</Body>
<Summary>Feedback loops are an essential component in product development because these loops provide regular opportunities to learn and adjust the product to have better fit. Qualitative feedback loops...</Summary>
<Website>http://feedproxy.google.com/~r/teamtreehouse/~3/8YNeZJ7Jb4I/product-analytics-101</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40557/guest@my.umbc.edu/1ff6f901a1ceea71cf64b4760397f187/api/pixel</TrackingUrl>
<Tag>analytics</Tag>
<Tag>android</Tag>
<Tag>code</Tag>
<Tag>css</Tag>
<Tag>design</Tag>
<Tag>development</Tag>
<Tag>html</Tag>
<Tag>ios</Tag>
<Tag>javascript</Tag>
<Tag>responsive</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, 23 Jan 2014 16:31:07 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="false" id="40553" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40553">
<Title>Women's Recreation Locker Room Closed Saturday, January 25th</Title>
<Tagline>Women's Recreation Locker Room Closed Saturday, January 25th</Tagline>
<Body>
<![CDATA[
    <div class="html-content">All UMBC Students, Faculty, Staff, and RAC Members:<br><br>The Women's Recreation Locker Room will be <u><strong>CLOSED</strong></u> from 11am-3:30pm on Saturday, January 25th for a basketball game. <br><br>Please use the Women's Pool Locker Room during that time.<br><br>Thank you for your cooperation!<br><br>--<br>RAC Staff<br>
    </div>
]]>
</Body>
<Summary>All UMBC Students, Faculty, Staff, and RAC Members:  The Women's Recreation Locker Room will be CLOSED from 11am-3:30pm on Saturday, January 25th for a basketball game.   Please use the Women's...</Summary>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40553/guest@my.umbc.edu/9378c6b25fc40772375e8c685d3cf0b2/api/pixel</TrackingUrl>
<Group token="recsports">Recreation at the RAC</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/recsports</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/xsmall.png?1661189624</AvatarUrl>
<AvatarUrl size="original">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/original.png?1661189624</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/xxlarge.png?1661189624</AvatarUrl>
<AvatarUrl size="xlarge">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/xlarge.png?1661189624</AvatarUrl>
<AvatarUrl size="large">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/large.png?1661189624</AvatarUrl>
<AvatarUrl size="medium">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/medium.png?1661189624</AvatarUrl>
<AvatarUrl size="small">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/small.png?1661189624</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/xsmall.png?1661189624</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/257/f9c91c92f9b48ff33e640906e80bc588/xxsmall.png?1661189624</AvatarUrl>
<Sponsor>Recreation at the RAC</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>false</CommentsAllowed>
<PostedAt>Thu, 23 Jan 2014 15:21:01 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="40552" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40552">
<Title>Validation and Exception Handling: From the UI to the Backend</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <a href="http://rss.buysellads.com/click.php?z=1260013&amp;k=d754f1e9ba63a736ba8ff5ece958f7dd&amp;a=36697&amp;c=1626737202" rel="nofollow external" class="bo"><img src="http://rss.buysellads.com/img.php?z=1260013&amp;k=d754f1e9ba63a736ba8ff5ece958f7dd&amp;a=36697&amp;c=1626737202" alt="" style="max-width: 100%; height: auto;"></a><p>Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached a point when we had to take architectural actions to accommodate all the exceptional cases our quite large software project needed to handle. Below is a list of practices we came to value and apply when it comes to validation and exception handling.</p>
    <p></p>
    <hr>
    <h2>Validation vs. Exception Handling</h2>
    <p>When we started discussing our problem, one thing surfaced very quickly. What is validation and what is exception handling? For example in a user registration form, we have some rules for the password (it must contain both numbers and letters). If the user enters only letters, is that a validation issue or an exception. Should the UI validate that, or just pass it to the backend and catch any exceptions that my be thrown?</p>
    <p>We reached a common conclusion that validation refers to rules defined by the system and verified against user provided data. A validation should not care about how the business logic works, or how the system for that matter works. For example, our operating system may expect, without any protests, a password composed of plain letters. However we want to enforce a combination of letters and numbers. This is a case for validation, a rule we want to impose.</p>
    <p>On the other hand, exceptions are cases when our system may function in an unpredicted way, wrongly, or not at all if some specific data is provided in a wrong format. For example, in the above example, if the username already exists on the system, it is a case of an exception. Our business logic should be able to throw the appropriate exception and the UI catch and handle it so that the user will see a nice message.</p>
    <hr>
    <h2>Validating in the User Interface</h2>
    <p>Now that we made clear what our goals are, let’s see some examples based on the same user registration form idea.</p>
    <h3>Validating in JavaScript</h3>
    <p>To most of today’s browsers, JavaScript is second nature. There is almost no webpage without some degree of JavaScript in it. One good practice is to validate some basic things in JavaScript.</p>
    <p>Let’s say we have a simple user registration form in <code>index.php</code>, as described below.</p>
    <pre>&lt;!DOCTYPE html&gt;&#x000A;    &lt;html&gt;&#x000A;    	&lt;head&gt;&#x000A;    		&lt;title&gt;User Registration&lt;/title&gt;&#x000A;    		&lt;meta charset="UTF-8"&gt;&#x000A;    	&lt;/head&gt;&#x000A;    	&lt;body&gt;&#x000A;    		&lt;h3&gt;Register new account&lt;/h3&gt;&#x000A;    		&lt;form&gt;&#x000A;    			Username:&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="text" /&gt;&#x000A;    			&lt;br/&gt;&#x000A;    			Password:&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="password" /&gt;&#x000A;    			&lt;br/&gt;&#x000A;    			Confirm:&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="password" /&gt;&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="submit" name="register" value="Register"&gt;&#x000A;    		&lt;/form&gt;&#x000A;    	&lt;/body&gt;&#x000A;    &lt;/html&gt;&#x000A;    </pre>
    <p>This will output something similar to the image below:</p>  <img src="http://cdn.tutsplus.com/net/uploads/2014/01/RegistrationForm.png" alt="RegistrationForm" width="244" height="227" style="max-width: 100%; height: auto;"><br> <p>Every such form should validate that the text entered in the two password fields are equal. Obviously this is to ensure the user does not make a mistake when typing in his or her password. With JavaScript, doing the validation is quite simple.</p>
    <p>First we need to update a little bit of our HTML code.</p>
    <pre>&lt;form onsubmit="return validatePasswords(this);"&gt;&#x000A;    	Username:&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="text" /&gt;&#x000A;    	&lt;br/&gt;&#x000A;    	Password:&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="password" name="password"/&gt;&#x000A;    	&lt;br/&gt;&#x000A;    	Confirm:&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="password" name="confirm"/&gt;&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="submit" name="register" value="Register"&gt;&#x000A;    &lt;/form&gt;&#x000A;    </pre>
    <p>We added names to the password input fields so we can identify them. Then we specified that on submit the form should return the result of a function called <code>validatePasswords()</code>. This function is the JavaScript we’ll write. Simple scripts like this can be kept in the HTML file, other, more sophisticated ones should go in their own JavaScript files.</p>
    <pre>&lt;script&gt;&#x000A;    	function validatePasswords(form) {&#x000A;    		if (form.password.value !== form.confirm.value) {&#x000A;    			alert("Passwords do not match");&#x000A;    			return false;&#x000A;    		}&#x000A;    		return true;&#x000A;    	}&#x000A;    &#x000A;    &lt;/script&gt;&#x000A;    </pre>
    <p>The only thing we do here is to compare the values of the two input fields named “<code>password</code>” and “<code>confirm</code>“. We can reference the form by the parameter we send in when calling the function. We used “<code>this</code>” in the form’s <code>onsubmit</code> attribute, so the form itself is sent to the function.</p>
    <p>When the values are the same, <code>true</code> will be returned and the form will be submitted, otherwise an alert message will be shown telling the user the passwords do not match.</p>  <img src="http://cdn.tutsplus.com/net/uploads/2014/01/PasswordDoNotMatchAlert.png" alt="PasswordDoNotMatchAlert" width="471" height="227" style="max-width: 100%; height: auto;"><br> <h3>HTML5 Validations</h3>
    <p>While we can use JavaScript to validate most of our inputs, there are cases when we want to go on an easier path. Some degree of input validation is available in HTML5, and most browsers are happy to apply them. Using HTML5 validation is simpler in some cases, though it offers less flexibility.</p>
    <pre>&lt;head&gt;&#x000A;    	&lt;title&gt;User Registration&lt;/title&gt;&#x000A;    	&lt;meta charset="UTF-8"&gt;&#x000A;    	&lt;style&gt;&#x000A;    		input {&#x000A;    			width: 200px;&#x000A;    		}&#x000A;    		input:required:valid {&#x000A;    			border-color: mediumspringgreen;&#x000A;    		}&#x000A;    		input:required:invalid {&#x000A;    			border-color: lightcoral;&#x000A;    		}&#x000A;    	&lt;/style&gt;&#x000A;    &lt;/head&gt;&#x000A;    &lt;body&gt;&#x000A;    	&lt;h3&gt;Register new account&lt;/h3&gt;&#x000A;    	&lt;form onsubmit="return validatePasswords(this);"&gt;&#x000A;    		Username:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="text" name="userName" required/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Password:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="password" name="password"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Confirm:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="password" name="confirm"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Email Address:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="email" name="email" required placeholder="A Valid Email Address"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Website:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="url" name="website" required pattern="https?://.+"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="submit" name="register" value="Register"&gt;&#x000A;    	&lt;/form&gt;&#x000A;    &lt;/body&gt;&#x000A;    </pre>
    <p>To demonstrate several validation cases, we extended our form a little bit. We added an email address and a website also. HTML validations were set on three fields.</p>
    <ul>
    <li> The text input <code>username</code> is just simply required. It will validate with any string longer than zero characters.</li>
    <li> The email address field is of type “<code>email</code>” and when we specify the “<code>required</code>” attribute, browsers will apply a validation to the field.</li>
    <li> Finally, the website field is of type “<code>url</code>“. We also specified a “<code>pattern</code>” attribute where you can write your regular expressions that validate the required fields.</li>
    </ul>
    <p>To make the user aware of the state of the fields, we also used a little bit of CSS to color the borders of the inputs in red or green, depending on the state of the required validation.</p>  <img src="http://cdn.tutsplus.com/net/uploads/2014/01/HTMLValidations.png" alt="HTMLValidations" width="251" height="324" style="max-width: 100%; height: auto;"><br> <p>The problem with HTML validations is that different browsers behave differently when you try to submit the form. Some browsers will just apply the CSS to inform the users, others will prevent the submission of the form altogether. I recommend you to test your HTML validations thoroughly in different browsers and if needed also provide a JavaScript fallback for those browsers that are not smart enough.</p>
    <hr>
    <h2>Validating in Models</h2>
    <p>By now many people know about Robert C. Martin’s clean architecture proposal, in which the MVC framework is only for presentation and not for business logic.</p>  <img src="http://cdn.tutsplus.com/net/uploads/2014/01/HighLevelDesign1.png" alt="HighLevelDesign" width="1142" height="739" style="max-width: 100%; height: auto;"><br> <p>Essentially, your business logic should reside in a separate, well isolated place, organized to reflect the architecture of your application, while the framework’s views and controllers should control the delivery of the content to the user and models could be dropped altogether or, if needed, used only to perform delivery related operations. One such operation is validation. Most frameworks have great validation features. It would be a shame to not put your models at work and do a little validation there.</p>
    <p>We will not install several MVC web frameworks to demonstrate how to validate our previous forms, but here are two approximate solutions in Laravel and CakePHP.</p>
    <h3>Validation in a Laravel Model</h3>
    <p>Laravel is designed so that you have more access to validation in the Controller where you also have direct access to the input from the user. The built-in validator kind of prefers to be used there. However there are suggestions on the Internet that validating in models is still a good thing to do in Laravel. A complete example and solution by Jeffrey Way can be found on <a href="https://github.com/JeffreyWay/Laravel-Model-Validation" rel="nofollow external" class="bo">his Github repository</a>.</p>
    <p>If you prefer to write your own solution, you could do something similar to the model below.</p>
    <pre>class UserACL extends Eloquent {&#x000A;        private $rules = array(&#x000A;            'userName' =&gt; 'required|alpha|min:5',&#x000A;            'password'  =&gt; 'required|min:6',&#x000A;    		'confirm' =&gt; 'required|min:6',&#x000A;    		'email' =&gt; 'required|email',&#x000A;    		'website' =&gt; 'url'&#x000A;        );&#x000A;    &#x000A;        private $errors;&#x000A;    &#x000A;        public function validate($data) {&#x000A;            $validator = Validator::make($data, $this-&gt;rules);&#x000A;    &#x000A;            if ($validator-&gt;fails()) {&#x000A;                $this-&gt;errors = $validator-&gt;errors;&#x000A;                return false;&#x000A;            }&#x000A;            return true;&#x000A;        }&#x000A;    &#x000A;        public function errors() {&#x000A;            return $this-&gt;errors;&#x000A;        }&#x000A;    }</pre>
    <p>You can use this from your controller by simply creating the <code>UserACL</code> object and call validate on it. You will probably have the “<code>register</code>” method also on this model, and the <code>register</code> will just delegate the already validated data to your business logic.</p>
    <h3>Validation in a CakePHP Model</h3>
    <p>CakePHP promotes validation in models as well. It has extensive validation functionality at model level. Here is about how a validation for our form would look like in CakePHP.</p>
    <pre>class UserACL extends AppModel {&#x000A;    &#x000A;        public $validate = [&#x000A;    		'userName' =&gt; [&#x000A;    			'rule' =&gt; ['minLength', 5],&#x000A;    			'required' =&gt; true,&#x000A;    			'allowEmpty' =&gt; false,&#x000A;    			'on' =&gt; 'create',&#x000A;    			'message' =&gt; 'User name must be at least 5 characters long.'&#x000A;    		],&#x000A;            'password' =&gt; [&#x000A;                'rule'    =&gt; ['equalsTo', 'confirm'],&#x000A;                'message' =&gt; 'The two passwords do not match. Please re-enter them.'&#x000A;            ]&#x000A;        ];&#x000A;    &#x000A;        public function equalsTo($checkedField, $otherField = null) {&#x000A;    		$value = $this-&gt;getFieldValue($checkedField);&#x000A;            return $value === $this-&gt;data[$this-&gt;name][$otherField];&#x000A;        }&#x000A;    &#x000A;    	private function getFieldValue($fieldName) {&#x000A;    	    return array_values($otherField)[0];&#x000A;    	}&#x000A;    }</pre>
    <p>We only exemplified the rules partially. It is enough to highlight the power of validation in the model. CakePHP is particularly good at this. It has a great number of built-in validation functions like “<code>minLength</code>” in the example and various ways to provide feedback to the user. Even more, concepts like “<code>required</code>” or “<code>allowEmpty</code>” are not actually validation rules. Cake will look at these when generating your view and put HTML validations also on fields marked with these parameters. However rules are great and can easily be extended by just simply creating methods on the model class as we did to compare the two password fields. Finally, you can always specify the message you want to send to the views in case of validation failure. More on CakePHP validation in <a href="http://book.cakephp.org/2.0/en/models/data-validation.html" rel="nofollow external" class="bo">the cookbook</a>.</p>
    <p>Validation in general at the model level has its advantages. Each framework provides easy access to the input fields and creates the mechanism to notify the user in case of validation failure. No need for try-catch statements or any other sophisticated steps. Validation on the server side also assures that the data gets validated, no matter what. The user can not trick our software any more as with HTML or JavaScript. Of course, each server side validation comes with the cost of a network round-trip and computing power on the provider’s side instead of the client’s side.</p>
    <hr>
    <h2>Throwing Exceptions from the Business Logic</h2>
    <p>The final step in checking data before committing it to the system is at the level of our business logic. Information that reaches this part of the system should be sanitized enough to be usable. The business logic should only check for cases that are critical for it. For example, adding a user that already exists is a case when we throw an exception. Checking the length of the user to be at least five characters should not happen at this level. We can safely assume that such limitations were enforced at higher levels.</p>
    <p>On the other hand, comparing the two passwords is a matter for discussion. For example, if we just encrypt and save the password near the user in a database, we could drop the check and assume previous layers made sure the passwords are equal. However, if we create a real user on the operating system using an API or a CLI tool that actually requires a username, password, and password confirmation, we may want to take the second entry also and send it to a CLI tool. Let it re-validate if the passwords match and be ready to throw an exception if they do not. This way we modeled our business logic to match how the real operating system behaves.</p>
    <h3>Throwing Exceptions from PHP</h3>
    <p>Throwing exceptions from PHP is very easy. Let’s create our user access control class, and demonstrate how to implement a user addition functionality.</p>
    <pre>class UserControlTest extends PHPUnit_Framework_TestCase {&#x000A;    	function testBehavior() {&#x000A;    		$this-&gt;assertTrue(true);&#x000A;    	}&#x000A;    }</pre>
    <p>I always like to start with something simple that gets me going. Creating a stupid test is a great way to do so. It also forces me to think about what I want to implement. A test named <code>UserControlTest</code> means I thought I will need a <code>UserControl</code> class to implement my method.</p>
    <pre>require_once __DIR__ . '/../UserControl.php';&#x000A;    class UserControlTest extends PHPUnit_Framework_TestCase {&#x000A;    &#x000A;    	/**&#x000A;    	 * @expectedException Exception&#x000A;    	 * @expectedExceptionMessage User can not be empty&#x000A;    	 */&#x000A;    	function testEmptyUsernameWillThrowException() {&#x000A;    		$userControl = new UserControl();&#x000A;    		$userControl-&gt;add('');&#x000A;    	}&#x000A;    &#x000A;    }</pre>
    <p>The next test to write is a degenerative case. We will not test for a specific user length, but we want to make sure we do not want to add an empty user. It is sometimes easy to lose the content of a variable from view to business, over all those layers of our application. This code will obviously fail, because we do not have a class yet.</p>
    <pre>PHP Warning:  require_once([long-path-here]/Test/../UserControl.php):&#x000A;    failed to open stream: No such file or directory in&#x000A;    [long-path-here]/Test/UserControlTest.php on line 2</pre>
    <p>Let’s create the class and run our tests. Now we have another problem.</p>
    <pre>PHP Fatal error:  Call to undefined method UserControl::add()</pre>
    <p>But we can fix that, too, in just a couple of seconds.</p>
    <pre>class UserControl {&#x000A;    &#x000A;    	public function add($username) {&#x000A;    &#x000A;    	}&#x000A;    &#x000A;    }</pre>
    <p>Now we can have a nice test failure telling us the whole story of our code.</p>
    <pre>1) UserControlTest::testEmptyUsernameWillThrowException&#x000A;    Failed asserting that exception of type "Exception" is thrown.</pre>
    <p>Finally we can do some actual coding.</p>
    <pre>public function add($username) {&#x000A;    	if(!$username) {&#x000A;    		throw new Exception();&#x000A;    	}&#x000A;    }</pre>
    <p>That makes the expectation for the exception pass, but without specifying a message the test will still fail.</p>
    <pre>1) UserControlTest::testEmptyUsernameWillThrowException&#x000A;    Failed asserting that exception message '' contains 'User can not be empty'.</pre>
    <p>Time to write the Exception’s message</p>
    <pre>public function add($username) {&#x000A;    	if(!$username) {&#x000A;    		throw new Exception('User can not be empty!');&#x000A;    	}&#x000A;    }</pre>
    <p>Now, that makes our test pass. As you can observe, PHPUnit verifies that the expected exception message is contained in the actually thrown exception. This is useful because it allows us to dynamically construct messages and only check for the stable part. A common example is when you throw an error with a base text and at the end you specify the reason for that exception. Reasons are usually provided by third party libraries or application.</p>
    <pre>/**&#x000A;     * @expectedException Exception&#x000A;     * @expectedExceptionMessage Cannot add user George&#x000A;     */&#x000A;    function testWillNotAddAnAlreadyExistingUser() {&#x000A;    	$command = \Mockery::mock('SystemCommand');&#x000A;    	$command-&gt;shouldReceive('execute')-&gt;once()-&gt;with('adduser George')-&gt;andReturn(false);&#x000A;    	$command-&gt;shouldReceive('getFailureMessage')-&gt;once()-&gt;andReturn('User already exists on the system.');&#x000A;    	$userControl = new UserControl($command);&#x000A;    	$userControl-&gt;add('George');&#x000A;    }</pre>
    <p>Throwing errors on duplicate users will allow us to explore this message construction a step further. The test above creates a mock which will simulate a system command, it will fail and on request, it will return a nice failure message. We will inject this command to the <code>UserControl</code> class for internal use.</p>
    <pre>class UserControl {&#x000A;    &#x000A;    	private $systemCommand;&#x000A;    &#x000A;    	public function __construct(SystemCommand $systemCommand = null) {&#x000A;    		$this-&gt;systemCommand = $systemCommand ? : new SystemCommand();&#x000A;    	}&#x000A;    &#x000A;    	public function add($username) {&#x000A;    		if (!$username) {&#x000A;    			throw new Exception('User can not be empty!');&#x000A;    		}&#x000A;    	}&#x000A;    &#x000A;    }&#x000A;    &#x000A;    class SystemCommand {&#x000A;    &#x000A;    }</pre>
    <p>Injecting the a <code>SystemCommand</code> instance was quite easy. We also created a <code>SystemCommand</code> class inside our test just to avoid syntax problems. We won’t implement it. Its scope exceeds this tutorial’s topic. However, we have another test failure message.</p>
    <pre>1) UserControlTest::testWillNotAddAnAlreadyExistingUser&#x000A;    Failed asserting that exception of type "Exception" is thrown.</pre>
    <p>Yep. We are not throwing any exceptions. The logic to call the system command and try to add the user is missing.</p>
    <pre>public function add($username) {&#x000A;    	if (!$username) {&#x000A;    		throw new Exception('User can not be empty!');&#x000A;    	}&#x000A;    &#x000A;    	if(!$this-&gt;systemCommand-&gt;execute(sprintf('adduser %s', $username))) {&#x000A;    		throw new Exception(&#x000A;    				sprintf('Cannot add user %s. Reason: %s',&#x000A;    						$username,&#x000A;    						$this-&gt;systemCommand-&gt;getFailureMessage()&#x000A;    				)&#x000A;    			);&#x000A;    	}&#x000A;    }</pre>
    <p>Now, those modifications to the <code>add()</code> method can do the trick. We try to execute our command on the system, no matter what, and if the system says it can not add the user for whatever reason we throw an exception. This exception’s message will be part hard-coded, with the user’s name attached and then the reason from the system command concatenated at the end. As you can see, this code makes our test pass.</p>
    <h3>Custom Exceptions</h3>
    <p>Throwing exceptions with different messages is enough in most cases. However, when you have a more complex system you also need to catch these exceptions and take different actions based on them. Analyzing an exception’s message and taking action solely on that can lead to some annoying problems. First, strings are part of the UI, presentation, and they have a volatile nature. Basing logic on ever changing strings will lead to dependency management nightmare. Second, calling a <code>getMessage()</code> method on the caught exception each time is also a strange way to decide what to do next.</p>
    <p>With all these in mind, creating our own exceptions is the next logical step to take.</p>
    <pre>/**&#x000A;     * @expectedException ExceptionCannotAddUser&#x000A;     * @expectedExceptionMessage Cannot add user George&#x000A;     */&#x000A;    function testWillNotAddAnAlreadyExistingUser() {&#x000A;    	$command = \Mockery::mock('SystemCommand');&#x000A;    	$command-&gt;shouldReceive('execute')-&gt;once()-&gt;with('adduser George')-&gt;andReturn(false);&#x000A;    	$command-&gt;shouldReceive('getFailureMessage')-&gt;once()-&gt;andReturn('User already exists on the system.');&#x000A;    	$userControl = new UserControl($command);&#x000A;    	$userControl-&gt;add('George');&#x000A;    }</pre>
    <p>We modified our test to expect our own custom exception, <code>ExceptionCannotAddUser</code>. The rest of the test is unchanged.</p>
    <pre>class ExceptionCannotAddUser extends Exception {&#x000A;    &#x000A;    	public function __construct($userName, $reason) {&#x000A;    		$message = sprintf(&#x000A;    			'Cannot add user %s. Reason: %s',&#x000A;    			$userName, $reason&#x000A;    		);&#x000A;    		parent::__construct($message, 13, null);&#x000A;    	}&#x000A;    }</pre>
    <p>The class that implements our custom exception is like any other class, but it has to extend <code>Exception</code>. Using custom exceptions also provides us a great place to do all the presentation related string manipulation. Moving the concatenation here, we also eliminated presentation from the business logic and respected the single responsibility principle.</p>
    <pre>public function add($username) {&#x000A;    	if (!$username) {&#x000A;    		throw new Exception('User can not be empty!');&#x000A;    	}&#x000A;    &#x000A;    	if(!$this-&gt;systemCommand-&gt;execute(sprintf('adduser %s', $username))) {&#x000A;    		throw new ExceptionCannotAddUser($username, $this-&gt;systemCommand-&gt;getFailureMessage());&#x000A;    	}&#x000A;    }</pre>
    <p>Throwing our own exception is just a matter of changing the old “<code>throw</code>” command to the new one and sending in two parameters instead of composing the message here. Of course all tests are passing.</p>
    <pre>PHPUnit 3.7.28 by Sebastian Bergmann.&#x000A;    &#x000A;    ..&#x000A;    &#x000A;    Time: 18 ms, Memory: 3.00Mb&#x000A;    &#x000A;    OK (2 tests, 4 assertions)&#x000A;    &#x000A;    Done.</pre>
    <h3>Catching Exceptions in Your MVC</h3>
    <p>Exceptions must be caught at some point, unless you want your user to see them as they are. If you are using an MVC framework you will probably want to catch exceptions in the controller or model. After the exception is caught, it is transformed in a message to the user and rendered inside your view. A common way to achieve this is to create a “<code>tryAction($action)</code>” method in your application’s base controller or model and always call it with the current action. In that method you can do the catching logic and nice message generation to suit your framework.</p>
    <p>If you do not use a web framework, or a web interface for that matter, your presentation layer should take care of catching and transforming these exceptions.</p>
    <p>If you develop a library, catching your exceptions will be the responsibility of your clients.</p>
    <hr>
    <h2>Final Thoughts</h2>
    <p>That’s it. We traversed all the layers of our application. We validated in JavaScript, HTML and in our models. We’ve thrown and caught exceptions from our business logic and even created our own custom exceptions. This approach to validation and exception handling can be applied from small to big projects without any severe problems. However if your validation logic is getting very complex, and different parts of your project uses overlapping parts of logic, you may consider extracting all validations that can be done at a specific level to a validation service or validation provider. These levels may include, but not need to be limited to JavaScript validator, backend PHP validator, third party communication validator and so on.</p>
    <p>Thank you for reading. Have a nice day.</p>
    </div>
]]>
</Body>
<Summary>Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached...</Summary>
<Website>http://feedproxy.google.com/~r/nettuts/~3/Xj4eVNKZjvw/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40552/guest@my.umbc.edu/e92bcd53ee5d1b1502f2dac1184318c7/api/pixel</TrackingUrl>
<Tag>css</Tag>
<Tag>development</Tag>
<Tag>exception-handling</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>php</Tag>
<Tag>sql</Tag>
<Tag>tutorials</Tag>
<Tag>validation-handling</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>Thu, 23 Jan 2014 14:41:29 -0500</PostedAt>
<EditAt>Thu, 23 Jan 2014 14:41:29 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="41059" important="false" status="posted" url="https://my3.my.umbc.edu/posts/41059">
<Title>Validation and Exception Handling: From the UI to the Backend</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <p>Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached a point when we had to take architectural actions to accommodate all the exceptional cases our quite large software project needed to handle. Below is a list of practices we came to value and apply when it comes to validation and exception handling.</p>
    <p></p>
    <hr>
    <h2>Validation vs. Exception Handling</h2>
    <p>When we started discussing our problem, one thing surfaced very quickly. What is validation and what is exception handling? For example in a user registration form, we have some rules for the password (it must contain both numbers and letters). If the user enters only letters, is that a validation issue or an exception. Should the UI validate that, or just pass it to the backend and catch any exceptions that my be thrown?</p>
    <p>We reached a common conclusion that validation refers to rules defined by the system and verified against user provided data. A validation should not care about how the business logic works, or how the system for that matter works. For example, our operating system may expect, without any protests, a password composed of plain letters. However we want to enforce a combination of letters and numbers. This is a case for validation, a rule we want to impose.</p>
    <p>On the other hand, exceptions are cases when our system may function in an unpredicted way, wrongly, or not at all if some specific data is provided in a wrong format. For example, in the above example, if the username already exists on the system, it is a case of an exception. Our business logic should be able to throw the appropriate exception and the UI catch and handle it so that the user will see a nice message.</p>
    <hr>
    <h2>Validating in the User Interface</h2>
    <p>Now that we made clear what our goals are, let's see some examples based on the same user registration form idea.</p>
    <h3>Validating in JavaScript</h3>
    <p>To most of today's browsers, JavaScript is second nature. There is almost no webpage without some degree of JavaScript in it. One good practice is to validate some basic things in JavaScript.</p>
    <p>Let's say we have a simple user registration form in <code>index.php</code>, as described below.</p>
    <pre>&lt;!DOCTYPE html&gt;&#x000A;    &lt;html&gt;&#x000A;    	&lt;head&gt;&#x000A;    		&lt;title&gt;User Registration&lt;/title&gt;&#x000A;    		&lt;meta charset="UTF-8"&gt;&#x000A;    	&lt;/head&gt;&#x000A;    	&lt;body&gt;&#x000A;    		&lt;h3&gt;Register new account&lt;/h3&gt;&#x000A;    		&lt;form&gt;&#x000A;    			Username:&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="text" /&gt;&#x000A;    			&lt;br/&gt;&#x000A;    			Password:&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="password" /&gt;&#x000A;    			&lt;br/&gt;&#x000A;    			Confirm:&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="password" /&gt;&#x000A;    			&lt;br/&gt;&#x000A;    			&lt;input type="submit" name="register" value="Register"&gt;&#x000A;    		&lt;/form&gt;&#x000A;    	&lt;/body&gt;&#x000A;    &lt;/html&gt;</pre>
    <p>This will output something similar to the image below:</p>
    
    <img src="http://cdn.tutsplus.com/net/uploads/2014/01/RegistrationForm.png" alt="RegistrationForm" width="244" height="227" style="max-width: 100%; height: auto;"><br>
    
    <p>Every such form should validate that the text entered in the two password fields are equal. Obviously this is to ensure the user does not make a mistake when typing in his or her password. With JavaScript, doing the validation is quite simple.</p>
    <p>First we need to update a little bit of our HTML code.</p>
    <pre>&lt;form onsubmit="return validatePasswords(this);"&gt;&#x000A;    	Username:&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="text" /&gt;&#x000A;    	&lt;br/&gt;&#x000A;    	Password:&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="password" name="password"/&gt;&#x000A;    	&lt;br/&gt;&#x000A;    	Confirm:&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="password" name="confirm"/&gt;&#x000A;    	&lt;br/&gt;&#x000A;    	&lt;input type="submit" name="register" value="Register"&gt;&#x000A;    &lt;/form&gt;</pre>
    <p>We added names to the password input fields so we can identify them. Then we specified that on submit the form should return the result of a function called <code>validatePasswords()</code>. This function is the JavaScript we'll write. Simple scripts like this can be kept in the HTML file, other, more sophisticated ones should go in their own JavaScript files.</p>
    <pre>&lt;script&gt;&#x000A;    	function validatePasswords(form) {&#x000A;    		if (form.password.value !== form.confirm.value) {&#x000A;    			alert("Passwords do not match");&#x000A;    			return false;&#x000A;    		}&#x000A;    		return true;&#x000A;    	}&#x000A;    &#x000A;    &lt;/script&gt;</pre>
    <p>The only thing we do here is to compare the values of the two input fields named "<code>password</code>" and "<code>confirm</code>". We can reference the form by the parameter we send in when calling the function. We used "<code>this</code>" in the form's <code>onsubmit</code> attribute, so the form itself is sent to the function.</p>
    <p>When the values are the same, <code>true</code> will be returned and the form will be submitted, otherwise an alert message will be shown telling the user the passwords do not match.</p>
    
    <img src="http://cdn.tutsplus.com/net/uploads/2014/01/PasswordDoNotMatchAlert.png" alt="PasswordDoNotMatchAlert" width="471" height="227" style="max-width: 100%; height: auto;"><br>
    
    <h3>HTML5 Validations</h3>
    <p>While we can use JavaScript to validate most of our inputs, there are cases when we want to go on an easier path. Some degree of input validation is available in HTML5, and most browsers are happy to apply them. Using HTML5 validation is simpler in some cases, though it offers less flexibility.</p>
    <pre>&lt;head&gt;&#x000A;    	&lt;title&gt;User Registration&lt;/title&gt;&#x000A;    	&lt;meta charset="UTF-8"&gt;&#x000A;    	&lt;style&gt;&#x000A;    		input {&#x000A;    			width: 200px;&#x000A;    		}&#x000A;    		input:required:valid {&#x000A;    			border-color: mediumspringgreen;&#x000A;    		}&#x000A;    		input:required:invalid {&#x000A;    			border-color: lightcoral;&#x000A;    		}&#x000A;    	&lt;/style&gt;&#x000A;    &lt;/head&gt;&#x000A;    &lt;body&gt;&#x000A;    	&lt;h3&gt;Register new account&lt;/h3&gt;&#x000A;    	&lt;form onsubmit="return validatePasswords(this);"&gt;&#x000A;    		Username:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="text" name="userName" required/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Password:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="password" name="password"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Confirm:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="password" name="confirm"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Email Address:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="email" name="email" required placeholder="A Valid Email Address"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		Website:&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="url" name="website" required pattern="https?://.+"/&gt;&#x000A;    		&lt;br/&gt;&#x000A;    		&lt;input type="submit" name="register" value="Register"&gt;&#x000A;    	&lt;/form&gt;&#x000A;    &lt;/body&gt;</pre>
    <p>To demonstrate several validation cases, we extended our form a little bit. We added an email address and a website also. HTML validations were set on three fields.</p>
    <ul>
    <li>
    		The text input <code>username</code> is just simply required. It will validate with any string longer than zero characters.
    	</li>
    <li>
    		The email address field is of type "<code>email</code>" and when we specify the "<code>required</code>" attribute, browsers will apply a validation to the field.
    	</li>
    <li>
    		Finally, the website field is of type "<code>url</code>". We also specified a "<code>pattern</code>" attribute where you can write your regular expressions that validate the required fields.
    	</li>
    </ul>
    <p>To make the user aware of the state of the fields, we also used a little bit of CSS to color the borders of the inputs in red or green, depending on the state of the required validation.</p>
    
    <img src="http://cdn.tutsplus.com/net/uploads/2014/01/HTMLValidations.png" alt="HTMLValidations" width="251" height="324" style="max-width: 100%; height: auto;"><br>
    
    <p>The problem with HTML validations is that different browsers behave differently when you try to submit the form. Some browsers will just apply the CSS to inform the users, others will prevent the submission of the form altogether. I recommend you to test your HTML validations thoroughly in different browsers and if needed also provide a JavaScript fallback for those browsers that are not smart enough.</p>
    <hr>
    <h2>Validating in Models</h2>
    <p>By now many people know about Robert C. Martin's clean architecture proposal, in which the MVC framework is only for presentation and not for business logic.</p>
    
    <img src="http://cdn.tutsplus.com/net/uploads/2014/01/HighLevelDesign1.png" alt="HighLevelDesign" width="1142" height="739" style="max-width: 100%; height: auto;"><br>
    
    <p>Essentially, your business logic should reside in a separate, well isolated place, organized to reflect the architecture of your application, while the framework's views and controllers should control the delivery of the content to the user and models could be dropped altogether or, if needed, used only to perform delivery related operations. One such operation is validation. Most frameworks have great validation features. It would be a shame to not put your models at work and do a little validation there.</p>
    <p>We will not install several MVC web frameworks to demonstrate how to validate our previous forms, but here are two approximate solutions in Laravel and CakePHP.</p>
    <h3>Validation in a Laravel Model</h3>
    <p>Laravel is designed so that you have more access to validation in the Controller where you also have direct access to the input from the user. The built-in validator kind of prefers to be used there. However there are suggestions on the Internet that validating in models is still a good thing to do in Laravel. A complete example and solution by Jeffrey Way can be found on <a href="https://github.com/JeffreyWay/Laravel-Model-Validation" rel="nofollow external" class="bo">his Github repository</a>.</p>
    <p>If you prefer to write your own solution, you could do something similar to the model below.</p>
    <pre>class UserACL extends Eloquent {&#x000A;        private $rules = array(&#x000A;            'userName' =&gt; 'required|alpha|min:5',&#x000A;            'password'  =&gt; 'required|min:6',&#x000A;    		'confirm' =&gt; 'required|min:6',&#x000A;    		'email' =&gt; 'required|email',&#x000A;    		'website' =&gt; 'url'&#x000A;        );&#x000A;    &#x000A;        private $errors;&#x000A;    &#x000A;        public function validate($data) {&#x000A;            $validator = Validator::make($data, $this-&gt;rules);&#x000A;    &#x000A;            if ($validator-&gt;fails()) {&#x000A;                $this-&gt;errors = $validator-&gt;errors;&#x000A;                return false;&#x000A;            }&#x000A;            return true;&#x000A;        }&#x000A;    &#x000A;        public function errors() {&#x000A;            return $this-&gt;errors;&#x000A;        }&#x000A;    }</pre>
    <p>You can use this from your controller by simply creating the <code>UserACL</code> object and call validate on it. You will probably have the "<code>register</code>" method also on this model, and the <code>register</code> will just delegate the already validated data to your business logic.</p>
    <h3>Validation in a CakePHP Model</h3>
    <p>CakePHP promotes validation in models as well. It has extensive validation functionality at model level. Here is about how a validation for our form would look like in CakePHP.</p>
    <pre>class UserACL extends AppModel {&#x000A;    &#x000A;        public $validate = [&#x000A;    		'userName' =&gt; [&#x000A;    			'rule' =&gt; ['minLength', 5],&#x000A;    			'required' =&gt; true,&#x000A;    			'allowEmpty' =&gt; false,&#x000A;    			'on' =&gt; 'create',&#x000A;    			'message' =&gt; 'User name must be at least 5 characters long.'&#x000A;    		],&#x000A;            'password' =&gt; [&#x000A;                'rule'    =&gt; ['equalsTo', 'confirm'],&#x000A;                'message' =&gt; 'The two passwords do not match. Please re-enter them.'&#x000A;            ]&#x000A;        ];&#x000A;    &#x000A;        public function equalsTo($checkedField, $otherField = null) {&#x000A;    		$value = $this-&gt;getFieldValue($checkedField);&#x000A;            return $value === $this-&gt;data[$this-&gt;name][$otherField];&#x000A;        }&#x000A;    &#x000A;    	private function getFieldValue($fieldName) {&#x000A;    	    return array_values($otherField)[0];&#x000A;    	}&#x000A;    }</pre>
    <p>We only exemplified the rules partially. It is enough to highlight the power of validation in the model. CakePHP is particularly good at this. It has a great number of built-in validation functions like "<code>minLength</code>" in the example and various ways to provide feedback to the user. Even more, concepts like "<code>required</code>" or "<code>allowEmpty</code>" are not actually validation rules. Cake will look at these when generating your view and put HTML validations also on fields marked with these parameters. However rules are great and can easily be extended by just simply creating methods on the model class as we did to compare the two password fields. Finally, you can always specify the message you want to send to the views in case of validation failure. More on CakePHP validation in <a href="http://book.cakephp.org/2.0/en/models/data-validation.html" rel="nofollow external" class="bo">the cookbook</a>.</p>
    <p>Validation in general at the model level has its advantages. Each framework provides easy access to the input fields and creates the mechanism to notify the user in case of validation failure. No need for try-catch statements or any other sophisticated steps. Validation on the server side also assures that the data gets validated, no matter what. The user can not trick our software any more as with HTML or JavaScript. Of course, each server side validation comes with the cost of a network round-trip and computing power on the provider's side instead of the client's side.</p>
    <hr>
    <h2>Throwing Exceptions from the Business Logic</h2>
    <p>The final step in checking data before committing it to the system is at the level of our business logic. Information that reaches this part of the system should be sanitized enough to be usable. The business logic should only check for cases that are critical for it. For example, adding a user that already exists is a case when we throw an exception. Checking the length of the user to be at least five characters should not happen at this level. We can safely assume that such limitations were enforced at higher levels.</p>
    <p>On the other hand, comparing the two passwords is a matter for discussion. For example, if we just encrypt and save the password near the user in a database, we could drop the check and assume previous layers made sure the passwords are equal. However, if we create a real user on the operating system using an API or a CLI tool that actually requires a username, password, and password confirmation, we may want to take the second entry also and send it to a CLI tool. Let it re-validate if the passwords match and be ready to throw an exception if they do not. This way we modeled our business logic to match how the real operating system behaves.</p>
    <h3>Throwing Exceptions from PHP</h3>
    <p>Throwing exceptions from PHP is very easy. Let's create our user access control class, and demonstrate how to implement a user addition functionality.</p>
    <pre>class UserControlTest extends PHPUnit_Framework_TestCase {&#x000A;    	function testBehavior() {&#x000A;    		$this-&gt;assertTrue(true);&#x000A;    	}&#x000A;    }</pre>
    <p>I always like to start with something simple that gets me going. Creating a stupid test is a great way to do so. It also forces me to think about what I want to implement. A test named <code>UserControlTest</code> means I thought I will need a <code>UserControl</code> class to implement my method.</p>
    <pre>require_once __DIR__ . '/../UserControl.php';&#x000A;    class UserControlTest extends PHPUnit_Framework_TestCase {&#x000A;    &#x000A;    	/**&#x000A;    	 * @expectedException Exception&#x000A;    	 * @expectedExceptionMessage User can not be empty&#x000A;    	 */&#x000A;    	function testEmptyUsernameWillThrowException() {&#x000A;    		$userControl = new UserControl();&#x000A;    		$userControl-&gt;add('');&#x000A;    	}&#x000A;    &#x000A;    }</pre>
    <p>The next test to write is a degenerative case. We will not test for a specific user length, but we want to make sure we do not want to add an empty user. It is sometimes easy to lose the content of a variable from view to business, over all those layers of our application. This code will obviously fail, because we do not have a class yet.</p>
    <pre>PHP Warning:  require_once([long-path-here]/Test/../UserControl.php):&#x000A;    failed to open stream: No such file or directory in&#x000A;    [long-path-here]/Test/UserControlTest.php on line 2</pre>
    <p>Let's create the class and run our tests. Now we have another problem.</p>
    <pre>PHP Fatal error:  Call to undefined method UserControl::add()</pre>
    <p>But we can fix that, too, in just a couple of seconds.</p>
    <pre>class UserControl {&#x000A;    &#x000A;    	public function add($username) {&#x000A;    &#x000A;    	}&#x000A;    &#x000A;    }</pre>
    <p>Now we can have a nice test failure telling us the whole story of our code.</p>
    <pre>1) UserControlTest::testEmptyUsernameWillThrowException&#x000A;    Failed asserting that exception of type "Exception" is thrown.</pre>
    <p>Finally we can do some actual coding.</p>
    <pre>public function add($username) {&#x000A;    	if(!$username) {&#x000A;    		throw new Exception();&#x000A;    	}&#x000A;    }</pre>
    <p>That makes the expectation for the exception pass, but without specifying a message the test will still fail.</p>
    <pre>1) UserControlTest::testEmptyUsernameWillThrowException&#x000A;    Failed asserting that exception message '' contains 'User can not be empty'.</pre>
    <p>Time to write the Exception's message</p>
    <pre>public function add($username) {&#x000A;    	if(!$username) {&#x000A;    		throw new Exception('User can not be empty!');&#x000A;    	}&#x000A;    }</pre>
    <p>Now, that makes our test pass. As you can observe, PHPUnit verifies that the expected exception message is contained in the actually thrown exception. This is useful because it allows us to dynamically construct messages and only check for the stable part. A common example is when you throw an error with a base text and at the end you specify the reason for that exception. Reasons are usually provided by third party libraries or application.</p>
    <pre>/**&#x000A;     * @expectedException Exception&#x000A;     * @expectedExceptionMessage Cannot add user George&#x000A;     */&#x000A;    function testWillNotAddAnAlreadyExistingUser() {&#x000A;    	$command = \Mockery::mock('SystemCommand');&#x000A;    	$command-&gt;shouldReceive('execute')-&gt;once()-&gt;with('adduser George')-&gt;andReturn(false);&#x000A;    	$command-&gt;shouldReceive('getFailureMessage')-&gt;once()-&gt;andReturn('User already exists on the system.');&#x000A;    	$userControl = new UserControl($command);&#x000A;    	$userControl-&gt;add('George');&#x000A;    }</pre>
    <p>Throwing errors on duplicate users will allow us to explore this message construction a step further. The test above creates a mock which will simulate a system command, it will fail and on request, it will return a nice failure message. We will inject this command to the <code>UserControl</code> class for internal use.</p>
    <pre>class UserControl {&#x000A;    &#x000A;    	private $systemCommand;&#x000A;    &#x000A;    	public function __construct(SystemCommand $systemCommand = null) {&#x000A;    		$this-&gt;systemCommand = $systemCommand ? : new SystemCommand();&#x000A;    	}&#x000A;    &#x000A;    	public function add($username) {&#x000A;    		if (!$username) {&#x000A;    			throw new Exception('User can not be empty!');&#x000A;    		}&#x000A;    	}&#x000A;    &#x000A;    }&#x000A;    &#x000A;    class SystemCommand {&#x000A;    &#x000A;    }</pre>
    <p>Injecting the a <code>SystemCommand</code> instance was quite easy. We also created a <code>SystemCommand</code> class inside our test just to avoid syntax problems. We won't implement it. Its scope exceeds this tutorial's topic. However, we have another test failure message.</p>
    <pre>1) UserControlTest::testWillNotAddAnAlreadyExistingUser&#x000A;    Failed asserting that exception of type "Exception" is thrown.</pre>
    <p>Yep. We are not throwing any exceptions. The logic to call the system command and try to add the user is missing.</p>
    <pre>public function add($username) {&#x000A;    	if (!$username) {&#x000A;    		throw new Exception('User can not be empty!');&#x000A;    	}&#x000A;    &#x000A;    	if(!$this-&gt;systemCommand-&gt;execute(sprintf('adduser %s', $username))) {&#x000A;    		throw new Exception(&#x000A;    				sprintf('Cannot add user %s. Reason: %s',&#x000A;    						$username,&#x000A;    						$this-&gt;systemCommand-&gt;getFailureMessage()&#x000A;    				)&#x000A;    			);&#x000A;    	}&#x000A;    }</pre>
    <p>Now, those modifications to the <code>add()</code> method can do the trick. We try to execute our command on the system, no matter what, and if the system says it can not add the user for whatever reason we throw an exception. This exception's message will be part hard-coded, with the user's name attached and then the reason from the system command concatenated at the end. As you can see, this code makes our test pass.</p>
    <h3>Custom Exceptions</h3>
    <p>Throwing exceptions with different messages is enough in most cases. However, when you have a more complex system you also need to catch these exceptions and take different actions based on them. Analyzing an exception's message and taking action solely on that can lead to some annoying problems. First, strings are part of the UI, presentation, and they have a volatile nature. Basing logic on ever changing strings will lead to dependency management nightmare. Second, calling a <code>getMessage()</code> method on the caught exception each time is also a strange way to decide what to do next.</p>
    <p>With all these in mind, creating our own exceptions is the next logical step to take.</p>
    <pre>/**&#x000A;     * @expectedException ExceptionCannotAddUser&#x000A;     * @expectedExceptionMessage Cannot add user George&#x000A;     */&#x000A;    function testWillNotAddAnAlreadyExistingUser() {&#x000A;    	$command = \Mockery::mock('SystemCommand');&#x000A;    	$command-&gt;shouldReceive('execute')-&gt;once()-&gt;with('adduser George')-&gt;andReturn(false);&#x000A;    	$command-&gt;shouldReceive('getFailureMessage')-&gt;once()-&gt;andReturn('User already exists on the system.');&#x000A;    	$userControl = new UserControl($command);&#x000A;    	$userControl-&gt;add('George');&#x000A;    }</pre>
    <p>We modified our test to expect our own custom exception, <code>ExceptionCannotAddUser</code>. The rest of the test is unchanged.</p>
    <pre>class ExceptionCannotAddUser extends Exception {&#x000A;    &#x000A;    	public function __construct($userName, $reason) {&#x000A;    		$message = sprintf(&#x000A;    			'Cannot add user %s. Reason: %s',&#x000A;    			$userName, $reason&#x000A;    		);&#x000A;    		parent::__construct($message, 13, null);&#x000A;    	}&#x000A;    }</pre>
    <p>The class that implements our custom exception is like any other class, but it has to extend <code>Exception</code>. Using custom exceptions also provides us a great place to do all the presentation related string manipulation. Moving the concatenation here, we also eliminated presentation from the business logic and respected the single responsibility principle.</p>
    <pre>public function add($username) {&#x000A;    	if (!$username) {&#x000A;    		throw new Exception('User can not be empty!');&#x000A;    	}&#x000A;    &#x000A;    	if(!$this-&gt;systemCommand-&gt;execute(sprintf('adduser %s', $username))) {&#x000A;    		throw new ExceptionCannotAddUser($username, $this-&gt;systemCommand-&gt;getFailureMessage());&#x000A;    	}&#x000A;    }</pre>
    <p>Throwing our own exception is just a matter of changing the old "<code>throw</code>" command to the new one and sending in two parameters instead of composing the message here. Of course all tests are passing.</p>
    <pre>PHPUnit 3.7.28 by Sebastian Bergmann.&#x000A;    &#x000A;    ..&#x000A;    &#x000A;    Time: 18 ms, Memory: 3.00Mb&#x000A;    &#x000A;    OK (2 tests, 4 assertions)&#x000A;    &#x000A;    Done.</pre>
    <h3>Catching Exceptions in Your MVC</h3>
    <p>Exceptions must be caught at some point, unless you want your user to see them as they are. If you are using an MVC framework you will probably want to catch exceptions in the controller or model. After the exception is caught, it is transformed in a message to the user and rendered inside your view. A common way to achieve this is to create a "<code>tryAction($action)</code>" method in your application's base controller or model and always call it with the current action. In that method you can do the catching logic and nice message generation to suit your framework.</p>
    <p>If you do not use a web framework, or a web interface for that matter, your presentation layer should take care of catching and transforming these exceptions.</p>
    <p>If you develop a library, catching your exceptions will be the responsibility of your clients.</p>
    <hr>
    <h2>Final Thoughts</h2>
    <p>That's it. We traversed all the layers of our application. We validated in JavaScript, HTML and in our models. We've thrown and caught exceptions from our business logic and even created our own custom exceptions. This approach to validation and exception handling can be applied from small to big projects without any severe problems. However if your validation logic is getting very complex, and different parts of your project uses overlapping parts of logic, you may consider extracting all validations that can be done at a specific level to a validation service or validation provider. These levels may include, but not need to be limited to JavaScript validator, backend PHP validator, third party communication validator and so on.</p>
    <p>Thank you for reading. Have a nice day.</p>
    </div>
]]>
</Body>
<Summary>Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached...</Summary>
<Website>http://code.tutsplus.com/tutorials/validation-and-exception-handling-from-the-ui-to-the-backend--net-36697</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/41059/guest@my.umbc.edu/ef8d7a6da4e3f7ebd8467b7d3dfda187/api/pixel</TrackingUrl>
<Tag>css</Tag>
<Tag>development</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>php</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>Thu, 23 Jan 2014 14:41:29 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="false" id="109862" important="false" status="posted" url="https://my3.my.umbc.edu/posts/109862">
<Title>Ken Corbett &#8217;14, Media and Communication Studies, on WYPR&#8217;s Maryland Morning</Title>
<Body>
<![CDATA[
    <div class="html-content">American jazz singer and songwriter Billie Holiday has a Baltimore connection. She spent the bulk of her childhood on a block of South Durham Street in Upper Fells Point. Residents and artists in that neighborhood are honoring her by starting a project that pays tribute to Holiday while also bringing new life to the block. Media and communication studies student Ken Corbett is an intern for WYPR’s Maryland Morning and produced and narrated a story about the project that aired on the program January 17. In December, Corbett spent a day on Holiday’s block on South Durham Street and put together …</div>
]]>
</Body>
<Summary>American jazz singer and songwriter Billie Holiday has a Baltimore connection. She spent the bulk of her childhood on a block of South Durham Street in Upper Fells Point. Residents and artists in...</Summary>
<Website>https://news.umbc.edu/ken-corbett-14-media-and-communication-studies-on-wyprs-maryland-morning/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/109862/guest@my.umbc.edu/4ed2d69844c40b208a2fb5ecdfc51d18/api/pixel</TrackingUrl>
<Tag>cahss</Tag>
<Tag>mcs</Tag>
<Tag>policy-and-society</Tag>
<Group token="umbc-news">UMBC News</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/umbc-news</GroupUrl>
<AvatarUrl>https://assets2-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/xsmall.png?1632921809</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/original.png?1632921809</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/xxlarge.png?1632921809</AvatarUrl>
<AvatarUrl size="xlarge">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/xlarge.png?1632921809</AvatarUrl>
<AvatarUrl size="large">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/large.png?1632921809</AvatarUrl>
<AvatarUrl size="medium">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/medium.png?1632921809</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/small.png?1632921809</AvatarUrl>
<AvatarUrl size="xsmall">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/xsmall.png?1632921809</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/001/944/2c79aeea85b1abb37f8cf9fbcdc382b0/xxsmall.png?1632921809</AvatarUrl>
<Sponsor>UMBC News</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>false</CommentsAllowed>
<PostedAt>Thu, 23 Jan 2014 14:27:22 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="40551" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40551">
<Title>Bits Blog: IBM Happy to Be Rid of Low-End Server Business</Title>
<Body>
<![CDATA[
    <div class="html-content">Cheap servers were a drag on Big Blue. Now the company plans to focus on computer hardware with better profit margins.<br><div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fbits.blogs.nytimes.com%2F2014%2F01%2F23%2Fibm-happy-to-be-rid-of-low-end-server-business%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+IBM+Happy+to+Be+Rid+of+Low-End+Server+Business" 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%2Fbits.blogs.nytimes.com%2F2014%2F01%2F23%2Fibm-happy-to-be-rid-of-low-end-server-business%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+IBM+Happy+to+Be+Rid+of+Low-End+Server+Business" 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%2Fbits.blogs.nytimes.com%2F2014%2F01%2F23%2Fibm-happy-to-be-rid-of-low-end-server-business%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+IBM+Happy+to+Be+Rid+of+Low-End+Server+Business" 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%2Fbits.blogs.nytimes.com%2F2014%2F01%2F23%2Fibm-happy-to-be-rid-of-low-end-server-business%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+IBM+Happy+to+Be+Rid+of+Low-End+Server+Business" 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%2Fbits.blogs.nytimes.com%2F2014%2F01%2F23%2Fibm-happy-to-be-rid-of-low-end-server-business%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+IBM+Happy+to+Be+Rid+of+Low-End+Server+Business" 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/186529514097/u/0/f/640387/c/34625/s/36467f48/sc/21/rc/1/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529514097/u/0/f/640387/c/34625/s/36467f48/sc/21/rc/1/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529514097/u/0/f/640387/c/34625/s/36467f48/sc/21/rc/2/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529514097/u/0/f/640387/c/34625/s/36467f48/sc/21/rc/2/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529514097/u/0/f/640387/c/34625/s/36467f48/sc/21/rc/3/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529514097/u/0/f/640387/c/34625/s/36467f48/sc/21/rc/3/rc.img" style="max-width: 100%; height: auto;"></a><br><br><a href="http://da.feedsportal.com/r/186529514097/u/0/f/640387/c/34625/s/36467f48/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529514097/u/0/f/640387/c/34625/s/36467f48/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>Cheap servers were a drag on Big Blue. Now the company plans to focus on computer hardware with better profit margins.      </Summary>
<Website>http://bits.blogs.nytimes.com/2014/01/23/ibm-happy-to-be-rid-of-low-end-server-business/?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40551/guest@my.umbc.edu/666d77193871ace38d088868bf0a5bc6/api/pixel</TrackingUrl>
<Tag>cloud-computing</Tag>
<Tag>enterprise-computing</Tag>
<Tag>international-business-machines-corporation</Tag>
<Tag>lenovo-group</Tag>
<Tag>mills-steve</Tag>
<Tag>new</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>Thu, 23 Jan 2014 14:19:07 -0500</PostedAt>
<EditAt>Thu, 23 Jan 2014 17:59:53 -0500</EditAt>
</NewsItem>

</News>
