<?xml version="1.0"?>
<News hasArchived="true" page="7859" pageCount="10751" pageSize="10" timestamp="Sat, 15 Aug 2026 18:03:30 -0400" url="https://my3.my.umbc.edu/posts.xml?mode=activity&amp;page=7859">
<NewsItem contentIssues="true" id="40674" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40674">
<Title>Bits Blog: Dell Moves Toward Cheap Networking</Title>
<Body>
<![CDATA[
    <div class="html-content">Dell wants to do the same thing to the networking business that the lower costs of open-source operating systems did to destroy the old computer server business.<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%2F28%2Fdell-moves-toward-cheap-networking%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+Dell+Moves+Toward+Cheap+Networking" 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%2F28%2Fdell-moves-toward-cheap-networking%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+Dell+Moves+Toward+Cheap+Networking" 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%2F28%2Fdell-moves-toward-cheap-networking%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+Dell+Moves+Toward+Cheap+Networking" 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%2F28%2Fdell-moves-toward-cheap-networking%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+Dell+Moves+Toward+Cheap+Networking" 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%2F28%2Fdell-moves-toward-cheap-networking%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+Dell+Moves+Toward+Cheap+Networking" 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/187557104897/u/0/f/640387/c/34625/s/3673a493/sc/4/rc/1/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/187557104897/u/0/f/640387/c/34625/s/3673a493/sc/4/rc/1/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/187557104897/u/0/f/640387/c/34625/s/3673a493/sc/4/rc/2/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/187557104897/u/0/f/640387/c/34625/s/3673a493/sc/4/rc/2/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/187557104897/u/0/f/640387/c/34625/s/3673a493/sc/4/rc/3/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/187557104897/u/0/f/640387/c/34625/s/3673a493/sc/4/rc/3/rc.img" style="max-width: 100%; height: auto;"></a><br><br><a href="http://da.feedsportal.com/r/187557104897/u/0/f/640387/c/34625/s/3673a493/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/187557104897/u/0/f/640387/c/34625/s/3673a493/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>Dell wants to do the same thing to the networking business that the lower costs of open-source operating systems did to destroy the old computer server business.      </Summary>
<Website>http://bits.blogs.nytimes.com/2014/01/28/dell-moves-toward-cheap-networking/?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40674/guest@my.umbc.edu/9702198e2dec1a654948042978e8e032/api/pixel</TrackingUrl>
<Tag>cisco-systems-inc</Tag>
<Tag>cisco-systems-inc-csco-nasdaq</Tag>
<Tag>cloud-computing</Tag>
<Tag>dell-inc</Tag>
<Tag>dell-inc-dell-nasdaq</Tag>
<Tag>enterprise-computing</Tag>
<Tag>hewlett-packard-company</Tag>
<Tag>hewlett-packard-company-hpq-nyse</Tag>
<Tag>juniper-networks-inc</Tag>
<Tag>juniper-networks-inc-jnpr-nyse</Tag>
<Tag>new</Tag>
<Tag>open-source-software</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>Tue, 28 Jan 2014 09:00:38 -0500</PostedAt>
<EditAt>Tue, 28 Jan 2014 12:44:39 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="41056" important="false" status="posted" url="https://my3.my.umbc.edu/posts/41056">
<Title>Setting Up a Local Mirror for Composer Packages With Satis</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <p>Installing all your PHP libraries with Composer is a great way to save time. But larger projects automatically tested and run at each commit to your software version control (SVC) system will take a long time to install all the required packages from the Internet. You want to run your tests as soon as possible through your continuous integration (CI) system so that you have fast feedback and quick reactions on failure. In this tutorial we will set up a local mirror to proxy all your packages required in your project's <code>composer.json</code> file. This will make our CI work much faster, install the packages over the local network or even hosted on the same machine, and make sure we have the specific versions of the packages always available.</p>
    <p></p>
    <hr>
    <h2>What Is Satis?</h2>
    <p>Satis is the name of the application we will use to mirror the various repositories for our project. It sits as a proxy between the Internet and your composer. Our solution will create a local mirror of a few packages and instruct our composer to use it instead of the sources found on the Internet.</p>
    <p>Here is an image that says more than a thousand words.</p>
    
    <img src="http://cdn.tutsplus.com/net/uploads/2014/01/Architecture.png" alt="Architecture" width="600" height="197" style="max-width: 100%; height: auto;"><br>
    
    <p>Our project will use composer as usual. It will be configured to use the local Satis server as the primary source. If a package is found there, it will be installed from there. If not, we will let composer use the default packagist.org to retrieve the package.</p>
    <hr>
    <h2>Getting Satis</h2>
    <p>Satis is available through composer, so installing it is very simple. In the attached source code archive, you will find Satis installed in the <code>Sources/Satis</code> folder. First we will install composer itself.</p>
    <pre>$ curl -sS <a href="https://getcomposer.org/installer">https://getcomposer.org/installer</a> | php&#x000A;    #!/usr/bin/env php All settings correct for using Composer Downloading...&#x000A;     Composer successfully installed to: /home/csaba/Personal/Programming/NetTuts/Setting up a local mirror for Composer packages with Satis/Sources/Satis/composer.phar Use it: php composer.phar</pre>
    <p>Then we will install Satis.</p>
    <pre>$ php composer.phar create-project composer/satis --stability=dev --keep-vcs Installing composer/satis (dev-master eddb78d52e8f7ea772436f2320d6625e18d5daf5)&#x000A;      - Installing composer/satis (dev-master master)&#x000A;        Cloning master&#x000A;     Created project in /home/csaba/Personal/Programming/NetTuts/Setting up a local mirror for Composer packages with Satis/Sources/Satis/satis Loading composer repositories with package information Installing dependencies (including require-dev) from lock file&#x000A;      - Installing symfony/process (dev-master 27b0fc6)&#x000A;        Cloning 27b0fc645a557b2fc7bc7735cfb05505de9351be&#x000A;    &#x000A;      - Installing symfony/finder (v2.4.0-BETA1)&#x000A;        Downloading: 100%&#x000A;    &#x000A;      - Installing symfony/console (dev-master f44cc6f)&#x000A;        Cloning f44cc6fabdaa853335d7f54f1b86c99622db518a&#x000A;    &#x000A;      - Installing seld/jsonlint (1.1.1)&#x000A;        Downloading: 100%&#x000A;    &#x000A;      - Installing justinrainbow/json-schema (1.1.0)&#x000A;        Downloading: 100%&#x000A;    &#x000A;      - Installing composer/composer (dev-master f8be812)&#x000A;        Cloning f8be812a496886c84918d6dd1b50db5c16da3cc3&#x000A;    &#x000A;      - Installing twig/twig (v1.14.1)&#x000A;        Downloading: 100%&#x000A;     symfony/console suggests installing symfony/event-dispatcher () Generating autoload files</pre>
    <hr>
    <h2>Configuring Satis</h2>
    <p>Satis is configured by a JSON file very similar to composer. You can use whatever name you want for your file and specify it for usage later. We will use "<code>mirrored-packages.conf</code>".</p>
    <pre>{&#x000A;        "name": "NetTuts Composer Mirror",&#x000A;        "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;        "repositories": [&#x000A;            { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;            { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;        ],&#x000A;    &#x000A;        "require": {&#x000A;            "monolog/monolog": "syneto-dev",&#x000A;            "mockery/mockery": "*",&#x000A;            "phpunit/phpunit": "*"&#x000A;        },&#x000A;        "require-dependencies": true&#x000A;    }</pre>
    <p>Let's analyze this configuration file.</p>
    <ul>
    <li>
    <code>name</code> - represents a string that will be shown on the web interface of our mirror.</li>
    <li>
    <code>homepage</code> - is the web address where our packages will be kept. This does not tell our web server to use that address and port, it is rather just information of a working configuration. We will set up the access to it on that addres and port later.</li>
    <li>
    <code>repositories</code> - a list of repositories ordered by preference. In our example, the first repository is a Github fork of the monolog logging libraries. It has some modifications and we want to use that specific fork when installing monolog. The type of this repository is "<code>vcs</code>". The second repository is of type "<code>composer</code>". Its URL is the default packagist.org site.</li>
    <li>
    <code>require</code> - lists the packages we want to mirror. It can represent a specific package with a specific version or branch, or any version for that matter. It uses the same syntax as your "<code>require</code>" or "<code>require-dev</code>" in your <code>composer.json</code>.</li>
    <li>
    <code>require-dependencies</code> - is the final option in our example. It will tell Satis to mirror not only the packages we specified in the "<code>require</code>" section but also all their dependencies.</li>
    </ul>
    <p>To quickly try out our settings we first need to tell Satis to create the mirrors. Run this command in the folder where you installed Satis.</p>
    <pre>$ php ./satis/bin/satis build ./mirrored-packages.conf ./packages-mirror Scanning packages Writing packages.json Writing web view</pre>
    <p>While the process is taking place, you will see how Satis mirrors each found version of the required packages. Be patient it may take a while to build all those packages.</p>
    <p>Satis requires that <code>date.timezone</code> to be specified in the <code>php.ini</code> file, so make sure it is and set to your local timezone. Otherwise an error will appear.</p>
    <pre>[Twig_Error_Runtime]&#x000A;      An exception has been thrown during the rendering of a template&#x000A;    ("date_default_timezone_get(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set) function.</pre>
    <p>Then we can run a PHP server instance in our console pointing to the recently created repository. PHP 5.4 or newer is required.</p>
    <pre>$ php -S localhost:4680 -t ./packages-mirror/ PHP 5.4.22-pl0-gentoo Development Server started at Sun Dec  8 14:47:48 2013 Listening on <a href="http://localhost:4680">http://localhost:4680</a> Document root is /home/csaba/Personal/Programming/NetTuts/Setting up a local mirror for Composer packages with Satis/Sources/Satis/packages-mirror Press Ctrl-C to quit.&#x000A;    [Sun Dec  8 14:48:09 2013] 127.0.0.1:56999 [200]: /&#x000A;    [Sun Dec  8 14:48:09 2013] 127.0.0.1:57000 [404]: /favicon.ico - No such file or directory</pre>
    <p>And we can now browse our mirrored packages and even search for specific ones by pointing our web browser to <code><a href="http://localhost:4680">http://localhost:4680</a></code>:</p>
    
    <img src="http://cdn.tutsplus.com/net/uploads/2014/01/MirrorWebpage.png" alt="MirrorWebpage" width="600" height="567" style="max-width: 100%; height: auto;"><br>
    
    <hr>
    <h2>Let's Host It on Apache</h2>
    <p>If you have a running Apache at hand, creating a virtual host for Satis will be quite simple.</p>
    <pre>Listen 4680&#x000A;    &#x000A;    &lt;Directory "/path/to/your/packages-mirror"&gt; Options -Indexes FollowSymLinks AllowOverride all Order allow,deny Allow from all&#x000A;    &lt;/Directory&gt;&#x000A;    &#x000A;    &lt;VirtualHost *:4680&gt; DocumentRoot "/path/to/your/packages-mirror" ServerName 127.0.0.1:4680 ServerAdmin <a href="mailto:admin@example.com">admin@example.com</a>&#x000A;     ErrorLog syslog:user&#x000A;    &#x000A;    &lt;/VirtualHost&gt;</pre>
    <p>We just use a <code>.conf</code> file like this, put in Apache's <code>conf.d</code> folder, usually <code>/etc/apache2/conf.d</code>. It creates a virtual host on the 4680 port and points it to our folder. Of course you can use whatever port you want.</p>
    <hr>
    <h2>Updating Our Mirrors</h2>
    <p>Satis can not automatically update the mirrors unless we tell it. So the easiest way, on any UNIX like system, is to just add a cron job to your system. That would be very easy, and just a simple script to execute our update command.</p>
    <pre>#!/bin/bash php /full/path/to/satis/bin/satis build \&#x000A;    /full/path/to/mirrored-packages.conf \&#x000A;    /full/path/to/packages-mirror</pre>
    <p>The drawback of this solution is that it is static. We have to manually update the <code>mirrored-packages.conf</code> every time we add another package to our project's <code>composer.json</code>. If you are part of a team in a company with a big project and a continuous integration server, you can't rely on people remembering to add the packages on the server. They may not even have permissions to access the CI infrastructure.</p>
    <hr>
    <h2>Dynamically Updating Satis Configuration</h2>
    <p>It's time for a PHP TDD exercise. If you just want your code ready and running, check out the source code attached to this tutorial.</p>
    <pre>require_once __DIR__ . '/../../../../vendor/autoload.php';&#x000A;     class SatisUpdaterTest extends PHPUnit_Framework_TestCase {&#x000A;        function testBehavior() {&#x000A;            $this-&gt;assertTrue(true);&#x000A;        }&#x000A;    }</pre>
    <p>As usual we start with a degenerative test, just enough to make sure we have a working testing framework. You may notice that I have quite a strange looking require_once line, this is because I want to avoid having to reinstall PHPUnit and Mockery for each small project. So I have them in a <code>vendor</code> folder in my <code>NetTuts</code>' root. You should just install them with composer and drop the <code>require_once</code> line altogether.</p>
    <pre>class SatisUpdaterTest extends PHPUnit_Framework_TestCase {&#x000A;    	function testDefaultConfigFile() {&#x000A;    		$expected = '{&#x000A;        "name": "NetTuts Composer Mirror",&#x000A;        "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;        "repositories": [&#x000A;            { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;            { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;        ],&#x000A;    &#x000A;        "require": {&#x000A;        },&#x000A;        "require-dependencies": true&#x000A;    }';&#x000A;    		$actual = $this-&gt;parseComposerConf('');&#x000A;    		$this-&gt;assertEquals($expected, $actual);&#x000A;    	}&#x000A;    }</pre>
    <p>That looks about right. All the fields except "<code>require</code>" are static. We need to generate only the packages. The repositories are pointing to our private git clones and to packagist as needed. Managing those is more of a sysadmin job than a software developer's.</p>
    <p>Of course this fails with:</p>
    <pre>PHP Fatal error:  Call to undefined method SatisUpdaterTest::parseComposerConf()</pre>
    <p>Fixing that is easy.</p>
    <pre>private function parseComposerConf($string) {&#x000A;    }</pre>
    <p>I just added an empty method with the required name, as private, to our test class. Cool, but now we have another error.</p>
    <pre>PHPUnit_Framework_ExpectationFailedException : Failed asserting that null matches expected '{ ... }'</pre>
    <p>So, null does not match our string containing all that default configuration.</p>
    <pre>private function parseComposerConf($string) {&#x000A;        return '{&#x000A;    "name": "NetTuts Composer Mirror",&#x000A;    "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;    "repositories": [&#x000A;        { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;        { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;    ],&#x000A;    &#x000A;    "require": {&#x000A;    },&#x000A;    "require-dependencies": true&#x000A;    }';&#x000A;    }</pre>
    <p>OK, that works. All tests are passing.</p>
    <pre>PHPUnit 3.7.28 by Sebastian Bergmann. Time: 15 ms, Memory: 2.50Mb OK (1 test, 1 assertion)</pre>
    <p>But we introduced a horrible duplication. All that static text in two places, written character by character in two different places. Let's fix it:</p>
    <pre>class SatisUpdaterTest extends PHPUnit_Framework_TestCase {&#x000A;    	static $DEFAULT_CONFIG = '{&#x000A;        "name": "NetTuts Composer Mirror",&#x000A;        "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;        "repositories": [&#x000A;            { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;            { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;        ],&#x000A;    &#x000A;        "require": {&#x000A;        },&#x000A;        "require-dependencies": true&#x000A;    }';&#x000A;    &#x000A;    	function testDefaultConfigFile() {&#x000A;    		$expected =  self::$DEFAULT_CONFIG;&#x000A;    &#x000A;    		$actual = $this-&gt;parseComposerConf('');&#x000A;    		$this-&gt;assertEquals($expected, $actual);&#x000A;    	}&#x000A;    &#x000A;    	private function parseComposerConf($string) {&#x000A;    		return self::$DEFAULT_CONFIG;&#x000A;    	}&#x000A;    }</pre>
    <p>Ahhh! That's better.</p>
    <pre>	function testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration() {&#x000A;        $expected = self::$DEFAULT_CONFIG;&#x000A;    &#x000A;        $actual = $this-&gt;parseComposerConf('{"require": {}}');&#x000A;        $this-&gt;assertEquals($expected, $actual);&#x000A;    }</pre>
    <p>Well. That also passes. But it also highlights some duplication and useless assignment.</p>
    <pre>	function testDefaultConfigFile() {&#x000A;        $actual = $this-&gt;parseComposerConf('');&#x000A;        $this-&gt;assertEquals(self::$DEFAULT_CONFIG, $actual);&#x000A;    }&#x000A;     function testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration() {&#x000A;        $actual = $this-&gt;parseComposerConf('{"require": {}}');&#x000A;        $this-&gt;assertEquals(self::$DEFAULT_CONFIG, $actual);&#x000A;    }</pre>
    <p>We inlined the <code>$expected</code> variable. <code>$actual</code> could also be inlined, but I like it better this way. It keeps the focus on what is tested.</p>
    <p>Now we have another problem. The next test I want to write would look like this:</p>
    <pre>function testARequiredPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;        '{"require": {&#x000A;            "Mockery/Mockery": "&gt;=0.7.2"&#x000A;        }}');&#x000A;        $this-&gt;assertContains('"Mockery/Mockery": "&gt;=0.7.2"', $actual);&#x000A;    }</pre>
    <p>But after writing the simple implementation, we will notice it requires <code>json_decode()</code> and <code>json_encode()</code>. And of course these functions reformat our string and matching strings will be difficult at best. We have to take a step back.</p>
    <pre>function testDefaultConfigFile() {&#x000A;        $actual = $this-&gt;parseComposerConf('');&#x000A;        $this-&gt;assertJsonStringEqualsJsonString($this-&gt;jsonRecode(self::$DEFAULT_CONFIG), $actual);&#x000A;    }&#x000A;     function testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration() {&#x000A;        $actual = $this-&gt;parseComposerConf('{"require": {}}');&#x000A;        $this-&gt;assertJsonStringEqualsJsonString($this-&gt;jsonRecode(self::$DEFAULT_CONFIG), $actual);&#x000A;    }&#x000A;     private function parseComposerConf($jsonConfig) {&#x000A;        return $this-&gt;jsonRecode(self::$DEFAULT_CONFIG);&#x000A;    }&#x000A;     private function jsonRecode($json) {&#x000A;        return json_encode(json_decode($json, true));&#x000A;    }</pre>
    <p>We changed our assertion method to compare JSON strings and we also recoded our <code>$actual</code> variable. <code>ParseComposerConf()</code> was also modified to use this method. You will see in a moment how it helps us. Our next test becomes more JSON specific.</p>
    <pre>function testARequiredPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('&gt;=0.7.2', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;    }</pre>
    <p>And making this test pass, along with the rest of the tests, is quite easy, again.</p>
    <pre>private function parseComposerConf($jsonConfig) {&#x000A;        $addedConfig = json_decode($jsonConfig, true);&#x000A;        $config = json_decode(self::$DEFAULT_CONFIG, true);&#x000A;        if (isset($addedConfig['require'])) {&#x000A;            $config['require'] = $addedConfig['require'];&#x000A;        }&#x000A;        return json_encode($config);&#x000A;    }</pre>
    <p>We take the input JSON string, decode it, and if it contains a "<code>require</code>" field we use that in our Satis configuration file instead. But we may want to mirror all versions of a package, not just the last one. So maybe we want to modify our test to check that the version is "*" in Satis, regardless of what exact version is in <code>composer.json</code>.</p>
    <pre>function testARequiredPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;    }</pre>
    <p>That obviously fails with a cool message:</p>
    <pre>PHPUnit_Framework_ExpectationFailedException : Failed asserting that two strings are equal. Expected :* Actual   :&gt;=0.7.2</pre>
    <p>Now, we need to actually edit our JSON before re-encoding it.</p>
    <pre>private function parseComposerConf($jsonConfig) {&#x000A;        $addedConfig = json_decode($jsonConfig, true);&#x000A;        $config = json_decode(self::$DEFAULT_CONFIG, true);&#x000A;        $config = $this-&gt;addNewRequires($addedConfig, $config);&#x000A;        return json_encode($config);&#x000A;    }&#x000A;     private function toAllVersions($config) {&#x000A;        foreach ($config['require'] as $package =&gt; $version) {&#x000A;            $config['require'][$package] = '*';&#x000A;        }&#x000A;        return $config;&#x000A;    }&#x000A;     private function addNewRequires($addedConfig, $config) {&#x000A;        if (isset($addedConfig['require'])) {&#x000A;            $config['require'] = $addedConfig['require'];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>To make the test pass we have to iterate over each element of the required packages array and set their version to '*'. See method <code>toAllVersion()</code> for more details. And to speed up this tutorial a little bit, we also extracted some private methods in the same step. This way, <code>parseComoserConf()</code> becomes very descriptive and easy to understand. We could also inline <code>$config</code> into the arguments of <code>addNewRequires()</code>, but for aesthetic reasons I left it on two lines.</p>
    <p>But what about "<code>require-dev</code>" in <code>composer.json</code>?</p>
    <pre>function testARquiredDevPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require-dev": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2",&#x000A;                "phpunit/phpunit": "3.7.28"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['phpunit/phpunit']);&#x000A;    }</pre>
    <p>That obviously fails. We can make it pass with just copy/pasting our if condition in <code>addNewRequires()</code>:</p>
    <pre>private function addNewRequires($addedConfig, $config) {&#x000A;        if (isset($addedConfig['require'])) {&#x000A;            $config['require'] = $addedConfig['require'];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        if (isset($addedConfig['require-dev'])) {&#x000A;            $config['require'] = $addedConfig['require-dev'];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>Yep, that makes it pass, but those duplicated if statements are nasty looking. Let's deal with them.</p>
    <pre>private function addNewRequires($addedConfig, $config) {&#x000A;        $config = $this-&gt;addRequire($addedConfig, 'require', $config);&#x000A;        $config = $this-&gt;addRequire($addedConfig, 'require-dev', $config);&#x000A;        return $config;&#x000A;    }&#x000A;     private function addRequire($addedConfig, $string, $config) {&#x000A;        if (isset($addedConfig[$string])) {&#x000A;            $config['require'] = $addedConfig[$string];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>We can be happy again, tests are green and we refactored our code. I think only one test is left to be written. What if we have both "<code>require</code>" and "<code>require-dev</code>" sections in <code>composer.json</code>?</p>
    <pre>function testItCanParseComposerJsonWithBothSections() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2"&#x000A;                },&#x000A;            "require-dev": {&#x000A;                "phpunit/phpunit": "3.7.28"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['phpunit/phpunit']);&#x000A;    }</pre>
    <p>That fails because the packages set by "<code>require-dev</code>" will overwrite those of "<code>require</code>" and we will have an error: </p>
    <pre>Undefined index: Mockery/Mockery</pre>
    <p>Just add a plus sign to merge the arrays, and we are done.</p>
    <pre>private function addRequire($addedConfig, $string, $config) {&#x000A;        if (isset($addedConfig[$string])) {&#x000A;            $config['require'] += $addedConfig[$string];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>Tests are passing. Our logic is finished. All we left to do is to extract the methods into their own file and class. The final version of the tests and the <code>SatisUpdater</code> class can be found in the attached source code. </p>
    <p>We can now modify our cron script to load our parser and run it on our <code>composer.json</code>. This will be specific to your projects' particular folders. Here is an example you can adapt to your system.</p>
    <pre>#!/usr/local/bin/php&#x000A;    &#x000A;    &lt;?php require_once __DIR__ . '/Configuration.php';&#x000A;    &#x000A;    $outputDir = '/path/to/your/packages-mirror';&#x000A;    $composerJsonFile = '/path/to/your/projects/composer.json';&#x000A;    $satisConf = '/path/to/your/mirrored-packages.conf';&#x000A;    &#x000A;    $satisUpdater = new SatisUpdater();&#x000A;    $conf = $satisUpdater-&gt;parseComposerConf(file_get_contents($composerJsonFile)); file_put_contents($satisConf, $conf);&#x000A;     system(sprintf('/path/to/satis/bin/satis build %s %s', $satisConf, $outputDir), $retval); exit($retval);</pre>
    <hr>
    <h2>Making Your Project Use the Mirror</h2>
    <p>We talked about a lot of things in this article, but we did not mention how we will instruct our project to use the mirror instead of the Internet. You know, the default is packagist.org? Unless we do something like this:</p>
    <pre>   "repositories": [&#x000A;            {&#x000A;                "type": "composer",&#x000A;                "url": "<a href="http://your-mirror-server:4680">http://your-mirror-server:4680</a>"&#x000A;            }&#x000A;        ],</pre>
    <p>That will make your mirror the first choise for composer. But adding just that into the <code>composer.json</code> of your project will not disable access to packagist.org. If a package can not be found on the local mirror, it will be downloaded from the Internet. If you wish to block this feature, you may also want to add the following line to the repositories section above:</p>
    <pre>"packagist": false</pre>
    <hr>
    <h2>Final Thoughts</h2>
    <p>That's it. Local mirror, with automatic adapting and updating packages. Your colleagues will never have to wait a long time while they or the CI server installs all the requirements of your projects. Have fun.</p>
    </div>
]]>
</Body>
<Summary>Installing all your PHP libraries with Composer is a great way to save time. But larger projects automatically tested and run at each commit to your software version control (SVC) system will take...</Summary>
<Website>http://code.tutsplus.com/tutorials/setting-up-a-local-mirror-for-composer-packages-with-satis--net-36726</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/41056/guest@my.umbc.edu/f2618511f0d80e07c7f59c40f2197dca/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>Tue, 28 Jan 2014 09:00:35 -0500</PostedAt>
<EditAt>Tue, 28 Jan 2014 09:00:34 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="40664" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40664">
<Title>Setting Up a Local Mirror for Composer Packages With Satis</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <a href="http://rss.buysellads.com/click.php?z=1260013&amp;k=d754f1e9ba63a736ba8ff5ece958f7dd&amp;a=36726&amp;c=558272831" rel="nofollow external" class="bo"><img src="http://rss.buysellads.com/img.php?z=1260013&amp;k=d754f1e9ba63a736ba8ff5ece958f7dd&amp;a=36726&amp;c=558272831" alt="" style="max-width: 100%; height: auto;"></a><p>Installing all your PHP libraries with Composer is a great way to save time. But larger projects automatically tested and run at each commit to your software version control (SVC) system will take a long time to install all the required packages from the Internet. You want to run your tests as soon as possible through your continuous integration (CI) system so that you have fast feedback and quick reactions on failure. In this tutorial we will set up a local mirror to proxy all your packages required in your project’s <code>composer.json</code> file. This will make our CI work much faster, install the packages over the local network or even hosted on the same machine, and make sure we have the specific versions of the packages always available.</p>
    <p></p>
    <hr>
    <h2>What Is Satis?</h2>
    <p>Satis is the name of the application we will use to mirror the various repositories for our project. It sits as a proxy between the Internet and your composer. Our solution will create a local mirror of a few packages and instruct our composer to use it instead of the sources found on the Internet.</p>
    <p>Here is an image that says more than a thousand words.</p>  <img src="http://cdn.tutsplus.com/net/uploads/2014/01/Architecture.png" alt="Architecture" width="600" height="197" style="max-width: 100%; height: auto;"><br> <p>Our project will use composer as usual. It will be configured to use the local Satis server as the primary source. If a package is found there, it will be installed from there. If not, we will let composer use the default packagist.org to retrieve the package.</p>
    <hr>
    <h2>Getting Satis</h2>
    <p>Satis is available through composer, so installing it is very simple. In the attached source code archive, you will find Satis installed in the <code>Sources/Satis</code> folder. First we will install composer itself.</p>
    <pre>$ curl -sS <a href="https://getcomposer.org/installer">https://getcomposer.org/installer</a> | php&#x000A;    #!/usr/bin/env php All settings correct for using Composer Downloading...&#x000A;     Composer successfully installed to: /home/csaba/Personal/Programming/NetTuts/Setting up a local mirror for Composer packages with Satis/Sources/Satis/composer.phar Use it: php composer.phar&#x000A;    </pre>
    <p>Then we will install Satis.</p>
    <pre>$ php composer.phar create-project composer/satis --stability=dev --keep-vcs Installing composer/satis (dev-master eddb78d52e8f7ea772436f2320d6625e18d5daf5)&#x000A;      - Installing composer/satis (dev-master master)&#x000A;        Cloning master&#x000A;     Created project in /home/csaba/Personal/Programming/NetTuts/Setting up a local mirror for Composer packages with Satis/Sources/Satis/satis Loading composer repositories with package information Installing dependencies (including require-dev) from lock file&#x000A;      - Installing symfony/process (dev-master 27b0fc6)&#x000A;        Cloning 27b0fc645a557b2fc7bc7735cfb05505de9351be&#x000A;    &#x000A;      - Installing symfony/finder (v2.4.0-BETA1)&#x000A;        Downloading: 100%&#x000A;    &#x000A;      - Installing symfony/console (dev-master f44cc6f)&#x000A;        Cloning f44cc6fabdaa853335d7f54f1b86c99622db518a&#x000A;    &#x000A;      - Installing seld/jsonlint (1.1.1)&#x000A;        Downloading: 100%&#x000A;    &#x000A;      - Installing justinrainbow/json-schema (1.1.0)&#x000A;        Downloading: 100%&#x000A;    &#x000A;      - Installing composer/composer (dev-master f8be812)&#x000A;        Cloning f8be812a496886c84918d6dd1b50db5c16da3cc3&#x000A;    &#x000A;      - Installing twig/twig (v1.14.1)&#x000A;        Downloading: 100%&#x000A;     symfony/console suggests installing symfony/event-dispatcher () Generating autoload files&#x000A;    </pre>
    <hr>
    <h2>Configuring Satis</h2>
    <p>Satis is configured by a JSON file very similar to composer. You can use whatever name you want for your file and specify it for usage later. We will use “<code>mirrored-packages.conf</code>“.</p>
    <pre>{&#x000A;        "name": "NetTuts Composer Mirror",&#x000A;        "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;        "repositories": [&#x000A;            { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;            { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;        ],&#x000A;    &#x000A;        "require": {&#x000A;            "monolog/monolog": "syneto-dev",&#x000A;            "mockery/mockery": "*",&#x000A;            "phpunit/phpunit": "*"&#x000A;        },&#x000A;        "require-dependencies": true&#x000A;    }</pre>
    <p>Let’s analyze this configuration file.</p>
    <ul>
    <li>
    <code>name</code> – represents a string that will be shown on the web interface of our mirror.</li>
    <li>
    <code>homepage</code> – is the web address where our packages will be kept. This does not tell our web server to use that address and port, it is rather just information of a working configuration. We will set up the access to it on that addres and port later.</li>
    <li>
    <code>repositories</code> – a list of repositories ordered by preference. In our example, the first repository is a Github fork of the monolog logging libraries. It has some modifications and we want to use that specific fork when installing monolog. The type of this repository is “<code>vcs</code>“. The second repository is of type “<code>composer</code>“. Its URL is the default packagist.org site.</li>
    <li>
    <code>require</code> – lists the packages we want to mirror. It can represent a specific package with a specific version or branch, or any version for that matter. It uses the same syntax as your “<code>require</code>” or “<code>require-dev</code>” in your <code>composer.json</code>.</li>
    <li>
    <code>require-dependencies</code> – is the final option in our example. It will tell Satis to mirror not only the packages we specified in the “<code>require</code>” section but also all their dependencies.</li>
    </ul>
    <p>To quickly try out our settings we first need to tell Satis to create the mirrors. Run this command in the folder where you installed Satis.</p>
    <pre>$ php ./satis/bin/satis build ./mirrored-packages.conf ./packages-mirror Scanning packages Writing packages.json Writing web view&#x000A;    </pre>
    <p>While the process is taking place, you will see how Satis mirrors each found version of the required packages. Be patient it may take a while to build all those packages.</p>
    <p>Satis requires that <code>date.timezone</code> to be specified in the <code>php.ini</code> file, so make sure it is and set to your local timezone. Otherwise an error will appear.</p>
    <pre>[Twig_Error_Runtime]&#x000A;      An exception has been thrown during the rendering of a template&#x000A;    ("date_default_timezone_get(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set) function. </pre>
    <p>Then we can run a PHP server instance in our console pointing to the recently created repository. PHP 5.4 or newer is required.</p>
    <pre>$ php -S localhost:4680 -t ./packages-mirror/ PHP 5.4.22-pl0-gentoo Development Server started at Sun Dec  8 14:47:48 2013 Listening on <a href="http://localhost:4680">http://localhost:4680</a> Document root is /home/csaba/Personal/Programming/NetTuts/Setting up a local mirror for Composer packages with Satis/Sources/Satis/packages-mirror Press Ctrl-C to quit.&#x000A;    [Sun Dec  8 14:48:09 2013] 127.0.0.1:56999 [200]: /&#x000A;    [Sun Dec  8 14:48:09 2013] 127.0.0.1:57000 [404]: /favicon.ico - No such file or directory&#x000A;    </pre>
    <p>And we can now browse our mirrored packages and even search for specific ones by pointing our web browser to <code><a href="http://localhost:4680">http://localhost:4680</a></code>:</p>  <img src="http://cdn.tutsplus.com/net/uploads/2014/01/MirrorWebpage.png" alt="MirrorWebpage" width="600" height="567" style="max-width: 100%; height: auto;"><br> <hr>
    <h2>Let’s Host It on Apache</h2>
    <p>If you have a running Apache at hand, creating a virtual host for Satis will be quite simple.</p>
    <pre>Listen 4680&#x000A;    &#x000A;    &lt;Directory "/path/to/your/packages-mirror"&gt; Options -Indexes FollowSymLinks AllowOverride all Order allow,deny Allow from all&#x000A;    &lt;/Directory&gt;&#x000A;    &#x000A;    &lt;VirtualHost *:4680&gt; DocumentRoot "/path/to/your/packages-mirror" ServerName 127.0.0.1:4680 ServerAdmin <a href="mailto:admin@example.com">admin@example.com</a>&#x000A;     ErrorLog syslog:user&#x000A;    &#x000A;    &lt;/VirtualHost&gt;&#x000A;    </pre>
    <p>We just use a <code>.conf</code> file like this, put in Apache’s <code>conf.d</code> folder, usually <code>/etc/apache2/conf.d</code>. It creates a virtual host on the 4680 port and points it to our folder. Of course you can use whatever port you want.</p>
    <hr>
    <h2>Updating Our Mirrors</h2>
    <p>Satis can not automatically update the mirrors unless we tell it. So the easiest way, on any UNIX like system, is to just add a cron job to your system. That would be very easy, and just a simple script to execute our update command.</p>
    <pre>#!/bin/bash php /full/path/to/satis/bin/satis build \&#x000A;    /full/path/to/mirrored-packages.conf \&#x000A;    /full/path/to/packages-mirror</pre>
    <p>The drawback of this solution is that it is static. We have to manually update the <code>mirrored-packages.conf</code> every time we add another package to our project’s <code>composer.json</code>. If you are part of a team in a company with a big project and a continuous integration server, you can’t rely on people remembering to add the packages on the server. They may not even have permissions to access the CI infrastructure.</p>
    <hr>
    <h2>Dynamically Updating Satis Configuration</h2>
    <p>It’s time for a PHP TDD exercise. If you just want your code ready and running, check out the source code attached to this tutorial.</p>
    <pre>require_once __DIR__ . '/../../../../vendor/autoload.php';&#x000A;     class SatisUpdaterTest extends PHPUnit_Framework_TestCase {&#x000A;        function testBehavior() {&#x000A;            $this-&gt;assertTrue(true);&#x000A;        }&#x000A;    }</pre>
    <p>As usual we start with a degenerative test, just enough to make sure we have a working testing framework. You may notice that I have quite a strange looking require_once line, this is because I want to avoid having to reinstall PHPUnit and Mockery for each small project. So I have them in a <code>vendor</code> folder in my <code>NetTuts</code>‘ root. You should just install them with composer and drop the <code>require_once</code> line altogether.</p>
    <pre>class SatisUpdaterTest extends PHPUnit_Framework_TestCase {&#x000A;    	function testDefaultConfigFile() {&#x000A;    		$expected = '{&#x000A;        "name": "NetTuts Composer Mirror",&#x000A;        "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;        "repositories": [&#x000A;            { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;            { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;        ],&#x000A;    &#x000A;        "require": {&#x000A;        },&#x000A;        "require-dependencies": true&#x000A;    }';&#x000A;    		$actual = $this-&gt;parseComposerConf('');&#x000A;    		$this-&gt;assertEquals($expected, $actual);&#x000A;    	}&#x000A;    }</pre>
    <p>That looks about right. All the fields except “<code>require</code>” are static. We need to generate only the packages. The repositories are pointing to our private git clones and to packagist as needed. Managing those is more of a sysadmin job than a software developer’s.</p>
    <p>Of course this fails with:</p>
    <pre>PHP Fatal error:  Call to undefined method SatisUpdaterTest::parseComposerConf()</pre>
    <p>Fixing that is easy.</p>
    <pre>private function parseComposerConf($string) {&#x000A;    }</pre>
    <p>I just added an empty method with the required name, as private, to our test class. Cool, but now we have another error.</p>
    <pre>PHPUnit_Framework_ExpectationFailedException : Failed asserting that null matches expected '{ ... }'</pre>
    <p>So, null does not match our string containing all that default configuration.</p>
    <pre>private function parseComposerConf($string) {&#x000A;        return '{&#x000A;    "name": "NetTuts Composer Mirror",&#x000A;    "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;    "repositories": [&#x000A;        { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;        { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;    ],&#x000A;    &#x000A;    "require": {&#x000A;    },&#x000A;    "require-dependencies": true&#x000A;    }';&#x000A;    }</pre>
    <p>OK, that works. All tests are passing.</p>
    <pre>PHPUnit 3.7.28 by Sebastian Bergmann. Time: 15 ms, Memory: 2.50Mb OK (1 test, 1 assertion)</pre>
    <p>But we introduced a horrible duplication. All that static text in two places, written character by character in two different places. Let’s fix it:</p>
    <pre>class SatisUpdaterTest extends PHPUnit_Framework_TestCase {&#x000A;    	static $DEFAULT_CONFIG = '{&#x000A;        "name": "NetTuts Composer Mirror",&#x000A;        "homepage": "<a href="http://localhost:4680">http://localhost:4680</a>",&#x000A;    &#x000A;        "repositories": [&#x000A;            { "type": "vcs", "url": "<a href="https://github.com/SynetoNet/monolog">https://github.com/SynetoNet/monolog</a>" },&#x000A;            { "type": "composer", "url": "<a href="https://packagist.org">https://packagist.org</a>" }&#x000A;        ],&#x000A;    &#x000A;        "require": {&#x000A;        },&#x000A;        "require-dependencies": true&#x000A;    }';&#x000A;    &#x000A;    	function testDefaultConfigFile() {&#x000A;    		$expected =  self::$DEFAULT_CONFIG;&#x000A;    &#x000A;    		$actual = $this-&gt;parseComposerConf('');&#x000A;    		$this-&gt;assertEquals($expected, $actual);&#x000A;    	}&#x000A;    &#x000A;    	private function parseComposerConf($string) {&#x000A;    		return self::$DEFAULT_CONFIG;&#x000A;    	}&#x000A;    }</pre>
    <p>Ahhh! That’s better.</p>
    <pre>	function testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration() {&#x000A;        $expected = self::$DEFAULT_CONFIG;&#x000A;    &#x000A;        $actual = $this-&gt;parseComposerConf('{"require": {}}');&#x000A;        $this-&gt;assertEquals($expected, $actual);&#x000A;    }</pre>
    <p>Well. That also passes. But it also highlights some duplication and useless assignment.</p>
    <pre>	function testDefaultConfigFile() {&#x000A;        $actual = $this-&gt;parseComposerConf('');&#x000A;        $this-&gt;assertEquals(self::$DEFAULT_CONFIG, $actual);&#x000A;    }&#x000A;     function testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration() {&#x000A;        $actual = $this-&gt;parseComposerConf('{"require": {}}');&#x000A;        $this-&gt;assertEquals(self::$DEFAULT_CONFIG, $actual);&#x000A;    }</pre>
    <p>We inlined the <code>$expected</code> variable. <code>$actual</code> could also be inlined, but I like it better this way. It keeps the focus on what is tested.</p>
    <p>Now we have another problem. The next test I want to write would look like this:</p>
    <pre>function testARequiredPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;        '{"require": {&#x000A;            "Mockery/Mockery": "&gt;=0.7.2"&#x000A;        }}');&#x000A;        $this-&gt;assertContains('"Mockery/Mockery": "&gt;=0.7.2"', $actual);&#x000A;    }</pre>
    <p>But after writing the simple implementation, we will notice it requires <code>json_decode()</code> and <code>json_encode()</code>. And of course these functions reformat our string and matching strings will be difficult at best. We have to take a step back.</p>
    <pre>function testDefaultConfigFile() {&#x000A;        $actual = $this-&gt;parseComposerConf('');&#x000A;        $this-&gt;assertJsonStringEqualsJsonString($this-&gt;jsonRecode(self::$DEFAULT_CONFIG), $actual);&#x000A;    }&#x000A;     function testEmptyRequiredPackagesInComposerJsonWillProduceDefaultConfiguration() {&#x000A;        $actual = $this-&gt;parseComposerConf('{"require": {}}');&#x000A;        $this-&gt;assertJsonStringEqualsJsonString($this-&gt;jsonRecode(self::$DEFAULT_CONFIG), $actual);&#x000A;    }&#x000A;     private function parseComposerConf($jsonConfig) {&#x000A;        return $this-&gt;jsonRecode(self::$DEFAULT_CONFIG);&#x000A;    }&#x000A;     private function jsonRecode($json) {&#x000A;        return json_encode(json_decode($json, true));&#x000A;    }</pre>
    <p>We changed our assertion method to compare JSON strings and we also recoded our <code>$actual</code> variable. <code>ParseComposerConf()</code> was also modified to use this method. You will see in a moment how it helps us. Our next test becomes more JSON specific.</p>
    <pre>function testARequiredPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('&gt;=0.7.2', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;    }</pre>
    <p>And making this test pass, along with the rest of the tests, is quite easy, again.</p>
    <pre>private function parseComposerConf($jsonConfig) {&#x000A;        $addedConfig = json_decode($jsonConfig, true);&#x000A;        $config = json_decode(self::$DEFAULT_CONFIG, true);&#x000A;        if (isset($addedConfig['require'])) {&#x000A;            $config['require'] = $addedConfig['require'];&#x000A;        }&#x000A;        return json_encode($config);&#x000A;    }</pre>
    <p>We take the input JSON string, decode it, and if it contains a “<code>require</code>” field we use that in our Satis configuration file instead. But we may want to mirror all versions of a package, not just the last one. So maybe we want to modify our test to check that the version is “*” in Satis, regardless of what exact version is in <code>composer.json</code>.</p>
    <pre>function testARequiredPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;    }</pre>
    <p>That obviously fails with a cool message:</p>
    <pre>PHPUnit_Framework_ExpectationFailedException : Failed asserting that two strings are equal. Expected :* Actual   :&gt;=0.7.2</pre>
    <p>Now, we need to actually edit our JSON before re-encoding it.</p>
    <pre>private function parseComposerConf($jsonConfig) {&#x000A;        $addedConfig = json_decode($jsonConfig, true);&#x000A;        $config = json_decode(self::$DEFAULT_CONFIG, true);&#x000A;        $config = $this-&gt;addNewRequires($addedConfig, $config);&#x000A;        return json_encode($config);&#x000A;    }&#x000A;     private function toAllVersions($config) {&#x000A;        foreach ($config['require'] as $package =&gt; $version) {&#x000A;            $config['require'][$package] = '*';&#x000A;        }&#x000A;        return $config;&#x000A;    }&#x000A;     private function addNewRequires($addedConfig, $config) {&#x000A;        if (isset($addedConfig['require'])) {&#x000A;            $config['require'] = $addedConfig['require'];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>To make the test pass we have to iterate over each element of the required packages array and set their version to ‘*’. See method <code>toAllVersion()</code> for more details. And to speed up this tutorial a little bit, we also extracted some private methods in the same step. This way, <code>parseComoserConf()</code> becomes very descriptive and easy to understand. We could also inline <code>$config</code> into the arguments of <code>addNewRequires()</code>, but for aesthetic reasons I left it on two lines.</p>
    <p>But what about “<code>require-dev</code>” in <code>composer.json</code>?</p>
    <pre>function testARquiredDevPackageInComposerWillBeInSatisAlso() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require-dev": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2",&#x000A;                "phpunit/phpunit": "3.7.28"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['phpunit/phpunit']);&#x000A;    }</pre>
    <p>That obviously fails. We can make it pass with just copy/pasting our if condition in <code>addNewRequires()</code>:</p>
    <pre>private function addNewRequires($addedConfig, $config) {&#x000A;        if (isset($addedConfig['require'])) {&#x000A;            $config['require'] = $addedConfig['require'];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        if (isset($addedConfig['require-dev'])) {&#x000A;            $config['require'] = $addedConfig['require-dev'];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>Yep, that makes it pass, but those duplicated if statements are nasty looking. Let’s deal with them.</p>
    <pre>private function addNewRequires($addedConfig, $config) {&#x000A;        $config = $this-&gt;addRequire($addedConfig, 'require', $config);&#x000A;        $config = $this-&gt;addRequire($addedConfig, 'require-dev', $config);&#x000A;        return $config;&#x000A;    }&#x000A;     private function addRequire($addedConfig, $string, $config) {&#x000A;        if (isset($addedConfig[$string])) {&#x000A;            $config['require'] = $addedConfig[$string];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>We can be happy again, tests are green and we refactored our code. I think only one test is left to be written. What if we have both “<code>require</code>” and “<code>require-dev</code>” sections in <code>composer.json</code>?</p>
    <pre>function testItCanParseComposerJsonWithBothSections() {&#x000A;        $actual = $this-&gt;parseComposerConf(&#x000A;            '{"require": {&#x000A;                "Mockery/Mockery": "&gt;=0.7.2"&#x000A;                },&#x000A;            "require-dev": {&#x000A;                "phpunit/phpunit": "3.7.28"&#x000A;            }}');&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['Mockery/Mockery']);&#x000A;        $this-&gt;assertEquals('*', json_decode($actual, true)['require']['phpunit/phpunit']);&#x000A;    }</pre>
    <p>That fails because the packages set by “<code>require-dev</code>” will overwrite those of “<code>require</code>” and we will have an error:</p>
    <pre>Undefined index: Mockery/Mockery</pre>
    <p>Just add a plus sign to merge the arrays, and we are done.</p>
    <pre>private function addRequire($addedConfig, $string, $config) {&#x000A;        if (isset($addedConfig[$string])) {&#x000A;            $config['require'] += $addedConfig[$string];&#x000A;            $config = $this-&gt;toAllVersions($config);&#x000A;        }&#x000A;        return $config;&#x000A;    }</pre>
    <p>Tests are passing. Our logic is finished. All we left to do is to extract the methods into their own file and class. The final version of the tests and the <code>SatisUpdater</code> class can be found in the attached source code.</p>
    <p>We can now modify our cron script to load our parser and run it on our <code>composer.json</code>. This will be specific to your projects’ particular folders. Here is an example you can adapt to your system.</p>
    <pre>#!/usr/local/bin/php&#x000A;    &#x000A;    &lt;?php require_once __DIR__ . '/Configuration.php';&#x000A;    &#x000A;    $outputDir = '/path/to/your/packages-mirror';&#x000A;    $composerJsonFile = '/path/to/your/projects/composer.json';&#x000A;    $satisConf = '/path/to/your/mirrored-packages.conf';&#x000A;    &#x000A;    $satisUpdater = new SatisUpdater();&#x000A;    $conf = $satisUpdater-&gt;parseComposerConf(file_get_contents($composerJsonFile)); file_put_contents($satisConf, $conf);&#x000A;     system(sprintf('/path/to/satis/bin/satis build %s %s', $satisConf, $outputDir), $retval); exit($retval);&#x000A;    </pre>
    <hr>
    <h2>Making Your Project Use the Mirror</h2>
    <p>We talked about a lot of things in this article, but we did not mention how we will instruct our project to use the mirror instead of the Internet. You know, the default is packagist.org? Unless we do something like this:</p>
    <pre>   "repositories": [&#x000A;            {&#x000A;                "type": "composer",&#x000A;                "url": "<a href="http://your-mirror-server:4680">http://your-mirror-server:4680</a>"&#x000A;            }&#x000A;        ],&#x000A;    </pre>
    <p>That will make your mirror the first choise for composer. But adding just that into the <code>composer.json</code> of your project will not disable access to packagist.org. If a package can not be found on the local mirror, it will be downloaded from the Internet. If you wish to block this feature, you may also want to add the following line to the repositories section above:</p>
    <pre>"packagist": false</pre>
    <hr>
    <h2>Final Thoughts</h2>
    <p>That’s it. Local mirror, with automatic adapting and updating packages. Your colleagues will never have to wait a long time while they or the CI server installs all the requirements of your projects. Have fun.</p>
    </div>
]]>
</Body>
<Summary>Installing all your PHP libraries with Composer is a great way to save time. But larger projects automatically tested and run at each commit to your software version control (SVC) system will take...</Summary>
<Website>http://feedproxy.google.com/~r/nettuts/~3/HhXIibR1I24/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40664/guest@my.umbc.edu/55ea852f7a0a9f37a6dffca9b4c6b500/api/pixel</TrackingUrl>
<Tag>composer</Tag>
<Tag>css</Tag>
<Tag>development</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>local-mirrors</Tag>
<Tag>mysql</Tag>
<Tag>php</Tag>
<Tag>satis</Tag>
<Tag>sql</Tag>
<Tag>tutorials</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>Tue, 28 Jan 2014 09:00:34 -0500</PostedAt>
<EditAt>Tue, 28 Jan 2014 09:00:34 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="40658" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40658">
<Title>&#8220;The Eighth Wonder Of The World!&#8221; Smashing Conference NYC 2014</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <table width="650">
    <tbody>
    <tr>
    <td>
    <div>
    <img src="http://statisches.auslieferung.commindo-media-ressourcen.de/advertisement.gif" alt="" style="max-width: 100%; height: auto;"><br><a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=1" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=1" alt="" style="max-width: 100%; height: auto;"></a> <a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=2" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=2" alt="" style="max-width: 100%; height: auto;"></a> <a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=3" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=3" alt="" style="max-width: 100%; height: auto;"></a>
    </div>
    </td>
    </tr>
    </tbody>
    </table>
    <p><a href="http://smashingconf.com/ny-2014/" rel="nofollow external" class="bo">SmashingConf NYC 2014</a> won’t solve all complex problems, but once it’s over you’ll know how to conquer yours. Our new conference in New York has 1 track, 2 conference days, 7 workshops, 18 excellent speakers and just 350 available tickets. With a heavy focus on networking and <strong>practical takeaways from real-life projects</strong>, you really don’t want to miss out on this one. <a href="http://smashingconf.com/ny-2014/registration" rel="nofollow external" class="bo">To the tickets.</a></p>
    <h3>About The Conference</h3>
    <p>When we organize a conference, we make sure that it provides a lot of value for everybody involved. <strong>We don’t care about volatile trends</strong> and we don’t believe in overnight success stories. Every success requires hard work and many <strong>failures</strong> along the way. There are lessons learned, takeaways, and  problems solved, but also many sketches and designs and ideas and prototypes thrown away because they didn’t work. We are interested in <strong>how problems are solved</strong>—ideas that failed, and <em>why</em> exactly they failed and what decisions were chosen instead.</p>
    <p><a href="http://smashingconf.com/ny-2014/" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/smashingconf-nyc-june-2014.png" width="520" height="460" alt="Smashing Conference New York 2014" style="max-width: 100%; height: auto;"></a><br><em><a href="http://smashingconf.com/ny-2014" rel="nofollow external" class="bo">SmashingConf NYC 2014</a> is a two-day event taking place at the TimesCenter in the very heart of New York. We won’t conquer all complex issues, but you’ll learn how to conquer yours.</em></p>
    <p>And this is what the conference is about: lessons learned in <strong>front-end, UX, workflow</strong> and other design-related disciplines. This is what we encourage our speakers to share and what we are all going to explore in the conversations before, during and after the conference days.</p>
    <p><strong>Networking is important</strong>, and we’ll provide many opportunities for networking throughout the conference. Ah, and the party — oh, the party! You’ll remember that one! Taking place on June 17–18th 2014 in the lovely <em>TimesCenter</em> just next to Times Square in New York City, the conference will be nothing short of smashing. <em>Booo-yah, baby!</em></p>
    <p><a href="http://smashingconf.com/ny-2014/registration" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/get-the-tickets.png" width="270" height="103" alt="To the tickets" style="max-width: 100%; height: auto;"></a></p>
    <h3>First Confirmed Speakers</h3>
    <p>Okay. Ready for the first confirmed speakers? Get set. Go!</p>
    <p><a href="http://smashingconf.com/ny-2014/speakers" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/first-ny-speakers-smashingconf.jpg" width="500" height="343" alt="First Speakers in SmashingConf New York City 2014" style="max-width: 100%; height: auto;"></a><br><em>First confirmed speakers, Brian Suda and Divya Manian. Divya might even volunteer to run the legendary party in the evening — you never know!</em></p>
    <ul>
    <li>
    <strong>Brian Suda</strong><br><span>(Data visualization, SVG)</span>
    </li>
    <li>
    <strong>Divya Manian</strong><br><span>(HTML5 Boilerplate, Adobe)</span>
    </li>
    <li>
    <strong>Scott Jehl</strong><br><span>(Responsive Web design</span>)</li>
    <li>
    <strong>Oliver Reichenstein</strong><br><span>(UX, typography, iA Writer)</span>
    </li>
    <li>
    <strong>Aarron Walter</strong><br><span>(User experience)</span>
    </li>
    <li>
    <strong>Jonathan Hoefler</strong><br><span>(Typography, H&amp;FJ)</span>
    </li>
    <li>
    <strong>Andy Hume</strong><br><span>(Front-end optimization)</span>
    </li>
    <li>
    <strong>Cassie McDaniel</strong><br><span>(Storytelling, UX, Mozilla)</span>
    </li>
    <li>
    <strong>Tim Brown</strong><br><span>(Web typography)</span>
    </li>
    <li>
    <strong>Eva-Lotta Lamm</strong><br><span>(Sketching, UX, UI, Google)</span>
    </li>
    <li>
    <strong>Jonathan Snook</strong><br><span>(Front-end development)</span>
    </li>
    <li>
    <strong>Mark Boulton</strong><br><span>(Grids, Web design)</span>
    </li>
    <li>
    <strong>Dan Rose</strong><br><span>(Photoshop, visual design)</span>
    </li>
    <li>
    <strong>Mystery Speaker</strong><br><span>(Someone you <em>definitely</em> know.)</span>
    </li>
    <li><span>…more speakers will be announced soon!</span></li>
    </ul>
    <p><a href="http://smashingconf.com/ny-2014/registration" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/get-the-tickets.png" width="270" height="103" alt="To the tickets" style="max-width: 100%; height: auto;"></a></p>
    <h3>Hands-On Workshops</h3>
    <p>We’ll also host <a href="http://smashingconf.com/ny-2014/workshops" rel="nofollow external" class="bo">hands-on full-day workshops</a> with practical techniques taught by masters of the craft. Be it responsive HTML newsletters, Photoshop techniques for responsive design, SVG techniques, or strategies for delightful UX, we carefully selected topics that will help you become better at what you do. So if you are going to attend the conference, why not attend a workshop as well?</p>
    <p>In fact, we reserved 50 <strong>early-bird tickets</strong> for $549, and if you <strong>book a workshop</strong> too, you’ll save $100 off the conference + workshop ticket price. Well, enough already. Tickets are on sale now, you know.</p>
    <p><a href="http://smashingconf.com/ny-2014/locations" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/timesquare.jpg" alt="Times Center" style="max-width: 100%; height: auto;"></a><br><em>If you always wanted to visit New York, or just happen to live around the corner (well, more or less), that’s just the opportunity you’ve been waiting for. <a href="https://shop.smashingmagazine.com/smashing-conference-new-york-2014.html" rel="nofollow external" class="bo">Grab your ticket.</a></em></p>
    <h3>Why This Conference Is For You</h3>
    <p>The conference is for you because you’ll learn many valuable techniques for your workflow, and you’ll meet fantastic, like-minded people from the industry. You’ll learn:</p>
    <ol>
    <li>Strategies for building <strong>fast responsive websites</strong>,</li>
    <li>Smart <strong>SVG</strong> techniques for scalable, future-proof designs,</li>
    <li>Clever <strong>Photoshop</strong> techniques for RWD workflow,</li>
    <li>Tricks to design and build <strong>responsive HTML newsletters</strong>,</li>
    <li>Insights about effective and smart problem-solving,</li>
    <li>Hands-on techniques for <strong>sketching interfaces</strong>,</li>
    <li>Techniques and guidelines for good Web typography,</li>
    <li>Guidelines for smarter front-end and CSS architecture,</li>
    <li>Advanced <strong>front-end tooling and setup</strong>,</li>
    <li>Guidelines for delightful, engaging UX,</li>
    <li>Mistakes and lessons learned from large projects,</li>
    <li>How to increase productivity and stay happy,</li>
    <li><span>…more practical takeaways from various areas of Web design.</span></li>
    </ol>
    <p><a href="http://smashingconf.com/ny-2014/registration" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/get-the-tickets.png" width="270" height="103" alt="To the tickets" style="max-width: 100%; height: auto;"></a></p>
    <h3>Ticket Sales And Pricing</h3>
    <p>Only 350 tickets are available, and we do everything possible to keep our prices fair and affordable. The price for two full conference days is $549 for <strong>early-bird</strong> tickets, and $599 for regular tickets. And again, if you book a workshop as well, you’ll save $100. Yay! <a href="http://smashingconf.com/ny-2014/registration" rel="nofollow external" class="bo">Get your ticket right away.</a></p>
    <p><a title="8 reasons why you should send your incredibly hard-working, deserving employee to the SmashingConf" href="http://smashingconf.com/pdf/8-reasons-for-Smashing-Conference-NYC-2014.pdf" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/convince-boss-preview.png" width="450" height="288" alt="SmashingConf NY: Convince Your Boss PDF" style="max-width: 100%; height: auto;"></a><br><em>“8 reasons why you should send your incredibly hard-working, deserving employee to the SmashingConf” (<a href="http://smashingconf.com/pdf/8-reasons-for-Smashing-Conference-NYC-2014.pdf" rel="nofollow external" class="bo">PDF</a>). Quite self-explanatory, really.</em></p>
    <p>We also prepared a neat <a href="http://smashingconf.com/pdf/8-reasons-for-Smashing-Conference-NYC-2014.pdf" rel="nofollow external" class="bo">Convince Your Boss (PDF)</a> (0.15 Mb) that you can use to convince your colleagues, friends, neighbors and total strangers to join you or send you to the event. We <em>know</em> that you will <em>not</em> be disappointed. Still not good enough? Well, tweet us <em>@smashingconf</em> — we can be quite convincing, too!</p>
    <h3>We Welcome Lovely Sponsors, You Know</h3>
    <p>We do everything possible to keep ticket prices affordable for everyone, and we welcome sponsors to help us create a truly unique, unforgettable conference experience. And you can be a major part of it.</p>
    <p>We have quite a number of attractive and creative <a href="http://smashingconf.com/pdf/Sponsorship-Opportunities-Smashing-Conference-2014-New-York-City.pdf" rel="nofollow external" class="bo">sponsorship packages</a> for you, and we are also flexible and would love to adjust them to your needs. So if you’re interested, please <strong>email</strong> us at <a href="mailto:hello@smashingconf.com" rel="nofollow external" class="bo">hello@smashingconf.com</a> — we’d love for you to be involved!</p>
    <h3>See You In New York!</h3>
    <p>We are looking forward to seeing you in New York, and who knows, perhaps we <em>can</em> tackle more complex problems that we thought we would! See you there!</p>
    <p><a href="http://smashingconf.com/ny-2014/locations" rel="nofollow external" class="bo"><img src="http://media.smashingmagazine.com/wp-content/uploads/2014/01/timescenter.jpg" alt="Times Center" style="max-width: 100%; height: auto;"></a><br><em>The conference will take place in the lovely TimesCenter, right off Times Square in New York. It doesn’t get any more central than that. <a href="https://shop.smashingmagazine.com/smashing-conference-new-york-2014.html" rel="nofollow external" class="bo">Grab your ticket.</a></em></p>
    <hr>
    <p><small>© Vitaly Friedman for <a href="http://www.smashingmagazine.com" rel="nofollow external" class="bo">Smashing Magazine</a>, 2014.</small></p>
    </div>
]]>
</Body>
<Summary>        SmashingConf NYC 2014 won’t solve all complex problems, but once it’s over you’ll know how to conquer yours. Our new conference in New York has 1 track, 2 conference days, 7 workshops, 18...</Summary>
<Website>http://www.smashingmagazine.com/2014/01/28/smashing-conference-new-york-2014/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40658/guest@my.umbc.edu/08d947b36a06b34da1916676b83c28b2/api/pixel</TrackingUrl>
<Tag>css</Tag>
<Tag>design</Tag>
<Tag>development</Tag>
<Tag>events</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>php</Tag>
<Tag>sql</Tag>
<Tag>web</Tag>
<Group token="retired-583">Web Developer - Build Group</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-583</GroupUrl>
<AvatarUrl>https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/original.jpg?1363101197</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xlarge.png?1363101197</AvatarUrl>
<AvatarUrl size="large">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/large.png?1363101197</AvatarUrl>
<AvatarUrl size="medium">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/medium.png?1363101197</AvatarUrl>
<AvatarUrl size="small">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/small.png?1363101197</AvatarUrl>
<AvatarUrl size="xsmall">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xsmall.png?1363101197</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/583/fc60f5d7abc2e080599bb6dc465db54d/xxsmall.png?1363101197</AvatarUrl>
<Sponsor>Web Developer - Build Group</Sponsor>
<PawCount>0</PawCount>
<CommentCount>0</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Tue, 28 Jan 2014 08:00:21 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="40659" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40659">
<Title>The New Smashing Mystery Riddle: Have You Figured It Out Yet?</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <table width="650">
    <tbody>
    <tr>
    <td>
    <div>
    <img src="http://statisches.auslieferung.commindo-media-ressourcen.de/advertisement.gif" alt="" style="max-width: 100%; height: auto;"><br><a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=1" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=1" alt="" style="max-width: 100%; height: auto;"></a> <a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=2" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=2" alt="" style="max-width: 100%; height: auto;"></a> <a href="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=target&amp;collection=smashing-rss&amp;position=3" rel="nofollow external" class="bo"><img src="http://auslieferung.commindo-media-ressourcen.de/random.php?mode=image&amp;collection=smashing-rss&amp;position=3" alt="" style="max-width: 100%; height: auto;"></a>
    </div>
    </td>
    </tr>
    </tbody>
    </table>
    <p>Remember the <a href="http://www.smashingmagazine.com/the-smashing-book-mystery-is-resolved-gifs-crazy-people-all-that-malarkey/" rel="nofollow external" class="bo">Smashing Book Mystery riddle</a> from a couple of months ago? Guess what: the Mystery continues! To celebrate the launch of the <a href="http://www.smashingmagazine.com/smashing-conference-new-york-2014/" rel="nofollow external" class="bo">SmashingConf NYC</a> and our lovely book, we’ve prepared a new riddle, and this time it’s not going to be any easier, but the reward is definitely worth the effort.</p>
    <p>How does it work? Below you’ll find an <strong>animated GIF that contains a hidden message</strong>. Once you resolved the mystery, you’ll see a Twitter hashtag displayed in plain text.</p>
    <p>Are you ready? Action! (<em>Hint: watch out for the file name</em>).</p>
    <p><img alt="Smashing Book Mystery" src="http://provide.smashingmagazine.com/mystery-riddle/catch-the-birds.gif" style="max-width: 100%; height: auto;"><br>
    <em>A hint: watch out for the file name.</em></p>
    <h3>What Can You Win?</h3>
    <p>Among the first readers who tweet us with the hidden hashtag, we’ll raffle a quite extraordinary, <em>smashing</em> prize (and a couple of other Smashing extras):</p>
    <ul>
    <li>a <strong>roundtrip flight to New York, USA</strong>,</li>
    <li>full accommodation in a fancy hotel,</li>
    <li>a ticket to the <a href="http://www.smashingconf.com/ny-2014/" rel="nofollow external" class="bo">Smashing Conference New York 2014</a>,</li>
    <li>any Smashing workshop ticket of your choice,</li>
    <li>full access to the Smashing eBook Library,</li>
    <li>a signed edition of the <a href="https://shop.smashingmagazine.com/smashing-book-4-new-perspectives-on-web-design.html?pk_campaign=front-page-shop" rel="nofollow external" class="bo">Smashing Book #4</a>,</li>
    <li>a truly Smashing laptop bag,</li>
    <li>your very own Smashing caricature, designed just for you.</li>
    </ul>
    <p>Please notice that to avoid spoilers, comments are closed for this post. And sorry, we aren’t going to make it too easy for you.</p>
    <p>Alright. Let’s get to work. Or have you already figured it out? ;-)</p>
    <hr>
    <p><small>© Vitaly Friedman for <a href="http://www.smashingmagazine.com" rel="nofollow external" class="bo">Smashing Magazine</a>, 2014.</small></p>
    </div>
]]>
</Body>
<Summary>        Remember the Smashing Book Mystery riddle from a couple of months ago? Guess what: the Mystery continues! To celebrate the launch of the SmashingConf NYC and our lovely book, we’ve...</Summary>
<Website>http://www.smashingmagazine.com/2014/01/28/the-new-smashing-mystery-riddle-smashingconf-ny/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40659/guest@my.umbc.edu/f4743eb98b6b3fdaea2648c1758bc6ec/api/pixel</TrackingUrl>
<Tag>css</Tag>
<Tag>design</Tag>
<Tag>developers-toolbox</Tag>
<Tag>development</Tag>
<Tag>html</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>php</Tag>
<Tag>sql</Tag>
<Tag>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>Tue, 28 Jan 2014 08:00:11 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="true" id="40661" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40661">
<Title>Bits Blog: The Crowdfunding Site Indiegogo Raises $40 Million</Title>
<Body>
<![CDATA[
    <div class="html-content">The infusion of money adds another notch of credibility and legitimacy to crowdfunding and the expanding ways the financing platform can be applied.<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%2F28%2Fcrowdfunding-site-indiegogo-raises-40-million%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+The+Crowdfunding+Site+Indiegogo+Raises+%2440+Million" 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%2F28%2Fcrowdfunding-site-indiegogo-raises-40-million%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+The+Crowdfunding+Site+Indiegogo+Raises+%2440+Million" 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%2F28%2Fcrowdfunding-site-indiegogo-raises-40-million%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+The+Crowdfunding+Site+Indiegogo+Raises+%2440+Million" 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%2F28%2Fcrowdfunding-site-indiegogo-raises-40-million%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+The+Crowdfunding+Site+Indiegogo+Raises+%2440+Million" 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%2F28%2Fcrowdfunding-site-indiegogo-raises-40-million%2F%3Fpartner%3Drss%26emc%3Drss&amp;t=Bits+Blog%3A+The+Crowdfunding+Site+Indiegogo+Raises+%2440+Million" 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/186529839140/u/0/f/640387/c/34625/s/36725cd5/sc/21/rc/1/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529839140/u/0/f/640387/c/34625/s/36725cd5/sc/21/rc/1/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529839140/u/0/f/640387/c/34625/s/36725cd5/sc/21/rc/2/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529839140/u/0/f/640387/c/34625/s/36725cd5/sc/21/rc/2/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529839140/u/0/f/640387/c/34625/s/36725cd5/sc/21/rc/3/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529839140/u/0/f/640387/c/34625/s/36725cd5/sc/21/rc/3/rc.img" style="max-width: 100%; height: auto;"></a><br><br><a href="http://da.feedsportal.com/r/186529839140/u/0/f/640387/c/34625/s/36725cd5/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529839140/u/0/f/640387/c/34625/s/36725cd5/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>The infusion of money adds another notch of credibility and legitimacy to crowdfunding and the expanding ways the financing platform can be applied.      </Summary>
<Website>http://bits.blogs.nytimes.com/2014/01/28/crowdfunding-site-indiegogo-raises-40-million/?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40661/guest@my.umbc.edu/f472f950d21105153d6d71adca2888ef/api/pixel</TrackingUrl>
<Tag>crowdfunding-internet</Tag>
<Tag>indiegogo-com</Tag>
<Tag>new</Tag>
<Tag>technology</Tag>
<Tag>venture-capital</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>Tue, 28 Jan 2014 05:00:51 -0500</PostedAt>
<EditAt>Tue, 28 Jan 2014 10:38:40 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="40657" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40657">
<Title>How to harness the power of a well-cropped photo</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <p><img alt="thumbnail" src="http://netdna.webdesignerdepot.com/uploads/2014/01/thumbnail10.jpg" width="200" height="160" style="max-width: 100%; height: auto;">As a website designer, I’ve always been fascinated by design (of any kind) that “works” and why.</p> <p>Of late, I’ve become particularly appreciative of the power of photography to anchor a visually appealing design that tells the right story, triggers the desired emotions, engages the viewer, and enhances the content (whether by breaking it up or calling it out).</p> <p>This newly refreshed appreciation for photos really took root in Venice this past fall.</p> <h1>Venice: described as the “Most beautiful city built by man”</h1> <p>I’d always heard about Venice! And I had expectations I tell you! Expectations of a quaint, beautiful and romantic city built on the water — utterly different than anything I’d ever seen.</p> <p>But in reality? … Venice is kind of a dump.</p> <p>It’s weather-beaten, crumbling and run down. When you view it as a whole, you see a bunch of disparate scenery mish-mashed together. It doesn’t mean to be, but Venice is poorly dressed in rags and tatters.</p> <p>But… When you look at Venice in tiny bits — when you let your eye “crop” out the surroundings and focus on one finite area — it’s a very different experience. There are so many details to delight the eye. Especially the eye of a designer who takes inspiration from everything that her gaze meets.</p> <p>After realizing this, I could shift my focus from “the whole” to the details and back again and appreciate ALL of it. Venice really <em>is</em> a beautiful city.</p> <p>The key takeaway here is, there is great power in cropping stuff <em>out,</em> so that what’s left <em>in</em> is a stronger.</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0022.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p><em>Picture of Venice, before cropping (left) and </em><em>after cropping (right).</em></p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0042.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p><em>One of my favorite “details” of Venice. The photo on the left is the raw image off the camera. Here, I did the cropping with the camera by zooming in to this detail. On the right, is the same image cropped further.</em></p> <p> </p> <h1>Photo crops that work</h1> <p>In the rest of this article, I’ll share some of my favorite photo crops from recent projects I’ve worked on.</p> <p>Some are personal projects. Some are professional work through Gravity Switch (the web design and marketing agency where I’m cofounder). But all of them tell a story.</p> <h2>Fitness Fusion</h2> <p>Fitness Fusion is a niche studio gym known for intense, creative and fun group training and 1-1 personal training. When redesigning their site, I poured over 600+ photos in search of pictures that’d work in an extremely horizontal format — not an uncommon format for a web banner image, but always a challenge.</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0062.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>At first glance, the image on the left looks like an unlikely candidate. But when rotated 90 degrees counter-clockwise, flipped horizontally, and with the (now backwards) word “coach” on the cap stamped out, it’s a powerful banner image. Perfect for the “Meet the Trainer” page!</p> <p>The crop above works because of:</p> <ul> <li>the warm lighting;</li> <li>the splashes of color;</li> <li>the genuine, focused, and approachable look in the eyes.</li> </ul> <p>Together with the smiley photo of the page body content, the overall feelings conveyed are approachability, expertise, hardworking, a down-to-earth attitude, and fun!</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0081.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>Here’s another of my favorite banner image crops from this project. By removing parts of the original image (cropping it), we get a more dynamic, intimate, and interesting visual experience — a cool image that shows hard-work, dedication and sweat. The subtle touch of the gym’s branding on the wristband was an accidental bonus.</p> <h2>Stockbridge School of Agriculture</h2> <p>Stockbridge School of Agriculture at UMass Amherst is known as the top school of its kind in the country. It has diverse programs that attracts very different types of students (e.g. the prospective who’s interested in Turf Management is very different than the person who’s interested in Sustainable Food &amp; Farming or Equine Management and so on.)</p> <p>The challenge was finding a home page photo that:</p> <ul> <li>felt appropriate for a school of agriculture;</li> <li>was a strong image to draw the viewer in;</li> <li>didn’t alienate any potential prospect to any potential program; Do no harm!</li> </ul> <p>On the technical side, we needed a photo that would work as a “floating crop” (i.e. the crop changes as the window is resized and it always needed to look good).</p> <p>We did a specific photo shoot just to capture the image for the home page. We got many great shots. We narrowed it down to these 7:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0101.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>Then we picked this one to go with:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0111.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>We played with the zoom level of the crop in PhotoShop, to confirm that the photo is strong as a floating crop. Here’s a few explorations:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0121.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>Here’s how it looks on mobile and desktop respectively:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0151.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <h2>Pioneer Valley Information Exchange – PVIX</h2> <p>Here’s a stock image that was used for a website in the healthcare industry… trying to show teamwork, family, positivity, energy, optimism:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0171.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>Here’s an image treatment I tried, but didn’t like. You lose too much context zoomed in this much:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0181.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>As an alternative (that I also didn’t like). I zoomed all the way out so you could see the whole photo. To make the width work right, I added blue space on the left:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0191.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>A happy medium (cropping some of the image) works best:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0201.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <h2>Parenting blog post</h2> <p>I was looking for a picture of my son to be the “banner image” in one of my <a href="http://xtinemark.wordpress.com" rel="nofollow external" class="bo">blog posts.</a></p> <p>I found this picture. I love the expression on my son’s face. But he’s so tiny in the image and you don’t appreciate that detail:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/0211.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p>Here’s how I cropped it. Works much better:</p> <p><img src="http://netdna.webdesignerdepot.com/uploads/2014/01/022.jpg" width="650" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"></p> <p> </p> <h1>Getting it right</h1> <p>We’ve all heard the saying, “A picture is worth 1,000 words.” But <em>how</em> do we craft our images so that they convey the 1,000 words that we want?</p> <p>Getting it right requires:</p> <p><strong>Good raw photos:</strong> If you can, hire a professional photographer so you can get shots and compositions that look good and are customized to your purpose. If you can’t hire a professional, there are many stock photography options — some are good, some less so. It’s best to have someone you trust to have an “eye for it” handle the job of photo hunting.</p> <p><strong>Good post-production:</strong> Color-correction, Clean-up and Cropping. I call it the three C’s.</p> <p><strong>Good photo placement:</strong> Incorporate photos into the design in the right spot, next to the right content, on the right page, and then the they will help tell the right story. Location, location, location.</p> <p> </p> <p><em><strong>How do you select images for your designs? How do you choose which parts of images to crop? Let us know in the comments.</strong></em></p> <p><br><br> </p>
    <table width="100%"> <tbody>
    <tr> <td> <a href="http://www.mightydeals.com/deal/design-kickstarter-bundle.html?ref=inwidget" rel="nofollow external" class="bo"><strong>The Design Kickstarter Bundle (400+ items) – only $17!</strong></a> </td> <td> <a href="http://www.mightydeals.com/?ref=inwidget" rel="nofollow external" class="bo"><br> <img src="http://mightydeals.com/web/images/widget-logo.png" height="40" width="90" alt="How to harness the power of a well cropped photo" style="max-width: 100%; height: auto;"><br> </a> </td> </tr> </tbody>
    </table> <p><br> </p> <a href="http://www.webdesignerdepot.com/2014/01/how-to-harness-the-power-of-a-well-cropped-photo/" rel="nofollow external" class="bo">Source</a> <br><div><table border="0"><tbody><tr><td>
    <a href="http://share.feedsportal.com/share/twitter/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2014%2F01%2Fhow-to-harness-the-power-of-a-well-cropped-photo%2F&amp;t=How+to+harness+the+power+of+a+well-cropped+photo" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/twitter.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/facebook/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2014%2F01%2Fhow-to-harness-the-power-of-a-well-cropped-photo%2F&amp;t=How+to+harness+the+power+of+a+well-cropped+photo" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/facebook.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/linkedin/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2014%2F01%2Fhow-to-harness-the-power-of-a-well-cropped-photo%2F&amp;t=How+to+harness+the+power+of+a+well-cropped+photo" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/linkedin.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/gplus/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2014%2F01%2Fhow-to-harness-the-power-of-a-well-cropped-photo%2F&amp;t=How+to+harness+the+power+of+a+well-cropped+photo" rel="nofollow external" class="bo"><img src="http://res3.feedsportal.com/social/googleplus.png" style="max-width: 100%; height: auto;"></a> <a href="http://share.feedsportal.com/share/email/?u=http%3A%2F%2Fwww.webdesignerdepot.com%2F2014%2F01%2Fhow-to-harness-the-power-of-a-well-cropped-photo%2F&amp;t=How+to+harness+the+power+of+a+well-cropped+photo" 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/186529773408/u/49/f/661066/c/35285/s/366ef1cb/sc/4/rc/1/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529773408/u/49/f/661066/c/35285/s/366ef1cb/sc/4/rc/1/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529773408/u/49/f/661066/c/35285/s/366ef1cb/sc/4/rc/2/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529773408/u/49/f/661066/c/35285/s/366ef1cb/sc/4/rc/2/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529773408/u/49/f/661066/c/35285/s/366ef1cb/sc/4/rc/3/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529773408/u/49/f/661066/c/35285/s/366ef1cb/sc/4/rc/3/rc.img" style="max-width: 100%; height: auto;"></a><br><br><a href="http://da.feedsportal.com/r/186529773408/u/49/f/661066/c/35285/s/366ef1cb/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529773408/u/49/f/661066/c/35285/s/366ef1cb/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>As a website designer, I’ve always been fascinated by design (of any kind) that “works” and why.   Of late, I’ve become particularly appreciative of the power of photography to anchor a visually...</Summary>
<Website>http://rss.feedsportal.com/c/35285/f/661066/s/366ef1cb/sc/4/l/0L0Swebdesignerdepot0N0C20A140C0A10Chow0Eto0Eharness0Ethe0Epower0Eof0Ea0Ewell0Ecropped0Ephoto0C/story01.htm</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40657/guest@my.umbc.edu/53c850c2c688ab630f44cb7d51fd57f1/api/pixel</TrackingUrl>
<Tag>art</Tag>
<Tag>creative-images-for-web-sites</Tag>
<Tag>cropping-images</Tag>
<Tag>css</Tag>
<Tag>design</Tag>
<Tag>development</Tag>
<Tag>html</Tag>
<Tag>html5</Tag>
<Tag>illustrator</Tag>
<Tag>javascript</Tag>
<Tag>mysql</Tag>
<Tag>oracle</Tag>
<Tag>photoshop</Tag>
<Tag>php</Tag>
<Tag>sql</Tag>
<Tag>the-best-images-for-web-design</Tag>
<Tag>using-images-in-web-design</Tag>
<Tag>web-design</Tag>
<Tag>web-page-layouts</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>Tue, 28 Jan 2014 03:15:04 -0500</PostedAt>
<EditAt>Tue, 28 Jan 2014 03:15:04 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="40656" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40656">
<Title>Marlboro Man: a Dying Profession</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <p><img alt="" src="http://usdemocrazy.net/wp-content/uploads/2014/01/malboro-adbuster-comparison.jpg" width="480" height="323" style="max-width: 100%; height: auto;"></p>
    <p>Eric Lawson is the latest to join the list of “<a href="http://en.wikipedia.org/wiki/Marlboro_man" rel="nofollow external" class="bo">Marlboro Men</a>” to be killed by cigarettes .</p>
    <p> Lawson passed away on January 10<sup>th </sup>at the age of 72. The cause of death was respiratory failure due to a smoking related illness.</p>
    <p> Lawson, an actor, donned the cowboy getup in the late 70s, taking on the persona of the über masculine “Marlboro Man.”</p>
    <p>The campaign originated in 1954 and quickly transformed the cigarettes, previously known for their feminine “Mild as May” tagline, into <a href="https://archive.org/details/tobacco_qja84e00" rel="nofollow external" class="bo">a mainstream macho must-have.</a></p>
    <p> James Twitchell, author of <a href="http://www.amazon.com/Twenty-That-Shook-World-Groundbreaking/dp/0609807234" rel="nofollow external" class="bo">20 Ads That Shook the World</a>, described the success of the campaign to the BBC:</p>
    <blockquote>
    <p>“The Marlboro Man was strong, powerful. He never speaks. He’s so tough… The genius of the ad is that at the same time there was a rising realization that this thing will kill you, it was identified with a character who was, on the face of it, indomitable.”</p>
    </blockquote>
    <p>Clearly, as time has told us, these men were not indomitable. Wayne McLaren was the youngest to pass away from lung cancer at 51. Richard Hammer and David McLean developed lung cancer and passed away at 69 and 73 respectively. David Millar died from emphysema at 81.</p>
    <p>Some may consider 81 a long life, but most would like to live past 51. The younger McLaren went on a crusade against Phillip Morris (owner of the Marlboro brand) after his diagnosis. He is quoted as saying:</p>
    <blockquote>
    <p>“Tobacco will kill you, and I am living proof of it.”</p>
    </blockquote>
    <p>Eric Lawson was a public supporter of anti-smoking campaigns, despite his inability to quit. After all of his struggles, his wife Susan had this to say about his death:</p>
    <blockquote>
    <p>“He knew the cigarettes had a hold on him. He knew, yet he couldn’t stop.”</p>
    </blockquote>
    <p>The Marlboro Man might be a symbol that dies with its last living icons. The reality of smoking has set in. There’s more at stake than your image.</p>
    </div>
]]>
</Body>
<Summary>Eric Lawson is the latest to join the list of “Marlboro Men” to be killed by cigarettes .    Lawson passed away on January 10th at the age of 72. The cause of death was respiratory failure due to...</Summary>
<Website>http://usdemocrazy.net/marlboro-man-a-dying-profession/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40656/guest@my.umbc.edu/fb845b6b691e53a5d658b28ef341c04e/api/pixel</TrackingUrl>
<Tag>ads</Tag>
<Tag>americana</Tag>
<Tag>current</Tag>
<Tag>democracy</Tag>
<Tag>health</Tag>
<Tag>marlboro</Tag>
<Tag>marlboro-man</Tag>
<Tag>news</Tag>
<Tag>politics</Tag>
<Tag>smoking</Tag>
<Tag>tobacco</Tag>
<Tag>us</Tag>
<Tag>usdemocrazy</Tag>
<Group token="retired-12">USDemocrazy</Group>
<GroupUrl>https://my3.my.umbc.edu/groups/retired-12</GroupUrl>
<AvatarUrl>https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/xsmall.png?1279120129</AvatarUrl>
<AvatarUrl size="original">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/original.jpg?1279120129</AvatarUrl>
<AvatarUrl size="xxlarge">https://assets3-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/xxlarge.png?1279120129</AvatarUrl>
<AvatarUrl size="xlarge">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/xlarge.png?1279120129</AvatarUrl>
<AvatarUrl size="large">https://assets4-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/large.png?1279120129</AvatarUrl>
<AvatarUrl size="medium">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/medium.png?1279120129</AvatarUrl>
<AvatarUrl size="small">https://assets2-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/small.png?1279120129</AvatarUrl>
<AvatarUrl size="xsmall">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/xsmall.png?1279120129</AvatarUrl>
<AvatarUrl size="xxsmall">https://assets1-my.umbc.edu/system/shared/avatars/groups/000/000/012/f0935e4cd5920aa6c7c996a5ee53a70f/xxsmall.png?1279120129</AvatarUrl>
<Sponsor>USDemocrazy</Sponsor>
<PawCount>16</PawCount>
<CommentCount>11</CommentCount>
<CommentsAllowed>true</CommentsAllowed>
<PostedAt>Tue, 28 Jan 2014 00:57:52 -0500</PostedAt>
<EditAt>Tue, 28 Jan 2014 00:52:52 -0500</EditAt>
</NewsItem>

<NewsItem contentIssues="true" id="40654" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40654">
<Title>Google Glass to Be Covered by Vision Care Insurer VSP</Title>
<Body>
<![CDATA[
    <div class="html-content">In a sign of how wearable devices are entering the mainstream, a major optical insurance provider will subsidize frames and prescription lenses for customers buying Google Glass.<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%2F28%2Ftechnology%2Fgoogle-glass-to-be-covered-by-vision-care-insurer-vsp.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Google+Glass+to+Be+Covered+by+Vision+Care+Insurer+VSP" 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%2F28%2Ftechnology%2Fgoogle-glass-to-be-covered-by-vision-care-insurer-vsp.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Google+Glass+to+Be+Covered+by+Vision+Care+Insurer+VSP" 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%2F28%2Ftechnology%2Fgoogle-glass-to-be-covered-by-vision-care-insurer-vsp.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Google+Glass+to+Be+Covered+by+Vision+Care+Insurer+VSP" 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%2F28%2Ftechnology%2Fgoogle-glass-to-be-covered-by-vision-care-insurer-vsp.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Google+Glass+to+Be+Covered+by+Vision+Care+Insurer+VSP" 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%2F28%2Ftechnology%2Fgoogle-glass-to-be-covered-by-vision-care-insurer-vsp.html%3Fpartner%3Drss%26emc%3Drss&amp;t=Google+Glass+to+Be+Covered+by+Vision+Care+Insurer+VSP" 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/186529854862/u/0/f/640387/c/34625/s/366d9d46/sc/15/rc/1/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529854862/u/0/f/640387/c/34625/s/366d9d46/sc/15/rc/1/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529854862/u/0/f/640387/c/34625/s/366d9d46/sc/15/rc/2/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529854862/u/0/f/640387/c/34625/s/366d9d46/sc/15/rc/2/rc.img" style="max-width: 100%; height: auto;"></a><br><a href="http://da.feedsportal.com/r/186529854862/u/0/f/640387/c/34625/s/366d9d46/sc/15/rc/3/rc.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529854862/u/0/f/640387/c/34625/s/366d9d46/sc/15/rc/3/rc.img" style="max-width: 100%; height: auto;"></a><br><br><a href="http://da.feedsportal.com/r/186529854862/u/0/f/640387/c/34625/s/366d9d46/a2.htm" rel="nofollow external" class="bo"><img src="http://da.feedsportal.com/r/186529854862/u/0/f/640387/c/34625/s/366d9d46/a2.img" style="max-width: 100%; height: auto;"></a>
    </div>
]]>
</Body>
<Summary>In a sign of how wearable devices are entering the mainstream, a major optical insurance provider will subsidize frames and prescription lenses for customers buying Google Glass.      </Summary>
<Website>http://www.nytimes.com/2014/01/28/technology/google-glass-to-be-covered-by-vision-care-insurer-vsp.html?partner=rss&amp;emc=rss</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40654/guest@my.umbc.edu/541f4acfbf0e109da2f5d89802297e31/api/pixel</TrackingUrl>
<Tag>apple-inc-aapl-nasdaq</Tag>
<Tag>ebay-inc-ebay-nasdaq</Tag>
<Tag>google-glass</Tag>
<Tag>google-inc-goog-nasdaq</Tag>
<Tag>health-insurance-and-managed-care</Tag>
<Tag>intel-corporation-intc-nasdaq</Tag>
<Tag>new</Tag>
<Tag>technology</Tag>
<Tag>vsp-global</Tag>
<Tag>wearable-computing</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>Tue, 28 Jan 2014 00:01:14 -0500</PostedAt>
</NewsItem>

<NewsItem contentIssues="false" id="40655" important="false" status="posted" url="https://my3.my.umbc.edu/posts/40655">
<Title>Not OK, Glass</Title>
<Body>
<![CDATA[
    <div class="html-content">
    <p>New software lets you mark places as off-limits for wearable camera gadgets like Google Glass.</p>
    <p>With last year’s launch of the <a href="http://www.getnarrative.com" rel="nofollow external" class="bo">Narrative Clip</a> and <a href="http://www.autographer.com" rel="nofollow external" class="bo">Autographer</a>, and Google Glass poised for release this year, technologies that can continuously capture our daily lives with photos and videos are inching closer to the mainstream. These gadgets can generate detailed visual diaries, drive self-improvement, and help those with memory problems. But do you really want to record in the bathroom or a sensitive work meeting?</p>
    </div>
]]>
</Body>
<Summary>New software lets you mark places as off-limits for wearable camera gadgets like Google Glass.  With last year’s launch of the Narrative Clip and Autographer, and Google Glass poised for release...</Summary>
<Website>http://www.technologyreview.com/news/523941/not-ok-glass/</Website>
<TrackingUrl>https://my3.my.umbc.edu/api/v0/pixel/news/40655/guest@my.umbc.edu/37f0ff6f3df1c44f0c440ea63214ae83/api/pixel</TrackingUrl>
<Tag>development</Tag>
<Tag>internet</Tag>
<Tag>mit</Tag>
<Tag>technology</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>Tue, 28 Jan 2014 00:00:00 -0500</PostedAt>
</NewsItem>

</News>
