Google AdWords Certified Professional

Today  I passed the second compulsory Google AdWords certification exam. The first exam is the Advertising Fundamentals, which covers AdWords account and campaign management, amongst other things. The other exam I passed was the Advanced Search which covers advanced best practices for campaign management and promotion.

As its name implies, the Advanced Search exam is a much more difficult proposition. It covers bidding strategies, maximising campaign profitability, AdWord tools, conversion rate optimisation, ROI calculation, amongst other aspects of AdWords campaign management.

It will probably take a couple of days before my Google Partner status is updated with my certification details. in the meantime, I’ll be implementing some of the strategies  I learned about to the benefit of my PPC customers.

Google AdWords Exam

Today I passed the Google AdWords “Advertising Fundamentals” exam (88% since you asked). It’s part of the certification process that’s a prerequisite for becoming a Google partner.

The Google Partner Programme appears to be geared towards helping SMEs to find accredited agencies that will manage their pay-per-click (PPC) advertising campaigns. As part of the programme, agencies will have access to training materials and learn about “best practice” whilst Google will will be able to measure the effectiveness of each agency.

The end goal seems to be to reinforce SME confidence in the value of Google’s AdWords programme. I hope that when I’m fully accredited I’ll be better placed to help my SME clients with their PPC campaigns.

 

Magento Multi-Website Checklist

One of Magento’s great features is the multi-site & multi-store capabilities. If you’re new to Magento, this means that you can run more than one website from the same Magento installation. Having just added yet another website to a client’s Magento installation, I thought I’d jot the steps down as an aide-mémoire. So below I’m going to outline a checklist of the actions required to set up a mutli-website store in Magento.

For the purposes of the this example, assume I already have a Mage website eddiemay.me.uk and want to add another website eddie-may.com. Also assume that I’m selling to the same country with the same Tax & shipping rules, payment gateway, etc. Also assume that I’m going to be using the same template but use a different skin to make it look different.

NB: This example is for Magento Community Edition 1.7.0.2 – earlier editions had different ways of routing requests using .htaccess or index.php.

  1. Buy a multi-domain SSL certificate. Make sure that new domain is associated with this certificate.
  2. Point your new domain name at your server.
  3. Log into Magento and do a backup – peace of mind, etc.
  4. Set up a new root category & then add any sub categories as required. This root category is going to be ‘home’ of the new website.
  5. Go to System/Manage Stores. Next, hit the Create Website button & fill in the necessary information – keep a note of the value you put in the “code” field. In my example I’m going to use “may” as the code value.
  6. Repeat the steps for Create Store & Create Store View. When you create a store you set the root category to be the one you’ve just created in step 4 above.
  7. Go to CMS/Pages and create a new home page & associate it with the new Website.
  8. Now go to System/Configuration and change the ‘Current Configuration Scope’ to your new website. You’ll notice that the values are a copy of the default website. You’re now going to have to go through a number of tabs and change the required values. Here I’ll refer only to the essential changes.
  9. Under System/Configuration/Web change the Secure & Unsecure urls to your new domain name – in this case eddie-may.com. Also under Default Pages set the CMS Home Page to the page you created in step 7. Under the Session Cookie Management tab, add your new domain as the Cookie Domain like so -> .eddie-may.com
  10. In the Design tab you now have a choice of determining how the website will look. Here I’m going to assume that you’ll use the same template but use CSS to change the style of the site.
  11. So, upload a new skin under the /skin/frontend/default. Lets assume the new skin is called eddiemay, that means you’ll have a folder like so /skin/frontend/default/eddiemay
  12. If that’s the case, the design settings will be Package = default; Skin (Images/CSS) = eddiemay; Layout & Default will be the same values as your first website.
  13. Still under the Design tab, change the HTML Head, Header, Footer, etc as required. From an SEO perspective, make sure that the Default Title & Description are unique.
  14. Now go to Store Emails – you’ll need to change email addresses to reflect the new domain name. Make sure that these email accounts actually exist before going live.
  15. Under Contacts change the main contact email as required.
  16. Under Sales/Invoice & Packing Slip Design – change the default logos to your new logo.
  17. Under Sales/Sales Emails – you may have to change the email templates if you’ve heavily customised your default email templates (you may have hardcoded your store name, for example).
  18. That’s more or less it for the System/Configuration settings.
  19. Now go to CMS section. Here you may have to create new CMS pages & Static Blocks, or at least associate existing ones with the new website. At a minimum you’ll need to associate the Contact, About, Privacy, Terms, Shipping pages with the new website.
  20. The next thing you’ll need to do is add the new website to your .htaccess file. You need to add the new domain like so:
  21. ## multidomain
    SetEnvIf Host www\.eddiemay\.me.uk MAGE_RUN_CODE=base
    SetEnvIf Host www\.eddiemay\.me.uk MAGE_RUN_TYPE=website
    SetEnvIf Host ^eddiemay\.me.uk MAGE_RUN_CODE=base
    SetEnvIf Host ^eddiemay\.me.uk MAGE_RUN_TYPE=website
    SetEnvIf Host www\.eddie-may\.com MAGE_RUN_CODE=may
    SetEnvIf Host www\.eddie-may\.com MAGE_RUN_TYPE=website
    SetEnvIf Host ^eddie-may\.com MAGE_RUN_CODE=may
    SetEnvIf Host ^eddie-may\.com MAGE_RUN_TYPE=website
  22. You’ll notice that I’ve used the code “may” – this was the value I entered in the new Website code field in step 5 above.
  23. Upload the new .htaccess file.
  24. Now test your new website – chances are that you’ll get an error. Downloading the error report will give a cryptic “SQLSTATE[42S02]: Base table or view not found: 1146 Table ‘database-name.catalog_category_flat_store_3′” doesn’t exist – or something similar. If this happens, log into your Magento administration UI, go to System/Index Management & you’ll probably see that several indexes need re-indexing. Once complete, your website should now work as expected.
  25. If all is good, its now time to add some products. You could copy products over from the original website but personally I’d create them from scratch or upload new ones. This is because you can then optimise their descriptions, etc., to avoid duplicate content penalties, etc.

Well, I think that covers all the bases. This will probably take you about 2 hours to complete, depending on the amount of customising of Email template, Static Blocks, etc. The CSS customisations are another thing altogether.

If I’ve left anything out, please let me know. Either way, I hope you find it useful.

Empty Magento Sales Report

I was recently onsite helping a Magento client do some things on her store. During the course of our conversation, she asked about exporting her sales records. She was stuck because the sales report was empty. The reason for this was because her sales statistics needed refreshing.

Magento Sales Report ScreenWhen you go to the sales reports at the top is a warning that you should refresh the last day’s statistics. Doing this is ok if you’ve kept on top of this particular task. If you’ve never updated your statistics before, then this won’t work – your sales stats will be empty or incomplete.

If this is the situation you find yourself in, then you should refresh the life time statistics. This can be done by going to the Refresh Statistics tab under Reports menu item.

Magento Reports menuThis will load the Statistics tab. Select the Orders row, select the ‘Refresh Lifetime Statistics’ option and then submit. A dialog box will warn you that your site could become unresponsive – this is a danger if you’ve never refreshed these statistics before. For this reason, I suggest you only ever do one row at a time.

Magento Refresh Stats UIOnce you have completed this task, go back to the Sales Report and run the orders report again. This time the stats should be reporting correctly.

PS

If your sales reports are out of date (have not been refreshed) then this points to a problem with  your Magento cron job. A cron job is a scheduled task that runs at a determined interval. Refreshing the sales and other reports is one of the things the Magento cron job does. Setting up the cron job is really important and is a topic I’ll address in the future.

Northampton Skip Hire

Northants Skip Hire
Northants Skip Hire

I’m pleased to announce that Northampton skip hire website is now live and bringing in orders.

This website is part of a wider portfolio of Joomla! websites owned by Bakers Waste Services, and is part of the expansion of their waste management operations in the East Midlands.

Leicestershire Point of Sale website launched

home page imageLast week Carter Design Group’s new website was ‘soft’ launched. Carter Design Group, based in the small Leicestershire village of Foxton, are point of sale display designers, manufactures and installers.

The website was designed by Wavy at The Mitchesons, while we worked on the site’s architecture and turning the design into a responsive WordPress template.

UPDATE

Less than a month since going live, the search engine results are very pleasing. For terms such as “point of sale designer” and “point of sale design” they rank 3rd and 5th in Google respectively. For “grocery merchandising” they are #2.

While this represents a great start to our SEO efforts, there is still much to do!

 

Magento 503 maintenance flag error

Today visiting one of the Magento sites I look after returned the 503 Server error, with the message that the server was down for maintenance or was out of resources. This was true for both the front end and the admin area.

Looking in my error logs returned this error message:

client denied by server configuration: MAGENTO_DIR/app/etc/local.xml

Google returned several possible solutions. But then I remembered I’d been in the back end, using Magento Connect Manager to upload a language pack. This had failed for some reason. It then dawned on me that Magento puts the store (CE 1.7.0.2) into maintenance mode when performing updates, etc. So, its probable that Magento did not return the site to live mode after the upload failed.

A quick look in the root showed that a file called “maintenance.flag” was there. Renaming the file meant both front and backend were now working as expected.

Writing a Joomla plugin

I’ve been working on a Joomla! project recently & have needed to write several plugins so I thought I’d write about how I wrote one that notified administrator when a user updates their JomSocial profile.

About Plugins

Joomla! plugins are code that implements additional features without the need to hack core code. Often they ‘plugin’ into system events (or hooks) & therefore enable developers to fire custom code at certain points in the execution cycle.

If you look under the plugins folder of your Joomla! installation, you’ll see that Joomla! ships with quite a few plugins, under folders such as ‘authentication’, ‘search’, ‘system’, etc. Each folder groups similar types of plugins & you’ll often find that a component will install its own plugins and/or folder of plugins.

Look within these folders and you’ll see that plugins follow a convention. There is a minimum of two files: a .php & a .xml file. Sometimes there is a blank index.html file (to prevent directory browsing) and sometimes a language file. We will deal with these files when we look at our example below.

The Requirement

The website I’ve been developing uses JomSocial & the administrator wanted to be alerted every time a member updated their JomSocial profile. Joomla! plugins are well suited to satisfy this type of requirement. The key is to find if JomSocial has any event or hook that we can call when the user saves their profile update.

Fortunately JomSocial has such an event: onAfterProfileUpdate($userId, $saveSuccess). So, the next stage is to figure out how to write a plugin that calls this code.

When you install JomSocial it creates a ‘community’ sub-folder with the plugin directory. There you will find several plugins, depending on the JomSocial feature set you have included.

So that’s the obvious place for us to include our plugin. I named my plugin notify, so it will sit under joomla/plugins/community/notify. It consists of 5 files: notify.php, notify.xml. index.html & then two language files under joomla/plugins/community/language/en-GB called en-GB.plg_community_notify.ini &  en-GB.plg_community_notify.sys.ini. These two ini files are not mandatory, but they provide a way of translating message strings if required.

So, to the two essential files: notify.php & notify.xml. The xml file is the easiest to create, since it follows a standard convention. It’s a file that tells Joomla! how & where to install your plugin & what files are dependencies of the plugin. Here’s the code within notify.xml:

<?xml version="1.0"?>
<extension version="2.5" type="plugin" group="community" method="upgrade">
 <name>Notify Administrator Jom Social Community Plugin</name>
 <version>1.1.1</version>
 <creationDate>Sept 2012</creationDate>
 <author>Eddie May</author>
 <authorEmail>ecm@freshwebservices.com</authorEmail>
 <authorUrl>http://www.freshwebservices.com/</authorUrl>
 <copyright>@freshwebservices</copyright>
 <license>GNU GPL v2.0</license>
 <description>This plugin alerts admin each time a user updates their JomSocial profile.</description>
 <files> 
 <filename plugin="notify">notify.php</filename>
 <filename>index.html</filename>
 <folder>language</folder>
 </files>
 <config>
 <fields name="params">
 <fieldset name="basic">
 <field
 name="admin_id"
 type="text"
 default=""
 label="PLG_COMMUNITY_NOTIFY_ADMIN_ID_LABEL"
 description="PLG_COMMUNITY_NOTIFY_ADMIN_ID_DESC"
 required="true"
 filter="string"
 size="50" />
 <field
 name="email_subject"
 type="text"
 default=""
 label="PLG_COMMUNITY_NOTIFY_EMAIL_SUBJECT_LABEL"
 description="PLG_COMMUNITY_NOTIFY_EMAIL_SUBJECT_DESC"
 required="true"
 filter="string"
 size="50" />
<field
 name="email_text"
 type="text"
 default=""
 label="PLG_COMMUNITY_NOTIFY_ADMIN_EMAIL_TEXT_LABEL"
 description="PLG_COMMUNITY_NOTIFY_ADMIN_EMAIL_TEXT_DESC"
 required="true"
 filter="string"
 size="50" />
 </fieldset>
 </fields>
 </config>
</extension>

Essentially this file tells Joomla! and the administrator what it is, who wrote it, what it does, what dependencies the plugin has (under the files bit) and that it has three fields that the administrator must complete: admin_id, email_subject, email_text. This is data that will be passed to the plugin to use when it executes.

You’ll notice that the convention is that required files have the same naming convention: notify in this example. They sit under a folder called notify, & there is notify.xml & notify.php. Let’s look at notify.php.

<?php
/**
 * This class notifies named site administrator who/when a profile is updated
 * 
 * @author Eddie May
 * @date Sept 20212
 */
// no direct access
defined('_JEXEC') or die;
jimport( 'joomla.plugin.plugin' );
jimport('joomla.mail.helper');
class plgCommunitynotify extends JPlugin{
 function onAfterProfileUpdate($userId, $saveSuccess){
 if(empty($saveSuccess)){
 return;
 }

 $adminID = $this->params->get('admin_id');
 $subject = $this->params->get('email_subject');
 $emailText = $this->params->get('email_text');

 if ($adminID == null || $adminID == '')
 {
 throw new Exception(JText::_('No Admin User ID - cannot send email notification without it'));
 }

 $user = JFactory::getUser($adminID);
 $userEmail = $user->get('email');
 $fromName = $user->get('username');
 $updatedUser = JFactory::getUser($userId);
 $updatedName = $updatedUser->get('username');
 $body = 'Hi, the following user ' . $updatedName .' with user id of ' . $userId .' ';
 $body .= 'has just updated their profile. ';
 $body .= 'Please review and update their public fields accordingly. ';
 $body .= ' '. $emailText . ' ';
 JFactory::getMailer()->sendMail($userEmail, $fromName, $userEmail, $subject, $body);
 return true; 
 }
}//eof class

So, we’ll examine this class is a little more detail. We start with a non-mandatory comment section, telling any future developer about the file. Then we have the  security call: defined(‘_JEXEC’) or die; This prevents the file from being called outside of the Joomla! application.

The next lines import dependencies the class requires. As I want to email the administrator, I import the Joomla! mailer functionality. Then I declare or create the class itself: class plgCommunitynotify extends JPlugin{ Note the naming convention – plg+folder+class name.

Then we’re into the functionality of the class. Essentially we build a function that calls the JomSocial event onAfterProfileUpdate($userId, $saveSuccess) which gives us two variables that we can work with. The id of the user who is updating their profile & a boolean to tell us if the update was successful.

So, we do a couple of checks – if $saveSuccess is false, it means the update failed & we don’t need to do anything else. So we exit.

If all is well, we then retrieve the information that the administrator entered when s/he installed the plugin

$adminID = $this->params->get('admin_id');
$subject = $this->params->get('email_subject');
$emailText = $this->params->get('email_text');

We bailout if the admin_id is missing: if ($adminID == null || $adminID == ”). If the information is there, we can use that later to get the administrator user.

In the next couple of lines we get the admin user, their email, etc:

$user = JFactory::getUser($adminID);
$userEmail = $user->get('email');
$fromName = $user->get('username');

& also the details of the JomSocial user:

$updatedUser = JFactory::getUser($userId);
$updatedName = $updatedUser->get('username');

Then we can construct the email that will be sent to the administrator:

$body = 'Hi, the following user ' . $updatedName .' with user id of ' . $userId .' ';
 $body .= 'has just updated their profile. ';
 $body .= 'Please review and update their public fields accordingly. ';
 $body .= ' '. $emailText . ' ';

Then we use the standard Joomla! mailer functionality to send the email:

JFactory::getMailer()->sendMail($userEmail, $fromName, $userEmail, $subject, $body);

Finally we exit our class by calling return true;

To deploy your plugin you just zip your notify folder up & install it using the Joomla! installer. You then need to publish the plugin within the Joomla! backend.

Conclusion

The key to writing such a plugin is to find the correct system or component event to call or hook into. Its worth looking at component APIs before you buy if you think that you may need to extend them with a plugin. A quick email to the component developer is a good tip – if s/he takes an age to respond, then it might be a signal that their support is not so good. If there’s no published API, that’s also a cause for concern – good software has good documentation!

Joomla Upgrade Issue

Today I upgraded a Joomla website from 2.5.4 to 2.5.6 using the Updater. However, when I went to the home page the featured article was not displaying. For some reason, the upgrade had apparently (although I could be wrong on this one) set the ‘Finish Publishing’ date to ‘0000-00-00 00:00:00’.

Clearing this setting resulted in the featured article displaying on the home page. Quite why this happened I’m not sure, since the date setting ‘0000-00-00 00:00:00’ basically means never stop publishing. However, setting it to blank and saving had the desired effect.

SEO vs AdWords – one story

I run what I consider to be a successful  search engine optimistion (seo) campaign for a local client, with lots of page one result in Google for his local keywords, and improving conversion rates to boot. However, I was recently asked by the client to compare my service with the equivalent cost of just running an AdWords PPC campaign (I think a Yell salesman had been bending his ear).

Well, the first thing I did was try to calculate the cost of getting 1,000 unique visitors a month via AdWords. What soon became clear was the following:

  1. Just using local search terms would not generate the same levels of traffic to this site
  2. Using more general or “national” terms would lead to an alarming rise in costs and poorer quality traffic.
  3. This traffic was less likely to convert since it was not “local” or “targeted” traffic and would therefore be less interested in my client’s locally based services.

So, the result of moving over entirely to AdWords would in all likelihood have been my client paying a lot more for a lot less. I calculated that the client would be spending about 4 times more on AdWords to generate the same amount of, but poorer quality, traffic.

There’s another angle to this story too. Inexperienced people think of AdWords as being simple and easy. However, running a successful, efficient AdWords campaign is a complex and time consuming process if you’re going to do it properly. This is because bids constantly change, adverts need frequent tweaks and equally frequent A/B testing – its almost a full time job.

So, while AdWords may seem like better value for money, it may prove to be the opposite.