Category Archives: EnvatoTutsCode

Magento Theme Development: Product Page, Part 1

Now that we have completed the home page and the category page, in this tutorial we’ll start editing the third most important page of the eCommerce website: the product page. Let’s first open up the product page, and see how it looks now and how we want it to look.

The product page in our HTML design looks like this:

HTML design of product page

Our current product page looks like this:

Product Page before editing

To start editing it, we’ll do the same step as we have done dozens of times so far, i.e. enabling the template hints to figure out which template files are responsible for rendering this page.

Enabling template hints

As we can see, the page is a compilation of over a dozen files, but luckily we don’t have to edit all of these. We’ll just have to edit three or four of these files, and for the rest we’ll just modify the styles to make them look like our design, without messing with the phtml files.

If you look closely, the overall structure of the product section is coming from the \template\catalog\product\view.phtml file. I’ve pointed that out through an arrow in the above image. This file is providing the outer structure of all components and then the inner parts are coming from other templates, but
we’ll edit this outer one first.

We’ll copy this view.phtml file into our new theme, and
start editing it. The current code of this file looks like this:

<?php $_helper = $this->helper('catalog/output'); ?>
<?php $_product = $this->getProduct(); ?>
<script type="text/javascript">
    var optionsPrice = new Product.OptionsPrice(<?php echo $this->getJsonConfig() ?>);
</script>
<div id="messages_product_view"><?php echo $this->getMessagesBlock()->toHtml() ?></div>
<div class="product-view">
    <div class="product-essential">
        <form action="<?php echo $this->getSubmitUrl($_product) ?>" method="post" id="product_addtocart_form"<?php if($_product->getOptions()): ?> enctype="multipart/form-data"<?php endif; ?>>
            <?php echo $this->getBlockHtml('formkey') ?>
            <div class="no-display">
                <input type="hidden" name="product" value="<?php echo $_product->getId() ?>" />
                <input type="hidden" name="related_product" id="related-products-field" value="" />
            </div>
            <div class="product-img-box">
                <div class="product-name">
                    <h1><?php echo $_helper->productAttribute($_product, $_product->getName(), 'name') ?></h1>
                </div>
                <?php echo $this->getChildHtml('media') ?>
            </div>
            <div class="product-shop">
                <div class="product-name">
                    <span class="h1"><?php echo $_helper->productAttribute($_product, $_product->getName(), 'name') ?></span>
                </div>
                <div class="price-info">
                    <?php echo $this->getPriceHtml($_product); ?>
                    <?php echo $this->getChildHtml('bundle_prices') ?>
                    <?php echo $this->getTierPriceHtml() ?>
                </div>
                <div class="extra-info">
                    <?php echo $this->getReviewsSummaryHtml($_product, 'default', false)?>
                    <?php echo $this->getChildHtml('product_type_availability'); ?>
                </div>
                <?php echo $this->getChildHtml('alert_urls') ?>
                <?php if ($_product->getShortDescription()):?>
                    <div class="short-description">
                        <div class="std"><?php echo $_helper->productAttribute($_product, nl2br($_product->getShortDescription()), 'short_description') ?></div>
                    </div>
                <?php endif;?>
                <?php echo $this->getChildHtml('other');?>
                <?php if ($_product->isSaleable() && $this->hasOptions()):?>
                    <?php echo $this->getChildChildHtml('container1', '', true, true) ?>
                <?php endif;?>
            </div>
            <div class="add-to-cart-wrapper">
                <?php echo $this->getChildHtml('product_type_data') ?>
                <?php echo $this->getChildHtml('extrahint') ?>
                <?php if (!$this->hasOptions()):?>
                    <div class="add-to-box">
                        <?php if($_product->isSaleable()): ?>
                            <?php echo $this->getChildHtml('addtocart') ?>
                            <?php if( $this->helper('wishlist')->isAllow() || $_compareUrl=$this->helper('catalog/product_compare')->getAddUrl($_product)): ?>
                                <span class="or"><?php echo $this->__('OR') ?></span>
                            <?php endif; ?>
                        <?php endif; ?>
                        <?php echo $this->getChildHtml('addto') ?>
                        <?php echo $this->getChildHtml('sharing') ?>
                    </div>
                    <?php echo $this->getChildHtml('extra_buttons') ?>
                <?php elseif (!$_product->isSaleable()): ?>
                    <div class="add-to-box">
                        <?php echo $this->getChildHtml('addto') ?>
                        <?php echo $this->getChildHtml('sharing') ?>
                    </div>
                <?php endif; ?>
            </div>
            <?php echo $this->getChildHtml('related_products') ?>
            <div class="clearer"></div>
            <?php if ($_product->isSaleable() && $this->hasOptions()):?>
                <?php echo $this->getChildChildHtml('container2', '', true, true) ?>
            <?php endif;?>
        </form>
        <script type="text/javascript">
        //<![CDATA[
            var productAddToCartForm = new VarienForm('product_addtocart_form');
            productAddToCartForm.submit = function(button, url) {
                if (this.validator.validate()) {
                    var form = this.form;
                    var oldUrl = form.action;

                    if (url) {
                       form.action = url;
                    }
                    var e = null;
                    try {
                        this.form.submit();
                    } catch (e) {
                    }
                    this.form.action = oldUrl;
                    if (e) {
                        throw e;
                    }

                    if (button && button != 'undefined') {
                        button.disabled = true;
                    }
                }
            }.bind(productAddToCartForm);
            productAddToCartForm.submitLight = function(button, url){
                if(this.validator) {
                    var nv = Validation.methods;
                    delete Validation.methods['required-entry'];
                    delete Validation.methods['validate-one-required'];
                    delete Validation.methods['validate-one-required-by-name'];
                    // Remove custom datetime validators
                    for (var methodName in Validation.methods) {
                        if (methodName.match(/^validate-datetime-.*/i)) {
                            delete Validation.methods[methodName];
                        }
                    }

                    if (this.validator.validate()) {
                        if (url) {
                            this.form.action = url;
                        }
                        this.form.submit();
                    }
                    Object.extend(Validation.methods, nv);
                }
            }.bind(productAddToCartForm);
        //]]>
        </script>
    </div>
    <div class="product-collateral toggle-content tabs">
        <?php if ($detailedInfoGroup = $this->getChildGroup('detailed_info', 'getChildHtml')):?>
            <dl id="collateral-tabs" class="collateral-tabs">
                <?php foreach ($detailedInfoGroup as $alias => $html):?>
                    <dt class="tab"><span><?php echo $this->escapeHtml($this->getChildData($alias, 'title')) ?></span></dt>
                    <dd class="tab-container">
                        <div class="tab-content"><?php echo $html ?></div>
                    </dd>
                <?php endforeach;?>
            </dl>
        <?php endif; ?>
    </div>
    <?php echo $this->getChildHtml('upsell_products') ?>
    <?php echo $this->getChildHtml('product_additional_data') ?>
</div>

read more

How to Simplify Managing Multiple WordPress Sites

What You’ll Be Creating

I pitched this story to Envato Tuts+ back in August 2014 but have been too busy running WordPress and plugin updates on all my sites to find time to write it (well, and I had brain surgery). Over time, I grew increasingly curious about WordPress network managers and whether they might save me time—ManageWP does. It’s a well-built administrative aggregator that lets you manage and post to your sites all from one service.

With more than a dozen WordPress updates this year and the cascading accompaniment of plugin upgrades, it becomes more and more time-consuming to keep up—especially if you run a lot of websites like many readers. Or, let’s say you find a great plugin and want to install it on a dozen of your client’s websites? Generally, there goes a couple of hours.

read more

iOS From Scratch With Swift: Data Persistence and Sandboxing on iOS

Persisting data across application launches is a requirement that most iOS applications have, from storing user preferences in the defaults system to managing large data sets in a relational database. In this article, we’ll explore the most common strategies used for storing data in an iOS application. I will also talk about the file system on iOS and how application sandboxing affects data persistence.

Introduction

You’ve come a long way, grasshopper, and you’ve learned a lot. But there’s one vital aspect of iOS development that we haven’t discussed yet, data persistence. Virtually every iOS application stores data for later use. The data your application stores can be anything from user preferences to temporary caches or even large relational data sets.

Before discussing the most common data persistence strategies developers have on the iOS platform, I’m going to spend a few minutes discussing the file system and the concept of application sandboxing. Did you really think you could store your application’s data wherever you’d like on the file system? Think again, padawan.

File System and Application Sandboxing

Security on the iOS platform has been one of Apple’s top priorities ever since the iPhone was introduced in 2007. In contrast to OS X applications, an iOS application is placed in an application sandbox. The sandbox of an application doesn’t only refer to an application’s sandbox directory in the file system. It also includes controlled and limited access to user data stored on the device, system services, and hardware.

With the introduction of the Mac App Store, Apple has begun to enforce application sandboxing on OS X as well. Even though the constraints put on OS X applications are not as stringent as the ones imposed on iOS applications, the basic concept is similar. There are differences, though. The application sandbox of an iOS application, for example, contains the application bundle, which isn’t true for OS X applications. The reasons for these differences are mainly historical.

Sandboxing and Directories

The operating system installs each iOS application in a sandbox directory that contains the application bundle directory and three additional directories, DocumentsLibrary, and tmp. The application’s sandbox directory, often referred to as its home directory, can be accessed by calling a simple Foundation function, NSHomeDirectory().

print(NSHomeDirectory())

You can try this yourself. Create a new Xcode project based on the Single View Application template and name it Data Persistence.

Project Setup

Open AppDelegate.swift and add the above code snippet to application(_:didFinishLaunchingWithOptions:). If you run the application in the simulator, the output in the console should look something like this:

/Users/Bart/Library/Developer/CoreSimulator/Devices/14F00EFB-2EAB-438C-B401-7FEFDA1D94AB/data/Containers/Data/Application/81B23594-3BA2-4AF9-B91A-F74A53FD6945

However, if you run the application on a physical device, the output looks a bit different as you can see below. The application sandbox and the limitations imposed are identical, though.

/var/mobile/Containers/Data/Application/41E7939B-6A39-4005-9C28-372FD9C7AD99

Retrieving the path to the application’s Documents directory requires a bit more work as you can see in the next code snippet.

let directories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)

if let documents = directories.first {
    print(documents)
}

We invoke the NSSearchPathForDirectoriesInDomains() function, which is defined in the Foundation framework. As the first argument, we pass in DocumentDirectory of type NSSearchPathDirectory to indicate that we’re only interested in the application’s Documents directory. The second and third argument are of less importance for our discussion. The function returns an array of type [String], containing one result, the path to the application’s Documents directory.

Why Sandboxing?

What is the benefit of sandboxing? The primary reason for sandboxing applications is security. By confining applications to their own sandbox, compromised applications cannot cause damage to the operating system or other applications.

By compromised applications, I mean both applications that have been hacked, applications that are intentionally malicious, as well as applications that contain critical bugs that may inadvertently cause damage.

Even though applications are sandboxed on the iOS platform, applications can request access to certain files or assets that are outside of their application sandbox through a number of system interfaces. An example of this is the music library stored on a device. Know, however, that the system frameworks are in charge of any operations related to file access.

What Goes Where?

Even though you can do pretty much anything you want in your application’s sandbox, Apple has provided a few guidelines with regards to what should be stored where. It’s important to know about these guidelines for several reasons. When an iOS device is backed up to your computer or to iCloud, not all the files in the sandbox are included in the backup.

The tmp directory, for example, should only be used for temporarily storing files. The operating system is free to empty this directory at any time, for example, when the device is low on disk space. The tmp directory isn’t included in backups.

The Documents directory is meant for user data, whereas the Library directory is used for application data that isn’t strictly tied to the user. The Caches directory in the Library directory is another directory that isn’t backed up.

Also keep in mind that your application isn’t supposed to modify the contents of the application bundle directory. The application bundle directory is signed when the application is installed. By modifying the contents of the application bundle directory in any way, the aforementioned signature is altered, which means the operating system doesn’t allow the application to launch again. This is another security measure put into place by Apple to protect customers.

Data Persistence Options

There are several strategies for storing application data on disk. In this article, we take a brief look at four common approaches on iOS:

  • defaults system
  • property lists
  • SQLite
  • Core Data

The options described in this article shouldn’t be considered as interchangeable. Each strategy has benefits and drawbacks. Let’s start by taking a look at the defaults system.

User Defaults

The defaults system is something that iOS inherited from OS X. Even though it was created and designed for storing user preferences, it can be used for storing any type of data as long as it’s a property list type, NSStringNSNumberNSDateNSArray,NSDictionary, and NSData, or any of their mutable variants.

What about Swift data types? Fortunately, Swift is smart enough. It can store strings and numbers by converting them to NSString and NSNumber. The same applies to Swift arrays and dictionaries.

The defaults system is nothing more than a collection of property lists, one property list per application. The property list is stored in a Preferences folder in the application’s Library folder, hinting at the property list’s purpose and function.

One of the reasons that developers like the defaults system is because it’s so easy to use. Take a look at the following example to see what I mean.

let userDefaults = NSUserDefaults.standardUserDefaults()

// Setting Values
userDefaults.setBool(true, forKey: "Key1")
userDefaults.setInteger(123, forKey: "Key2")
userDefaults.setObject("Some Object", forKey: "Key3")
userDefaults.setObject([1, 2, 3, 4], forKey: "Key4")

// Getting Values
userDefaults.boolForKey("Key1")
userDefaults.integerForKey("Key2")
userDefaults.objectForKey("Key3")
userDefaults.objectForKey("Key4")

userDefaults.synchronize()

read more

iOS From Scratch With Swift: Exploring Tab Bar Controller

In the previous tutorial, we discussed how a navigation controller enables the user to navigate hierarchical content or complex data by managing a stack of view controllers. Tab bar controllers also manage an array of view controllers. The difference is that the view controllers of a tab bar controller don’t necessarily have a relation to one another. In this tutorial, we will explore tab bar controllers in more detail by creating a tabbed application from scratch.

Introduction

UITabBarController is another UIViewController subclass. While navigation controllers manage a stack of related view controllers, tab bar controllers manage an array of view controllers that have no explicit relation to one another.

The Clock and Music applications on iOS are two prime examples of tab bar controllers. Just like any other UIViewController subclass, a tab bar controller manages a UIView instance.

The Clock application is a great example of a tab bar controller in use

The view of a tab bar controller is composed of two subviews:

  • the tab bar at the bottom of the view
  • the view of one of the view controllers the tab bar controller manages
Anatomy of a Tab Bar Controller

Before We Start

There are a few caveats to be aware of when working with tab bar controllers. Even though instances of UITabBar can only display five tabs, UITabBarController can manage more view controllers. If a tab bar controller manages more than five view controllers, the tab bar’s last tab is titled More.

A tab bar controller can manage an unlimited number of view controllers

The additional view controllers can be accessed via this tab and it is even possible to edit the position of the tabs in the tab bar.

The additional view controllers can be accessed via the More tab

Even though tab bar controllers manage a view, your application isn’t supposed to directly interact with a tab bar controller’s view. The tab bar controller is required to be the root view controller of the application window. In other words, the root view of the application window is always the tab bar controller’s view. A tab bar controller should never be installed as a child of another view controller. This is one of the key differences with navigation controllers.

Tabbed Library

In this article, we revisit the Library application that we built in the previous article. Doing so will let us reuse several classes and get up to speed faster. In addition, it will show you that navigation controllers and tab bar controllers are quite different and that they are used in different situations and use cases.

Because the application we build in this lesson is based on the UITabBarController class, it is going to give a very specific look and feel to the application, allowing for little flexibility in terms of user interface and experience. Tab bar controllers are incredibly useful, but you have to accept that they put constraints on your application’s user interface to some extent.

Open Xcode, create a new project (File > New > Project…), and select the Single View Application template.

Choosing an Application Template

Name the project Tabbed Library, assign an organization name and identifier, set Language to Swift, and set Devices to iPhone. Tell Xcode where you want to save the project and click Create.

Configuring the Project

Even though Xcode includes a Tabbed Application template, I prefer to start with a basic application template so you understand how the various pieces of the puzzle fit together. You’ll notice that tab bar controllers aren’t that complicated.

Taking a Head Start

When the Tabbed Library application is finished, the tab bar controller of the application will manage six view controllers. Instead of creating every view controller class from scratch, we’re going to cheat a little by reusing the view controller classes we created in the previous article. In addition, we’ll create several instances of the same view controller class to save us some time. The goal of this article is not to create a bunch of view controller classes. At this point, you should be pretty familiar with how that works.

Download the source code from the previous article and open the Xcode project that’s included in the source files in a new Finder window. Find the AuthorsViewControllerBooksViewController, and BookCoverViewController classes and drag them to your new project. Make sure to copy the files to the new project by checking the checkbox Copy items if needed and don’t forget to add the files to the Tabbed Library target.

Copy Files to Project

In addition to these three classes, we also need to copy the folder of resources, containing Books.plist and the image files, to our new project. Drag the folder named Resources to our project and use the same settings that we used to copy the class files. We’re now ready to instantiate the application’s tab bar controller and populate it with its first view controller.

Adding a Tab Bar Controller

If you open Main.storyboard, you’ll notice that the storyboard contains an instance of the ViewController class. Select the view controller and press delete or backspace. Open the Object Library on the right and drag a tab bar controller to the workspace.

Xcode automatically adds two child view controllers to the tab bar controller. Because I want to show how you how to manually add child view controllers to a tab bar controller, we’re going to delete the ones Xcode has created for us. Select the child view controllers and press delete or backspace to delete them.

Select the tab bar controller, open the Attributes Inspector, and check the checkbox Is Initial View Controller. If we don’t set the tab bar controller as the initial view controller, the application will crash on launch.

Adding a Tab Bar Controller

If you run the application in the simulator, you should see a tab bar at the bottom and a black background. This may seem unimportant, but it shows how the tab bar controller works. The tab bar controller manages an array of view controllers, similar to how a navigation controller manages a stack of view controllers.

We need to add a few view controllers to the storyboard and add them to the viewControllers property of the tab bar controller. Let’s see how this works.

Adding View Controllers

Drag a UITableViewController instance from the Object Library to the workspace and set its class to AuthorsViewController in the Identity Inspector. Select the view controller’s table view and set Prototype Cells to 0 in the Attributes Inspector.

Adding a Table View Controller

To add the authors view controller to the tab bar controller’s array of view controllers, drag from the tab bar controller to the authors view controller, holding down the Control key. Select Relationship Segue > view controllers from the menu that appears.

Creating a Relationship Segue
Creating a Relationship Segue

A tab bar controller with one tab isn’t that useful so let’s add another view controller to the mix. Drag another UITableViewController instance from the Object Library, set its class to BooksViewController, and set Prototype Cells to 0. Create the relationship segue as we did for the authors view controller.

Adding Another Table View Controller

Add a UIViewController instance to the workspace and set its class to BookCoverViewController in the Identity Inspector. Add a UIImageView instance to the view controller, connect it with the bookCoverView outlet of the view controller, and apply the necessary layout constraints. Create the relationship segue with the tab bar controller like we did for the table view controllers.

A storyboard can sometimes become a bit cluttered. If you’re having trouble creating segues between view controllers, know that you can also create connections, such as segues, in the navigator on the left. This is often much easier and less clunky.

Creating Segues Between View Controllers

Build and run the application. At this point, the tab bar contains three tabs. Tapping the second or third tab crashes the application. Why is that? It’s time for some debugging.

Fixing Bugs

Fixing the Authors View Controller

When you tap an author’s name in the authors view controller, the application crashes. The first thing you should do when you’re faced with a crash is inspect Xcode’s console. This is what it tells me.

Tabbed Library[1141:54864] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<Tabbed_Library.AuthorsViewController: 0x7fde3943d050>) has no segue with identifier 'BooksViewController''

The message that Xcode displays in the console is not difficult to decipher. We tell the view controller to perform a segue with identifier BooksViewController, but that segue doesn’t exist. The result is a crash.

This bug is easy to fix by embedding the authors view controller in a navigation controller, creating a segue to the BooksViewController instance, and naming the segue BooksViewController. I leave that up to you as an assignment since we already did this in the previous article. The source files of this tutorial contain the solution.

The more important issue in this example is understanding that the three view controllers that are linked to the tab bar controller don’t communicate with one another. The idea behind a tab bar controller and a navigation controller is very different. A navigation controller maintains an array of view controllers, the navigation stack. The view controllers in the navigation stack have an implicit relation with one another in the sense that they are part of the same navigation stack.

A tab bar controller also manages an array of view controllers, but the view controllers don’t know about each other. They cannot communicate with each other through the tab bar controller. In the previous article, we passed data from the authors view controller to the books view controller when the user tapped an author. This pattern isn’t applicable in a tab bar controller. Of course, we could implement a solution to show the user the books view controller when an author is tapped in the authors view controller, but it’s important that you understand that that isn’t the goal of a tab bar controller.

The Clock and Music applications on iOS are good examples of how a tab bar controller should be used. The view controllers the tab bar controller manages in the Music application have no relation to one another apart from the fact that they show songs.

Before you continue, make sure that you understand the concepts of a navigation controller and a tab bar controller. They are important later in this series.

Fixing the Books View Controller

When you tap the second tab of the tab bar controller, the application also crashes. This is what I see in Xcode’s console.

fatal error: unexpectedly found nil while unwrapping an Optional value

This isn’t surprising either since we don’t set the author property of the books view controller. Let’s update the books view controller so that it shows every book in Books.plist.

We need to make three changes. Let’s start with the easiest change first. Open BooksViewController.swift and change the type of the author property to [String: AnyObject]?. The author property is now an optional instead of a forced unwrapped optional.

var author: [String: AnyObject]?

For the second change, we need to update the implementation of viewDidLoad(). We display the name of the author if author is not nil. If author is nil, we set title to "Books".

override func viewDidLoad() {
    super.viewDidLoad()
    
    if let author = author, let name = author["Author"] as? String {
        title = name
    } else {
        title = "Books"
    }
    
    tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier)
}

read more

A Beginners Guide to Titan Framework: Adding a Code Type Option

Sometimes while building a plugin or a theme, developers like to provide end users with the ability to add custom code. Today, I’ll discuss how to add the code type option with Titan Framework, which lets you create an editor in which you can add any custom code. Let’s take a look at it!

The Code Type Option in Titan Framework

In Titan Framework, it is possible to create a text area for inputting the code via its code type option. The textarea supports syntax highlighting, so in whichever language you code, its corresponding highlighting is adopted. This option has an ability to automatically enqueue CSS and JavaScript—how cool is that? In your dashboard, it appears like this:

Adding Code Type Options in Titan

Let’s look at the parameters of this option:

  • name: It specifies the display name of this option.
  • id: It assigns a unique name which retrieves saved values.
  • desc: It adds a one-line description with the option name.
  • default(Optional) It sets the default value of the option.
  • livepreview: (Optional) Whenever you add a code type option in a theme customizer section, you get to see a live preview of changes with this parameter.
  • lang: (Optional) This parameter defines the language used for syntax highlighting. The default is set to css. Here’s a list of all supported languages.
  • theme: (Optional) You can set the color theme with this parameter. The default is chrome. Here’s a list of all supported themes.
  • height: (Optional) It configures the height of the code editor. The default height is 200 px.

All the parameters are string by type, except ‘height’ which is int.

Available Containers for the Code Type Option

You can add this option inside: 

  • Admin Panel
  • Admin Tabs
  • Metabox
  • Theme Customizer Section

You can add this option inside the containers by following these steps:

  • First get an instance via the getInstance() function.
  • Next add an option via the createOption() function.
  • Finally, get saved options values via the getOption() function.

To revise your concepts about creating containers with Titan Framework, read the previous articles of this series.

Creating a Code Type Option Inside an Admin Panel

Example Declaration

Let’s create this option inside an admin panel first.

<?php
    /**
     * 
     * Create code type option in an Admin Panel
     * 
     */
    $aa_panel->createOption( array(

        'id'   => 'aa_code_opt', // The ID which will be used to get the value of this option
        'type' => 'code', // Type of option we are creating
        'name' => 'Enter Custom Code', // Name of the option which will be displayed in the admin panel
        'desc' => 'This is our option', // Description of the option which will be displayed in the admin panel
        'lang' => 'css' // Language used for syntax highlighting

    ) );

read more