News

Welcome to End Point’s blog

Ongoing observations by End Point people

Reach customers and drive sales with MailChimp

It's a good idea for ecommerce stores to regularly contact their customers. This not only reminds customers that your business exists, but also allows the sharing of new products and resources that can enrich the lives of your customers and clients. One of the easiest ways to stay in touch is by using an email newsletter service, such as MailChimp.

MailChimp offers the regular suite of email newsletter services: lists, campaigns, and reports — but in addition, they allow an ecommerce store to integrate sales data back into MailChimp. When you have detailed shopping statistics for each subscriber, it open new possibilities for customized marketing campaigns.

Endless possibilities

For example, imagine you have an email mailing list with 1,000 recipients. Instead of mailing the same generic newsletter to each subscriber, what if you could segment the list to identify your 100 best customers, and email them a special campaign?

Additional ideas could include:

  • Reach out to inactive subscribers, offering a coupon
  • Invite your best customers to a secret sale
  • Re-engage customers who placed items in their cart, but left without purchasing
  • Offer complementary products to purchasers of Product X

Automatic marketing

Once your store has sales data for subscribers, and you've decided on the campaigns you want to run with this data, the next step is to automate the process. This is where MailChimp's Automation feature comes in. Spend some time up-front to craft the automated campaigns, then sit back and let MailChimp run them for you; day in, day out.

Steps to implement

There are several off-the-shelf integrations for ecommerce stores, including Magento and BigCommerce.

Users of Perl and Interchange can use our newly-released toolsets of the Mail::Chimp3 CPAN module and the integration for Interchange5.

Contact us today for expert help integrating one of these solutions with your ecommerce store.

Go beyond the simple newsletter

Most businesses already have an email newsletter. Hopefully, you are sending regular email campaigns with it. This is a great first step. Going beyond this to segment your email list and reach out to these segments with relevant information to each of them, is the next step. Not only can this increase your sales, but it also respects your clients' and customers' time and preferences. It's a win-win for all.

Additional resource: MailChimp for Online Sellers

Create categories in Virtuemart 3 with Joomla 2.5 and 3.5 programmatically

Introduction

Code that I'm going to show you is ready to download here: https://github.com/peter-hank/com_morevirtuemart

Virtuemart is an open-source e-commerce application written in PHP. It's pretty popular with a 4% share of the whole e-commerce market (https://blog.aheadworks.com/2015/05/ecommerce-platforms-popularity-may-2015-two-platforms-take-half/). Today I will show you how to extend it and use its functionality from an external Joomla (free and open-source content management system) component.

Creating a component

We are going to create a new component to show the code in a nice and clear form. I'm using a component generator from here: https://www.component-creator.com. We don't need any tables, models and views for the purpose of this blog post. Plain and simple component is what we need. After creating the component download it and install in a Joomla administration interface.

Component overview

The component structure looks like this:

# tree components/com_morevirtuemart
components/com_morevirtuemart
├── controller.php
├── controllers
│   └── index.html
├── helpers
│   ├── index.html
│   └── morevirtuemart.php
├── index.html
├── models
│   ├── fields
│   │   ├── createdby.php
│   │   ├── filemultiple.php
│   │   ├── foreignkey.php
│   │   ├── index.html
│   │   ├── modifiedby.php
│   │   ├── submit.php
│   │   ├── timecreated.php
│   │   └── timeupdated.php
│   ├── forms
│   │   └── index.html
│   └── index.html
├── morevirtuemart.php
├── router.php
└── views
    └── index.html

We don't need to use more than a generic controller in a main directory. Its content is not very exciting for now:

<?php

// No direct access
defined('_JEXEC') or die;

jimport('joomla.application.component.controller');

/**
 * Class MorevirtuemartController
 *
 * @since  1.6
 */
class MorevirtuemartController extends JControllerLegacy
{
 /**
  * Method to display a view.
  *
  * @param   boolean $cachable  If true, the view output will be cached
  * @param   mixed   $urlparams An array of safe url parameters and their variable types, for valid values see {@link JFilterInput::clean()}.
  *
  * @return  JController   This object to support chaining.
  *
  * @since    1.5
  */
 public function display($cachable = false, $urlparams = false)
 {
  $view = JFactory::getApplication()->input->getCmd('view', '');
  JFactory::getApplication()->input->set('view', $view);

  parent::display($cachable, $urlparams);

  return $this;
 }
}

We can remove a display function and replace it with a function called createCategory so a MorevirtuemartController class will look like this:

class MorevirtuemartController extends JControllerLegacy
{
 public function createCategory ()
 {
  echo 'I\'m alive!';

  die();
 }
}

To test that our component is fully working right now try to open Joomla with this url: index.php?option=com_morevirtuemart&task=createCategory. Of course you need to prepend it with your domain name to make it work. The result should be just an empty page with our text: "I'm alive!"

Now you know how to call a controller task within a browser.

Creating a category

To use Virtuemart classes we need to initialize all of its logic and configuration. Look here:

<?php

// No direct access
defined('_JEXEC') or die;

jimport('joomla.application.component.controller');

// loading Virtuemart classes and configuration
if (!class_exists( 'VmConfig' )) require(JPATH_ADMINISTRATOR . DS . 'components' . DS . 'com_virtuemart'.DS.'helpers'.DS.'config.php');
$config= VmConfig::loadConfig();
if (!class_exists( 'VirtueMartModelVendor' )) require(JPATH_VM_ADMINISTRATOR.DS.'models'.DS.'vendor.php');
if(!class_exists('TableMedias')) require(JPATH_VM_ADMINISTRATOR.DS.'tables'.DS.'medias.php');
if(!class_exists('TableCategories')) require(JPATH_VM_ADMINISTRATOR.DS.'tables'.DS.'categories.php');
if (!class_exists( 'VirtueMartModelCategory' )) require(JPATH_VM_ADMINISTRATOR.DS.'models'.DS.'category.php');

/**
 * Class MorevirtuemartController
 *
 * @since  1.6
 */
class MorevirtuemartController extends JControllerLegacy
{
 public function createCategory ()
 {
  echo 'I\'m alive!';
  die();
 }
}

What we are doing here is importing needed Virtuemart classes and initializing the configuration of each. We are all set now and ready for an implementation.

The most difficult thing here is how to work with a Virtuemart internal authorization system. Before an every CRUD action there is a check for an authorization access. We need to find a workaround for this, there are multiple way of doing this, I will show you how I did that. Create a new file in components/com_morevirtuemart/model and call it category.php. Inside this file put this piece of code:

<?php

// No direct access
defined('_JEXEC') or die;

class VirtueMartModelCategoryLocal extends VirtueMartModelCategory {
 public function __construct() {
  parent::__construct();
 }
}

I hope it's pretty clear what we are doing here: extending a Virtuemart category model. What I'm going to do now is to copy a store function from a VirtueMartModelCategory class and put it in our local class of a category model and remove an authentication check from it (you need to secure it by yourself but using a different method). The result of this action is:

<?php

// No direct access
defined('_JEXEC') or die;

class VirtueMartModelCategoryLocal extends VirtueMartModelCategory {
  public function __construct() 
  {
    parent::__construct();
  }

  public function store(&$data) 
  {
    $table = $this->getTable('categories');

    if ( !array_key_exists ('category_template' , $data ) ){
      $data['category_template'] = $data['category_layout'] = $data['category_product_layout'] = 0 ;
    }
    if(VmConfig::get('categorytemplate') == $data['category_template'] ){
      $data['category_template'] = 0;
    }

    if(VmConfig::get('categorylayout') == $data['category_layout']){
      $data['category_layout'] = 0;
    }

    if(VmConfig::get('productlayout') == $data['category_product_layout']){
      $data['category_product_layout'] = 0;
    }

    $table->bindChecknStore($data);

    if(!empty($data['virtuemart_category_id'])){
      $xdata['category_child_id'] = (int)$data['virtuemart_category_id'];
      $xdata['category_parent_id'] = empty($data['category_parent_id'])? 0:(int)$data['category_parent_id'];
      $xdata['ordering'] = empty($data['ordering'])? 0: (int)$data['ordering'];

        $table = $this->getTable('category_categories');

      $table->bindChecknStore($xdata);

    }

    $this->clearCategoryRelatedCaches();

    return $data['virtuemart_category_id'] ;
  }
}

In addition, I removed call to media creation. If you need to create media along with the categories you need to extend it as well, the same as we did with a category model. We are all set now to create a new category from a component controller, we just need to include an extended model there and call a store function. Our controller should look like this now:

categoryModel = new VirtueMartModelCategoryLocal();
 }

 public function createCategory ()
 {
  $catData = [
   'category_name' => 'Brand new Virtuemart category',
  'category_parent_id' => 0,
   'published' => 0
   ];

  $catId = $this->categoryModel->store($catData);

  echo 'Created a new category';

  die();
 }
}

Wow! The new category should be there. There are many more available attributes to be set, we've used a needed minimum: category name, its parent and status.

The end

There are a few things I should mention:

  • ensure to add an authentication to your code, you can use a Joomla authentication system,
  • be aware that Virtuemart code can change in time so be careful with updates,
  • I recommend to use a CLI script for this kind of operations.
Thanks for reading.

Creating a recent activity feed for Thredded

Introduction

Thredded is open source forum/message board software for Rails 4.2+. The project is still alive and maintained by its author actively. For a new Rails project for a client in End Point we used Thredded as a part of an application stack. It works pretty well, but sometimes it lacks some important features. Fortunately, it's coded very nice and easy to extend its core functionality. This time we wanted to create a recent activity feed for a current user. I wanted to share this with you because I think that it's a popular widget in many social community sites.

Plan

We decided to put two main data sources for the feed:

  • user personal message notifications,
  • user forum message notifications (you may want to put some extra features here to show posts only from topics where the user is directly involved in by writing a message, for our needs it was enough to show every new post that the user was able to see).

Getting data

Let's say that we created a WidgetController controller. How to get data that we are interested in? It's not so difficult. First, this is how we are getting the latest 5 private messages for a user:

private_messages = Thredded::PrivateTopic
  .distinct
  .for_user(current_user)
  .order_recently_updated_first
  .includes(:user)
  .limit(5)
We use a PrivateTopic model to get private message for a current_user, sorted by date in descending order, including a message author and limiting to 5 records.

Now, to get a regular topic posts we need to:

posts = current_user
  .thredded_posts
  .where(messageboard_id: Pundit.policy_scope(current_user, Thredded::Messageboard.all).pluck(:id))
  .order_newest_first
  .limit(5)

We get the latest 5 posts from all the threads that a user has access to. Assuming that an action name is show, this is how it can looks all together (widget_controller.rb):

class WidgetController < ApplicationController
  def show 
    @messages = []

    posts = current_user
      .thredded_posts
      .where(messageboard_id: Pundit.policy_scope(current_user, Thredded::Messageboard.all).pluck(:id))
      .order_newest_first
      .limit(5)
    posts.each do |post|
      topic = Thredded::Topic.find(post.postable_id)

      @messages << {
        title: topic.title,
        path: Thredded::UrlsHelper::topic_url(topic, only_path: true),
        author: User.find(post.user_id),
        type: 'forum',
        created_at: post.created_at
      }
    end  

    private_messages = Thredded::PrivateTopic
      .distinct
      .for_user(current_user)
      .order_recently_updated_first
      .includes(:last_user, :user)
      .limit(5)

    private_messages.each do |pm|
      @messages << {
        title: pm.title,
        path: Thredded::UrlsHelper::topic_url(pm, only_path: true),
        author: User.find(pm.user_id),
        type: 'pm',
        created_at: pm.created_at
      }
    end   

    @messages = @messages.sort_by{|e| e[:created_at]}.reverse.take(5)
  end  
end
We merged two lists of items, sorted by date and limited to show only 5 items and created a view variable to access it from a view that we are going to create now.

Showing data

The data is ready now, the only thing that we need to do now is to create a view. We are going to use haml as a templating language. Create a file called show.html.haml. Put this piece of code in there:

%table
  %tbody
    - @messages.each do |message|
      %tr
        %td
          - if message[:type] == 'pm'
            %strong= link_to truncate('Private message', :length => 25), message[:path]
            from
            = ' '
            = link_to message[:author].username, user_path(message[:author])
            = ' '
            = time_ago_in_words(message[:created_at])
          - else
            %strong= link_to truncate(message[:title], :length => 25), message[:path]
            by
            = ' '
            = link_to message[:author].username, user_path(message[:author])
            = ' '
            = time_ago_in_words(message[:created_at])

We are looping through a list of messages. We need to distinguish between a private message and a regular post, just to make it nicer and user friendly. You may need to play a little bit with an author's property, it may be different for your project. If you have any problems, please comment! Good luck!

The end

The only documentation I know about is here: https://github.com/thredded/thredded

Thanks for reading.

Showing Your Client Measurable Progress with Video Updates

I’ve recently started working on a big new fancy project -- the kind everyone dreams of -- a nice budget, a great client, and lots of fun stuff to dig into. I happened to stumble on a fun and easy way to get a client a weekly update on progress, Videos!

I used Google Chrome’s Screencastify app to capture my screen along with a vocal narrative to give the client a visual update of what we have accomplished so far this week. I have been using Screencastify Lite and was really happy with the end product. Combined with Google’s developer tools device mode you can toggle between the site’s full screen or mobile view.

This is a great tool to show a client some measurable progress without going through the pain of showing them an unfinished site, letting them dig into lots of broken links, and having them inevitably stumble on more questions than answers. Plus, your client can digest the content you’ve shown them, avoid the hassle of scheduling a face to face demo, and allow them some time to compose their feedback.

Next time you need an easy fast way to show progress on a project, try a video!

Report on The Perl Conference 2016

In June, I traveled to Orlando, Florida to attend the event formerly known as Yet Another Perl Conference (or YAPC::NA), now known as The Perl Conference. This was my second time in a row to attend this conference (after my first attendance back in 2007).

Conferences are a great place to learn how others are using various tools, hear about new features, and interact with the community. If you are speaking, it's a great opportunity to brush up on your subject, which was true for me in the extreme, as I was able to give a talk on the PostgreSQL database, which I hadn't used in a long time (more on that later).

The conference name

The event organizers were able to license the name The Perl Conference from O'Reilly Media, as O'Reilly doesn't hold conferences by this name anymore. This name is now preferred over "YAPC" as it is more friendly to newcomers and more accurately describes the conference. More on the name change.

Notes from the conference

Over the three days of the conference, I was able to take in many talks. Here are some of my more interesting notes from various sessions:

  • MetaCPAN is the best way to browse and search for Perl modules. Anyone can help with development of this fine project, via their GitHub.
  • Ricardo Signes says "use subroutine signatures!" They are "experimental", but are around to stay.
  • Perl6 is written in Perl6 (and something called "Not Quite Perl"). This allows one to read the source to figure out how something is done. (There were many talks on Perl6, which is viewed as a different programming language, not a replacement for Perl5.)
  • jq is a command-line utility that can pretty-print JSON (non Perl, but nice!)
  • Ricardo Signes gave a talk on encoding that was over my head, but very interesting.
  • The presenter of Emacs as Perl IDE couldn't attend, so Damian Conway spoke on VIM as Perl IDE (photo above)

From John Anderson's talk:

  • Just say "no" to system Perl. Use plenv or the like.
  • There's a DuckDuckGo bang command for searching MetaCPAN: !cpan [module]
  • Use JSON::MaybeXS over the plain JSON module.
  • Use Moo for object-oriented programming in Perl, or Moose if you must.
  • Subscribe to Perl Weekly
  • Submit your module on PrePAN first, to receive feedback before posting to CPAN.

Lee Johnson gave a talk called Battling a legacy schema with DBIx::Class (video). Key takeaways:

  • When should I use DBIC?
  • Something that has grown organically could be considered "legacy," as it accumulates technical debt
  • With DBIC, you can start to manage that debt by adding relationships to your model, even if they aren't in your database
  • RapidApp's rdbic can help you visualize an existing database

D. Ruth Bavousett spoke on Perl::Critic, which is a tool for encouraging consistency. Basically, Perl::Critic looks at your source code, and makes suggestions for improvement, etc. These suggestions are known as "policies" and can be configured to enable or disable any of them, or to even write new policies. One suggestion was to create a Git hook to run the perlcritic command at the time code is committed to the source code repository (possibly using App::GitHooks). End Point has its own perlcritic configuration, which I have started trying to use more.

Logan Bell shared Strategies for leading a remote team. Some of the tools and techniques he uses include:

  • tmate for terminal sharing
  • HipChat, with a chat room just for complaining called "head to desk"
  • Holds one-on-one meetings every Monday for those he works with and directs
  • Has new team members work on-site with another team member their first week or so, to help understand personalities that don't often come across well over chat
  • Tries to have in-person meetings every quarter or at least twice a year, to bring the team together

My talk

Finally, my own talk was titled Stranger in a Strange Land: PostgreSQL for MySQL users (video). I hadn't used Postgres in about seven years, and I wanted to get re-acquainted with it, so naturally, I submitted a talk on it to spur myself into action!

In my talk, I covered:

  • the history of MySQL and Postgres
  • how to pronounce "PostgreSQL"
  • why one might be preferred over the other
  • how to convert an existing database to Postgres
  • and some tools and tips.

I enjoyed giving this talk, and hope others found it helpful. All in all, The Perl Conference was a great experience, and I hope to continue attending in the future!

All videos from this year's conference

Liquid Galaxy Featured on Reef Builders

The Liquid Galaxy was recently featured on the front page of Reef Builders, the original and longest running saltwater fish blog and a leading source of aquarium news. Reef Builders writer Nicole Helgason wrote the story, which can be found here.

The Liquid Galaxy is an amazing tool for aquariums (not to mention other kinds of museums, offices, educational institutions, and more) around the world. It is a particularly effective for aquariums due to the underwater imagery shown on the system, as well as End Point's Content Management System that allows users the opportunity to tell underwater "stories" with supporting images, videos, and content. We have deployed to aquariums and science museums throughout the US, Europe, and East Asia.

The Liquid Galaxy lets visitors explore coral reefs and underwater environments the exact same way they navigate Street View (it's the same app and data set) with a full set of screens to give a totally immersive experience. While viewing the dazzling immersive display, the user can make use of the Liquid Galaxy touchpad and 3D joystick to look around and navigate through the display.

A video demonstrating how the Liquid Galaxy is utilized in aquariums can be found below. If you're interested in learning more about Liquid Galaxy for your aquarium, please contact us here for more information.

Case Study: Responsive Design Return on Investment

Responsive design has been a hot topic in the e-commerce world for several years now. End Point has worked on many sites over the last few years to transition clients to a responsive design website model. While many large sized retailers have already transitioned to a responsive design, there are many smaller e-commerce sites that are still on an older design model and I would like to show that the return on investment for those stores is still noteworthy.

The lead of our Interchange team, Greg Hanson, led a responsive design project and I’d like to summarize that work on our blog. For confidentiality, I am leaving out the client’s name in this case.

Why Go Responsive?

There are two main reasons every e-commerce website, even a small one, needs to become responsive:

  • Your customers
  • Google

The march toward mobile sales at this point is undeniable and unstoppable. As more and more people become comfortable using their phones and tablets to purchase things, the bigger this market share will become. Also, Google has begun de-prioritizing websites that do not cater to mobile users. If you are waiting to go responsive because of budget, you might surprised to learn how dramatically the mobile revenue increased for the customer in this case.

Goals

This client is a small e-commerce business with a heavy ‘on’ season and ‘off’ season where the business owners could focus on this project. They wanted:

  • To accommodate the increasing mobile user base; they knew it was there from looking at their Google Analytics.
  • To increase revenue from mobile users. They could see that they had a 10% mobile user base, but they were converting only a small percentage.

End Point’s strategy

Our strategy with this small client, to minimize impact to the business and cost was to:

Use Bootstrap

Bootstrap is one of many front end frameworks that allows you to create a design template and roll that out to all the pages within a website without having to re-code each and every page in a website. This dramatically increases the speed and decreases cost.

Break up the work into segments

In this case, we had a three phase approach:

  • Phase I, re­design the site using Bootstrap but still at fixed width
  • Phase II, site checkout sequence changed to responsive
  • Phase III, entire site responsive

The Project

Converting the site to Bootstrap was the biggest chunk of the time and money spent on this project. Since we knew this would be the foundation for the changes to come, we took our time getting it right, keeping the client involved every step of the way. We didn’t want to get in the way of their busy season either, so we completed that project and waited a half a year to begin the next piece.

The second step was updating checkout sequence to be responsive since this was arguably the hardest part about using the non-responsive site on a mobile device. The client considered this piece top priority. Plus, since it was only a few pages, it allowed us all to better understand the scope of coding the rest of the site and give the client an accurate picture of the budget.

Last, once we had a responsive checkout sequence, we changed the rest of the internal pages to be responsive and rolled out the entire site as responsive and mobile friendly.

Results

The first time we looked at the analytics following the responsive conversion, we were SHOCKED! Using a small sample period, from just a year prior, we saw a 280% increase in mobile purchases.


The timeframe for these comparison numbers was August 2014 before the responsive transition and August of 2015, after the transition.

To sanity check those numbers, we just re-ran some of the analytics recently and in May of 2016, revenue from mobile users is still up over 90% and the clients total revenue year-over-year is up 12%.


We also ran some numbers on the growth of mobile revenue as a percentage of overall revenue through this process. As you can see, two years ago mobile revenue was less than 1% and is now near 12%.

Untitled drawing.jpg

In this case, I didn’t go into all the ways to slice and dice the numbers in Google Analytics. However I'm happy to speak with you if you want to know more about mobile users, this client’s data, or how responsive design might help grow your e-commerce business.

The Lesson

The lesson here is that any store will benefit from going responsive. Having an experienced partner like End Point that can work with your budget and timeline can make this is a doable project for any size store and budget.