Breakdown of a Star Wars Scene Created With After Effects and Element 3D

In this tutorial I’ll breakdown this Star Wars inspired scene created using After Effects and the plug-in Element 3D V2

You can download the Star Wars models used for this tutorial free from Videocopilot.net

I’ll explore everything that went into creating this scene, from the composition and texturing, all the way to color-grading and final compositing.

Screencast

Mentioned in This Tutorial

Download Breakdown of a Star Wars Scene Created With After Effects and Element 3D

Taking Control of the tvOS Focus Engine

Introduction

On iOS, users normally interact with your apps via the device’s touch screen. On tvOS, however, user interaction is handled by moving the current focus between views on the screen.

Luckily, the tvOS implementations of the UIKit APIs handle the changing of focus between views automatically. While this built-in system works very well, for specific view layouts and/or purposes, it may be necessary to sometimes manually control the focus engine.

In this tutorial, we take an in-depth look at the tvOS focus engine. You learn how it works and how to control it however you want to.

Prerequisites

This tutorial requires that you are running Xcode 7.3 or higher with the latest tvOS 9.2 SDK. If you want to follow along, you also need to download the starter project from GitHub.

1. Focus Engine Overview

The purpose of the focus engine of tvOS is to help developers concentrate on their own app’s unique content rather than reimplementing basic navigation behaviors. This means that, while many users will use Apple TV’s Siri Remote, the focus engine automatically supports all current and future Apple TV input devices.

This means that, as a developer, you don’t have to worry about how a user is interacting with your app. Another important goal of the focus engine is to create a consistent user experience between applications. Because of this, there is no API that allows an application to move the focus.

Focus Movement

When the user interacts with the remote of the Apple TV by swiping on the glass Touch surface in a particular direction, the focus engine looks for a possible focusable view in that direction and, if found, moves the focus to that view. If no focusable view is found, the focus remains where it currently is.

In addition to moving the focus in a particular direction, the focus engine also handles several other, more advanced behaviors, such as:

  • moving the focus past particular views if, for example, the user swipes fast on the Touch surface of the Apple TV remote
  • running animations at speeds based on the velocity of the focus change
  • playing navigation sounds when the focus changes
  • animating scroll view offsets automatically when the focus needs to move to a currently off-screen view

When determining where the focus should move to in an app, the focus engine takes an internal picture of your app’s current interface and highlights all of the visible elements that are focusable. This means that any hidden views, including views with an alpha value of 0, cannot be focused. This also means that, for any view that is hidden by another view, only the visible part is considered by the focus engine.

If the focus engine finds a view it can move the focus to, it notifies the objects conforming to the UIFocusEnvironment protocol that are involved with the change. The UIKit classes that conform to the UIFocusEnvironment protocol are UIWindow, UIViewControllerUIView, and UIPresentationController. The focus engine calls the shouldUpdateFocusInContext(_:) method of all the focus environment objects that contain either the currently focused view or the view the focus is moving to. If any of these method calls returns false, the focus is not changed.

Initial Focus

The UIFocusEnvironment protocol represents an object that is known as a focus environment. The protocol defines a preferredFocusView property that specifies where the focus should move to if the current environment becomes focussed itself.

For example, a UIViewController object’s default preferredFocusView is its root view. As each UIView object can also specify its own preferred focus view, a preferred focus chain can be created. The tvOS focus engine follows this chain until a particular object returns either self or nil from its preferredFocusView property. By using these properties, you can redirect focus throughout the user interface and also specify which view  should be focussed first when a view controller appears on-screen.

It is important to note that, if you don’t change any of the preferredFocusView properties of your views and view controllers, the focus by default engine focuses the view closest to the top left corner of the screen.

Focus Update

A focus update occurs when one of three events take place:

  • the user causes a focus movement
  • the app explicitly requests a focus update
  • the system triggers and automatic update

Whenever an update takes place, the following events follow:

  • The current UIScreen object’s focusedView property is changed to the view that the focus is moving to.
  • The focus engine calls the didUpdateFocusInContext(_:withAnimationCoordinator:) of every focus environment object involved in the focus update. These are the same set of objects which the focus engine checks by calling each object’s shouldUpdateFocusInContext(_:) method before updating the focus. It is at this point that you can add custom animations to run in conjunction with the focus-related animations the system provides.
  • All of the coordinated animations, both system and custom animations, are run simultaneously.
  • If the view the focus is moving to is currently off-screen and in a scroll view, the system scrolls the view on-screen so that the view becomes visible to the user.

To manually update the focus in the user interface, you can invoke the setNeedsFocusUpdate() method of any focus environment object. This resets the focus and moves it back to the environment’s preferredFocusView.

The system can also trigger an automatic focus update in several situations, including when a focussed view is removed from the view hierarchy, a table or collection view reloads its data, or when a new view controller is presented or dismissed.

While the tvOS focus engine is quite complex and has a lot of moving parts, the UIKit APIs provided to you make it very easy to utilize this system and make it work how you want it to.

2. Controlling the Focus Engine

Focus Guides

To extend the focus engine, we are going to implement a wrap-around behavior. Our current app has a grid of six buttons as shown in the below screenshot.

Project Setup

What we are going to do is allow the user to move the focus towards the right, from buttons 3 and 6, and make the focus wrap back around to buttons 1 and 4 respectively. As the focus engine ignores any invisible views, this can not be done by inserting an invisible UIView (including a view with a width and height of 0) and changing its preferredFocusedView property.

Instead, we can accomplish this using the UIFocusGuide class. This class is a subclass of UILayoutGuide and represents a rectangular focusable region on the screen while being completely invisible and not interacting with the view hierarchy. On top of all the UILayoutGuide properties and methods, the UIFocusGuide class adds the following properties:

  • preferredFocusedView: This property works as I described earlier. You can think of this as the view that you want the focus guide to redirect to.
  • enabled: This property lets you enable or disable the focus guide.

In your project, open ViewController.swift and implement the viewDidAppear(_:) method of the ViewController class as shown below:

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    
    let rightButtonIds = [3, 6]
    for buttonId in rightButtonIds {
        if let button = buttonWithTag(buttonId) {
            let focusGuide = UIFocusGuide()
            view.addLayoutGuide(focusGuide)
            focusGuide.widthAnchor.constraintEqualToAnchor(button.widthAnchor).active = true
            focusGuide.heightAnchor.constraintEqualToAnchor(button.heightAnchor).active = true
            focusGuide.leadingAnchor.constraintEqualToAnchor(button.trailingAnchor, constant: 60.0).active = true
            focusGuide.centerYAnchor.constraintEqualToAnchor(button.centerYAnchor).active = true
            focusGuide.preferredFocusedView = buttonWithTag(buttonId-2)
        }
    }
    
    let leftButtonIds = [1, 4]
    for buttonId in leftButtonIds {
        if let button = buttonWithTag(buttonId) {
            let focusGuide = UIFocusGuide()
            view.addLayoutGuide(focusGuide)
            focusGuide.widthAnchor.constraintEqualToAnchor(button.widthAnchor).active = true
            focusGuide.heightAnchor.constraintEqualToAnchor(button.heightAnchor).active = true
            focusGuide.trailingAnchor.constraintEqualToAnchor(button.leadingAnchor, constant: -60.0).active = true
            focusGuide.centerYAnchor.constraintEqualToAnchor(button.centerYAnchor).active = true
            focusGuide.preferredFocusedView = buttonWithTag(buttonId+2)
        }
    }
}

In viewDidAppear(_:), we create focus guides to the right of buttons 3 and 6, and to the left of buttons 1 and 4. As these focus guides represent a focusable region in the user interface, they must have a set height and width. With this code, we make the regions the same size as the other buttons so that the momentum-based logic of the focus engine feels  consistent with the visible buttons.

Coordinated Animations

To illustrate how coordinated animations work, we update the alpha property of the buttons when the focus changes. In ViewController.swift, implement the didUpdateFocusInContext(_:withAnimationCoordinator:) method in the ViewController class:

override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
    super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator)
    
    if let focusedButton = context.previouslyFocusedView as? UIButton where buttons.contains(focusedButton) {
        coordinator.addCoordinatedAnimations({ 
            focusedButton.alpha = 0.5
        }, completion: { 
            // Run completed animation
        })
    }
}

The context parameter of didUpdateFocusInContext(_:withAnimationCoordinator:) is a UIFocusUpdateContext object that has the following properties:

  • previouslyFocusedView: references the view the focus is moving from
  • nextFocusedView: references the view the focus is moving to
  • focusHeading: a UIFocusHeading enumeration value representing the direction the focus is moving in

With the implementation of didUpdateFocusInContext(_:withAnimationCoordinator:), we add a coordinated animation to change the alpha value of the previously focused button to 0.5 and that of the currently focused button to 1.0.

Run the app in the simulator and move the focus between the buttons in the user interface. You can see that the currently focused button has an alpha of 1.0 while the previously focused button has an alpha of 0.5.

Transparent Buttons

The first closure of the addCoordinatedAnimations(_:completion:) method works similarly to a regular UIView animation closure. The difference is that it inherits its duration  and timing function from the focus engine.

If you want to run an animation with a custom duration, you can add any UIView animation within this closure with the OverrideInheritedDuration animation option. The following code is an example of how to implement a custom animation that runs in half the time of the focus animations:

// Running custom timed animation
let duration = UIView.inheritedAnimationDuration()
UIView.animateWithDuration(duration/2.0, delay: 0.0, options: .OverrideInheritedDuration, animations: { 
    // Animations
}, completion: { (completed: Bool) in
    // Completion block
})

By using the UIFocusGuide class and by utilizing custom animations, you can extend the standard behavior of the tvOS focus engine to suit your needs.

Limiting the Focus Engine

As I mentioned earlier, when deciding whether or not the focus should be moved from one view to another, the focus engine calls the shouldUpdateFocusInContext(_:) method on every focus environment involved. If any of these method calls returns false, the focus is not changed.

In our app, we are going to override this method in the ViewController class so that the focus cannot be moved down if the currently focused button is 2 or 3. To do so, implement shouldUpdateFocusInContext(_:) in the ViewController class as shown below:

override func shouldUpdateFocusInContext(context: UIFocusUpdateContext) -> Bool {
    let focusedButton = context.previouslyFocusedView as? UIButton
    
    if focusedButton == buttonWithTag(2) || focusedButton == buttonWithTag(3) {
        if context.focusHeading == .Down {
            return false
        }
    }
    
    return super.shouldUpdateFocusInContext(context)
}

In shouldUpdateFocusInContext(_:), we first check whether the previously focused view is button 2 or 3. We then inspect the focus heading. If the heading is equal to Down, we return false so that the current focus does not change.

Run your app one last time. You cannot move the focus down from buttons 2 and 3 to buttons 5 and 6.

Conclusion

You should now be comfortable controlling and working with the focus engine of tvOS. You now know how the focus engine works and how you can manipulate it to fit whatever needs you have for your own Apple TV apps.

As always, be sure to leave your comments and feedback in the comments below.

Download Taking Control of the tvOS Focus Engine

10 Ways To Share Your Business Video

You’ve created a
compelling video about your business. You’ve invested time and money in the
content and messaging, and are confident it sells. How do you get people to
watch it when there’s so much competition for eyeballs online?

TV screens
Untitled by geralt/Pixabay

Uploading your video to YouTube is a no-brainer. But don’t stop there! You need to share that
video far and wide to maximize reach and engagement.

Here are ten ways
to share your business video, with tips and best practices for each.

YouTube

YouTube is the
second largest search engine online after Google, so uploading your video here
is essential. YouTube is especially well suited for educational content such as
“how to” videos and product demonstrations. The downside? There are hundreds of
millions of hours of videos on YouTube, and getting found can be difficult. Bringing traffic back to your website is even harder still.

Tips: Fill out the
title and description of each video with relevant keywords and tags for search engine optimization (SEO). Organize your videos into clearly defined playlists so they’re easy to find on your channel. Use YouTube cards
to direct people to your website or related content when they’re finished
watching.

Vimeo

If you’re more
interested in a quality viewing experience than being found, upload your video
to Vimeo. This platform doesn’t have nearly as big an audience as YouTube, but there are no banner or pre-roll ads or distracting cat videos. The other plus? Your video will plays in high definition without the viewer having to adjust
settings.

Tip: Create a
custom thumbnail image for your video after you’ve uploaded it. You can almost always do better than the
one selected automatically! 

Your Website

Once you’ve
uploaded your video to YouTube or Vimeo, grab the embed code and post
it on your company’s website. Make your video a prominent size and place it “above
the fold” – in other words, high on a page instead of buried at the bottom.

You can host your own video, but specialized video hosting services, like Wistia (the service we use to host our course videos), VHX, and Uscreen offer solid advantages for certain kinds of business video. These “white label” hosts let you display video on your site without any unwanted branding (or with your own), provide robust metrics and engagment tools, and won’t try and drive your traffic back to their platform.

Website with video
A website with the video placed “above the fold” by Cindy Burgess

Don’t forget to write
a blog post about your video too!

Tip: Avoid auto
play. Most viewers like to have control over when or whether a video plays.

Social Media Sites

Social media sites
like Facebook, Twitter, LinkedIn, Google+, Instagram, and Pinterest are made for sharing. Post your video to
all of your company’s social media accounts – repeatedly. Keep in mind that not
everyone is going to see your video the first time you post it.

Whenever possible, upload your video directly to the platform in question, rather than posting a link to YouTube. Sites like Facebook give a higher priority to videos that are uploaded natively. Also, consider
making different versions of your video for different social media sites. For example,
you might want to create a 15-second teaser video for Instagram to drive traffic to the
full-length video on your website.

Tip: Use online tools
like Hootsuite to schedule your posts at different hours and on different days
to maximize viewing.

Newsletters

Add your video to
the next email blast or newsletter you send out to clients. They’re already
interested in the content you provide, so chances are you’ll have a high click
rate.

Newsletter with video
A newsletter with embedded video by Cindy Burgess

Tip: Encourage
clients to share your video with others. Ask them what kind of video content
they’d like to see in the future.

Email Signature

Promote your video
in your email signature. Just write a line of text to tease the video, then add
a hyperlink. Here’s a fictional example to illustrate what I’m talking about:

John Doe | Widgets Inc. | (555) 123-4567
Have you seen our
newest product line? Watch the
video!

Tip: Change the
signature every time you release a new video.

Press Releases

Most press
releases are sent online these days, so it’s easy to link to a video. The next
time your business introduces a new product or service or wins an award, make
sure the press release includes a link to your video.

Tip: Include an
eye-catching thumbnail image to entice people to click on the video.

Partner Blogs

Contact your
industry partners and ask if they’ll feature your video content on their blog
or social media sites. For example, one of my clients is a golf pro who created
a series of short tip videos. She has sponsors for clubs, tees, and clothing
that might be interested in sharing her videos because they serve the same
audience.

Tip: Offer to
promote some of their content in exchange.

Community Web Sites

Many communities
have blogs or websites that profile local businesses and people, and they’re
usually hungry for free content. It’s worth searching online to see what’s out
there. Ask if they’d be interested in linking to your video or writing a blog
post about it.

Tip: Offer to
write a short introduction to the video to help make their job easier.

Stores, Trade Shows, Conferences

If you have a
bricks-and-mortar storefront, set up a flat screen monitor to play your video. Same goes for trade shows and conferences where your business
might have a booth.

Tip: Make a
version of your video with subtitles so you can play it without audio.

Conclusion

As you can see,
there are lots of ways to share your business video beyond YouTube. Which
methods you choose will depend on your business and the type of video you’ve
created. So start experimenting!

Download 10 Ways To Share Your Business Video

Quick Tip: How to Use Zeplin to Generate Style Guides

We previously covered how designers can collaborate with the dev team using Zeplin, but there’s a bit more to it; Zeplin is a great tool for generating style guides too. It makes sure your team creates consistent experiences by having an up-to-date online guide accessible to everyone from anywhere. 

It allows you to save and rename fonts and colour information, and even export CSS. The style guide and the inspect feature will save a lot of effort when the time comes for QA (quality assurance). Let’s quickly see how it works!

Adding Styles

First, select a screen from the project dashboard.

Then select the element whose properties you want to add to the style guide (for example: select H1 title to add its font family, size and colour).

Next, hover over the inspector tool and look for the icon Aa+ which will appear by the text styles to add them to the font book:

selected titles properties in zeplin
Selected title and its properties in the inspector

Adding colours works in a similar way. Click the drop icon next to each one to build your colour palette:

same deal with colours
Same deal, with colours

Your Style Guide

To see your style guide in action, select the second tab Styleguide at the top of the page:

All the previously added elements will be there. You can now go ahead and give each one a more suitable name. To do this, hover over the default colour name and click to change it.

Note: use names and naming conventions which are familiar to the entire team):

Do the same for fonts; update them with a name which designers and devs will both be familiar with. Don’t forget you’re helping with the dev handover, but also creating a repository for other designers to use.

What you’ll end up with is something like this:

Exporting

At this point, if you’re part of the dev operation, you might want to copy the CSS from the right-hand side column. You’ll find it’s available in whichever syntax you prefer; Sass, LESS, Stylus, or plain CSS.

Each rule can be individually selected for download, whilst Command + A will select everything for copying and pasting. 

Conclusion

No more outdated style guides saved somewhere no one can find, no more chains of emails to find out the latest colour; Zeplin can help your whole team design efficiently. Cheers to productivity!

Download Quick Tip: How to Use Zeplin to Generate Style Guides

New Course: How to Record Better Travel Videos

You often see amazing sights when you travel, but creating a video that other people want to watch is harder than it seems.

In our new course, How to Record Better Travel Videos, you’ll learn how to prepare for and capture the highlights of your trip and tell video stories that are worth watching and sharing! 

What You’ll Learn

Cindy Burgess has been telling stories with video for more than 20 years, and in this course she’ll teach you all about must-pack items, proper recording techniques, how to tell a story through characters, setting and natural sound, how to deal with local customs and culture, how to protect your gear from the elements, and more!

Watch the Introduction

Start Learning With a Free Trial

You can take our new course straight away with a free 10-day trial of our monthly subscription. If you decide to continue, it costs just $15 a month, and you’ll get access to hundreds of courses, with new ones added every week.

You can also find some useful resources for improving your travel videos on Envato Market. There are thousands of professional-looking transitions, backgrounds, overlays, and more.

Download New Course: How to Record Better Travel Videos

Take Our New Course on Dynamic Gesture Drawing

Final product image
What You’ll Be Creating

Want to improve your drawing technique? In our new course, Dynamic Gesture Drawing, digital artist Brian Lee will teach you the fundamentals of gesture drawing step by step. 

You will learn the importance of refining gestures into lines of action and simple shapes, creating strong silhouettes, effectively using negative space, and projecting ideas from a pose rather than just structural anatomy. Finally, you’ll learn how to tell stories with different gestures. By the end of this course you should be able to draw any gesture you want.

You can take our new course straight away with a free 10-day trial of our monthly subscription. If you decide to continue, it costs just $15 a month, and you’ll get access to hundreds of courses, with new ones added every week.

Download Take Our New Course on Dynamic Gesture Drawing

How to Use Ajax With OpenCart

The inclusion of jQuery libraries in the core of OpenCart makes Ajax implementation a breeze and more than a pleasant experience. In fact, you’ll find several snippets spread across the whole framework that show the heavy use of jQuery, if you try to dig into the view files specifically.

Throughout the course of this article, we’ll build a custom page to demonstrate the use of Ajax in OpenCart. It’ll be a simple interface that allows you to select a product from the drop-down box and displays a nice product summary block of the selected product. The interesting part of the use-case is the way the product summary block is built—it’ll be prepared using Ajax on-the-fly. Of course, it’s not something that makes it an out of the world example, but I guess it’ll serve the basic purpose of showing how things work in OpenCart.

I assume that you’re using the latest version of OpenCart, which is 2.1.x.x! Also, the primary discussion of this article concentrates on Ajax with OpenCart, so I’ll skid through the basics of custom module development in OpenCart. However, if you’re not familiar with it, a quick explanation of the code snippets in between makes sure that you can follow till the end!

A Quick Glance at File Organization

Let’s quickly go through the file setup required for our custom page.

  • catalog/controller/ajax/index.php: It’s a controller file that provides the application logic of the usual controller in OpenCart.
  • catalog/language/english/ajax/index.php: It’s a language file that helps set up language labels.
  • catalog/view/theme/default/template/ajax/index.tpl: It’s a view template file that holds the XHTML of the custom page.
  • catalog/view/theme/default/template/ajax/product.tpl: It’s a view template file that holds the XHTML of the AJAX response.

So, that’s a quick list of the files we’re going to implement today.

Create Module Files

Go ahead and create a file catalog/controller/ajax/index.php with the following contents.

<?php
// catalog/controller/ajax/index.php
class ControllerAjaxIndex extends Controller {
  public function index() {
    $this->load->language('ajax/index');
    $this->load->model('catalog/product');
    
    $this->document->setTitle($this->language->get('heading_title'));
    
    // load all products
    $products = $this->model_catalog_product->getProducts();
    $data['products'] = $products;
    
    $data['breadcrumbs'] = array();

    $data['breadcrumbs'][] = array(
      'href' => $this->url->link('common/home'),
      'text' => $this->language->get('text_home')
    );

    $data['breadcrumbs'][] = array(
      'href' => $this->url->link('ajax/index'),
      'text' => $this->language->get('heading_title')
    );

    $data['heading_title'] = $this->language->get('heading_title');
    $data['text_product_dropdown_label'] = $this->language->get('text_product_dropdown_label');

    $data['column_left'] = $this->load->controller('common/column_left');
    $data['column_right'] = $this->load->controller('common/column_right');
    $data['content_top'] = $this->load->controller('common/content_top');
    $data['content_bottom'] = $this->load->controller('common/content_bottom');
    $data['footer'] = $this->load->controller('common/footer');
    $data['header'] = $this->load->controller('common/header');

    if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/ajax/index.tpl')) {
      $this->response->setOutput($this->load->view($this->config->get('config_template') . '/template/ajax/index.tpl', $data));
    } else {
      $this->response->setOutput($this->load->view('default/template/ajax/index.tpl', $data));
    }
  }
 
  // ajax call method
  public function ajaxGetProduct() {
    if (isset($this->request->get['product_id'])) {
      $product_id = (int) $this->request->get['product_id'];

      if ($product_id > 0) {
        // load the particular product requested in ajax
        $this->load->model('catalog/product');
        $product = $this->model_catalog_product->getProduct($product_id);
        $data['product'] = $product;
        
        // prepare thumb image
        $this->load->model('tool/image');
        if ($product['image']) {
          $data['thumb'] = $this->model_tool_image->resize($product['image'], $this->config->get('config_image_thumb_width'), $this->config->get('config_image_thumb_height'));
        }
        
        // format price
        $data['price'] = $this->currency->format($this->tax->calculate($product['price'], $product['tax_class_id'], $this->config->get('config_tax')));
        
        $this->load->language('product/product');
        $data['text_manufacturer'] = $this->language->get('text_manufacturer');
        $data['text_model'] = $this->language->get('text_model');
        $data['text_note'] = $this->language->get('text_note');
        $data['tab_description'] = $this->language->get('tab_description');
        
        if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/ajax/product.tpl')) {
          $this->response->setOutput($this->load->view($this->config->get('config_template') . '/template/ajax/product.tpl', $data));
        } else {
          $this->response->setOutput($this->load->view('default/template/ajax/product.tpl', $data));
        }
      }
    }
  }
}

To start with, the index method of the controller is used to load the language and model files and set up the common variables for the usual OpenCart template. We’re loading the product model available in the core itself, so we don’t have to duplicate the code to fetch the product information.

After loading the product model, we’re using the getProducts method to load all the products. Finally, we conclude the index method by setting index.tpl as our main template file.

Next is the important ajaxGetProduct method, which is used to build a product summary block based on the product id passed in the Ajax call, as we’ll see soon in the template file. It loads the same product model as we did in the index method, and calls the getProduct method to fetch specific product information based on the product id.

At the end, the product.tpl template is set as a template for this method. Specific to the requirements in our case, we’re using the template to build our Ajax output, but you could also send the JSON response instead.

Moving ahead, let’s create a language file catalog/language/english/ajax/index.php to hold the static label information.

<?php
// catalog/language/english/ajax/index.php
// Heading
$_['heading_title']            = 'Simple Ajax Example';
$_['text_product_dropdown_label']   = 'Select Product';

The view template file, one of the most important files in the context of this tutorial, should be created at catalog/view/theme/default/template/ajax/index.tpl with the following contents.

<!-- catalog/view/theme/default/template/ajax/index.tpl -->
<?php echo $header; ?>
<div class="container">
  <ul class="breadcrumb">
    <?php foreach ($breadcrumbs as $breadcrumb) { ?>
    <li><a href="<?php echo $breadcrumb['href']; ?>"><?php echo $breadcrumb['text']; ?></a></li>
    <?php } ?>
  </ul>
  <div class="row"><?php echo $column_left; ?>
    <?php if ($column_left && $column_right) { ?>
    <?php $class = 'col-sm-6'; ?>
    <?php } elseif ($column_left || $column_right) { ?>
    <?php $class = 'col-sm-9'; ?>
    <?php } else { ?>
    <?php $class = 'col-sm-12'; ?>
    <?php } ?>
    <div id="content" class="<?php echo $class; ?>"><?php echo $content_top; ?>
      <h2><?php echo $heading_title; ?></h2>
      <fieldset id="account">
      <div>
        <label class="col-sm-2 control-label" for="product"><?php echo $text_product_dropdown_label; ?></label>
        <div class="col-sm-10">
          <select name="product" class="form-control" id="product">
            <option>-- <?php echo $text_product_dropdown_label; ?> --</option>
            <?php foreach ($products as $product) { ?>
              <option value="<?php echo $product['product_id']; ?>"><?php echo $product['name']; ?></option>
            <?php } ?>
          </select>
        </div>
      </div>
      </fieldset>
      <div id="product_summary"></div>
      <?php echo $content_bottom; ?></div>
    <?php echo $column_right; ?></div>
</div>
<?php echo $footer; ?>

<script type="text/javascript">
  $('#product').on('change', function() {
    $.ajax({
      url: 'index.php?route=ajax/index/ajaxGetProduct&product_id=' + this.value,
      dataType: 'html',
      success: function(htmlText) {
        $('#product_summary').html(htmlText);
      },
      error: function(xhr, ajaxOptions, thrownError) {
        alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
      }
    });
  });
</script>

The snippet of our interest is at the end of index.tpl, the JavaScript code that makes use of jQuery methods to bind change and Ajax events. When the user selects a product from the drop-down box, the change event is fired that eventually makes an Ajax call. In the Ajax call, we’re sending the product_id appended as a GET query-string variable.

On the other hand, as we’ve already discussed in the controller setup, the ajaxGetProduct sends the XHTML of the product summary block based on the product_id query-string variable. In the success method, we append the XHTML response to the div tag that has the id attribute set to product_summary.

Finally, go ahead and make a template file catalog/view/theme/default/template/ajax/product.tpl with the following contents for an Ajax call.

<!-- catalog/view/theme/default/template/ajax/product.tpl -->
<div style="border: 1px solid #CCC;padding:10px;margin:10px;">
  <h3><?php echo $product['name']; ?> - <?php echo $price; ?></h3>
  <p><img src="<?php echo $thumb; ?>"/></p>
  <p><b><?php echo $text_manufacturer; ?></b>&nbsp;<?php echo $product['manufacturer']; ?></p>
  <p><b><?php echo $text_model; ?></b>&nbsp;<?php echo $product['model']; ?></p>
</div>

Nothing fancy here—We’ve just included a basic product summary block XHTML.

So, that’s it as far as the file setup is concerned. In our next section, we’ll go through the front-end to test what we’ve built so far.

Front-End Testing

So we’ve done all the hard work, and now it’s time for some testing! Head over to the front-end of OpenCart and visit the URL http://www.yourstore.com/index.php?route=ajax/index. It should display a nice-looking interface as shown in the following screenshot.

Custom Page Drop-Down

It’s our custom page, and it’s displaying a drop-down box containing the list of all products. Now, let’s try to select a product from the select box, and it’ll make an Ajax call in the background.

As a result, you should see a nice product summary block displayed right under the drop-down box as shown in the following screenshot.

AJAX Preview

If you’ve followed the tutorial and implemented all the files as explained, it should work smoothly for you as well as it did for me! Of course, that was a pretty simple demonstration of how Ajax works in OpenCart, but you could stretch it to the next level as per your requirements.

Go ahead and play with it, and try to make some interactive stuff using Ajax as it’s the best way to learn. So, that’s it for today, and I’ll be back soon with some more exciting stuff.

Conclusion

It was Ajax with OpenCart that was the central attraction of today’s article. As usual, we took a custom module approach to demonstrate it and made a simple yet effective use-case. I hope that it was informative and useful, and don’t hesitate to leave your queries and comments!

Download How to Use Ajax With OpenCart

How to Master Proper Business Email Format – and Avoid Professional Disaster

Email has become the main way that many business professionals communicate. Most of us send out more business emails than any other form of business communication. 

Every day you have email going out to your boss, your clients, your vendors, and your professional contacts.

Your emails are important. A good email can lead to business success. While a bad email can harm a professional relationship, sidetrack your project, or cause damage to your reputation.

Despite its importance, email formatting is often ignored. Proper formatting improves the chances that a recipient will read, respond, and react positively to your email. 

In this tutorial, you’ll learn how to create professional business emails by using proper business email format. I’ll also provide you with examples of good and bad business emails which you an apply to your own communications. 

1. Find Your Business Email Audience

Not all email audiences are the same. For that reason, not all business email should be formatted and written in the same way. Some business email should be structured in a more formal style. But, if you’re more familiar with your recipient, it’s okay to structure your email in a less formal style.

The email style you choose determines your email opening and closing. It can also determine the language you use and the tone of the email. To learn more about formal and informal email style read my recent post:

Here’s a quick summary of some general rules about when to use a formal versus informal email style.

Use a formal email style:

  • if it is required in your organization
  • if the recipients are not well known to you
  • when sending an email to someone who is above you in authority such as your boss or a teacher

 Use an informal email style:

  • if it is encouraged in your organization (your boss and others use an informal style)
  • for a business colleague who is well-known to you and/or on friendly terms
  • for personal emails that may contain both business and non-business topics

The choice between formal and informal style varies from company to company. That’s why it’s important to know what’s accepted in your company. If you’re not sure whether you should use a formal email structure or an informal email structure, it’s often safer to use a more formal style.

2. Know the Structure of a Business Email

How you format your business email makes a difference. A good business email structure helps communicate your message clearly. A badly formatted email can leave a poor impression.

All good business emails should have at least these parts:

  • Subject Line. The subject line is the first part of the email your reader sees. It can determine whether your reader opens the email. Most experts agree that subject lines should be short (six to ten words) and specific.
  • Salutation. The salutation is how you great the reader. It’s important that you understand whether you are addressing a formal or informal audience.
  • Body Text. The body text conveys the main message of the email. It should also include an introduction and conclusion. It should be written in short paragraphs of one to two sentences. Where it will add clarity, use bulleted or numbered lists.
  • Signature. Your signature can be formal or informal, depending on your audience. A good email signature template helps your reader find all the information about you that they need. Enavto Market has helpful signature templates to make your email signature more professional.

Use a proper email format. Structure your email so that the first few sentences of the body text explain what the email is about. The last few sentences should be a conclusion that summarizes the business email. The conclusion is also a good place to include a call to action. A call to action tells the email reader what to do next and how to do it. 

3. The Problem With Most Emails

Many of us never learned the right way to structure a business email. That is why so many people just throw an email together and hope for the best. Sadly, those hasty emails often do more harm to our professional reputation than good.

In this section I dissect two poorly written and poorly formatted email samples We’ll look at both examples and I’ll explain what’s wrong with each of them. In the next section, I’ll do a make over on each of these emails and show you how to improve them.

Formal Email Example – Before

Here is an example of a formal email that was not formatted or written well. Take a minute to look at the example and see if you can tell what’s wrong with it. I’ll detail the problems with this email below.

Let’s look at this business email carefully to determine what’s wrong. Here are some of the problems:

  • Subject Line. The subject line “Stuff” is too short and too vague. It consists of only a single word. With this subject line, some email systems may even send it straight to the spam filter.
  • Salutation. The salutation of “Hey” is too informal for an introductory message like this. The email author mentions they’ve never met some of the team members.
  • Body Text. The body of this email is one giant paragraph. That makes it hard for the average reader to follow. The body also includes items that would be better if they were formatted as a list.
  • Typos and Grammar Problems. The body text includes several poorly worded phrases. There are some run-on sentences. There are also typos and even a repeated word, “make make.”
  • Poor or No Closing. “Catch you on the flip side” is too casual of a closing for most business emails.
  • Signature. There’s no signature for this email. If the team is a new one, they might wonder who the sender is.

It’s likely that the recipients of this email would miss some of the points being made due to the way it is formatted and worded. Plus, it looks sloppy and poorly organized. All in all, this email is not a good way to represent a business organization.

Many businesses would find the above email embarrassing. Yet it’s not unusual for a business professional to write emails quickly and without thinking. Some of those rushed emails include some of the same problems as our example email.

Informal Email Example – Before

Here is an example of an informal email that was badly formatted and poorly worded. Take a minute to look at it and see if you can tell what’s wrong. I’ll detail the problems with this email below.

Let’s look at each part of the email carefully to determine what’s wrong. Here are some of the problems:

  • Subject Line. The subject line “Get It Together!!!” is a little bit too short. It’s also vague and accusatory.
  • Salutation. The salutation of the person’s name is not horrible for an informal email. However, the three exclamation points add an unneeded angry or upset tone.
  • Introduction. There is no introductory paragraph in this email.
  • Body Text. The body text is all one paragraph. It is written in an angry tone with phrases like “Grrr” which aren’t helpful. It also includes emoticons, which are not generally acceptable in a business email.
  • Conclusion. There’s no concluding sentence. The recipient may be unsure of what to do next.
  • Signature. There’s no signature in this email.

Not only is this email poorly formatted, it also conveys an unprofessional, negative tone. Rather than getting the results the sender wants, it’s likely that the reader would become angry and defensive.

4. Now to Properly Format These Business Emails

Bad emails can be improved with effective editing and formatting. Let’s remake the previous emails to make them more effective. I’ll change the formatting and wording to show you how to fix the poor email examples.

Formal Email Example – After

Here is an example of the formal email, rewritten and reformatted to convey a more professional image.

Let’s look at what we did to improve the email:

  • Subject Line. The subject line is the ideal length, six to ten words. It’s also more specific. It identifies the project by name and describes the topic of the email.
  • Salutation. The new salutation makes it clear that this email is for the project team members.
  • Introduction. The introductory sentence tells the reader what the email is about. It also conveys a positive tone by using the word “Welcome.”
  • Body Text. The body text is neatly organized in short paragraphs of no more than three sentences. It also includes a numbered list that can be quickly scanned for easy comprehension.
  • Conclusion. The conclusion summarizes the post. It includes the call to action “Let me know if you have any questions.”
  • Signature. The signature block identifies the sender as the project team lead.

The revised email is a business communication that any organization would be proud of. It’s been reworked with a professional email format. It also conveys the message clearly and succinctly in a 

businesslike manner.

Tip: The email could be improved even further by including a professional email signature template such as the ones found in Envato Market  Here are three examples of how a professional email signature template could be used:

Corporate Email Signature Template
Corporate Email Signature template examples

Learn more about how to craft a professional email signature: 

Informal Email Example – After

Here is an example of an informal email, rewritten and reformatted to convey a more professional image:

 

 What changed? Let’s take a look:

  • Subject line. The subject line is more specific, without being accusatory. It also includes the words “Please Reply.” Many experts agree that including phrases like “Please Reply” or “Response Needed” make it more likely that an email recipient will respond.
  • Salutation. By adding the word “Hi” to the salutation we’ve softened the tone of the email. The reader is less likely to be defensive.
  • Introduction. The introduction explains what the email is about in two sentences.
  • Body Text. The tone in the body text is much more friendly. The aim is to get the project back on track, not to make the reader angry.
  • Conclusion. The conclusion encourages the reader to respond.
  • Signature. The simple signature is fine for an informal business email.

As you can see, friendly wording and better formatting leave the reader with a more positive impression. The reader of this email is much more likely to respond and the problem is more likely to be solved.

You can learn even more about composing effective emails in this Tuts+ tutorial from David Masters: 

5. How to Avoid Email Disaster

So how do you avoid email disaster? With considered planning you can master proper business email format and create an email that conveys your message without ruining your business reputation.

Here are four easy guidelines to writing proper business emails:

  1. Don’t Rush. A hurried email is often a badly formatted, badly written email. Allow yourself enough time to properly format every email you write.
  2. Avoid Emotion. It’s never a good idea to send out an email when you’re upset. Instead, wait until you have calmed down.
  3. Be Concise. Use short sentences and short paragraphs. If appropriate, make use of bulleted or numbered lists.
  4. Review. Double-check your email for typos, grammar mistakes, and other errors. It’s also a good idea to make sure all the parts of the email are included.

Conclusion

Your business emails can make the difference between achieving your business goal or making your company look bad. Put some thought into how your emails are worded and formatted.

Your emails are important! Set a goal to work on properly formatting all your business emails. To learn even more about writing effective business emails review our guide to Writing Effective Business Emails, or get started with the first tutorial in the multi-part series: 

Download How to Master Proper Business Email Format – and Avoid Professional Disaster

Deploy Your PHP Application With Rocketeer

There used to be a time when PHP developers had to use deployment tools that were aimed at general web applications. You can see this in Johannes Schickling’s tutorial on deploying Laravel applications with Capistrano, for example. It’s a Ruby tool, and you need to write Ruby code. These tools get the job done but have limited integration with PHP applications. This can result in hacky solutions for certain scenarios. 

But nowadays, we’re blessed with a few deployment tools written in our language that enable deeper integration. One of these tools is Rocketeer, a tool that takes inspiration from Capistrano and the Laravel framework.

Rocketeer is a modern tool that brings a great approach for your deployment needs. That is to run tasks and manage your application across different environments and servers. On top of that, it also has some magic in it, like installing Composer dependencies if it detects a composer.json file. You get sane defaults and automation of common tasks for a modern PHP application. And you have the ability to customise and extend everything.

You could describe it as an SSH task runner that runs client-side. The commands execute on servers through an SSH connection. If you use a shared hosting provider with only FTP access, you’re out of luck, unfortunately. What you also need is a remote repository where the tool can fetch your code from. There is support for Git and SVN by default. Need support for another version control system? Write your own implementation with the provided interfaces.

Installation

You can install Rocketeer in two different ways. Either you download the phar file and make it executable or you install it through Composer. I’m a supporter of the latter. Having it as a dependency allows for easy installation when cloning the repository. This can benefit anyone cloning the repository to get them up and running.

Installing with Composer:

$ composer require anahkiasen/rocketeer --dev

I don’t recommend that you install it globally. Keeping it on the repository level will ensure that each person deploying is running the same version. What I do recommend is that you add vendor/bin to your PATH. Then you can use the binary by typing rocketeer in your project root.

Ignition

Let’s get started! First you bootstrap the directories and files for configuration. You do this by running rocketeer ignite in the root of your project.

When your application ignites, the tool creates a .rocketeer folder in your project. The contents of the directory will look like this:

| .rocketeer
| -- config.php
| -- hooks.php
| -- paths.php
| -- remote.php
| -- scm.php
| -- stages.php
| -- strategies.php

These are all the configuration files you need to start setting up your deployments. Whenever I refer to a configuration file from here on out, it exists in the .rocketeer/ directory.

Remote Folder Structure

It’s important to understand how Rocketeer manages its folder structure on the server side, since it’s a bit different to a regular setup. It uses a few directories for managing certain aspects of the deployment, so it can be effective at what it does. You specify a path to where you want to deploy your application on your server, and the tool will take care of the rest. That folder will look like this, if you have /var/www/app as your application directory.

| /var/www/app
|  | -- current => /var/www/app/releases/20160103183754
|  | -- releases
|  |  | -- 20160101161243
|  |  |  |-- uploads => /var/www/app/shared/uploads
|  |  | -- 20160103183754
|  |  |  |-- uploads => /var/www/app/shared/uploads
|  | -- shared
|  |  | -- uploads

The most important folder is current, which points to your latest release. That is where your web server’s document root should be set to. So what happens when you deploy?

  1. The tool creates a time-stamped folder in the releases directory.
  2. Completes all tasks to make a ready release.
  3. Updates the symbolic link current to the new release.

This process makes your deployment transparent to the user. The switch between releases is almost instant, usually referred to as atomic deployments.

Some data should be persistent between your deployments. This can be file uploads, user sessions and logs, for example. Those files or folders go into the shared directory. The tool creates symbolic links inside each release for the ones you’ve configured.

Events

Events drive the tool, and all strategies and tasks fire a before and after event when they run. They also provide a special halt event when a task fails. This could be for example dependencies.halt, or deploy.halt for general failure. This allows us to hook into the process where we need to.

The default events that happen during a deployment are:

  • deploy.before: before anything happens.
  • create-release.before: before it creates a new release directory.
  • create-release.after: after creating a new release directory.
  • dependencies.before: before installing or updating dependencies.
  • dependencies.after: after installing or updating dependencies. Perhaps make sure that binaries in your vendor folder are executable.
  • test.before: before running tests.
  • test.after: after running tests.
  • migrate.before: before running database migrations. Maybe you want to do a backup of your database?
  • migrate.after: after running database migrations.
  • deploy.before-symlink: before symlinking the release as our current release.
  • deploy.after: completed. You could notify people that everything was smooth sailing or otherwise.

We can also create our own events that we can fire and listen to. For now, we’ll stick with these events provided for us. They will be enough for us right now.

Tasks

At the heart of Rocketeer, we find a concept called tasks. Most of what is happening under the hood are core tasks. The definition of a task could be a set of instructions to perform as a step in a deployment. If we look at some of the classes that the tool provides, we can get a general feel of what tasks are: classes such as DeploySetupMigrateRollback, and Dependencies. When you deploy, the deploy command itself is a task with sub-tasks.

Types of Tasks

This is where you’ll start to see how integrated the tool is with PHP, since you’ll write tasks in the language. You can create your own tasks in three different ways:

Arbitrary terminal commands. These are one-liners that you want to run on your server. Can be useful for a lot of things, like running gulp build  ---production for example.

Closures. If you need a bit more flexibility or complexity, you can write a task as a closure (anonymous function). Say you’d like to generate documentation for an API during your deployment.

function($task) {
    return $task->runForCurrentRelease('apigen generate  source src  destination api');
}

Classes. For more complex tasks, you should utilise the option to create classes for tasks. You create a class and extend Rocketeer\Abstracts\AbstractTask. Then you must provide at least a description and an execute() method. Here’s a completely useless example just to show the structure of a task class:

namespace MyDeployableApp\Deploy\Tasks;

class HelloWorld extends \Rocketeer\Abstracts\AbstractTask
{
    /**
     * Description of the Task
     *
     * @var string
     */
    protected $description = 'Says hello to the world';

    /**
     * Executes the Task
     *
     * @return void
     */
    public function execute() {
        $this->explainer->line('Hello world!');

        return true;
    }
}

Note that you have to register task classes yourself. Either you do this through the hooks.php file and add it to the custom array…

'custom' => array('MyDeployableApp\Deploy\Tasks\HelloWorld',),

… or you can do this through the facade:

Rocketeer::add('MyDeployableApp\Deploy\Tasks\HelloWorld');

Once you register it, you can execute it:

$ rocketeer hello:world
staging/0 | HelloWorld (Says hello to the world)
staging/0 |=> Hello world!
Execution time: 0.004s

Defining Tasks

We discussed events first because we hook tasks in where we need them in the process. You can do this in a few ways. Go with the one you like and that meets requirements for your level of complexity.

The easiest way of defining your tasks is in the hooks.php file. It provides two arrays for this, specifying task execution before or after certain events.

'before' => [
    'setup' => [],
    'deploy' => ['hello:world'],
    'cleanup' => [],
],

Strategies

You might be able to tell already that the tasks provided are quite generic. Take Dependencies, for example. What kind of dependencies are we talking about and which package manager? 

This is where strategies come into play. A strategy is a specific implementation of a task, such as running tests with Behat or using Gulp for building your front-end. Tasks have a default strategy with the option of running the other strategies through the CLI. We can list the strategies available like this:

$ rocketeer strategies
+--------------+----------------+-----------------------------------------------------------------------+
| Strategy     | Implementation | Description                                                           |
+--------------+----------------+-----------------------------------------------------------------------+
| check        | Php            | Checks if the server is ready to receive a PHP application            |
| check        | Ruby           | Checks if the server is ready to receive a Ruby application           |
| check        | Node           | Checks if the server is ready to receive a Node application           |
| deploy       | Clone          | Clones a fresh instance of the repository by SCM                      |
| deploy       | Copy           | Copies the previously cloned instance of the repository and update it |
| deploy       | Sync           | Uses rsync to create or update a release from the local files         |
| test         | Phpunit        | Run the tests with PHPUnit                                            |
| migrate      | Artisan        | Migrates your database with Laravel's Artisan CLI                     |
| dependencies | Composer       | Installs dependencies with Composer                                   |
| dependencies | Bundler        | Installs dependencies with Bundler                                    |
| dependencies | Npm            | Installs dependencies with NPM                                        |
| dependencies | Bower          | Installs dependencies with Bower                                      |
| dependencies | Polyglot       | Runs all of the above package managers if necessary                   |
+--------------+----------------+-----------------------------------------------------------------------+

Creating Your Own Strategies

Say you’re doing BDD With Behat for your application instead of TDD. Then you want to run your tests with Behat instead of PHPUnit. Since it is a test runner, there is already a strategy namespace for that, but no implementation. Create the directory .rocketeer/strategies/ and place your new BehatStrategy.php in there.

namespace MyDeployableApp\Deploy\Strategies;

use Rocketeer\Abstracts\Strategies\AbstractStrategy;use Rocketeer\Interfaces\Strategies\TestStrategyInterface;

class BehatStrategy extends AbstractStrategy implements TestStrategyInterface
{
    public function test() {
        return $this->binary('vendor/bin/behat')->runForCurrentRelease();
    }
}

You can now switch out your test strategy to the new implementation in strategies.php.

'test' => 'Behat',

Connections & Stages

It doesn’t matter if you have an infrastructure in place or have one in mind. It doesn’t matter if your application deploys to many environments across many servers. Rocketeer will be there for you. You can even have many varying locations on the same server. This is where the terms connections and stages enter.

connection is a server where you deploy your application to. This is often called an environment, and production and staging are examples of this. Configuring these connections is a breeze in the tool. Either you do it through a nested array or by keeping separate files for each connection. Each connection can also have multiple servers in it.

Stages are like connections inside connections, a kind of “connectionception”. You could set up a staging and a production environment on a single server with the use of stages. So instead of having two separate connections, you have one connection with two stages in it.

Plugins

A great feature is that we can extend our process using plugins. There are a few official ones for integration with LaravelSlackHipChat and Campfire. Then there are a few, but not that many, on Packagist. Installing plugins is an easy task through the CLI:

$ rocketeer plugin:install rocketeers/rocketeer-slack

Even though there’s a limited number of plugins, it leaves room for developing plugins in the future. It tells of a good philosophy. And why not develop one of your own?

Setting Up Your Deployment

To get your application off of the ground, you need some basic configuration. You need to tell Rocketeer where to find your application and where it should deploy it to. Let’s start by setting an application name and configuring a production server in config.php.

'application_name' => 'my-deployable-app',

// [...]

'connections' => [
    'staging' => [
        'host' => 'staging.my-deployable-app.com',
        'username' => '',
        'password' => '',
        'key' => '/Users/niklas/.ssh/id_rsa',
        'keyphrase' => '',
        'agent' => '',
        'db_role' => true,
    ],
    'production' => [
        'host' =>
        'www.my-deployable-app.com',
        'username' => '',
        'password' => '',
        'key' => '/Users/niklas/.ssh/id_rsa',
        'keyphrase' => '',
        'agent' => '',
        'db_role' => true,
    ],
],

You now have an application name and a server to deploy your application to. This setup uses SSH key authentication, but you can connect with a username and password also. To get prompted for username and password, set 'key' => ''. The tool will store the credentials on your local machine and use them each time later on. I don’t recommend setting a username and a password in the config file, because you never want credentials committed to your repository.

What you should now do is change the default connection that you deploy to. Having the default set to production is not ideal. You don’t want to deploy to production by accident. So in the same file, look for the default key and change the value to staging instead.

'default' => ['staging'],

The application name itself isn’t that important. But if you don’t specify a folder to deploy to, it will use this as the folder name in the root directory. By default, the root is set to /home/www. With this application name, it will deploy it to /home/www/my-deployable-app. If you want to change your root directory, you can change this in remote.php.

// Deploys to /var/www/my-deployable-app/
'root_directory' => '/var/www/',

In the same file, you have the ability to override the application name and specify a directory for your application.

// Deploys to /var/www/tutsplus-tutorial
'app_directory' => 'tutsplus-tutorial',

Now you have a receiving end of the deployment, but you also need to set up the location of your code so it can be fetched. You do this by setting up your remote repository in scm.php. By default it uses Git, but it has support for SVN also. You tell it the address of our repository and supply credentials if needed. I suggest you use SSH key authentication here as well, and leave the username and password empty.

'repository' => '[email protected]:owner/name.git',
'username'   => '',
'password'   => '',
'branch'     => 'master',

Since you added a connection to the production server, you want to deploy the master branch.

Connection & Stages Specific Configuration

In most cases, you don’t want the same configuration option for all your connections or stages. Say, for example, you want to deploy another branch to the staging environment. Rocketeer allows you to override configuration values for connections and stages using config.php. To deploy a branch called staging on your staging connection, you do this:

'on' => [
    'connections' => [
        'staging' => [
            'scm' => [
                'branch' => 'staging',
            ]
        ]
    ],
],

It uses a nested array to override configuration values. Under the staging key, find the corresponding key in the file you want to change. In this case it’s branch in scm.php.

Deploy, Lift Off!

Now you have everything set up to make a successful deployment. You haven’t met your requirements for a complete deployment, but it’s enough to get your application cloned to your server and served to the end users. First you can execute the check strategy to see if your server meets the requirements.

$ rocketeer check

If everything is okay, you can deploy by running:

$ rocketeer deploy

Since this was your first deployment, Rocketeer will make sure everything is up to par. The tool creates the directories it needs and that our application will live in. If everything is smooth sailing, you should have a complete build of your application on the server.

If you changed the default connection to staging in the previous section, it will always deploy to staging. That is, of course, unless you tell it to deploy to somewhere else. When you want to deploy on a different connection or more than one, you use the --on switch.

# Deploy to production
$ rocketeer deploy --on="production"

# Deploy to staging and production
$ rocketeer deploy --on="staging,production"

Want to have a look at what will happen on your server once you hit the button? Use the --pretend flag to let the tool tell you what it will execute on the server.

$ rocketeer deploy --pretend

Houston, We Got a Problem. We Need a Rollback.

Unfortunately we need to take care of deployments that break functionality or wreak havoc in the infrastructure.  Then you need to make a quick rollback to your latest release. Luckily it’s a simple operation—just run the command:

$ rocketeer rollback

Since it stores a number of builds, performing a rollback is fast. It changes the symbolic link of current to the previous release.

Shared Directories

Setting up shared directories is simple—just add them to the shared array found in remote.php. Rocketeer will create and link these folders for you in the deployments after. The specified paths should be relative to your root folder.

'shared' => [
    'storage/logs',
    'storage/sessions',
    'storage/uploads',
    '.env',
],

Writable Directories

Most shared directories will also need the web server to be able to write to them. Writing logs, sessions or file uploads is often a task performed by any application. These you add to the permissions.files array in remote.php.

'permissions' => [
    'files' => [
        'storage/sessions',
        'storage/logs',
        'storage/uploads',
    ],
    // [...]
],

Install or Update Dependencies

Installing or updating dependencies is something you need if the application relies on any kind of dependencies. The tool comes with support for the most popular package managers. Configuring anything is not necessary if you have the default setup for them. It will detect and install or update dependencies for ComposerNpmBower and Bundler. The default strategy for dependencies is set to Polyglot. This is the tool’s way of detecting and installing dependencies for the different package managers.

But let’s say that you want to install all dependencies on staging, and the tool uses the --no-dev flag by default. Perhaps you want to install PHPUnit for running tests, which is a development dependency. In strategies.php, you can find the composer key, which tells the tool how to execute Composer. You can then override this in config.php:

use Rocketeer\Binaries\PackageManagers\Composer;

// [...]

'on' => [
    // [...]
    'connections' => [
        'staging' => [
            'strategies' => [
                'composer' => [
                    'install' => function (Composer $composer, $task) {
                        return $composer->install([], ['--no-interaction' => null, '--prefer-dist' => null]);
                    }
                ],
            ],
        ]
    ],
],

Database Migrations

Migrating databases is often something you want to do when you have a complete release, just before it symlinks to current. Whatever tool you use, you can tell it to run before the deploy.before-symlink. This hook is not a regular one, but an internal hook. You then need to register it someplace else than hooks.php. You can this do this in events.php, which you can create if it doesn’t exist already.

use Rocketeer\Facades\Rocketeer;

// Laravel
Rocketeer::addTaskListeners('deploy', 'before-symlink', function ($task) {
    $task->runForCurrentRelease('php artisan migrate');
});

// Symfony2
Rocketeer::addTaskListeners('deploy', 'before-symlink', function ($task) {
    $task->runForCurrentRelease('php app/console doctrine:migrations:migrate');
});

// Stand-alone Doctrine
Rocketeer::addTaskListeners('deploy', 'before-symlink', function ($task) {
    $task->runForCurrentRelease('doctrine migrations:migrate --no-interaction');
});

Running Tests

Running tests in a deployment process is a great way of ensuring that no broken code or tests slip through the cracks. By default, the tool uses PHPUnit, and you can hook the test runner to run after dependencies are installed or updated.

'after' => [
    'setup'        => [],
    'deploy'       => [],
    'dependencies' => ['test'],
    'cleanup'      => [],
],

We should now see it’s executing PHPUnit on each deployment, and in case of any failing tests it will abort. Make sure you see output from it, otherwise it might have a problem with finding a PHPUnit binary or your test suite.

staging/0 |---- Test (Run the tests on the server and displays the output) fired by dependencies.after
staging/0 |------ Test/Phpunit (Run the tests with PHPUnit)
$ cd /var/www/my-deployable-app/releases/20160129220251$ /var/www/my-deployable-app/releases/20160129220251/vendor/bin/phpunit --stop-on-failure
[[email protected]] (staging) PHPUnit 4.8.21 by Sebastian Bergmann and contributors.
[[email protected]] (staging)
[[email protected]] (staging) .
[[email protected]] (staging) Time: 4.79 seconds, Memory: 6.00Mb
[[email protected]] (staging) OK (1 test, 1 assertion)
[[email protected]] (staging)staging/0 |=====> Tests passed successfully

Front-End Build Tools

Often our applications are not only a back end, unless they are a REST API for example. Running build tools for our front end is a common task with tools such as GruntGulp or Webpack. Making this part of our deployment process is nothing fancier than using a hook to run the commands such as:

'after' => [
    'setup'        => [],
    'deploy'       => [],
    'dependencies' => ['gulp build'],
    'cleanup'      => [],
],

A front end often relies on dependencies as well, so run tools after installing or updating them.

Tips & Tricks

Running Updates

If you do not want to create a new release when you deploy, you have the option of running an update. Be cautious when doing this since you won’t be able to roll back to the previous version, only the previous release. But it is a quick and simple way of updating your application with the latest changes with:

$ rocketeer update

Local Tasks

Sometimes it can be nice to run tasks in your local environment. Say you want to run a PHPCS check or build your static assets and upload them to the server, removing the need of certain binaries on the server. If you create a task class, you can set the protected variable $local to true.

class MyTask extends Rocketeer\Abstracts\AbstractTask
{
    protected $local = true;

    // [...]
}

Conclusion

The deployment process is an important part of an application’s lifecycle. Tools like Rocketeer allow you with ease to make this an uncomplicated matter. This is especially true when using it for a PHP application since it integrates so well with it.

Writing an introductory tutorial to Rocketeer turned out to be a hard task. The tool is so flexible that drawing the lines on where to stop isn’t easy. I hope I got across the possibilities in using this tool and how it can benefit you and your application. If you want to dig deeper, I suggest reading the full documentation. There’s much more to the tool than what I could cover in this article.

Download Deploy Your PHP Application With Rocketeer

How to Create Nature-Inspired, Decorative Arrows in Adobe Illustrator

Final product image
What You’ll Be Creating

In this tutorial, you will learn how to create fancy, decorated arrows.
We will use basic shapes and then apply Warp, Roughen, and Zig-Zag
effects. You will also learn how to create different feathers from basic
shapes.

Before we begin, you can check out Envato Market for more
inspiration. I promise that you’ll find so many beautiful decorative
arrows
made by many talented illustrators.

And now—let’s get started with our tutorial!

1. Create the First Arrow

Step 1

Let’s start out by creating our first arrow from a regular rectangle. Take
the Rectangle Tool (M) and draw a long rectangle. The fill color you
can see in the image below. 

Since we want to create colorful, ornamental
arrows which we see on Pinterest rather than those that we see in Game
of Thrones, we will decorate our arrows. So let’s make a copy of the
light brown rectangle that we just created in front (Control-C,
Control-F
), make it shorter, and change the fill color to blue. 

Holding
the Alt and Shift keys on your keyboard, move the blue rectangle to the
left and you will see that you now have two blue rectangles. Change the
fill color of the newly created rectangle to yellow. Again, holding the
Alt and Shift keys, create a copy of the yellow rectangle and change the
fill color to red. 

You should now have four different colored rectangles. We just created the wooden shaft of the arrow.

creating the wooden shaft of the first arrow

Step 2

Let’s move on to the tip of the arrow, known as the arrowhead.

Start with the Ellipse Tool (L) and draw an ellipse. Then hit the
Convert Anchor Point Tool (Shift-C) and click on the right anchor
point—you’ll end up with a sharp point. Using the Direct Selection Tool
(A)
, select the top and bottom anchor points and move them to the left
by pressing the left arrow keys on your keyboard. Add a rectangle using the Rectangle Tool (M) with the same fill color, and your arrowhead is done.

creating the arrowhead

Place the arrowhead on the shaft.

placing the arrowhead

Step 3

Let’s start adding some decorations—we’ll start with a feather. Some
time ago, someone asked me to create a tutorial on drawing feathers.
Your wish has been granted!

Draw an ellipse using the Ellipse Tool (L). While keeping it selected,
go to Effect > Warp > Arc and apply the options you see below.
Expand the shape (Object > Expand Appearance). For the stem of the
feather, we will use a copy of this shape. Just make the copy darker and
narrower. Place the stem in front of the first ellipse and centered in the middle.

creating the violet feather

Step 4

Hold your horses—our feather is not done yet. Let’s add some details.
Take the Polygon Tool and click on your artboard. In the new dialogue
window, enter 3 Sides with any Radius and press OK. Rotate the triangle
to the right. 

A little trick: if you want to rotate a shape exactly 90
degrees
, select the shape, hit the Shift key and start rotating. You’ll
notice that you are rotating exactly in 45 or 90 degree increments.

Make the rotated triangle very narrow, and then apply the warp options. Go to go
to Effect > Warp > Flag. Enter the options shown below. We will
call it “a flag shape”. Save a copy of this flag shape for later.

creating the flag shape

Step 5

Make a few copies of the flag shape and place them on the feather shape.
First, place them on the left side. Then make a reflection of them by
right-clicking the mouse, Transform > Reflect. Check Vertical, Angle
90 degrees
in the dialogue window and click Copy. Place them on
the right side of the feather. 

When you are satisfied with the result,
select all these little flag shapes along with the feather, but not the
stem. Go to the Pathfinder panel and press the Minus Front button. Now,
select the stem and put it in the front (Control-X, Control-F).

creating the violet feather

Step 6

Place the feather on the arrow and warp it: go to Effect > Warp >
Arc
. Expand the feather (Object > Expand Appearance).

placing the violet feather

Step 7

Now we will create another feather. Instead of a feather with a narrow top and wide
bottom, we will make one with a wide top and narrow bottom. Take the stem from the previous feather and recolor it blue
(see fill color below). Then add a light blue ellipse in the back. 

Using
the Direct Selection Tool (A), select the left and right Anchor Points
of the light blue ellipse and move them up. Remember the flag shapes?
Bring those and place them on it. Select all the flag shapes along with
light blue ellipse and press Minus Front in Pathfinder. Be sure to put
the stem in front (Control-X, Control-F).

creating the blue feather

Place the feather close to the previous feather that we created before.

placing the blue feather

Step 8

To finish off our arrow, we will decorate it with a wavy green branch.
Delete the fill color and set the stroke color as shown below. Take the
Line Segment Tool (\)
and draw a line over the shaft. Adjust the stroke
Weight on the Stroke panel as you want. While keeping it, selected go to
Effect > Distort & Transform > Zig Zag… and enter the
options you see in the image below.

creating the green waving branch

Step 9

Now, choose the previous stroke color as the fill color and draw an
ellipse. Take the Convert Anchor Point Tool (Shift-C) and click on the
left and right anchor points to make them sharp. This will be our leaf.

creating the leaf

Step 10

Create a bunch of the leaves and place them on the wavy branch. Our first arrow is done!

placing green leaves

2. Create the Second Arrow

Step 1

The shaft of the second arrow will be the same fill color as the first one.
Draw a tiny dark gray rectangle on the right side of the arrow. While
holding down the Alt and Shift keys, move it to the left. Keep pressing
Control-D
, and a copy of this tiny rectangle will move further. Change
the fill color of every second tiny rectangle to light gray.

A quick side note: All the decorations that I’m adding are just examples—feel free to decorate these arrows to your taste!

creating the wooden shaft of the second arrow

Step 2

Now the arrowhead. Let’s first place a gray rectangle on the left side
of the arrow shaft. Then draw a little square. While holding down the
Shift key, rotate it 45 degrees. Take the Direct Selection Tool (A)
and move the left anchor point of the square to the left. Can you see
the pointy arrowhead now?

creating the arrowhead

Step 3

Using the Rounded Rectangle Tool, create three identical tiny rounded
rectangles as marked with the black arrow. 

Remember the violet feather
that we created for the first arrow? Make a copy of it, and change the fill
color to yellow—we’ll use it for the second arrow. To make it little bit
different, create a horizontal reflection of this yellow feather:
right-click
 > Transform > Reflect and check Horizontal,
Angle 0 degrees
and press OK. Place it as shown below, but remember to
place it behind those three tiny rounded rectangles.

creating and placing the yellow feather

Step 4

Let’s continue decorating. In the next few steps, we will make a green
branch. In a newly created green ellipse, move the left and right anchor
points up. That’s our simple leaf.

creating the leaf

Step 5

Set the stroke color to the same color and delete the fill color. Using
the Line Segment Tool (\), draw a stalk. Place the previously created
leaf on top. Make a copy of this leaf, rotate it 45 degrees and place it as
shown in the third image below. 

Holding together Shift (for moving
it straight) and Alt (for making a copy), move that leaf down a little bit. Keep pressing Control-D to repeat your last movement until you
finish filling in the stalk on the left side. Then select all the
leaves on the left side and make a vertical reflection: right-click > Transform > Reflect. In the new dialogue window, enter
Vertical, Angle 90 degrees and press Copy. Move it to the right. Our
green branch is almost ready.

creating the green branch

Step 6

Let’s show that the branch is flexible. Just go to Effect > Warp >
Arc
and enter the options you see below. Don’t forget to expand the
branch (Object > Expand Appearance). We are expanding the shapes all
the time because we want to rotate them without distorting.

warping the green branch

Put the green branch close to the yellow feather.

placing the branch

Step 7

Delete the fill color and set the stroke color. Go to the Stroke panel
and make the stroke Weight thick, and check Round Cap. Using the Line
Segment Tool (\)
, draw a brown branch. To make it more realistic, go to
Effect > Distort and Transform > Roughen and enter the options
shown below. Expand the branch.

creating the brown branch

Place the branch between the green leaves and yellow feather.

placing the brown branch

Step 8

Let’s create one more feather. Be sure to delete the stroke color and
set the fill color. Draw a dark gray ellipse and move up the left and
right anchor points. Add a stem (just take a copy from some previous
feather) and change its fill color. 

Remember the flag shape? Take a copy
of it and place it on the stem as shown below. Make a few of them and then
reflect them on the other side as well.

creating the gray feather

Step 9

The last detail we want to add is the stains. Draw a few light gray ellipses
and then apply a Roughen effect to them (Effect > Distort and
Transform > Roughen
). The options will depend on the size of your
stains.

creating the stains on the gray feather

Place this dark gray feather on the arrow.

placing the gray feather

3. Create the Third Arrow

Step 1

So here is where it gets easy. I took the first arrow and changed the arrowhead—I created it from a triangle, using the Polygon Tool.

creating the shaft and arrowhead of the third arrow

Step 2 

Now, let’s have a quick practice—create a pink feather just following the image shown below. Try it—don’t be afraid!

creating the pink feather

Step 3

Place your beautiful feather on the arrow and warp it. Select the
feather, go to Effect > Warp > Arc and in the new window, move the
slider in the Bend section to your taste. When it looks good, click OK.
Expand the feather.

placing the pink feather

Step 4

Let’s create a green ellipse. Make the top and bottom anchor points
sharp. Go to Effect > Distort & Transform > Zig Zag… and enter
in the new dialogue window the options you see below. Your options can be
different than mine—just try to find what suits you best.

creating the green leaf

Place this new leaf close to the pink feather.

placing the leaf

Step 5

Delete the fill color and set the stroke color as you see in the image
below. Draw three stalks using the Line Segment Tool (\). Now—remember
our first arrow when you created sharp cornered leaves? Make three
copies of it, and place them on the stalk we just created.

creating the green branch

Place it on the arrow.

placing the branch

Step 6

Take the Spiral Tool and click on your artboard. A new dialogue window
will pop up; enter the options you see below. Then take the Direct
Selection Tool (A)
and move the handles of the anchor points to achieve
a shape that looks like this:

creating the green swirl

Make a copy of it and place both of these on the arrow.

placing the swirl

Step 7 

Let’s create a flower, something like a ranunculus, a peony, or even a
rose—it all depends on the options you enter later. 

First, draw a
circle and go to Effect > Distort and Transform > Roughen. Check
Points Smooth, and move the sliders in the Options section how you like.
After that, create a smaller and darker circle in the front, and again apply Roughen effect options to it. And then again a smaller and darker
circle and apply the Roughen effect. In the end, draw a few tiny circles inside the
flower, and it’s complete.

creating the pink flower

Place the flower on the third arrow.

placing the pink flower

Step 8

Move up the left and right anchor points in the newly created blue
ellipse. Create a copy in back and make it darker. Rotate it slightly to
the left. Right-click the mouse and Transform > Reflect. In the new
window, enter Vertical, Angle 90 degrees, Copy. Then make a green Ellipse in the back and add a curve using the Arc Tool (no Fill color,
stroke color shown below). The blue flower is ready.

creating the blue flower

Step 9

We want to add some leaves too. Remember the green branch we made for
the third arrow before? Take a copy of it, but let’s change the stalk:
instead of a straight one, make a stalk using the Arc Tool. Be sure to
check Round Cap on the Stroke panel.

creating the branch

Step 10

Place the blue flower with the leaves on the third arrow closer to the
arrowhead. Then use the Rounded Rectangle Tool to create three tiny
rounded rectangles, to show that the flower is fixed to the arrow.

placing the branch

4. Create the Background

Step 1

Now that we have completed three decorative arrows, let’s arrange them as you want or place them like this:

placing all the arrows together

Step 2

Be sure to delete the fill color and set the stroke color to blue. With the Rectangle Tool, draw a thick stroked frame for your illustration.

creating the frame

Step 3

Keep the frame selected and make a copy in front (Control-C, Control-F). Change the stroke color and now make the stroke weight thin.

continue to make the frame

Step 4

Go to Effect > Distort & Transform > Zig Zag… and enter the
options you see in the image below or just make your own.

Zig-Zag effect options

Voila!

applying Zig-Zag effect to the frame

Fantastic, You’re Done!

Awesome work! Now you know how to create feathers, different branches, leaves, and flowers. You’ve learned how to make arrows with different arrowheads. After this tutorial you will be familiar with the Roughen and Zig-Zag effects, as well as the Warp effect and moving anchor points in basic shapes.

I hope you enjoyed this tutorial and got a lot more ideas to create thousands of arrows!

End result

Download How to Create Nature-Inspired, Decorative Arrows in Adobe Illustrator

From Lightroom to WordPress: How to Create SEO-Optimized Images

WordPress is a piece of software that powers as much as 25% of the web’s websites. Major websites like The New York Times, CNN, and Forbes all use WordPress to power their websites.

What WordPress is to web developers, Adobe Lightroom is to photographers. In this tutorial you’ll learn how to use these two powerful tools together. You’ll learn how to take your images from Lightroom and into a WordPress driven site quickly and easily. Most importantly, we’ll do it in a way that’s SEO-friendly so that your images get discovered.

Image SEO For Beginners

Search engine optimization (also known as SEO) is a set of inter-related design, publishing, and data management practices that help web pages to be found. The truth is, there isn’t really any magic to SEO when it comes to images. It all boils down to adding context to your pictures in a way that that makes sense to computers.

Semantic Information

Think of it this way: you enter a search query into the search engine of your choice. The search engine shows you a list of pages that match what it thinks you’re looking for. Search engines use crawlers—computer programs and algonrhithms— that explore the web automatically and build listings of sites that match keywords. 

A search engine can tell that images are on a webpage, but it can’t easily determine what’s an image depicts, so we need to supplement it with additional data, a kind that it can understand. This kind of data is called semantic information: it’s information that helps build meaning for individual photographs in context to each other and the rest of the webpage. We use metadata fields to transmit this sematic data.

Google looks to some of the tags hidden in our HTML to help find images. We can add things like titles, tags, and alt text in our website’s code to help a search engine index our site as easily as possible. There’s no guarantee that by adding sematic informating you’ll be able
to move your site to the first page of Google’s results, but taking a
few basic SEO steps can definitely help your chances. It’s also just good data hygene!

Here are four key pieces of SEO optimization information to add to your images:

File names

If you do nothing else, make sure that you rename your images in a way that describes them. A simple filename like “resizing-images-tutorial.jpg” is a massive improvement over “IMG_9052.cr2.” Using a filename with some words that describe it will improve its chances of being found.

When exporting from Lightroom, my favorite way to do this is to use the “File Naming” section of the export window. Keep in mind that we want to rename the exported image, not the original file. Find the File Naming section and check the Rename To box. Then choose a dropdown option that includes “custom name” so that we can add our own custom text.

The last step is to add the custom text.  For the web, it’s best to separate the words in a filename using dashes instead of spaces. Customize each image’s file name for the best results. 

Custom Name - Sequence
Use a custom name option to give your file names a better chance of being found. A few words that describe the contents of an image go a long way!

Alt Text

Alternative text, or “alt text”, is used to provide a text description of the contents of an image. Remember that a search engine can’t determine what’s in an image (yet) so we need to help it by using alt text.

You’ll probably never see alt text; the important part is that a search engine will.

Image alt text
An image’s alt text should also be set in HTML. The alt text should briefly describe the image. We can set this alt text in the WordPress Media Library, and WordPress will automatically add it.

Alt text is placed in the same bit of HTML that contains your images. Use it to add a description of the image. A short description of the contents of an image is critical for SEO-friendliness. If writing code isn’t you’re thing, no worries; we’ll look at how to add this alt text using WordPress in the next section of this guide.

Having alt text for your images is also crucial to the visually impaired. Screen reading software designed to help those who have full or partial loss of sight will read the alt text in place of an image. Amherst College has a great guide to optimizing your web pages for accessibility.

Titles

An image’s title should also be contained in the HTML tag. The title should be an even shorter version of the description, with only a few words that signal the content of an image.

Image title text
You should also set an image title in HTML with the “title=” tag in the same image block. Again, WordPress makes this easy to add.

Titles are another piece of the puzzle for helping search engines index our site. WordPress contains a title field that will help our SEO efforts.

Captions

Think of a caption as the text that shows immediately after an image that provides further commentary. Captions aren’t added directly to an image tag directly, but certainly do supplement it. In HTML5, the <figcaption> tag is part of the <figure> group, which is even better. You can learn more about how to write effective captions in Dawn Oosterhoff’s Caption, Description, Title, ALT: How to Add Semantic Information to Images. Writing a strong caption is one of the most under-rated skills, one that’s useful for every photographer!

Mapping Metadata from Lightroom to WordPress

Now that we understand the pillars of image SEO, let’s look at how to add them easily and quickly to our WordPress-driven site. We can carry forward an image’s title and caption from our Lightroom catalog to WordPress.

In the Lightroom Library module, find the metadata panel on the right side. You can see the image title and caption. 

When you’re exporting an image from Lightroom, make sure that you’re including “all metadata.” This will ensure that the title and caption are saved to the JPEG image and will be read and included by WordPress.

Include metadata on export
Make sure that when exporting an image, you include metadata in the file. This will make that metadata available in WordPress.

Upload to WordPress

Once you’ve exported a neatly named image file with a title and caption set, you’re ready to upload it to WordPress. Go to the WordPress Media Library and upload this image as you normally would. The uploaded image will carry through the same title and caption that we set in the Lightroom metadata panel!

Image Uploaded to WordPress
Finally, the fruits of our labor! The title and caption that we

You’ll need to manually add alt text to images once they reach WordPress. Unfortunately, there isn’t a field in Lightroom that maps to “alternative text” in WordPress. Remember that this is the text hidden in the HTML code that will help search engines find and index our images.

Alt text
Alt text will need to be added to images within the WordPress Media Library.

After setting the alt text, press Update to save the image. When you insert it into a post or page, WordPress will carry through the metadata and include the metadata we’ve added in the HTML code.

These steps are a major aid for optimizing an image for the web. We can also go one step further by resizing and compressing images for the web.

Resizing for the Web

Getting images ready for the web is about more than just loading them up with keywords and dropping them in WordPress. We also need to think about the file size and dimensions. Remember that a huge portion of the internet connected population isn’t lucky enough to be on a great broadband connection, and we should consider their experience as well.

mobiForge recently wrote that the average webpage file size is larger than the entire 1993 classic video game Doom. This isn’t inherently a bad thing, but is a great reminder that webpages are more full of multimedia and large images than they’ve ever been. It serves as a great reminder that we should always optimize your image file sizes. 

When we’re ready to send images to the web, there are two key factors to optimize for: dimensions, and quality. Let’s look at how to perfect both.

Dimensions

The dimensions of a digital image are the length and width of the image, measured in pixels. An image said to be “600 by 400 pixels” is inferred to be 600 pixels in width and 400 pixels in height. 

Images captured with my Canon 6D are 5472 x 3648 pixels. By the time you see them on Tuts+, they’re constrained in the layout to 850 pixels on the long side. Theoretically, I could upload the full image, but no matter what it’s going to appear at a maximum of 850 pixels. 

This effect is exactly why I always resize my images to a web-friendly format. My viewers don’t have monitors that show every single pixel, so it only makes sense to resize the image and save them the load time.

Web Size Simulation
This simulation illustrates just how large an original 10 megapixel image is versus how it will appear on the web. The large image preview you see is scaled version of the full, 10 megapixel image I captured. In the inset, the size has been compared to how it appears on Tuts+. A full-width 10 megapixel image is over 4000 pixels in width, while it appears in Tuts+ articles at just 850 pixels on the long side. This is a great illustration of why resizing is so important.

What’s the correct dimensions for an image on the web? The answer is, “it depends on where it’s going.” If you’re using WordPress, check the documentation for your theme for the image widths supported. The developer determines how images will appear in a WordPress theme. If documentation isn’t available, try out a site like PiliApp to do your own measurements on an image on the site you’ll be publishing to.

Quality

High quality images use more disk space. The more detailed and colorful an image is, the larger the file will be. Although our image archive should include these full quality images, preparing an image for the web means making trade-offs between quality and file size. Giving up a bit of quality leads to a much faster load time for the viewer.

When exporting in Lightroom, you can use the Quality slider to adjust the output quality of an image. It’s a 0-100 scale, with a 0 quality image rendering the smallest file size. A 100 quality image maximizes both quality and file size.

Image quality comparison
This image quality comparison shows some subtle difference between the various quality settings. Some differences can be observed in the finer details, as well as the colors in the sky. Note that in the lowest quality image, there’s some “banding” in the sky (subtle lines) while the higher quality images. The 25 quality image gave me a 91kb image, while 100 quality image was a 366kb image. It’s all about finding the balance of quality and file size.

One of my favorite ways to control file size is to use the “Limit File Size To” optionI frequently use this option when I’m exporting images to publish on Tuts+, given that Tuts+ has a limit of 150 kilobytes for images in articles.

Using the “Limit File Size To” option bypasses the quality slider altogether. Instead of choosing a quality between 0 and 100, you can simply set a maximum file size (in kilobytes) and Lightroom will choose a quality setting for you.

Limit File Size screenshot
The “Limit File Size To” box can be used instead of the quality slider. When you check the box in the export window, choose the maximum file size for your image and Lightroom will make it work.

From my experience, when using the quality slider for web images, I typically will leave the quality setting in the 60-75 range. This seems to be the sweet spot for file size and image quality. Increasing the slider far beyond that yields a much larger file without much visual difference.

If you want to learn more about the keys to resizing images for the web, check out my write-up from last year, How to Export JPGs for the Web from Adobe Photoshop Lightroom. While the focus of this tutorial is the metadata for SEO, image size is still an essential part of being search-friendly. 

Recap & Keep Learning

In this tutorial, we’ve covered how to pick the low-hanging fruit for exporting images from Lightroom to a WordPress website. Make no mistake; this won’t instantly shoot your site to the top of Google for common terms, but it will go a long way in helping someone find your images.

It’s worth noting that there are several WordPress plugins, or add-on’s, that can automate this process. However, plugins that aren’t frequently updated can open your site to security vulnerabilities. You also have to count on a developer maintaining the plugin’s compatibility with new versions of WordPress. Two popular plugins include WP/LR Sync and LR/Blog.

If you want to learn more about WordPress, the Tuts+ code section has a wealth of content for modifying and extending WordPress. The photo and video section also has some extensive coverage of Adobe Lightroom if you want to sharpen those skills.

Finally, SEO is an ever-changing area of research. Google constantly updates their algorithm used to find the pages that they think match the search query the best. There’s a ton of bad advice and “tips” for SEO out there. My go-to source for SEO is the Moz Blog. The geniuses at Smashing Magazine also have a great write-up on how to build an SEO optimized website.

What are you doing to get your images ready for the web? Let me know in the comments section.

Download From Lightroom to WordPress: How to Create SEO-Optimized Images

Basic 2D Platformer Physics, Part 4

Ledge Grabbing

Now that we can jump, drop down from one-way platforms and run around, we can also implement ledge grabbing. Ledge-grabbing mechanics are definitely not a must-have in every game, but it’s a very popular method of extending a player’s possible movement range while still not doing something extreme like a double jump.

Let’s look at how we determine whether a ledge can be grabbed. To determine whether the character can grab a ledge, we’ll constantly check the side the character is moving towards. If we find an empty tile at the top of the AABB, and then a solid tile below it, then the top of that solid tile is the ledge our character can grab onto.

Setting Up Variables

Let’s go to our Character class, where we’ll implement ledge grabbing. There’s no point doing this in the MovingObject class, since most of the objects won’t have an option to grab a ledge, so it would be a waste to do any processing in that direction there.

First, we need to add a couple of constants. Let’s start by creating the sensor offset constants.

public const float cGrabLedgeStartY = 0.0f;
public const float cGrabLedgeEndY = 2.0f;

The cGrabLedgeStartY and cGrabLedgeEndY are offsets from the top of the AABB; the first one is the first sensor point, and the second one is the ending sensor point. As you can see, the character will need to find a ledge within 2 pixels.

We also need an additional constant to align the character to the tile it just grabbed. For our character, this will be set to -4.

public const float cGrabLedgeTileOffsetY = -4.0f;

Aside from that, we’ll want to remember the coordinates of the tile that we grabbed. Let’s save those as a character’s member variable.

public Vector2i mLedgeTile;

Implementation

We’ll need to see if we can grab the ledge from the jump state, so let’s head over there. Right after we check whether the character has landed on the ground, let’s see if the conditions to grab a ledge are fulfilled. The primary conditions are as follows:

  • The vertical speed is less than or equal to zero (the character is falling).
  • The character is not at the ceiling—no use grabbing a ledge if you can’t jump off it.
  • The character collides with the wall and moves towards it.
if (mOnGround)
{
    //if there's no movement change state to standing
    if (KeyState(KeyInput.GoRight) == KeyState(KeyInput.GoLeft))
    {
        mCurrentState = CharacterState.Stand;
        mSpeed = Vector2.zero;
        mAudioSource.PlayOneShot(mHitWallSfx, 0.5f);
    }
    else    //either go right or go left are pressed so we change the state to walk
    {
        mCurrentState = CharacterState.Walk;
        mSpeed.y = 0.0f;
        mAudioSource.PlayOneShot(mHitWallSfx, 0.5f);
    }
}
else if (mSpeed.y <= 0.0f 
    && !mAtCeiling
    && ((mPushesRightWall && KeyState(KeyInput.GoRight)) || (mPushesLeftWall && KeyState(KeyInput.GoLeft))))
{
}

If those three conditions are met, then we need to look for the ledge to grab. Let’s start by calculating the top position of the sensor, which is going to be either the top left or top right corner of the AABB. 

Vector2 aabbCornerOffset;

if (mPushesRightWall && mInputs[(int)KeyInput.GoRight])
    aabbCornerOffset = mAABB.halfSize;
else
    aabbCornerOffset = new Vector2(-mAABB.halfSize.x - 1.0f, mAABB.halfSize.y);

Now, as you may imagine, here we’ll encounter a similar problem to the one we found when implementing the collision checks—if the character is falling very fast, it is actually very likely to miss the hotspot at which it can grab the ledge. That’s why we’ll need to check for the tile we need to grab not starting from the current frame’s corner, but the previous one’s—as illustrated here:

The top image of a character is its position in the previous frame. In this situation, we need to start looking for opportunities to grab a ledge from the top-right corner of the previous frame’s AABB and stop at the current frame’s position.

Let’s get the coordinates of the tiles we need to check, starting by declaring the variables. We’ll be checking tiles in a single column, so all we need is the X coordinate of the column as well as its top and bottom Y coordinates.

int tileX, topY, bottomY;

Let’s get the X coordinate of the AABB’s corner.

int tileX, topY, bottomY;
tileX = mMap.GetMapTileXAtPoint(mAABB.center.x + aabbCornerOffset.x);

We want to start looking for a ledge from the previous frame’s position only if we actually were already moving towards the pushed wall in that time—so our character’s X position didn’t change.

if ((mPushedLeftWall && mPushesLeftWall) || (mPushedRightWall && mPushesRightWall))
{
    topY = mMap.GetMapTileYAtPoint(mOldPosition.y + mAABBOffset.y + aabbCornerOffset.y - Constants.cGrabLedgeStartY);
    bottomY = mMap.GetMapTileYAtPoint(mAABB.center.y + aabbCornerOffset.y - Constants.cGrabLedgeEndY);
}

As you can see, in that case we’re calculating the topY using the previous frame’s position, and the bottom one using that of the current frame. If we weren’t next to any wall, then we’re simply going to see if we can grab a ledge using only the object’s position in the current frame.

if ((mPushedLeftWall && mPushesLeftWall) || (mPushedRightWall && mPushesRightWall))
{
    topY = mMap.GetMapTileYAtPoint(mOldPosition.y + mAABBOffset.y + aabbCornerOffset.y - Constants.cGrabLedgeStartY);
    bottomY = mMap.GetMapTileYAtPoint(mAABB.center.y + aabbCornerOffset.y - Constants.cGrabLedgeEndY);
}
else
{
    topY = mMap.GetMapTileYAtPoint(mAABB.center.y + aabbCornerOffset.y - Constants.cGrabLedgeStartY);
    bottomY = mMap.GetMapTileYAtPoint(mAABB.center.y + aabbCornerOffset.y - Constants.cGrabLedgeEndY);
}

Alright, now that we know which tiles to check, we can start iterating through them. We’ll be going from the top to the bottom, because this order makes the most sense as we allow for ledge grabbing only when the character is falling.

for (int y = topY; y >= bottomY; --y)
{
}

Now let’s check whether the tile we are iterating fulfills the conditions which allow the character to grab a ledge. The conditions, as explained before, are as follows:

  • The tile is empty.
  • The tile below it is a solid tile (this is the tile we want to grab).
for (int y = topY; y >= bottomY; --y)
{
    if (!mMap.IsObstacle(tileX, y)
        && mMap.IsObstacle(tileX, y - 1))
    {
    }
}

The next step is to calculate the position of the corner of the tile we want to grab. This is pretty simple—we just need to get the tile’s position and then offset it by the tile’s size.

if (!mMap.IsObstacle(tileX, y)
        && mMap.IsObstacle(tileX, y - 1))
{
    var tileCorner = mMap.GetMapTilePosition(tileX, y - 1);
    tileCorner.x -= Mathf.Sign(aabbCornerOffset.x) * Map.cTileSize / 2;
    tileCorner.y += Map.cTileSize / 2;
}

Now that we know this, we should check whether the corner is between our sensor points. Of course we want to do that only if we’re checking the tile concerning the current frame’s position, which is the tile with Y coordinate equal to the bottomY. If that’s not the case, then we can safely assume that we passed the ledge between the previous and the current frame—so we want to grab the ledge anyway.

if (!mMap.IsObstacle(tileX, y)
        && mMap.IsObstacle(tileX, y - 1))
{
    var tileCorner = mMap.GetMapTilePosition(tileX, y - 1);
    tileCorner.x -= Mathf.Sign(aabbCornerOffset.x) * Map.cTileSize / 2;
    tileCorner.y += Map.cTileSize / 2;
    
    if (y > bottomY ||
        ((mAABB.center.y + aabbCornerOffset.y) - tileCorner.y <= Constants.cGrabLedgeEndY
        && tileCorner.y - (mAABB.center.y + aabbCornerOffset.y) >= Constants.cGrabLedgeStartY))
    {
    }
}

Now we’re home, we have found the ledge that we want to grab. First, let’s save the grabbed ledge’s tile position.

if (y > bottomY ||
    ((mAABB.center.y + aabbCornerOffset.y) - tileCorner.y <= Constants.cGrabLedgeEndY
    && tileCorner.y - (mAABB.center.y + aabbCornerOffset.y) >= Constants.cGrabLedgeStartY))
{
    mLedgeTile = new Vector2i(tileX, y - 1);
}

We also need to align the character with the ledge. What we want to do is align the top of the character’s ledge sensor with the top of the tile, and then offset that position by cGrabLedgeTileOffsetY.

mPosition.y = tileCorner.y - aabbCornerOffset.y - mAABBOffset.y - Constants.cGrabLedgeStartY + Constants.cGrabLedgeTileOffsetY;

Aside from this, we need to do things like set the speed to zero and change the state to CharacterState.GrabLedge. After this, we can break from the loop because there’s no point iterating through the rest of the tiles.

mPosition.y = tileCorner.y - aabbCornerOffset.y - mAABBOffset.y - Constants.cGrabLedgeStartY + Constants.cGrabLedgeTileOffsetY;

mSpeed = Vector2.zero;
mCurrentState = CharacterState.GrabLedge;
break;

That’s going to be it! The ledges can now be detected and grabbed, so now we just need to implement the GrabLedge state, which we skipped earlier.

Ledge Grab Controls

Once the character is grabbing a ledge, the player has two options: they can either jump up or drop down. Jumping works as normal; the player presses the jump key and the jump’s force is identical to the force applied when jumping from the ground. Dropping down is done by pressing the down button, or the directional key that points away from the ledge.

Controls Implementation

The first thing here to do is to detect whether the ledge is to the left or to the right of the character. We can do this because we saved the coordinates of the ledge the character is grabbing.

bool ledgeOnLeft = mLedgeTile.x * Map.cTileSize < mPosition.x;
bool ledgeOnRight = !ledgeOnLeft;

We can use that information to determine whether the character is supposed to drop off the ledge. To drop down, the player needs to either:

  • press the down button
  • press the left button when we’re grabbing a ledge on the right, or
  • press the right button when we’re grabbing a ledge on the left
bool ledgeOnLeft = mLedgeTile.x * Map.cTileSize < mPosition.x;
bool ledgeOnRight = !ledgeOnLeft;

if (mInputs[(int)KeyInput.GoDown]
    || (mInputs[(int)KeyInput.GoLeft] && ledgeOnRight)
    || (mInputs[(int)KeyInput.GoRight] && ledgeOnLeft))
{
}

There’s a small caveat here. Consider a situation when we’re holding the down button and the right button, when the character is holding onto a ledge to the right. It’ll result in the following situation:

The problem here is that the character grabs the ledge immediately after it lets go of it. 

A simple solution to this is to lock movement towards the ledge for a couple frames after we dropped off the ledge. For that we need to add two new variables; let’s call them mCannotGoLeftFrames and mCannotGoRightFrames.

public int mCannotGoLeftFrames = 0;
public int mCannotGoRightFrames = 0;

When the character drops off the ledge, we need to set those variables and change the state to jump.

bool ledgeOnLeft = mLedgeTile.x * Map.cTileSize < mPosition.x;
bool ledgeOnRight = !ledgeOnLeft;

if (mInputs[(int)KeyInput.GoDown]
    || (mInputs[(int)KeyInput.GoLeft] && ledgeOnRight)
    || (mInputs[(int)KeyInput.GoRight] && ledgeOnLeft))
{
    if (ledgeOnLeft)
        mCannotGoLeftFrames = 3;
    else
        mCannotGoRightFrames = 3;

    mCurrentState = CharacterState.Jump;
}

Now let’s go back for a bit to the Jump state, and let’s make sure that it respects our ban on moving either left or right after dropping off the ledge. Let’s reset the inputs right before we check if we should look for a ledge to grab.

if (mCannotGoLeftFrames > 0)
{
    --mCannotGoLeftFrames;
    mInputs[(int)KeyInput.GoLeft] = false;
}
if (mCannotGoRightFrames > 0)
{
    --mCannotGoRightFrames;
    mInputs[(int)KeyInput.GoRight] = false;
}

if (mSpeed.y <= 0.0f && !mAtCeiling
    && ((mPushesRightWall && mInputs[(int)KeyInput.GoRight]) || (mPushesLeftWall && mInputs[(int)KeyInput.GoLeft])))
{

As you can see, this way we won’t fulfill the conditions needed to grab a ledge as long as the blocked direction is the same as the direction of the ledge the character may try to grab. Each time we deny a particular input, we decrement from the remaining blocking frames, so eventually we’ll be able to move again—in our case, after 3 frames.

Now let’s continue working on the GrabLedge state. Since we handled dropping off the ledge, we now need to make it possible to jump from the grabbing position.

If the character didn’t drop from the ledge, we need to check whether the jump key has been pressed; if so, we need to set the jump’s vertical speed and change the state:

if (mInputs[(int)KeyInput.GoDown]
    || (mInputs[(int)KeyInput.GoLeft] && ledgeOnRight)
    || (mInputs[(int)KeyInput.GoRight] && ledgeOnLeft))
{
    if (ledgeOnLeft)
        mCannotGoLeftFrames = 3;
    else
        mCannotGoRightFrames = 3;

    mCurrentState = CharacterState.Jump;
}
else if (mInputs[(int)KeyInput.Jump])
{
    mSpeed.y = mJumpSpeed;
    mCurrentState = CharacterState.Jump;
}

That’s pretty much it! Now the ledge grabbing should work properly in all kinds of situations.

Allow the Character to Jump Shortly After Leaving a Platform

Often, to make jumps easier in platformer games, the character is allowed to jump if it just stepped off the edge of a platform and is no longer on the ground. This is a popular method to mitigate an illusion that the player has pressed the jump button but the character didn’t jump, which might have appeared due to input lag or the player pressing the jump button right after the character has moved off the platform.

Let’s implement such a mechanic now. First of all, we need to add a constant of how many frames after the character steps off the platform it can still perform a jump.

public const int cJumpFramesThreshold = 4;

We’ll also need a frame counter in the Character class, so we know how many frames the character is in the air already.

protected int mFramesFromJumpStart = 0;

Now let’s set the mFramesFromJumpStart to 0 every time we just left the ground. Let’s do that right after we call  UpdatePhysics.

UpdatePhysics();

if (mWasOnGround && !mOnGround)
    mFramesFromJumpStart = 0;

And let’s increment it every frame we’re in the jump state.

case CharacterState.Jump:

    ++mFramesFromJumpStart;

If we’re in the jump state, we cannot allow an in-air jump if we’re either at the ceiling or have a positive vertical speed. Positive vertical speed would mean that the character hasn’t missed a jump.

++mFramesFromJumpStart;

if (mFramesFromJumpStart <= Constants.cJumpFramesThreshold)
{
    if (mAtCeiling || mSpeed.y > 0.0f)
        mFramesFromJumpStart = Constants.cJumpFramesThreshold + 1;
}

If that’s not the case and the jump key is pressed, all we need to do is set the vertical speed to the jump value, as if we jumped normally, even though the character is in the jump state already.

if (mFramesFromJumpStart <= Constants.cJumpFramesThreshold)
{
    if (mAtCeiling || mSpeed.y > 0.0f)
        mFramesFromJumpStart = Constants.cJumpFramesThreshold + 1;
    else if (KeyState(KeyInput.Jump))
        mSpeed.y = mJumpSpeed;
}

And that’s it! We can set the cJumpFramesThreshold to a big value like 10 frames to make sure that it works.

The effect here is quite exaggerated. It’s not very noticeable if we’re allowing the character to jump just 1-4 frames after it is in fact no longer on the ground, but overall this allows us to modify how lenient we want our jumps to be.

Scaling the Objects

Let’s make it possible to scale the objects. We already have the mScale in the MovingObject class, so all we actually need to do is make sure it affects the AABB and the AABB offset properly.

First of all, let’s edit our AABB class so it has a scale component.

public struct AABB
{
    public Vector2 scale;
    public Vector2 center;
	public Vector2 halfSize;
    
    public AABB(Vector2 center, Vector2 halfSize)
    {
        scale = Vector2.one;
        this.center = center;
        this.halfSize = halfSize;
    }

Now let’s edit the halfSize, so that when we access it, we actually get a scaled size instead of the unscaled one.

public Vector2 scale;
public Vector2 center;

private Vector2 halfSize;
public Vector2 HalfSize
{
    set { halfSize = value; }
    get { return new Vector2(halfSize.x * scale.x, halfSize.y * scale.y); }
}

We’ll also want to be able to get or set only an X or Y value of the half size, so we need to make separate getters and setters for those as well.

public float HalfSizeX
{
    set { halfSize.x = value; }
    get { return halfSize.x * scale.x; }
}

public float HalfSizeY
{
    set { halfSize.y = value; }
    get { return halfSize.y * scale.y; }
}

Besides scaling the AABB itself, we’ll also need to scale the mAABBOffset, so that after we scale the object, its sprite will still match the AABB the same way it did when the object was unscaled. Let’s head back over to the MovingObject class to edit it.

private Vector2 mAABBOffset;
public Vector2 AABBOffset
{
    set { mAABBOffset = value; }
    get { return new Vector2(mAABBOffset.x * mScale.x, mAABBOffset.y * mScale.y); }
}

The same as previously, we’ll want to have access to X and Y components separately too.

public float AABBOffsetX
{
    set { mAABBOffset.x = value; }
    get { return mAABBOffset.x * mScale.x; }
}

public float AABBOffsetY
{
    set { mAABBOffset.y = value; }
    get { return mAABBOffset.y * mScale.y; }
}

Finally, we also need to make sure that when the scale is modified in the MovingObject, it also is modified in the AABB. The object’s scale can be negative, but the AABB itself shouldn’t have a negative scale because we rely on half size to be always positive. That’s why instead of simply passing the scale to the AABB, we’re going to pass a scale that has all components positive.

private Vector2 mScale;
public Vector2 Scale
{
    set {
        mScale = value;
        mAABB.scale = new Vector2(Mathf.Abs(value.x), Mathf.Abs(value.y));
    }
    get { return mScale; }
}
public float ScaleX
{
    set
    {
        mScale.x = value;
        mAABB.scale.x = Mathf.Abs(value);
    }
    get { return mScale.x; }
}
public float ScaleY
{
    set
    {
        mScale.y = value;
        mAABB.scale.y = Mathf.Abs(value);
    }
    get { return mScale.y; }
}

All that’s left to do now is to make sure that wherever we used the variables directly, we use them through the getters and setters now. Wherever we used halfSize.x, we’ll want to use HalfSizeX, wherever we used halfSize.y, we’ll want to use HalfSizeY, and so on. A few uses of a find and replace function should deal with this well.

Check the Results

The scaling should work well now, and because of the way we built our collision detection functions, it doesn’t matter if the character is giant or tiny—it should interact with the map well.

Summary

This part concludes our work with the tilemap. In the next parts, we’ll be setting things up to detect collisions between objects. 

It took some time and effort, but the system in general should be very robust. One thing that may be lacking right now is the support for slopes. Many games don’t rely on them, but a lot of them do, so that’s the biggest improvement goal to this system. Thanks for reading so far, see you in the next part!

Download Basic 2D Platformer Physics, Part 4

What Is PrestaShop?

PrestaShop home page

What Is PrestaShop?

PrestaShop is an immensely feature-rich, free, open-source e-commerce solution which you can use to run stores in the cloud or via self-hosting. It’s currently used by 250,000 shops worldwide and is available in 65 different languages. Although it takes a bit of work to get a store launched, I’m impressed with what the platform offers.

PrestaShop’s business model rests on selling services for commercial partners to its customer base, including optional add-on features and themes. This seems to allow it to offer a sophisticated e-commerce option for anyone interested in selling digital or physical goods online.

PrestaShop is fairly easy to use, provides a powerfully responsive store interface for shoppers, offers a comprehensive set of features, and it’s free.

If you’d like a quick look, explore the PrestaShop demo store, which includes both a front-end store and a back-end management dashboard. Here’s an example of the demo running in vertical tablet mode:

PrestaShop - Tablet Demo Store

Here’s a screenshot of the back-end store dashboard within the demo:

PrestaShop - Demo of Administrative Back End

Back in early 2015, I wrote a tutorial on building a t-shirt store using WordPress and the Spreadshirt plugin which you can check out here: Build Your Own T-Shirt Shop With the Spreadshirt WordPress Plugin (Envato Tuts+). Below is a screenshot of the site built for that tutorial, Sociables.io:

Sociables - My WordPress Stored Powered by the Spreadshirt Plugin

In the end, I actually preferred the ease of use and simplicity of other specialty t-shirt application vendors. PrestaShop offers a WordPress-like web interface with far more sophisticated capabilities regardless of what kind of product you wish to sell.

In this tutorial, I’m going to guide you through signing up for and creating a store with PrestaShop. I’ll also explain a bit more about the platform and ways you can access it.

If you have any requests for future tutorials or questions and comments on today’s, please post them below. You can also reach me on Twitter @reifman directly.

PrestaShop’s Key Features

PrestaShop has an impressive breadth of high-quality, powerful features.

PrestaShop - Features
  • Cloud or Self-hosting: You can sign up in minutes for a free store in the PrestaShop cloud or you can download the open source PHP code and host it yourself. 
  • Templates: Similar to Envato’s ThemeForest, PrestaShop sells a variety of high-quality themes to professionally customize your store. 
  • Store Builder: With a WordPress-like dashboard, PrestaShop offers a feature-rich store construction interface, including detailed product listings, navigation and search.
  • Shopping Cart: PrestaShop integrates with a variety of payment providers, major shipping carriers and tax rules.
  • Mobile: PrestaShop’s responsive stores and checkout systems make it incredibly useful. Even its back-end dashboards are responsive for on-the-go management.
  • eMarketing: PrestaShop provides built-in product and catalog SEO integration for optimizing your search placement, and it offers built-in coupons, promotions, email integration, etc.
  • InternationalPrestaShop provides 65 language translations for your store including flexible currency and shipping rules based on location. It’s cloud administrative dashboard is available in eight languages: English, French, Spanish, German, Italian, Portuguese, Polish, and Dutch.
  • Integrations: PrestaShop offers a variety of themes and add-ons, premium support if you need it, and a directory of agency partners for hire. If you want help launching your store, it’s available.
PrestaShop - Dashboard Translations

Getting Help

If you want extra help but prefer to work independently, PrestaShop’s Premium Support Plans may be the best for you:

PrestaShop - Paid Support Plans

The PrestaShop Forums offer a place where users can freely support each other and have questions answered by others.

For more end-to-end support, you can also check out their partner directory.

Start a Store in the Cloud

Now, follow me as I launch a store in the image of my WordPress-powered t-shirt store, Sociables.io, and see how easy it is to start your own store with PrestaShop. Click the Start my Store button on the home page. 

You’ll be asked for your store name and e-mail address:

PrestaShop - Start a Store

Then, select a sub-domain for the store URL:

PrestaShop - Store URL

You’ll later be able to provide a custom domain name and be able to map DNS CNAME settings to the cloud store:

PrestaShop - Adding a Custom Domain Name

For now, you’ll be asked to provide an account configuration as the store manager:

PrestaShop - Store Owner Information

Congratulations! The store foundation is ready:

PrestaShop - Your Store is Created

In your email, you’ll receive an Activate my account request:

PrestaShop - Welcome Email

The Store Dashboard

You’ll be taken to the back-end Store Dashboard or its management interface:

PrestaShop - Store Management Dashboard

As you can see, PrestaShop offers a 30% discount off domain name registration.

Your Default Store Theme

Here’s my Sociables front page with sample products and layout powered by the PrestaShop cloud:

PrestaShop - Initial Store Front End Default Page

The Getting Started Wizard

When you enter the back-end Dashboard (here shown in responsive Tablet mode), you’re offered a Getting Started wizard which we’ll roughly follow:

PrestaShop - Management Dashboard Tour in Tablet Mode

Configuring Our Store

Customize Your Shop’s Look and Feel

First, we’re asked to customize our shop’s look and feel:

PrestaShop - Store Configuration Wizard - Look and Feel

You’re shown the default theme and given customization settings:

PrestaShop - Store Configuration Wizard - Your Theme

But you can also purchase professional themes, which helps support the PrestaShop open-source platform and cloud service:

PrestaShop - Store Configuration Wizard - Theme Directory

For purposes of this tutorial, I did not purchase a theme. They are a little bit expensive; these are mostly more than $100. (Note to Envato Tuts+ editorial masters, maybe it’s time to offer writers an expense account? Readers feel free to comment on this below.)

Add Products to Your Catalog

Next, we’ll add our first product:

PrestaShop - Store Configuration Wizard - Add Products

Here’s an example dashboard page for adding my first product. I’ll use the Looking for Me on Tinder t-shirt from Sociables.io as a product example:

PrestaShop - Add Product Details

As I said, the interface resembles WordPress making it familiar and easy to use for most:

PrestaShop - More Product Details

Below, I configure pricing and notice how PrestaShop also records wholesale prices for reporting profit:

PrestaShop - Add Product Pricing

Then, I upload the product image:

PrestaShop - Upload Product Images

Set Up Your Payment Methods

PrestaShop - Store Configuration Wizard - Set Up Payment Methods

PrestaShop easily integrates with a number of common payment providers:

PrestaShop - Payment Method List

Here’s what it looks like when you want to connect to your PayPal account and the service’s API-driven interfaces:

PrestaShop - Add PayPal Connection

You can enable a variety of PayPal product options:

PrestaShop - Add PayPal Options

Set Up Your Shipping Methods

PrestaShop - Store Configuration Wizard - Set Up Your Shipping Methods

PrestaShop allows you to integrate with common carriers as well as general postal service shipping choices:

PrestaShop - Add Shipping Carrier

Here I create a USPS shipping rule based on costs and transit time I expect to maintain:

PrestaShop - Add USPS Carrier Settings

Launch Your Store

Finally, the basics are done and we can check out our store:

PrestaShop - Store Configuration Wizard - Hurrah Success

Note: There’s kind of a cool rocket animation when you click Launchbut it’s not easy to capture here. Launch your own store if you want to see it. 

You’ll also receive an email:

PrestaShop - Your Store is Live Email

I didn’t spend time in this tutorial reconfiguring the product catalog and front page layout, but here is an example of the product detail page we created and custom logo, pricing, and payment: 

PrestaShop - The Sociables T-shirt Product Detail Page in Store

Shopping With PrestaShop

PrestaShop’s catalog, shopping cart and checkout sophistication are some of its most valuable features. 

Here’s an example of adding a t-shirt to my shopping cart and starting the checkout process:

PrestaShop - Shopping Cart

The customer is asked to identify themselves:

PrestaShop - Checkout Account Creating or Sign In

And provide basic account information:

PrestaShop - Checkout Personal Information Gathering

And they are asked to provide a shipping address:

PrestaShop - Checkout Address Information

And then select their shipping preference:

PrestaShop - Checkout Shipping Options

And finally, payment:

PrestaShop - Checkout Final Pricing

Since the product’s not real at this time, I didn’t complete the transaction here, but that should give you a pretty detailed view of the high-level breadth of PrestaShop. It’s an impressive open-source store platform and free cloud service.

Self-Hosting the PrestaShop Download

Let’s say you want to host PrestaShop yourself. It’s written in PHP using MySQL, making hosting fairly easy. If you want to host try self-hosting, visit the Download Page:

PrestaShop - Download

You can download and install your own version or try their premier installation partner, 1&1, as well as the directory of agencies we mentioned above that will help you run your site for a fee:

PrestaShop - Partner Agency Directory

You can browse their Partner Agency Directory or sign up for paid PrestaShop Training sessions to learn more on your own.

If you want to contribute to the PrestaShop codebase, visit PrestaShop Forge.

In Summary

I hope you give PrestaShop a try. I was quite impressed with its power, breadth of features, and especially that it’s free. While setting up a store requires a significant bit of effort, detailed e-commerce is always like that. That’s why I’m a technologist and not a store owner.

Please let us know what you think of PrestaShop in the comments below. You can also tell me on Twitter @reifman directly. And, be sure to browse my Envato Tuts+ instructor page to see some of my other tutorials (like my script to clone WordPress sites in Linux for efficient, economical hosting of multiple sites).

Good luck with your e-commerce adventures!

Related Links

Download What Is PrestaShop?

New Course on Adobe Photoshop’s Smudge Tool

Final product image
What You’ll Be Creating

The Smudge Tool is one of the most powerful tools that Adobe Photoshop has to offer. In our new Coffee Break Course, How to Use Photoshop’s Smudge Tool for Digital Painting, digital artist Brian Lee will show you exactly how to use it. 

You’ll get a brief introduction to the tool and how to set it up. Then you’ll learn how to manipulate it to achieve different types of results for your digital artworks. 

Watch Brian’s introduction below to find out more.

You can take our new Coffee Break Course straight away with a free 10-day trial of our monthly subscription. If you decide to continue, it costs just $15 a month, and you’ll get access to hundreds of courses, with new ones added every week.

If you’re looking for other ways to create smudge effects, check out the range of smudge brushes and Photoshop actions on Envato Market, such as Creative Liquid-Smudge HD Action.

Creative Liquid-Smudge HD Action
Creative Liquid-Smudge HD Action

Download New Course on Adobe Photoshop’s Smudge Tool

CodePen Challenge #6: Results!

Last week we asked you to take part in our sixth CodePen Challenge: to decorate a drop cap using CSS. This one seemed to stump you all a bit, only eleven of you took part, but we have some interesting results–here’s what you all did!

The Challenge

We asked you to take this drop cap and style it creatively:

The idea came from a relatively new CSS property–read Better CSS Drop Caps With “initial-letter” if you’re interested in learning more.

The Collection

All of the entries were pulled into a collection on CodePen, go and check them out! Meanwhile, here are just a few of our favourites, including a fair bit of purple which seems appropriate given yesterday’s sad news.

Next Time

Next time we’ll come up with a challenge that none of you can resist–expect another one very soon! 

Download CodePen Challenge #6: Results!