<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>burnmind.com</title>
	<atom:link href="http://burnmind.com/feed" rel="self" type="application/rss+xml" />
	<link>http://burnmind.com/</link>
	<description>read, think, act.</description>
	<lastBuildDate>Tue, 29 Dec 2020 17:00:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Closure</title>
		<link>http://burnmind.com/articles/closure</link>
					<comments>http://burnmind.com/articles/closure#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Tue, 29 Dec 2020 17:00:34 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=2030</guid>

					<description><![CDATA[Everything comes to an end. It's inevitable, but there is a feeling of sadness when things are left abandoned without any closure. It's been 12 years since I started this blog, and shy of 5 years since I haven't updated it...]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2020/12/closure.png" alt="" class="left" /></p>
<p>Everything comes to an end. It&#8217;s inevitable, but there is a feeling of sadness when things are left abandoned without any closure.</p>
<p>It&#8217;s been 12 years since I started this blog, and shy of 5 years since I haven&#8217;t updated it, after my role at work evolved to be more managerial, my free time reduced dramatically, and the topics I became interested in drifted away from programming, thus were not really a good fit for this blog. Burnmind was a great experience; I met lots of people — which led to amazing opportunities, I expressed my creativity, and I learned so many things while working on it.</p>
<p>I always had at the back of my mind that I&#8217;ll resume writing here at some point, but it was not meant to be. I feel the need to write, but instead of pivoting this blog into a new direction, I&#8217;ll <a href="http://burnmind.com/articles/sometimes-you-have-to-let-go">let it go</a>, and start fresh on a new blog section on my <a href="https://stathisg.com/">personal site</a>. I&#8217;ll keep Burnmind online though, since it&#8217;s an important part of my journey.</p>
<p>Farewell, my good friend!</p>
<p><small>Photo by <a href="https://www.flickr.com/photos/peterbardell/5950489994/">Peter Bardell</a></small></p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/articles/closure/feed</wfw:commentRss>
			<slash:comments>33</slash:comments>
		
		
			</item>
		<item>
		<title>Memories</title>
		<link>http://burnmind.com/articles/memories</link>
					<comments>http://burnmind.com/articles/memories#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Sat, 09 Apr 2016 11:15:16 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=2024</guid>

					<description><![CDATA[I find extremely fascinating the way <strong>human brain deals with memories</strong>. How we are able to remember so much information, how we unconsciously remember even more, how we jump from one simple memory to a series of them, how a simple image, sound, or smell can trigger memories. But most of all, I love that moment when just for a few milliseconds your brain can actually make you <strong>feel</strong> a memory.]]></description>
										<content:encoded><![CDATA[<p>I find extremely fascinating the way <strong>human brain deals with memories</strong>. How we are able to remember so much information, how we unconsciously remember even more, how we jump from one simple memory to a series of them, how a simple image, sound, or smell can trigger memories. But most of all, I love that moment when just for a few milliseconds your brain can actually make you <strong>feel</strong> a memory.</p>
<p><span style="text-align: center; display: block;"><img decoding="async" src="http://burnmind.com/wp-content/uploads/2016/04/lunarbaboon-memories.jpg" alt="Comic Strip" /></span></p>
<p>The other day, I took a sip from a carbonated lemonade drink. While taking that sip, just for a split second, it was summertime and I was again a little boy in the small village where my grandparents lived, drinking a lemonade that my grandmother gave me. And I could feel everything. The taste, the sun, the wind, the presence of my grandmother&#8230;</p>
<p>Thanks brain! :-)</p>
<p><small><em>This story was originally published as part of <a href="http://inequilibrium.co/issues/19">my newsletter’s editorial</a> and was also <a href="https://medium.com/@stathisg/memories-927f1f76a44#.p4m93iiwa">published on medium</a>.</em></small></p>
<p><small>Comic strip by <a href="http://www.lunarbaboon.com/comics/sniff.html">Lunarbaboon</a>.</small></p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/articles/memories/feed</wfw:commentRss>
			<slash:comments>24</slash:comments>
		
		
			</item>
		<item>
		<title>How to restrict the drag movement of a sprite to both X &#038; Y axis in Phaser</title>
		<link>http://burnmind.com/tutorials/drag-sprite-horizontally-vertically-phaser</link>
					<comments>http://burnmind.com/tutorials/drag-sprite-horizontally-vertically-phaser#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Sat, 02 Apr 2016 10:44:24 +0000</pubDate>
				<category><![CDATA[tutorials]]></category>
		<category><![CDATA[game development]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[phaser]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=2018</guid>

					<description><![CDATA[<a href="http://phaser.io/">Phaser</a> has a built-in function (i.e. <code><a href="http://phaser.io/docs/2.4.6/Phaser.InputHandler.html#setDragLock">setDragLock</a></code>) that restricts a sprite's drag movement only on a given axis (i.e. either horizontal or vertical). There are cases though that you want to <strong>allow a sprite to move both horizontally and vertically</strong>, but not allow movement to any other directions.

I run into a situation like that a few days ago, while building -just for fun- a match-3 type game (like Bejeweled and Candy Crush Saga). Although I ended up making the tiles clickable instead of draggable, I managed to find a workaround that would restrict movement to both axis.]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2016/03/game-like-candy-crush-saga.jpg" alt="" class="left" /></p>
<p><a href="http://phaser.io/">Phaser</a> has a built-in function (i.e. <code><a href="http://phaser.io/docs/2.4.6/Phaser.InputHandler.html#setDragLock">setDragLock</a></code>) that restricts a sprite&#8217;s drag movement only on a given axis (i.e. either horizontal or vertical). There are cases though that you want to <strong>allow a sprite to move both horizontally and vertically</strong>, but not allow movement to any other directions.</p>
<p>I ran into a situation like that a few days ago, while building -just for fun- a match-3 type game (like Bejeweled and Candy Crush Saga). Although I ended up making the tiles clickable instead of draggable, I managed to find a workaround that would restrict movement to both axis.</p>
<p>Before we dive into the code, have a look at <a href="http://burnmind.com/demos/phaser-restrict-drag/">this demo</a> to understand what we are about to build. All four squares are draggable; square 1 can be dragged freely in all directions; square 2&#8217;s movement is restricted to the horizontal axis; square 3&#8217;s to the vertical axis; and finally square 4&#8217;s to both axis.</p>
<p>The HTML/CSS part is very simple, therefore I&#8217;ll omit it; you can always have a look at the <a href="https://gist.github.com/stathisg/c43d77ad55e076311170">source code</a>. Since what we&#8217;re going to build is a small proof of concept, we will use a very simple Phaser setup without different states, etc. Let&#8217;s start by initialising Phaser and the basic functions we&#8217;ll use.</p>
<pre data="lang:javascript">
window.onload = function() {
    //Initialise Phaser
    var game = new Phaser.Game(800, 600, Phaser.AUTO, 'gameContainer', {
        init: init,
        preload: preload,
        create: create
    });

    //Set some configuration values
    function init() {
        this.input.maxPointers = 1;
        this.stage.disableVisibilityChange = true;
        this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
        this.scale.maxWidth = 800;
        this.scale.maxHeight = 600;
        this.scale.windowConstraints.bottom = "visual";
        this.scale.pageAlignHorizontally = true;
    }

    //Preload assets
    function preload() {
        this.load.image('square_1', 'images/square-1.jpg');
        this.load.image('square_2', 'images/square-2.jpg');
        this.load.image('square_3', 'images/square-3.jpg');
        this.load.image('square_4', 'images/square-4.jpg');
    }

    //Add assets to the game
    function create() {}
};
</pre>
<p>In the <code>init</code> function, we set some configuration values for the game, i.e. its maximum height and width, its scale mode, etc., and in the <code>preload</code> function we load the assets we&#8217;ll use in the demo. In the <code>create</code> function we&#8217;ll add the assets in the game, but we&#8217;ll get to that towards the end of the tutorial.</p>
<p>To make things easier, we&#8217;ll include all the logic for a square in a class-like function called <code>Square</code>. Note that functions/variables which are declared using the <code>var</code> keyword in front of them are only accessible within the <code>Square</code> function.</p>
<pre data="lang:javascript">
function Square(newOptions) {
    var options = {};
    var defaultOptions = {
        phaser: null, //an instance of Phaser
        assetKey: '', //the asset's key
        x: 0, //x position
        y: 0, //y position
        dragType: '', //type of drag movement allowed
        bounds: null //an instance of a Phaser Rectangle
    };

    var extend = function(out) {
        //source: http://youmightnotneedjquery.com/#extend
    };
    options = extend({}, defaultOptions, newOptions);

    var square = null;
    var initCursorPositionX = null;
    var initCursorPositionY = null;
    var lockedAxis = false;

    this.init = function() {}
    var dragUpdate = function() {}
    var dragStop = function() {}
}
</pre>
<p>We have a set of default options stored in the <code>defaultOptions</code> object. We combine those with the <code>newOptions</code> object that we pass when we create a new <code>Square</code> object. To merge them giving &#8220;priority&#8221; to any values stored in <code>newOptions</code>, we use the <code>extend</code> function. Its functionality is identical to jQuery&#8217;s <code><a href="https://api.jquery.com/jquery.extend/">extend</a></code> function and was taken from <a href="http://youmightnotneedjquery.com/#extend">here</a>, just to avoid loading jQuery solely for one function.</p>
<p><code>this.init</code> function will be called from Phaser&#8217;s <code>create</code> function (thus the declaration using <code>this</code>) to initialise the square. <code>dragUpdate</code> and <code>dragStop</code> will be called based on the square&#8217;s drag events. The four variables will be used inside those functions (we&#8217;ll see how while we discuss them). Let&#8217;s start with <code>this.init</code>:</p>
<pre data="lang:javascript">
this.init = function() {
    //create the sprite
    square = options.phaser.add.sprite(options.x, options.y, options.assetKey);

    //enable input
    square.inputEnabled = true;

    //change the cursor to the hand version on hover
    square.input.useHandCursor = true;

    //enable drag
    square.input.enableDrag(false, true);

    //restrict dragging within the Phaser rectangle (in our case it covers the whole visible screen)
    square.input.boundsRect = options.bounds;

    //restrict dragging movement based on selected drag type
    switch (options.dragType) {
        case 'horizontal':
            square.input.setDragLock(true, false);
            break;
        case 'vertical':
            square.input.setDragLock(false, true);
            break;
        case 'both':
            square.events.onDragUpdate.add(dragUpdate, this);
            square.events.onDragStop.add(dragStop, this);
            break;
    }
}
</pre>
<p>Most of the function is pretty basic; we create the sprite, enable dragging, and restrict its dragging bounds. That part is common for all of our squares. Their difference lies in the value of <code>options.dragType</code>.</p>
<p>When there is no <code>dragType</code> selected (that will be the case for square 1), no restrictions will be placed and the square can be dragged freely within the bounds; note that we don&#8217;t have a <code>default</code> case in our <code>switch</code> statement. In <code>horizontal</code> and <code>vertical</code> cases, we utilise Phaser&#8217;s build-in <code><a href="http://phaser.io/docs/2.4.6/Phaser.InputHandler.html#setDragLock">setDragLock</a></code> function to restrict movement to either axis.</p>
<p>To be able to restrict the dragging movement to both axis (based on the position we started dragging from), we apply two events on our sprite; <code><a href="http://phaser.io/docs/2.4.6/Phaser.Events.html#onDragUpdate">onDragUpdate</a></code> which will be constantly fired while the sprite is being dragged, and <code><a href="http://phaser.io/docs/2.4.6/Phaser.Events.html#onDragStop">onDragStop</a></code> which will be fired once when the dragging stops.</p>
<p>The idea is that we want to allow the player to move the sprite to any direction for a few pixels, just enough to allow us to determine the axis she wants to move the sprite on. As soon as we do, we will lock the movement to that axis using Phaser&#8217;s <code>setDragLock</code> function.</p>
<pre data="lang:javascript">
var dragUpdate = function() {
    //if we already locked to either axis, then exit
    if(lockedAxis) {
        return false;
    }

    //if we don't have a record of the initial cursor's position when it started dragging, grab one and exit
    if(initCursorPositionX === null || initCursorPositionY === null) {
        initCursorPositionX = options.phaser.input.x;
        initCursorPositionY = options.phaser.input.y;
        return false;
    }

    //calculate the absolute difference between the initial cursor position and the current one for both axis
    var differenceX = Math.abs(initCursorPositionX - options.phaser.input.x);
    var differenceY = Math.abs(initCursorPositionY - options.phaser.input.y);

    //allow at least one of the axis to move 5 pixels before restricting movement to either
    if(differenceX < 5 &#038;&#038; differenceY < 5) {
        return false;
    }

    //if the cursor moved a greater distance in X-axis than in Y-axis, then restrict dragging horizontally
    if(differenceX > differenceY) {
        square.y = options.y;
        square.input.setDragLock(true, false);
        lockedAxis = true;
        return false;
    }

    //alternatively, restrict dragging vertically
    square.x = options.x;
    square.input.setDragLock(false, true);
    lockedAxis = true;
};

//reset values and store new X & Y coordinates
var dragStop = function() {
    initCursorPositionX = null;
    initCursorPositionY = null;
    options.x = square.x;
    options.y = square.y;
    lockedAxis = false;
};
</pre>
<p>As the <code>dragUpdate</code> function will be constantly called during the dragging, we want to keep some values outside of it; this is where <code>lockedAxis</code>, <code>initCursorPositionX</code>, and <code>initCursorPositionY</code> variables are used. The first thing to do in the function is to determine if we already locked the movement to either axis. If that&#8217;s the case, then there is nothing else to do, and we exit. If we ​haven&#8217;t locked the movement, we check if we have stored the initial X &#038; Y coordinates of the cursor. If we haven&#8217;t stored the coordinates already, we store them and exit.</p>
<p>As soon as we have that information, we calculate the absolute difference between the initial cursor position and the current one for both axis. We wait until one of those values is at least 5 pixels, and then we determine which one is greater than the other. If the cursor has covered more distance on the X-axis, we restrict dragging horizontally, alternatively we restrict dragging vertically.</p>
<p>Finally, in the <code>dragStop</code> function we reset the cursor position and <code>lockedAxis</code> values and we store the new X &#038; Y coordinates of the sprite so we can be ready for the next drag action. The only thing left to do is to create and initialise our squares. Let&#8217;s do that in our <code>create</code> function:</p>
<pre data="lang:javascript">
function create() {
    //this is used to restrict dragging within the visible screen
    var bounds = new Phaser.Rectangle(0, 0, 800, 600);

    var square1 = new Square({
        phaser: this,
        assetKey: 'square_1',
        x: 96,
        y: 56,
        bounds: bounds
    });

    var square2 = new Square({
        phaser: this,
        assetKey: 'square_2',
        x: 272,
        y: 192,
        dragType: 'horizontal',
        bounds: bounds
    });

    var square3 = new Square({
        phaser: this,
        assetKey: 'square_3',
        x: 448,
        y: 328,
        dragType: 'vertical',
        bounds: bounds
    });

    var square4 = new Square({
        phaser: this,
        assetKey: 'square_4',
        x: 624,
        y: 464,
        dragType: 'both',
        bounds: bounds
    });

    square1.init();
    square2.init();
    square3.init();
    square4.init();
}
</pre>
<p>We&#8217;re done! <a href="http://burnmind.com/demos/phaser-restrict-drag/">Here&#8217;s</a> what we&#8217;ve just built. You can download the source code of this tutorial from <a href="https://gist.github.com/stathisg/c43d77ad55e076311170">this Gist</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/tutorials/drag-sprite-horizontally-vertically-phaser/feed</wfw:commentRss>
			<slash:comments>50</slash:comments>
		
		
			</item>
		<item>
		<title>A list of Podcasts I listen to</title>
		<link>http://burnmind.com/articles/podcast-list</link>
					<comments>http://burnmind.com/articles/podcast-list#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Mon, 04 Jan 2016 09:57:33 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=2010</guid>

					<description><![CDATA[Podcasts became popular again in recent years, and there are some really great ones out there. I compiled a list which contains some of the podcasts I regularly listen to. Without further ado, and in no particular order (well, they are actually in the order they were featured in <a href="http://inequilibrium.co/">my newsletter</a>), here's the list...]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2016/01/podcasting.jpg" alt="" class="left" /></p>
<p>Podcasts became popular again in recent years, and there are some really great ones out there. I compiled a list which contains some of the podcasts I regularly listen to. Without further ado, and in no particular order (well, they are actually in the order they were featured in <a href="http://inequilibrium.co/">my newsletter</a>), here&#8217;s the list:</p>
<h2><a href="http://bootstrapped.fm/">Bootstrapped &rarr;</a></h2>
<p>As the title suggests, it is a podcast about bootstrapped startups, where its hosts (Ian Landsman and Andrey Butov) discuss about their startup adventures. Just to warn you, during their discussions, they sometimes go off-track (e.g. talking about <a href="http://bootstrapped.fm/bootstrapped-episode-5-lets-all-go-to-the-movies/">movies</a>), but that helps the show to develop its own unique character.</p>
<h2><a href="https://devchat.tv/freelancers/">Freelancers&#8217; Show &rarr;</a></h2>
<p>Initially called &#8220;The Ruby Freelancers Show&#8221;, it later rebranded to a more generalised resource for freelancers. The podcast features discussions between freelancers on the topics of running a business, finding clients, marketing, etc. Also, at the end of each show, the panellists pick items that they enjoy or that make freelancing easier.</p>
<h2><a href="http://podcasts.thoughtbot.com/giantrobots">Giant Robots Smashing Into Other Giant Robots &rarr;</a></h2>
<p>The Giant Robots Smashing into Other Giant Robots podcast is a weekly technical podcast discussing development, design, and the business of software development.</p>
<h2><a href="http://www.earwolf.com/episode/freelancer-or-entrepreneur/">Seth Godin’s Startup School &rarr;</a></h2>
<p>A few years ago, Seth Godin hosted a small startup school spending a few days with a number of startup founders, discussing startup-related topics. He recorded these discussions and released a selection of them in the form of an extremely interesting fifteen episode podcast.</p>
<h2><a href="http://www.chasingproduct.com/">Chasing Product &rarr;</a></h2>
<p>Chasing Product is a podcast which aims to help freelancers and consultants transition from client work to launching bootstrapped software products.</p>
<h2><a href="http://bootstrappedweb.com/">Bootstrapped Web &rarr;</a></h2>
<p>As its title suggests, Bootstrapped Web is a podcast focusing on (existing or aspiring) founders of bootstrapped online businesses, covering most of the topics you would expect from such a podcast.</p>
<h2><a href="http://www.startupstoriespodcast.com/">Launch &rarr;</a></h2>
<p>Launch is a two hour audio documentary which follows the two creators (Rob Walling and Derrick Reimer) of their startup (which is called <a href="https://www.getdrip.com/">Drip</a>) <em>&#8220;from its first days of inception through the agonizing months of writing code, talking to customers, and the never-ending uncertainty of whether anyone will ever use what they are building&#8221;</em>.</p>
<h2><a href="http://www.fiveminutegeekshow.com/">The Five-Minute Geek Show &rarr;</a></h2>
<p>The Five-Minute Geek Show is <em>&#8220;a twice-weekly show about web development and everything around it. It&#8217;s one topic per episode about front-end, back-end, mobile, project management, design, entrepreneurship&#8230; Whatever; if it&#8217;s geeky, it fits!&#8221;</em></p>
<p>I really like this podcast. Its episodes are between 5 and 10 minutes, so they are short and easy digestible, and I find most of the topics so far quite interesting. I discovered it around episode 25 and I listened to all of the available episodes at that point within a couple of days.</p>
<h2><a href="http://fullstackradio.com/">Full Stack Radio &rarr;</a></h2>
<p>Full Stack Radio is a podcast for developers interested in building great software products. In each episode, Adam Wathan (the show&#8217;s host), talks to people in the software industry about everything from user experience and product design to unit testing and system administration.</p>
<h2><a href="http://www.laravelpodcast.com/">The Laravel Podcast &rarr;</a></h2>
<p>The Laravel Podcast brings you Laravel and PHP development news and discussion. The podcast is hosted by Matt Stauffer (host of the Five Minute Geek Show) and its regular guests include Taylor Otwell (the creator of Laravel) and Jeffrey Way.</p>
<h2><a href="http://consultingpipelinepodcast.com/">The Consulting Pipeline Podcast &rarr;</a></h2>
<p>The Consulting Pipeline Podcast, is hosted by Philip Morgan and is focused on building your consulting pipeline through positioning, education-based content marketing, and marketing automation.</p>
<h2><a href="http://podcast.laravel-news.com/">Laravel News Podcast &rarr;</a></h2>
<p>The Laravel News Podcast brings you all the latest news and events related to Laravel. You can think of it at an audio version of the <a href="http://laravel-news.com/newsletter/">Laravel News newsletter</a>.</p>
<h2>Want more?</h2>
<p>A few years ago, I published a similar <a href="http://burnmind.com/articles/my-podcast-list">list of podcasts</a>. Check it out; some of them are still active. You should also subscribe to Equilibrium, my <a href="http://inequilibrium.co/">monthly newsletter</a>, where I regularly feature other interesting podcasts.</p>
<p><small>Photo by <a href="https://www.flickr.com/photos/pxlstory/4735303517/">Brian Steele</a>.</small></p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/articles/podcast-list/feed</wfw:commentRss>
			<slash:comments>14</slash:comments>
		
		
			</item>
		<item>
		<title>The fear of using the wrong tool</title>
		<link>http://burnmind.com/articles/the-fear-of-using-the-wrong-tool</link>
					<comments>http://burnmind.com/articles/the-fear-of-using-the-wrong-tool#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Sat, 21 Nov 2015 11:51:23 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=1997</guid>

					<description><![CDATA[If you have been participating in any kind of community, you most likely encountered or even took part in a flame war regarding which tool to use for a specific thing. Code editors, frameworks, image manipulation tools, you name it.]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/11/tools.jpg" alt="" class="left" /></p>
<p>If you have been participating in any kind of community, you most likely encountered or even took part in a flame war regarding which tool to use for a specific thing. Code editors, frameworks, image manipulation tools, you name it.</p>
<p>Although I&#8217;m guilty of participating in such discussions in the past, I consciously stay out of them for several years now. That decision mostly stems from my belief that the best tool for a job is usually quite subjective.</p>
<p>I do like to mention the tools I use (as I occasionally do in this blog or <a href="http://inequilibrium.co">on my newsletter</a>) and read what other people prefer to use, but I don&#8217;t get dragged into arguments about which one is better.</p>
<p>This is what I tell people when I find myself involved in such discussions:</p>
<p><em>Use whatever you feel comfortable with. When you want to experiment, do it by all means, but don&#8217;t feel bad and obliged to use the new &#8220;hot&#8221; thing in the industry if you still can do the job quickly, efficiently, and with the same quality with the tools you are already familiar with.</em></p>
<p>In the bottom line, <strong>tools only matter to us, the engineers</strong>. The end users have no idea of the mechanics of how something was built and they don&#8217;t care about them; they only perceive the end result.</p>
<p><small><em>This story was originally published as part of <a href="http://inequilibrium.co/issues/11">my newsletter’s editorial</a>.</em></small></p>
<p><small>Photo by <a href="https://www.flickr.com/photos/kelehen/6454106773">Rudolf Vlček</a>.</small></p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/articles/the-fear-of-using-the-wrong-tool/feed</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>PriceAlert &#8211; price tracking and price history charts for Greek e-commerce websites</title>
		<link>http://burnmind.com/projects/pricealert-price-tracking-history-ecommerce</link>
					<comments>http://burnmind.com/projects/pricealert-price-tracking-history-ecommerce#respond</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Sat, 14 Nov 2015 00:01:44 +0000</pubDate>
				<category><![CDATA[projects]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=2007</guid>

					<description><![CDATA[I recently launched a new web app called <a href="https://pricealert.gr/">PriceAlert</a>, which is a service that provides <strong>price tracking</strong> and <strong>price history charts</strong> for some of the most popular e-commerce stores operating in Greece.]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/11/pricealert-logo.png" alt="PriceAlert logo" class="left" /></p>
<p>I recently launched a new web app called <a href="https://pricealert.gr/">PriceAlert</a>, which is a service that provides <strong>price tracking</strong> and <strong>price history charts</strong> for some of the most popular e-commerce stores operating in Greece.</p>
<p>Its UI is written in Greek, so I believe that unfortunately most of you won&#8217;t be able to navigate through it. Therefore, <a href="https://pricealert.gr/eshops/mediamarkt/product/samsung-lt24-e390ew-en~1163662">here&#8217;s a direct link</a> to a product I chose randomly so you can see how a product page looks like. At the bottom of it, you can find the price history chart.</p>
<h2>Why?</h2>
<p>Around two and a half years ago, I needed to buy something from Greece. Being a <a href="http://uk.camelcamelcamel.com/">camelcamelcamel</a> (a price tracker for Amazon) user for years, I searched for a similar service focused on Greek e-shops, but I couldn&#8217;t find one. So, as an interesting side-project, I decided to build an automated system which would track the price of specified products and it would notify me when they reached a certain threshold.</p>
<p>I quickly decided that I wanted to share it with other people, so I created a basic UI, bought the domain, installed the system to a server, and decided that after some changes that wouldn&#8217;t take more than a few days I would release it.</p>
<p>Those few days quickly became weeks, then months, and eventually years. I never considered it as one of these projects that <a href="http://burnmind.com/articles/sometimes-you-have-to-let-go">I would let go</a> though, so it might have taken a while, but a few months ago I decided to spend some time on it so it could be released to the public, which happened on November 1st.</p>
<h2>How?</h2>
<p>Moving to the technical side, the back-end code that powers everything related to the user&#8217;s interaction with the site is still part of the original code built in mid-2013 using <a href="https://codeigniter.com/">CodeIgniter</a>. As a login system, it uses my own <a href="http://burnmind.com/freebies/php-password-less-login-system">PasswordLessLogin library</a>.</p>
<p>The system which automatically handles -among other tasks- the product updates, price changes, and email notifications though, is a <a href="http://laravel.com/">Laravel</a> 5.1 application. All transactional emails are sent through <a href="https://www.mandrill.com/">Mandrill</a>.</p>
<p>Most of the front-end code is dated, since it was built using <a href="http://getbootstrap.com/">Bootstrap</a> 2.3.2 (yeap, that was the most recent stable version back then), and the <a href="http://designmodo.github.io/Flat-UI/">Flat UI</a> and <a href="https://fortawesome.github.io/Font-Awesome/">Font Awesome</a> versions that it supported. The price history charts are generated using <a href="http://www.highcharts.com/">Highcharts</a>; I also used <a href="https://github.com/duncanmcdougall/Responsive-Lightbox">Responsive-Lightbox</a> in a few places.</p>
<p>Finally, PriceAlert is deployed on a <a href="https://www.digitalocean.com/?refcode=53911d371136">DigitalOcean</a>* server, managed by <a href="https://forge.laravel.com/">Laravel Forge</a>. The CodeIgniter and Laravel apps are deployed as different domains, which share the same database.</p>
<p>*If you want to try DigitalOcean out, sign up using <a href="https://www.digitalocean.com/?refcode=53911d371136">this link</a> and you&#8217;ll get $10 in credit for free.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/projects/pricealert-price-tracking-history-ecommerce/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to backup a MySQL database to Amazon Simple Storage Service (S3)</title>
		<link>http://burnmind.com/servers/backup-mysql-database-to-amazon-s3</link>
					<comments>http://burnmind.com/servers/backup-mysql-database-to-amazon-s3#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Tue, 10 Nov 2015 20:47:26 +0000</pubDate>
				<category><![CDATA[servers]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=1999</guid>

					<description><![CDATA[One of my projects is running on a <a href="https://www.digitalocean.com/?refcode=53911d371136">Digital Ocean VPS</a>, deployed there using <a href="https://forge.laravel.com/">Laravel Forge</a> (an amazing tool which automates a lot of painful server-related tasks). Although I have enabled the backup service offered by Digital Ocean, I needed to create an additional off-site backup solution for the project's database, so I decided to store them in AWS S3 service. This small tutorial covers the task of creating a shell script that will automatically backup a MySQL database to a set of AWS S3 buckets.]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/10/dell-server-rack.jpg" alt="server rack" class="left" /> One of my projects is running on a <a href="https://www.digitalocean.com/?refcode=53911d371136">Digital Ocean VPS</a>, deployed there using <a href="https://forge.laravel.com/">Laravel Forge</a> (an amazing tool which automates a lot of painful server-related tasks). Although I have enabled the backup service offered by Digital Ocean, I needed to create an additional off-site backup solution for the project&#8217;s database, so I decided to store them in AWS S3 service.</p>
<p>This small tutorial covers the task of creating a shell script that will automatically backup a MySQL database to a set of AWS S3 buckets.</p>
<h2>The backup plan</h2>
<p>For my project&#8217;s needs, I needed three sets of complete database backups (i.e. without excluding any table), running on different frequencies: daily, weekly, and monthly.</p>
<p>At any given point of time, I need to have access to the latest 7 daily, 4 weekly, and 12 monthly backups.</p>
<p>The idea is to create a shell script that will run on regular intervals (i.e. daily, weekly, and monthly) using cron jobs which will create database backups saving them in a set of local folders, which will then be synchronised to the relevant S3 buckets.</p>
<h2>Amazon S3 setup</h2>
<p>Obviously, you need to have an AWS account. You also need to create an IAM user (remember to store the credentials) that will have access to the S3 service.</p>
<p>Then, you need to create one or more S3 buckets to store the backups. I decided to store each type of backup in a separate S3 bucket, so I created the following three buckets:</p>
<ol>
<li>projectname-daily-db-backup</li>
<li>projectname-weekly-db-backup</li>
<li>projectname-monthly-db-backup</li>
</ol>
<h2>Server Setup</h2>
<p>Moving to your own server now (by the way, my server runs on Ubuntu 14.04), you need a way for our server to communicate with S3, so <a href="http://docs.aws.amazon.com/cli/latest/userguide/installing.html">install the AWS Command Line Interface</a> and then run <code>aws configure</code> to set up the credentials of the user you just created. You can verify that you have access to your S3 account by running <code>aws s3 ls</code>, which should show you a list of your S3 buckets.</p>
<p>After you are done with that, you need to create some folders where the backups will be saved. For my needs, I created the following set of folders:</p>
<ol>
<li>/usr/database_backups/daily</li>
<li>/usr/database_backups/weekly</li>
<li>/usr/database_backups/monthly</li>
</ol>
<p>Remember to change the owner of the folders to the user/group who will run the scripts. For my setup, the user/group is called forge, so I run the following command:</p>
<p><code>sudo chown -R forge:forge /usr/database_backups</code></p>
<h2>Shell script</h2>
<p>Create a file, give it a name of your preference (e.g. <code>db_backup.sh</code>), and paste the following commands. Don&#8217;t forget to add your database name and credentials in the appropriate variables, as well as to modify the paths based on your server and AWS setup. Also, remember to make it executable: <code>chmod +x db_backup.sh</code></p>
<pre>
#!/bin/sh

DB_NAME=""
DB_USERNAME=""
DB_PASSWORD=""
BACKUP_DATE=`date +%d%m%y%H%M`

case $1 in
"daily")
    DAYS_TO_DELETE=7;;
"weekly")
    DAYS_TO_DELETE=31;;
"monthly")
    DAYS_TO_DELETE=365;;
*)
    exit 0;;
esac

# export database
mysqldump -u ${DB_USERNAME} -p${DB_PASSWORD} ${DB_NAME} | gzip > /usr/database_backups/${1}/db_backup_${DB_NAME}_${BACKUP_DATE}.sql.gz

# remove local backups older than $DAYS_TO_DELETE days
find /usr/database_backups/${1}/db* -mtime +${DAYS_TO_DELETE} -exec rm {} \;

# synchronise relevant AWS S3 bucket
/usr/local/bin/aws s3 sync /usr/database_backups/${1} s3://projectname-${1}-db-backup --delete
</pre>
<p>The script accepts a single argument, which can be one of the following: <code>daily</code>, <code>weekly</code>, or <code>monthly</code>. Example of usage: <code>./db_backup.sh daily</code></p>
<p><small>Photo by <a href="https://www.flickr.com/photos/blam4c/2966736578/">Dominik Deobald</a></small></p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/servers/backup-mysql-database-to-amazon-s3/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>How to resize a logo to specific dimensions keeping its aspect ratio using PHP</title>
		<link>http://burnmind.com/tutorials/resize-logo-keeping-aspect-ratio</link>
					<comments>http://burnmind.com/tutorials/resize-logo-keeping-aspect-ratio#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Sat, 16 May 2015 14:12:42 +0000</pubDate>
				<category><![CDATA[tutorials]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=1993</guid>

					<description><![CDATA[I recently had a case in a B2B project I was working on where we wanted to allow users to upload their company logo, but resize them automatically to specific dimensions, while keeping the aspect ratio of the logo intact (and without cropping any part of it). Here's how I tackled the issue.]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/05/resized-logo.jpg" alt="" class="left" /> I recently had a case in a B2B project I was working on where we wanted to allow users to upload their company logo, but resize them automatically to specific dimensions, while keeping the aspect ratio of the logo intact (and without cropping any part of it). Here&#8217;s how I tackled the issue.</p>
<p>Before I begin with the tutorial, I want to highlight that I&#8217;m talking specifically about a logo and not a generic image, since logos usually don&#8217;t come with a background, or more specifically they come either with a transparent, or a solid colour background (which -as you&#8217;ll see in a bit- is a requirement for what we want to accomplish).</p>
<p>To begin with, we will use a package called <a href="http://image.intervention.io/">Intervention Image</a>. You can easily install it using <a href="https://getcomposer.org/">composer</a> (it&#8217;s also easily integrated with Laravel if that&#8217;s your framework of choice). For the rest of the tutorial I&#8217;ll assume that you have it installed and configured.</p>
<p>To be able to test what we&#8217;re building, let&#8217;s create a simple form which will allow us to upload a logo:</p>
<pre data="lang:html">
<form method="POST" action="" enctype="multipart/form-data">
    <input type="file" name="logo" />
    <input type="submit" value="Upload logo" />
</form>
</pre>
<p>Let&#8217;s put that in a file which we&#8217;ll call <em>logo-resize.php</em> (place it at the root of our project), which will also load composer&#8217;s autoload file, and for now let&#8217;s put just a simple placeholder check that will help us deal with the uploaded logo.</p>
<pre data="lang:php">
<?php
require_once('vendor/autoload.php');

if(isset($_FILES['logo'])) {
    //do something with the uploaded logo
}
?>

<form method="POST" action="" enctype="multipart/form-data">
    <input type="file" name="logo" />
    <input type="submit" value="Upload logo" />
</form>
</pre>
<p>We&#8217;ll have to do a few things to accomplish the resize that we want, so to make things cleaner and reusable, we&#8217;ll wrap all our logic into a new class which we&#8217;ll call <code>LogoResizer</code> and store it in the <em>LogoResizer.php</em> file (place it under <em>/src/LogoResizer/</em> folder).</p>
<pre data="lang:php">
namespace LogoResizer;

use Intervention\Image\ImageManagerStatic as Image;

class LogoResizer {

    private $desired_width;
    private $desired_height;
    private $desired_aspect_ratio;
    private $source_logo;
    private $resized_logo;

    public function __construct($desired_width = 300, $desired_height = 200) {
        $this->desired_width = $desired_width;
        $this->desired_height = $desired_height;
        $this->desired_aspect_ratio = $this->get_aspect_ratio($desired_width, $desired_height);
    }

    public function set_source_logo($source_image) {
        $this->source_logo = $source_image;
    }

    public function is_source_logo_valid() {
        $details = getimagesize($this->source_logo['tmp_name']);

        if($details === false) {
            return false;
        }

        return true;
    }

    public function get_resized_logo() {
        return $this->resized_logo;
    }

    public function resize_logo() {

    }

    private function get_aspect_ratio($width, $height) {
        return $width / $height;
    }
}
</pre>
<p>Let&#8217;s walk through the above code. Initially, we added a namespace, we created an alias (i.e. <code>Image</code>) for Intervention&#8217;s <code>ImageManagerStatic</code>, and created a constructor, a few functions, and a few private variables that will help us along the way.</p>
<p>The constructor is pretty straightforward; the two required variables are the desired width and height of the resulting logo (i.e. the one after the resize of the original). We also calculate the desired aspect ratio based on these values, using the <code>get_aspect_ratio</code> function.</p>
<p>The <code>set_source_logo</code> function is just a setter which accepts the uploaded logo. We could have that in the constructor as well, but it makes the class easier to reuse (without having to initialise it again). In the same manner, we could also have setters for the desired width and height, but I&#8217;ll omit them in this tutorial.</p>
<p>In the <code>is_source_logo_valid</code> function, we can add checks that will make sure that the logo is exactly what we need. In the above implementation, I just added a check to make sure that the logo is actually an image, but you can do whatever you want; e.g. check its MIME type, etc.</p>
<p>Another way to implement that, is to incorporate these checks to the <code>set_source_logo</code> function and make it either return <code>true</code> if the logo is ok, or <code>false</code> in case there is anything wrong with it.</p>
<p>We also added a getter function (i.e. <code>get_resized_logo</code>) which will return the resized logo. In a proper implementation, it will probably make sense to also add a <code>save_resized_logo</code> function that will save the logo in the file system, but in this tutorial, we&#8217;ll just show the resized logo (as soon as it&#8217;s done), so we will omit that.</p>
<p>Finally, we added a <code>resize_logo</code> function which -for now- is just a placeholder for the function which will hold our main logic.</p>
<p>Let&#8217;s move back to our <em>logo-resize.php</em> file and modify it (I will omit the HTML code; it will stay the same for the rest of the tutorial) to use this class.</p>
<pre data="lang:php">
require_once('vendor/autoload.php');

use LogoResizer\LogoResizer;

function get_resized_logo($logo) {
    $resizer = new LogoResizer(300, 200);
    $resizer->set_source_logo($logo);

    if(!$resizer->is_source_logo_valid()) {
        return false;
    }

    $logo_resized = $resizer->rezise_logo();

    if(!$logo_resized) {
        return false;
    }

    return $resizer->get_resized_logo();
}

if(isset($_FILES['logo'])) {
    $resized_logo = get_resized_logo($_FILES['logo']);

    if($resized_logo === false) {
        //show error
    } else {
        echo $resized_logo->response('jpg', 70);
        die();
    }
}
</pre>
<p>The <code>get_resized_logo</code> function initialises an object of our <code>LogoResizer</code> class, sets the uploaded logo as the source logo, checks if the source logo is valid, instructs our class to resize the logo, and finally -if the resize was performed successfully- returns the resized logo. In case of any error, it returns <code>false</code>. That&#8217;s enough for this tutorial, but in a real implementation you might want to return relevant error messages to let the user know what happened in case of an error.</p>
<p>So, if the user uploads a logo, we call the <code>get_resized_logo</code> to resize the original and get the resized logo back. In case of an error, we deal with it, and if the image came back correctly, we just display it to the user and kill the script. We do that using Intervention&#8217;s <code>response</code> function.</p>
<p>By the way, you might have noticed that although we use our <code>LogoResizer</code> class, we don&#8217;t include the path to it somewhere in the <em>logo-resize.php</em> file (like we do with composer&#8217;s <em>autoload.php</em>). Instead, we are auto-loading the class using composer. To do that, modify your <em>composer.json</em> file to look similar to the following:</p>
<pre data="lang:json">
{
    "require": {
        "intervention/image": "~2.1"
    },
    "autoload": {
        "psr-0": {
            "LogoResizer": "src/"
        }
    }
}
</pre>
<p>Obviously, what we did so far won&#8217;t work for now, since we have not yet implemented the <code>resize_logo</code> function of our <code>LogoResizer</code> class. Let&#8217;s do that now.</p>
<pre data="lang:php">
public function resize_logo() {
    try {
        $logo = Image::make($this->source_logo['tmp_name']);

         if($logo->width() === $this->desired_width && $logo->height() === $this->desired_height) {
            $this->resized_logo = $logo;
            return true;
        }

        $logo_aspect_ratio = $this->get_aspect_ratio($logo->width(), $logo->height());

        if($this->desired_aspect_ratio === $logo_aspect_ratio) {
            $logo = $this->resize($logo, $this->desired_width, $this->desired_height);
            $this->resized_logo = $logo;
            return true;
        }

        $background_colour = $logo->pickColor(0, 0, 'hex');

        if($this->desired_aspect_ratio > $logo_aspect_ratio) {
            $logo = $this->resize($logo, null, $this->desired_height);
        } else {
            $logo = $this->resize($logo, $this->desired_width, null);
        }

        $logo->resizeCanvas($this->desired_width, $this->desired_height, 'center', false, $background_colour);

        $this->resized_logo = $logo;
        return true;

    } catch (Exception $e) {
        return false;
    }
}

private function resize($logo, $width, $height) {
    $logo->resize($width, $height, function ($constraint) {
        $constraint->aspectRatio();
    });

    return $logo;
}
</pre>
<p>The first step in our <code>resize_logo</code> function is to initialise an instance of Intervention using the source logo. Then, we check if the logo has already the desired dimensions. If so, we copy it to the <code>$resized_logo</code> variable (so we can grab it using our relevant getter) and stop the process returning <code>true</code>; we wrap everything we do in this function in a <code>try catch</code> statement, returning <code>false</code> if an exception is thrown and <code>true</code> in case of a success.</p>
<p>The next step is to calculate the aspect ratio of the source logo, and compare it to the desired ratio.</p>
<p>The easy case is when the two ratios are equal, where we just have to resize the logo to the required dimensions using our <code>resize</code> function (which is basically a wrapper for Intervention&#8217;s homonymous function) and stop the process.</p>
<p>If they are different, we will resize the logo only specifying either the new -desired- height in case the desired aspect ratio is larger than the ratio of the source logo, or the desired width if it&#8217;s the other way around. As you may have noticed, our <code>resize</code> function resizes the logo keeping the aspect ratio, so by doing what we just described we will end up with a logo which has only one of its dimensions set correctly.</p>
<p>To fix that, we will just need to resize the logo canvas to the desired with and height, set all the content that we already have to its centre, and use colour of the top left corner (0x0 pixels) of the logo as the background colour of the canvas.</p>
<p>To further understand why we needed to do that, let me give you a quick visual example. Let&#8217;s assume that we will use the following image as a logo (its original dimensions are 1022&#215;766 pixels):</p>
<p><span style="text-align: center; display: block;"><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/05/original-logo.jpg" alt="" /></span></p>
<p>The desired aspect ratio (of 300&#215;200 pixels) is 1.5 and the aspect ratio of the uploaded image is ~1.33, so based on our code we will resize the logo specifying that the new height should be 200px, but leaving the width to be calculated automatically so we can keep the aspect ratio intact. That will result in a 267&#215;200 pixels logo.</p>
<p>Then, we will resize the canvas of our logo to 300&#215;200 pixels, and put our 267&#215;200 logo at its centre. Have a look at the following image to visualise why.</p>
<p><span style="text-align: center; display: block;"><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/05/canvas-explanation.jpg" alt="" /></span></p>
<p>Finally, we just have to set the background colour of the canvas to #e1ecb4, so it will blend nicely with the rest of the logo.</p>
<p><span style="text-align: center; display: block;"><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/05/resized-logo.jpg" alt="" /></span></p>
<p>You can download the source code of this tutorial from <a href="https://gist.github.com/stathisg/3a549fc31d03fd230272">this Gist</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/tutorials/resize-logo-keeping-aspect-ratio/feed</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>The Art of Deception</title>
		<link>http://burnmind.com/articles/the-art-of-deception</link>
					<comments>http://burnmind.com/articles/the-art-of-deception#respond</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Sat, 04 Apr 2015 08:00:11 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=1987</guid>

					<description><![CDATA[Although many years have passed since then, I'm pretty sure it was 2003 when I read <a href="http://en.wikipedia.org/wiki/Kevin_Mitnick">Kevin Mitnick’s</a> book <a href="http://en.wikipedia.org/wiki/The_Art_of_Deception">The Art of Deception</a>. In it, he describes several real and hypothetical “hacking” cases, illustrating that humans are the weakest link in any secure system, thus a very good target for such attacks. The whole concept is called <a href="http://en.wikipedia.org/wiki/Social_engineering_%28security%29">social engineering</a>.]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/04/social-engineering.jpeg" alt="" class="left" /></p>
<p>Although many years have passed since then, I&#8217;m pretty sure it was 2003 when I read <a href="http://en.wikipedia.org/wiki/Kevin_Mitnick">Kevin Mitnick’s</a> book <a href="http://en.wikipedia.org/wiki/The_Art_of_Deception">The Art of Deception</a>. In it, he describes several real and hypothetical “hacking” cases, illustrating that humans are the weakest link in any secure system, thus a very good target for such attacks. The whole concept is called <a href="http://en.wikipedia.org/wiki/Social_engineering_%28security%29">social engineering</a>.</p>
<p>During that period, I was an undergraduate student. One afternoon, while we were hanging out at my apartment with some friends, we started talking about Mitnick’s book that I was reading these days, so I explained the concept of social engineering to them.</p>
<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/04/the-art-of-deception-cover.jpeg" alt="The Art of Deception Book Cover" class="left" /></p>
<p>The conversation was interrupted by another friend of ours, who came into the apartment frustrated. He was coming from his travel agent -located just a few minutes from my home- where he went to get his plane ticket. When he arrived there, he discovered that the agent’s internet connection was down, therefore couldn&#8217;t retrieve the ticket information and print it, so he had to wait until that was fixed.</p>
<p>By the way, although DSL connections existed, the most common internet connections at that time in Greece were PSTN (remember their <a href="https://www.youtube.com/watch?v=gsNaR6FRuO0">sound</a>?) &#038; ISDN. The travel agent had an ISDN connection. How we knew that?</p>
<p>They told my friend something along the lines of <em>“we are waiting for the ISDN guy of company X to come back; he was here about an hour ago and said he had to go and check something at the exchange”</em>.</p>
<p>I’m sure that a lot of you would have the exact same idea! :)</p>
<p>I quickly fired up my own ISDN connection, found the agent’s details and gave them a call.</p>
<p><em>Me: Hi, I’m calling from company X; you’re still having issues with your ISDN connection, right?</p>
<p>Agent: Yes, that’s correct.</p>
<p><span style="text-align: center; display: block;"><img decoding="async" fetchpriority="high" src="http://burnmind.com/wp-content/uploads/2015/04/phone-phreakers.jpeg" alt="phone-phreakers" width="300" height="200" class="aligncenter size-full wp-image-1990" /></span></p>
<p>M: As I can see in my records, he had an engineer over at your place earlier today which informed us that there is a problem with Y (something that would be difficult to follow without a technical background).</p>
<p>(after a couple of minutes)</p>
<p>M: The problem should now be fixed; please, try to connect.</p>
<p>A: I still get the same error.</p>
<p>M: Then, we need to test with your credentials, since on our end everything seems fine. Can you please give me your username and password to try them out and also make sure you are using the correct combination?</p>
<p>A: Sure (…)</em></p>
<p>After thanking and telling him that I would run some tests and call him straight back, I tried his credentials using my ISDN modem. They worked. A few minutes later, I called him back and explained that I was actually a student doing some research on security, found a good excuse about how I knew he had a problem with his internet connection, told him that I was sorry for wasting a few minutes of his day and advised him not to give sensitive information over the phone, to change his username &#038; password, etc.</p>
<p>Of course, I immediately threw away the credentials (I did the whole thing just to try out if that whole thing worked) and never used them again — I never even tried them again just to test if he actually changed them, but that taught all of us a very important lesson. To always keep in mind that no matter how secure a computing system is, there is always a human available to screw it up!</p>
<p>The book might be old, Mitnick hacking days might be older, but such attacks still exist. The latest example that comes to mind is an <a href="http://www.ebayinc.com/in_the_news/story/ebay-inc-ask-ebay-users-change-passwords">attack against eBay</a>, where the attackers stole <em>“a database containing encrypted passwords and other non-financial data”</em> (still a lot of personal data though).</p>
<p>How the attack was performed? <em>“Cyberattackers compromised a small number of employee log-in credentials, allowing unauthorized access to eBay’s corporate network”</em>.</p>
<p>They never had to hack eBay itself; they just had to hack some of its employees!</p>
<p><small><em>This story was originally published as part of <a href="http://inequilibrium.co/issues/10">my newsletter’s editorial</a> and was also <a href="https://medium.com/@stathisg/the-art-of-deception-2692afd10ac7">published on medium</a>.</em></small></p>
<p><small>Images by <a href="http://mustified.com/facebookcoverphotos/2012/02/v-for-vendetta/">mustified</a>, <a href="http://en.wikipedia.org/wiki/The_Art_of_Deception">wikipedia</a>, and <a href="https://www.flickr.com/photos/wardomatic/8489053831">Ward Jenkins</a></small></p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/articles/the-art-of-deception/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>A tale of bad customer service &#8211; The case of Currys/PC World</title>
		<link>http://burnmind.com/articles/bad-customer-service-currys-pc-world</link>
					<comments>http://burnmind.com/articles/bad-customer-service-currys-pc-world#comments</comments>
		
		<dc:creator><![CDATA[Stathis]]></dc:creator>
		<pubDate>Wed, 11 Mar 2015 22:48:03 +0000</pubDate>
				<category><![CDATA[articles]]></category>
		<guid isPermaLink="false">http://burnmind.com/?p=1979</guid>

					<description><![CDATA[A couple of months ago, I went to a Currys/PC World store (for those of you who are unfamiliar with the brand, it is a British electrical retailer) to pick up a vacuum cleaner I reserved online (I needed it quickly, therefore I didn't want to wait for home delivery).

The quite spacious store was almost empty of customers and a lot of sales people were wandering around doing nothing. Before going to pick up my order, I decided to have a quick look at some laptops they had on display.]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="http://burnmind.com/wp-content/uploads/2015/03/laptops.jpg" alt="" class="left" /> A couple of months ago, I went to a Currys/PC World store (for those of you who are unfamiliar with the brand, it is a British electrical retailer) to pick up a vacuum cleaner I reserved online (I needed it quickly, therefore I didn&#8217;t want to wait for home delivery).</p>
<p>The quite spacious store was almost empty of customers and a lot of sales people were wandering around doing nothing. Before going to pick up my order, I decided to have a quick look at some laptops they had on display.</p>
<p>While heading to the laptops section, a salesman stopped me and asked me if I needed help with anything. I didn&#8217;t. While checking out a few laptops, two other salesmen asked me the same. I still didn&#8217;t. After a few minutes, I tried to find out where I could pick my item from. There was no sign indicating where I had to go, so I asked a salesman who was not very sure, but showed me a queue of 5–6 people. It was the “customer services” desk.</p>
<p>I quickly noticed that everyone in that queue had items they had a problem with and wanted to return or fix. There were only two people serving that queue, so it was moving extremely slowly. After a few minutes, I asked a saleswoman if I was standing in the right queue for what I came for. She asked me what kind of item I reserved, and assured me that I was waiting in the right place (since they had to bring it from the warehouse).</p>
<p>After about 20 more minutes which felt like a couple of hours, ​it was finally my turn to be served. After explaining what I was waiting for and that the sales staff told me to wait there, the guy on the desk just turned me away, telling me that they only deal with “large items” and I should go to one of the checkout tills… ​Since he didn&#8217;t even apologise for the delay, ​​I ​expressed how unhappy I was since I had been waiting 20–25 minutes without any reason​, and headed for the till.</p>
<p><img decoding="async" width="640" height="200" src="http://burnmind.com/wp-content/uploads/2015/03/dogbert-consults.gif" alt="" class="alignnone size-full wp-image-1982" /></p>
<p>After waiting there for a little bit and after I was told that I should have gone to the customer services desk (no further comments on that), they ended up finding my order and sent someone to the warehouse to pick it up. I paid and left, but forgot to ask if any bags were included in the package. I went back (with the item in hand) and asked about it at the same till.</p>
<p>The person there assured me that it did, since (quoting her) “the features list of the vacuum mentions bags, so that means that it has bags inside”. I decided not to explain to her that this is not what it means, so I just went to my car opened the box there, confirmed that there were no bags in it, and went back to buy some.</p>
<p>When I went to pay, there were 6–7 people waiting in the only open till at that point. I looked around and noticed that most of the sales persons were sitting idle in random corridors, waiting for the next customer to appear; probably they were not allowed (or trained) to operate a till. The process in the tills was too slow that literally two of the people in front of me in the queue left the stuff they had decided to buy and left the store.</p>
<p>I paid and left, praying that the vacuum cleaner won’t break within its warranty period, and I won’t have to go back to that place ever again!</p>
<p><em>This story was originally published on my <a href="http://inequilibrium.co/issues/6">newsletter</a> and is also <a href="https://medium.com/@stathisg/a-tale-of-bad-customer-service-babd25d8c052">published on medium</a>.</em></p>
<p><small>Photo by <a href="https://www.flickr.com/photos/alexsegre/15551579622">Alex Segre</a>; Cartoon by <a href="http://dilbert.com/strip/2004-02-05">Dilbert</a></small></p>
]]></content:encoded>
					
					<wfw:commentRss>http://burnmind.com/articles/bad-customer-service-currys-pc-world/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/


Served from: burnmind.com @ 2026-04-13 10:16:37 by W3 Total Cache
-->