Magento collection filtered

Here’s a simple setup for a product collection with the following filters used in most of my modules, ideal for things like product feeds etc.

It has the following filters

– By Store
– Must have price > 0
– Must cost > 0
– Only simple products
– Visible either Catalog,Search or Both

$visibility = array(
    Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
    Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_CATALOG,
    Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH
);

$storeId = 1;
$collection = Mage::getResourceModel('catalog/product_collection');
$collection->getEntity()->setStoreId($storeId);

// Reset the select.
$collection->getSelect()->reset();

// Update table name.
$reflectionMethod = new ReflectionMethod($collection, '_initSelect');
$reflectionMethod->setAccessible(true);
$reflectionMethod->invoke($collection);

$collection
 ->addAttributeToSelect('sku')
 ->addAttributeToSelect('name')
 ->addAttributeToSelect('description')
 ->addAttributeToSelect('small_image')
 ->addAttributeToSelect('manufacturer')
 ->addAttributeToSelect('msrp')
 ->addAttributeToFilter('visibility', $visibility)
 ->addAttributeToFilter('status', '1')
 ->addAttributeToFilter('type_id', array('eq' => 'simple'))
 ->addFinalPrice()
 ->setPageSize(900000)
 ->setCurPage(1);

Full Stack Frontend catchup & workflow

I have come to the realisation recently I’m more of a full stack developer. Taking some time recently to run through the recent developments in frontend if you haven’t explored this area for a while you will find it’s moved on quite a bit.

What you need to know:

Get familiar with npm and node-js because you will be using them a lot, of course it isn’t the only package manager out there and more for frontend dev’s is bower the debate is still out on which is best. If you want a good explanation of both see this Stackoverflow answer what’s the difference between node-js and bower? you can of course use the tools together.

Just like composer for php and other languages that now user composer type tools these help you manage your dependencies.

Along with these you can use further bundled packages that in their own way try to speed up development e.g INIT and Yeoman. INIT bundles together HTML5 Boilerplate, Bower and Grunt and some other things to give you a head start there are many others out there. Yeoman sort of serves as library manager for installing these, all of the above act like a scaffolding for a new project.

If you’ve barely touched Grunt , you would be better starting off writing your own Grunt config and learning about how underlying tools work you would be surprised how easy it is.

There’s also Gulp which is very similar to Grunt it’s good to give both a try as different agencies may have a preference. What they allow you to do is pull off some impressive automation into your development for example maintaining a /build folder structure from the same source dir and having different process’s, files, settings etc for each e.g a testing scenario vs a live build. Some common tasks like css/js compression, whitespace removal, concatenating a bunch of files together basically giving you a squeaky clean page structure which can be run every time you make changes to your project through a one line command.

But the really clever stuff is automating things like changing your URL’s to a CDN , compressing images, minifying html etc.. Roll in automated testing, compiling sass, less, copying files, compiling html and even run other command line tools.

There’s still some lively debate about which is best, but they both have good support and achieve the same outcome only currently gulp has some performance advantages that I think only apply to large projects, so as always it may be a decision based on the project.

What else is current:

JS

Most dev’s will have worked on some project or another that used Angular, Backbone or some other MVC based framework I gave Agility.js a go last year, Angular is definitely looking pretty strong and backed by Google it’s likely to grow further it’s pretty popular in job listings as well.

ECMAScript was released this month (June 17th 2015) what it brings to JS is classes and a ton of other features see http://es6-features.org/ or there’s a good overview of the main changes with some examples here or view the full spec you don’t have to wait till it’s safe to use it, there are many compilers (Babeljs) that offer backward compatibility allowing you to use it today.

CSS/HTML

There’s still plenty of frameworks popping up with Bootstrap, Foundation, Semantic UI, Pure, Inuit, UI Kit all serve different needs some more basic than others, my framework of choice for frontend websites is Foundation as the name suggests it feels solid and seems to offer just the right balance of components but not feeling too bloated.

By now your very familiar with transitions, drop-shadows and all that CSS3 brought, CSS4 is on the horizon it’s still a working draft so it’s not worth spending more than a glance on what it might have to offer at this stage as you would still have to polyfill some of CSS3’s features for older browsers.

Mostly pain free features that you could be using:

  • SVG with IE8 Polyfill (npm install grunt-badass –save-dev)
  • vh/vw useful for fullscreen layouts (https://github.com/saabi/vminpoly)
  • Semantic markup (that’s a given by now)
  • HTML5 form (mostly ok IE9 lacks support without polyfill)

Here’s a set up for a small mainly static project I recently developed:

Technology Stack

  • Nginx
  • PHP5-FPM
  • Rackspace Load Balancer
  • Lsyncd
  • MySQL
  • Cloudflare
  • Modernizr
  • Angular JS
  • Foundation 5
  • JQuery
  • Typekit

Folder Structure

  • /project/app
  • /project/app/api
  • /project/app/assets
  • /project/app/assets/css
  • /project/app/assets/fonts
  • /project/app/assets/img
  • /project/app/assets/js
  • /project/app/views
  • /project/app/views/partials
  • /project/src
  • /project/src/js
  • /project/src/img
  • /project/src/sass

Development Tools

  • Phpstorm
  • Browserstack
  • New Relic
  • Blitz.io
  • Virtual Box
  • Ampps
  • LiveReload

gitignore

node_modules
.npm-debug.log
tmp
bower_components
.sass-cache

Grunt build tasks

grunt.registerTask('build', ['clean', 'jshint', 'sass', 'concat', 'cssmin', 'copy','uglify','htmlmin', 'cdnify', 'svgmin'])
grunt.registerTask('test', ['jshint', 'sass', 'concat', 'copy','uglify']

The benefit or running all this stuff can be seen by putting your site through Pagespeed Insights it can become an obsession to get the highest score possible but at the end the speed is worth it.

There are some caveats:

  • IE8/9 can only read a maximum of around 280kb in file size or 4096 rules so putting everything into one file might not be possible at least that’s true for CSS files
  • If your using concat and pre-minifed versions of your JS dependencies make sure to use the separator option to prevent errors
    separator: ';\n',
  • Crushing everything into one file might not be the best for your project, as if everything is efficiently structured, you can utilise the browsers connections better by splitting them into a few files e.g app.js, index.js etc.. furthermore you can use things like https://github.com/ocombe/ocLazyLoad or RequireJS to load files as you need them. But ideally if you can achieve a small file size and one file that’s the goal
  • Angular JS does not support IE8
  • SVG not supported in IE8
  • Using the html5mode in Angular did not work for me in iOS safari and other browser versions when loading a subpage.
    $locationProvider.html5Mode(true);

 

Calculating selling price from the cost price

Recently in my price matching system I had to calculate the selling price of a product in a way that also calculates a transaction fee percentage on the final selling price without knowing the selling price, and without knowing what the transaction fee is until you’ve calculated the total price. Had fun chasing your tail yet?

You can do this in Excel with circular references but you will get circular reference warnings and the only way is to allow these warnings and an iteration limit of 100.

Stepping aside from the magic of excel I was able to work out the exact formula for this and below is a calculation for anyone else struggling to work out how to do this.

$cp = 9.56; // cost price
$d = 2.50; // delivery fee
$m = 0.15; // margin 15%
$tf = 0.019; // transaction fee

$sellingPrice = (($cp+$d)*(1+($m)))/(1-((1/6)+$tf)*(1+($m)));

$costPrice = $x / (1+(.15));

echo $sellingPrice."\n";
echo $costPrice;

The end for website designers and developers?

The end for web design, not quite

ai

Update: Again I was notified by a friend “have you checked this out” a new product PageCloud read more at the end of this post

Recently a friend notified me to thegrid.io’s new AI product “a website that designs itself”, in a bid to alert me I might one day be out of a job, instead of template’s there are filters and they claim it can automatically work out the content and how best to display it no matter what you throw at it. More interesting to me was the ecommerce offering although not yet available till late 2015 claiming to “just work” you won’t need to know or do anything in relation to hosting, SSL certificates etc.. past this there aren’t any details on this just yet.

Now I have to admit I haven’t used their product or tested them other than the markup produced for any of this article as there’s no free trial. But I did look at the source code for thegrid.io’s website, the website itself claims to be built by this and anyone could in theory have the same layout filters.

Testing

I put it through Google’s Pagespeed tool to analyse some of the markup produced that’s mostly a pass on this, from the user experience POV and it would be a bit picky to complain about some of the speed optimisations as almost any site I’ve put in here Google claims can be optimised further.

Schema
what about Schema?, on this front the site doesn’t deliver as there was no Schema data where there could have been, precision coding from a computer? clearly not.

Images
All the images seem to be background images, I couldn’t find one ALT tag that’s a few points off your SEO rank and some keywords lost.

General markup review
I counted 23 H1 tags that can’t be a good thing, isn’t the AI smart enough to know there shouldn’t be so many? Matt Cutts says if your page is full of H1’s  it’s not a great idea it’s fine if your using this in sections but many are so close together I can’t see this as a positive ranking factor other elements are like this too.

Most of the page is chock full of inline CSS and JS, ok this is perfectly valid but I can’t help thinking this could have been organised a bit better and is one of the things flagged on the Google Pagespeed test

“Compacting HTML code, including any inline JavaScript and CSS contained in it, can save many bytes of data and speed up download and parse times.”

This is just the tip of the iceberg an in-depth review might reveal more problems.

Replacing creativity

Next is the question of wether you can really replace creativity with computers? Time and time again i’ve tried it myself a client chooses a template they like and try to punch and squash it into their brand and it rarely ever works, by the time you’ve spent the countless hours amending it, you may as well have designed it from scratch. At the same time providing a much richer brand experience.

I get the feeling this is how the AI would work, it can work out what a YouTube link is or someone’s face but can it decide properly on how to use it and where to place it so your really back to changing your brand and content to fit the template.

How many layout filters are available? There has to be a limit, your site is going to look exactly like someone else’s. One of the great things about web design is the freedom to create a unique feel and look for your brand not some generic repeated computer generated layouts. At least with a template you can make it your own.

I believe that websites are not getting any simpler to make.

people are now starting to realise you can’t spend $8 a month and expect your website to work for  you, investing much more in your online business is the way forward.

It’s actually getting harder to get the balance right between functional design, content strategy, creative and code structure and for that you need experienced professionals.

In the end I think this product is aimed at amateurs and business people who really don’t understand the online space, thus giving them a quick solution.

In comes PageCloud

Yep it’s the end again, since my post thegrid.io has failed to launch and a new rival has appeared with a debut video at TechCrunch’s Disruptors seminar with a tagline “the web is about to change forever”.

Now as a developer it’s hard not to scoff at the words some of these companies use, from the video which is all there is at the moment PageCloud does look a bit different in it’s approach but these tools already exist. I can already copy and paste from photoshop into gmail for example so calling the copy/paste feature a world first seems a bit over the top.

That said it looks like a great product but in terms of changing the web industry for professionals that simply isn’t going to happen anytime soon.

Magento image not loading when using cron, only getting placeholder

I have a module which sends out an email via Mailchimp with a scheduled task for Magento’s internal cron and in doing so looks up the product image.

Mage::helper('catalog/image')->init($product, 'small_image')->resize(150));

The problem being only the placeholder was being returned when the cron job was executed. To confuse matters when I ran the script in testing from the browser it would return the correct image.

This is a nasty problem at first I thought this might be the answer Sangay details there that if your config does not specify a M,GB etc that it will return the wrong calculation, but then why the different results depending on how I ran the script?

To get to the bottom of this I ran what is being run in Mage_Catalog_Helper_Image

$model = Mage::getModel('catalog/product_image');
$model->setBaseFile($this->product->getData('small_image'))->resize(600,600);
$url = $model->saveFile()->getUrl();

PHP Fatal error:  Uncaught exception 'Varien_Exception' with message 'Memory limit has been reached.' in /lib/Varien/Image/Adapter/Gd2.php:58

Returning this shows the actual exception I did not get any Exception in the Mage exception log, and not in the host’s error log. On my part a server configuration oversight as the CLI php.ini did not have the error log set up. In any case that’s the error.

Because the value returned from _convertToByte function in Mage_Catalog_Helper_Image is -1 as the CLI php.ini sets the memory_limit to -1 (unlimited).

This could be an oversight by Magento or a design feature not to run this task on unlimited memory, personally I think it’s a bug. I modified the function to the following to set a 2G limit in this case.

   if (stripos($memoryValue, 'M') !== false) {
            return (int)$memoryValue * 1024 * 1024;
        }
        elseif (stripos($memoryValue, 'KB') !== false) {
            return (int)$memoryValue * 1024;
        }
        elseif ($memoryValue == "-1") {
            return '2147483648'; //2G
        }

Clearing expired magento shopping/sales rules programatically

Here’s a nice cleanup script I use to clear out expired shopping cart rules and catalog price rules and apply those changes. It’s one of those annoying things that you would think there would be a button for.

I’ve set the expiry check to the previous day and date comparison using strtotime you might want to adjust that, and use php’s dateTime library for a more thorough comparison as this relies on your server’s timezone configuration matching magento’s and being set up correctly.

If your wondering what setState does that’s to remove the message in magento admin about un-applied rules.

require '../app/Mage.php';

umask(0);
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);


$model = Mage::getModel('catalogrule/rule')
    ->getCollection();


foreach($model as $rule) {
    $today = strtotime('Yesterday');

    if(strtotime($rule->getData('to_date')) < $today) {
      $rule->delete();
    }

}

$model = Mage::getModel('salesrule/rule')
    ->getCollection();


foreach($model as $rule) {
    $today = strtotime('Yesterday');

    if(strtotime($rule->getData('to_date')) < $today) {
       $rule->delete();
    }

}

try {
    Mage::getModel('catalogrule/rule')->applyAll();
    Mage::getModel('catalogrule/flag')->loadSelf()
        ->setState(0)
        ->save();
    Mage::app()->removeCache('catalog_rules_dirty');
    echo Mage::helper('catalogrule')->__('The rules have been applied.');
} catch (Exception $e) {
    echo Mage::helper('catalogrule')->__('Unable to apply rules.');
    print_r($e);
}

Add sort by stock availability

How to add sort by stock availability to product toolbar

Edit

/app/code/core/Mage/Catalog/Model/Resource/Product/Collection.php 

Add

public function sortByStock($dir){ 
$table = $this->getTable('cataloginventory/stock_item'); $entity_code_id = Mage::getModel('review/review')->getEntityIdByCode(Mage_Rating_Model_Rating::ENTITY_PRODUCT_CODE); $cond = $this->getConnection()->quoteInto('t2.product_id = e.entity_id'); $this->getSelect()->joinLeft(array('t2'=>$table), $cond,array('qty'))->order("qty $dir"); }

Edit

/app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php 

Add

if($this->getCurrentOrder() == 'stock'){ $this->_collection->sortByStock($this->getCurrentDirection()); } else

Edit

/app/code/core/Mage/Catalog/Model/Config.php
public function getAttributeUsedForSortByArray() { $options = array( 'position' => Mage::helper('catalog')->__('Position'), 'stock' => Mage::helper('catalog')->__('Availability') ); foreach ($this->getAttributesUsedForSortBy() as $attribute) { /* @var $attribute Mage_Eav_Model_Entity_Attribute_Abstract */ $options[$attribute->getAttributeCode()] = $attribute->getStoreLabel(); } return $options; }

Fixing Contact form 7 v4.1 for campaign monitor plugin

The campaign monitor plugin for Contact Form 7 has not been updated in over 2 years, it’s frustrating as it works really well in conjunction with CF7.

I delved into the code and fixed up the issues that prevented it working properly.

In this file

/wp-content/plugins/contact-form-7-campaignmonitor-addon/cf7-campaignmonitor.php

Replace

add_action( 'wpcf7_admin_before_subsubsub', 'add_cm_meta' );

With

add_action( 'wpcf7_add_meta_boxes', 'add_cm_meta' );

Replace

$cf7_cm = get_option( 'cf7_cm_'.$obj->id );

With

$cf7_cm = get_option( 'cf7_cm_'.$obj->id );
$submission = WPCF7_Submission::get_instance();

Replace all instances of $obj->posted_data with $submission->get_posted_data()

Replace

$replaced = apply_filters( 'wpcf7_mail_tag_replaced', $replaced, $submitted );

With

$replaced = apply_filters( 'wpcf7_mail_tag_replaced', $replaced, $submitted, $html );

Replace

if ( $special = apply_filters( 'wpcf7_special_mail_tags', '', $matches[1] ) )

With

if ( $special = apply_filters( 'wpcf7_special_mail_tags', '', $matches[1], $html ) )

Replace

$url = wpcf7_admin_url( array( 'page' => 'wpcf7' ) );

With

$url = admin_url( array( 'page' => 'wpcf7' ) );

You will then have a fully compatible campaign monitor plugin.

Here is the full file:

<?php
/*
Plugin Name: Contact Form 7 - Campaign Monitor Addon
Plugin URI: http://www.bettigole.us/published-work/wordpress-contributions/campaign-monitor-addon-for-contact-form-7/
Description: Add the power of CampaignMonitor to Contact Form 7
Author: Joshua Bettigole
Author URI: http://www.bettigole.us
Version: 1.06
*/

/* Copyright 2010 Joshua Bettigole (email: joshua at bettigole.us)

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

define( 'WPCF7_CM_VERSION', '1.06' );

if ( ! defined( 'WPCF7_CM_PLUGIN_BASENAME' ) )
define( 'WPCF7_CM_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

add_action( 'wpcf7_after_save', 'wpcf7_cm_save_campaignmonitor' );

function wpcf7_cm_save_campaignmonitor($args)
{
update_option( 'cf7_cm_'.$args->id, $_POST['wpcf7-campaignmonitor'] );
}

add_action( 'wpcf7_add_meta_boxes', 'add_cm_meta' );

function add_cm_meta (){
if ( wpcf7_admin_has_edit_cap() ) {
add_meta_box( 'cf7cmdiv', __( 'Campaign Monitor', 'wpcf7' ),
'wpcf7_cm_add_campaignmonitor', 'cfseven', 'cf7_cm', 'core',
array(
'id' => 'wpcf7-cf7',
'name' => 'cf7_cm',
'use' => __( 'Use Campaign Monitor', 'wpcf7' ) ) );
}
}

add_action( 'wpcf7_admin_after_mail_2', 'show_cm_metabox' );

function show_cm_metabox($cf){
do_meta_boxes( 'cfseven', 'cf7_cm', $cf );
}

function wpcf7_cm_add_campaignmonitor($args)
{
$cf7_cm_defaults = array();
$cf7_cm = get_option( 'cf7_cm_'.$args->id, $cf7_cm_defaults );
?>

<div class="mail-field">
<input type="checkbox" id="wpcf7-campaignmonitor-active" name="wpcf7-campaignmonitor[active]" value="1"<?php echo ( $cf7_cm['active']==1 ) ? ' checked="checked"' : ''; ?> />
<label for="wpcf7-campaignmonitor-active"><?php echo esc_html( __( 'Use CampaignMonitor', 'wpcf7' ) ); ?></label>
<div class="pseudo-hr"></div>
</div>

<br class="clear" />

<div class="mail-fields">
<div class="half-left">
<div class="mail-field">
<label for="wpcf7-campaignmonitor-email"><?php echo esc_html( __( 'Subscriber Email:', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-email" name="wpcf7-campaignmonitor[email]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['email'] ); ?>" />
</div>

<div class="mail-field">
<label for="wpcf7-campaignmonitor-name"><?php echo esc_html( __( 'Subscriber Full Name:', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-name" name="wpcf7-campaignmonitor[name]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['name'] ); ?>" />
</div>

<div class="mail-field">
<label for="wpcf7-campaignmonitor-accept"><?php echo esc_html( __( 'Required Acceptance Field:', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-accept" name="wpcf7-campaignmonitor[accept]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['accept'] ); ?>" />
</div>

<div class="mail-field"><br/>
<input type="checkbox" id="wpcf7-campaignmonitor-cf-active" name="wpcf7-campaignmonitor[cfactive]" value="1"<?php echo ( $cf7_cm['cfactive'] ) ? ' checked="checked"' : ''; ?> />
<label for="wpcf7-campaignmonitor-cfactive"><?php echo esc_html( __( 'Use Custom Fields', 'wpcf7' ) ); ?></label><br/><br/>
</div>
</div>

<div class="half-right">
<div class="mail-field">
<label for="wpcf7-campaignmonitor-api"><?php echo esc_html( __( 'API Key:', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-api" name="wpcf7-campaignmonitor[api]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['api'] ); ?>" />
</div>

<div class="mail-field">
<label for="wpcf7-campaignmonitor-client"><?php echo esc_html( __( 'Client ID:', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-client" name="wpcf7-campaignmonitor[client]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['client'] ); ?>" />
</div>

<div class="mail-field">
<label for="wpcf7-campaignmonitor-list"><?php echo esc_html( __( 'List ID:', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-list" name="wpcf7-campaignmonitor[list]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['list'] ); ?>" />
</div>

<div class="mail-field"><br/>
<input type="checkbox" id="wpcf7-campaignmonitor-resubscribeoption" name="wpcf7-campaignmonitor[resubscribeoption]" value="1"<?php echo ( $cf7_cm['resubscribeoption'] ) ? ' checked="checked"' : ''; ?> />
<label for="wpcf7-campaignmonitor-resubscribeoption"><?php echo esc_html( __( 'Allow Users to Resubscribe after being Deleted or Unsubscribed? (checked = true)', 'wpcf7' ) ); ?></label><br/><br/>
</div>
</div>

<br class="clear" />

<div class="campaignmonitor-custom-fields">
<?php for($i=1;$i<=20;$i++){ ?>
<div class="half-left">
<div class="mail-field">
<label for="wpcf7-campaignmonitor-CustomKey<?php echo $i; ?>"><?php echo esc_html( __( 'CustomKey'.$i.':', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-CustomKey<?php echo $i; ?>" name="wpcf7-campaignmonitor[CustomKey<?php echo $i; ?>]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['CustomKey'.$i] ); ?>" />
</div>
</div>
<div class="half-left">
<div class="mail-field">
<label for="wpcf7-campaignmonitor-CustomValue<?php echo $i; ?>"><?php echo esc_html( __( 'CustomValue'.$i.':', 'wpcf7' ) ); ?></label><br />
<input type="text" id="wpcf7-campaignmonitor-CustomValue<?php echo $i; ?>" name="wpcf7-campaignmonitor[CustomValue<?php echo $i; ?>]" class="wide" size="70" value="<?php echo esc_attr( $cf7_cm['CustomValue'.$i] ); ?>" />
</div>
</div>
<br class="clear" />
<?php } ?>

</div>
</div>

<?php

}

add_action( 'admin_print_scripts', 'wpcf7_cm_admin_enqueue_scripts' );

function wpcf7_cm_admin_enqueue_scripts ()
{
global $plugin_page;

if ( ! isset( $plugin_page ) || 'wpcf7' != $plugin_page )
return;

wp_enqueue_script( 'wpcf7-cm-admin', wpcf7_cm_plugin_url( 'scripts.js' ),
array( 'jquery', 'wpcf7-admin' ), WPCF7_CM_VERSION, true );
}

add_action( 'wpcf7_before_send_mail', 'wpcf7_cm_subscribe' );

function wpcf7_cm_subscribe($obj)
{
$submission = WPCF7_Submission::get_instance();

$cf7_cm = get_option( 'cf7_cm_'.$obj->id );
if( $cf7_cm )
{
$subscribe = false;

$regex = '/\[\s*([a-zA-Z_][0-9a-zA-Z:._-]*)\s*\]/';
$callback = array( &$obj, 'cf7_cm_callback' );

$email = cf7_cm_tag_replace( $regex, $cf7_cm['email'], $submission->get_posted_data() );
$name = cf7_cm_tag_replace( $regex, $cf7_cm['name'], $submission->get_posted_data() );

$lists = cf7_cm_tag_replace( $regex, $cf7_cm['list'], $submission->get_posted_data() );
$listarr = explode(',',$lists);

if( isset($cf7_cm['accept']) && strlen($cf7_cm['accept']) != 0 )
{
$accept = cf7_cm_tag_replace( $regex, $cf7_cm['accept'], $submission->get_posted_data() );
if($accept != $cf7_cm['accept'])
{
if(strlen($accept) > 0)
$subscribe = true;
}
}
else
{
$subscribe = true;
}

for($i=1;$i<=20;$i++){

if( isset($cf7_cm['CustomKey'.$i]) && isset($cf7_cm['CustomValue'.$i]) && strlen(trim($cf7_cm['CustomValue'.$i])) != 0 )
{
$CustomFields[] = array('Key'=>trim($cf7_cm['CustomKey'.$i]), 'Value'=>cf7_cm_tag_replace( $regex, trim($cf7_cm['CustomValue'.$i]), $submission->get_posted_data() ) );
}

}

if( isset($cf7_cm['resubscribeoption']) && strlen($cf7_cm['resubscribeoption']) != 0 )
{
$ResubscribeOption = true;
}
else
{
$ResubscribeOption = false;
}

if($subscribe && $email != $cf7_cm['email'])
{

require_once(dirname(__FILE__).DIRECTORY_SEPARATOR.'csrest_subscribers.php');

$wrap = new CF7CM_CS_REST_Subscribers( trim($listarr[0]), $cf7_cm['api'] );
foreach($listarr as $listid)
{
$wrap->set_list_id(trim($listid));
$wrap->add(array(
'EmailAddress' => $email,
'Name' => $name,
'CustomFields' => $CustomFields,
'Resubscribe' => $ResubscribeOption
));
}

}

}
}

function cf7_cm_tag_replace( $pattern, $subject, $posted_data, $html = false ) {
if( preg_match($pattern,$subject,$matches) > 0)
{

if ( isset( $posted_data[$matches[1]] ) ) {
$submitted = $posted_data[$matches[1]];

if ( is_array( $submitted ) )
$replaced = join( ', ', $submitted );
else
$replaced = $submitted;

if ( $html ) {
$replaced = strip_tags( $replaced );
$replaced = wptexturize( $replaced );
}

$replaced = apply_filters( 'wpcf7_mail_tag_replaced', $replaced, $submitted, $html );

return stripslashes( $replaced );
}

if ( $special = apply_filters( 'wpcf7_special_mail_tags', '', $matches[1],$html ) )

return $special;

return $matches[0];
}
return $subject;
}

function wpcf7_cm_plugin_url( $path = '' ) {
return plugins_url( $path, WPCF7_CM_PLUGIN_BASENAME );
}

add_filter( 'plugin_action_links', 'wpcf7_cm_plugin_action_links', 10, 2 );

function wpcf7_cm_plugin_action_links( $links, $file ) {

if ( $file != plugin_basename( __FILE__ ) )
return $links;

$url = admin_url( array( 'page' => 'wpcf7' ) );

$settings_link = '<a href="' . esc_attr( $url ) . '">' . esc_html( __( 'Settings', 'wpcf7' ) ) . '</a>';

array_unshift( $links, $settings_link );

return $links;

}

The essential go-live checklist for any website

You’ve finally got to the stage where you site can go live, design,development all done and now it’s just a matter of flicking the switch. Research has shown in other industries such as medical, aviation and others that a checklist can prevent errors. The digital world isn’t quite so keen to have a consistent approach due to it’s very nature but perhaps we should.

Need a website review or just want a second opinion?, please get in touch and for a fixed fee I can provide a detailed report on your site.

Here’s my definitive checklist broken down into areas

Cross browser appearance ✓

Check for broken layouts on IE, Firefox and others

10-helpful-resources-for-cross-browser-testing

How to check:

Browsershots
Virtualbox
IE Tester
IE Developer Toolbar
Mac in the Cloud

Don’t always rely on developer toolbars or testing apps, looking at the real thing is the best way to ensure your site is glitch free.

Responsive design ✓

If your site is responsive test across as many devices as possible, ideally the real devices themselves, depending on the framework your using or your media query implementation using preview tools that try to emulate the devices may not provide an entirely accurate preview.

Responsive-Web-Design-stats

How to check:

Iphone Tester – Quick iphone preview
Ipad Peek – Quick ipad preview
Perfecto Mobile – Cross device testing for a fee
Browser Stack – Run official mobile emulators, alternatively download them yourself
The Responsinator – Preview a multitude of devices
Your browser – Firefox, Chrome and even IE11 now have built in developer tools

SEO ✓

schema

Check all page titles and meta descriptions, the most important part of the website, make sure the titles are different on every page, and relate to the content. The description is your selling window to the world so make it short and snappy.

How to check:

SEOMoz – Sign up to the 30 day trial if you don’t have an account and you can run a detailed analysis on every page
Google Webmaster Tools – Enable this to check for 404’s and any other issues
Google Structured testing tool – Check your schema markup. Haven’t done this? It’s easy enough to add last minute.

Content ✓

Check every page, and there’s only one real way to do it – proof reading, if it’s a large site assign a section to various people to help with the workload the more people that read through it the better.

That’s assuming the tone and feel of the text is consistent throughout, if it isn’t consider getting a copywriter to re-write your text.

Also check for broken links, there’s a number of automated tools to do this.

How to check:

Spellery – Online tool to spell check a page
Copify – Cheap and professional copywriting service
Integrity – Great affordable tool for Mac
Sleuth’s Link Checker – An oldie but a goodie PC based tool

 

Feedback from real people! ✓

solutions_emotional_trends

Most projects don’t have the budget or the time for this, you can instead farm out the link to as many people as you know. If you own a business send it out to everyone.

How to check:

Another option is to set out your own goals for example fill out the contact form and watch how people interact with the site in person. You will be surprised!

Usabilia – Set up real tests for real people

Developer Tests ✓

mysql-database-connection-utf-8

The developers play the most crucial role, there’s been many times where some small mistake turns into a major issue.

  • Data input/output in the correct formats
  • Data saved in the right format
  • Allowed for internationalisation?
  • Form submissions
  • Removed test files
  • Uploaded all required files
  • Dummy test of live URL with hosts file
  • File permissions set for uploading

How to check:

Not an easy answer where possible unit testing can achieve much of the above. There are some automated tools you could use to perform simple/advanced checks.

Selenium – Automated browsing for testing purposes
There’s various Firefox, Chrome plugins as well
Monitis offer this sort of testing too

Speed ✓

Often overlooked Google has long indicated that it considers this as a ranking factor how important we don’t know, but consider your visitors they don’t want to hang around either.

Get a decent hosting solution many small business’s often use poor hosting solutions from the likes of GoDaddy and 1and1 whilst they are cheap and cheerful backup is often not included and the service can be slow at times not to mention the poor customer support.

How to check:

Pingdom
Google Page speed
Dotcom Monitor

Run tests at different times, I cover some other aspects in one my Magento articles.

Load Testing ✓

Static-loading-test-in-progress

Email campaign or launching a product load testing is an essential part of going live. First get a decent monitoring package and then blast visitors to your site. That’s probably a crude approach but may be satisfactory depending on your needs you might be better with goal based load testing.

How to check:

New Relic
Monitis
Blazemeter
Jmeter

 

Peopleperhour PPH be prepared to pay for nothing – Dubious Developers

I recently put a job on peopleperhour as an hourly rate quite a decent hourly rate as I had no luck with a previous developer I sought to get a more experienced professional. After some hours of frustration on my part the problem was not resolved I subsequently raised a refund request.

Requesting a refund from Peopleperhour

The developer disputed the request and it went to a review with PPH , the developer had sneakly recorded our sessions which I thought worked in my favour as much of the time the developer spent Googling solutions for the problem in which my screen was still.

But if you check their terms and conditions the hourlie jobs are excluded from many of the safegaurds ie. poor quality of work or time deadlines so the buyer has no protection, basically your gambling your money away as the seller can claim they have done work and PPH do not consider the technical aspects of a project.

Section 7 of their Terms & Conditions

c) for Disputes concerning the quality of the work delivered: PPH will consider whether the Seller has met general quality standards as defined in Section 3.3. Assessments on quality i) down to perceived taste, or ii) requiring specialist technical or subject matter expertise, will not form part of the resolution decision;

So how do they then assess it? I’ve emailed them to ask.

No protection for Buyers

A developer can simply be sat there recording the screen clicking around edit a few variables and people per hour will consider it job done, work completed!

Thanks Peopleperhour for alerting me to this fact before putting my money in Escrow.

I’m a developer myself and I would never expect money for a poor job and definitely not in the case where I did not resolve the issue, but PPH and the developer in question have different values.

Getting your money back?

The only option left to get my money back is to claim a fraudulent transaction from my bank and from PayPal as the funds where taken without my consent.

Seems I’m not the only one

http://www.complaintsboard.com/complaints/peopleperhourcom-it-is-illegal-c627718.html


http://www.realscam.com/f10/please-careful-peopleperhour-com-2330/

Yet to hear a response from Peopleperhour.

Update:

Here’s my request to support

—————

Hi,

I posted a job on here and worked with two sellers, as you can see one of the sellers accepted my refund request because they where not able to help me with my issue.

Please can you justify why you think I should pay £80 when I have not received anything for my money.

Kind regards,
Joel

———

Peopleperhour response:

Hello Joel

Thank you for your message.

We are sorry for the inconvenience.

We found that your seller has raised dispute for the refund request, please rest assured our disputes team will soon check the workstream and resolve the issue. We suggest you to update the details of the issue on your workstream, so that our disputes team can know about the issue clearly.

Please let me know if I can help you with anything else.

Thanks

Sivakesh
PPH Customer Support

———

They decided in the sellers favour in the end, I’m now trying to claim under PayPal’s buyer protection, and then a final option is to claim from the credit card company, once I opened my PayPal dispute PPH immediately cancelled my account.

PPH final response was this:

Peopleperhour response:

From the WorkStream we can see that the job was agreed on an hourly rate basis. As defined in the PPH terms and conditions, for hourly rate jobs (unlike for fixed price jobs) the Seller is agreed to be paid for the time spent, not for deliverables produced. From the activity in the WorkStream there is sufficient evidence that time was spent on this job and that the number of hours invoiced is credible. As such, the seller will be allocated the sum in escrow.

——–

Update Two:

Finally I got all my money back through PayPal’s buyer protection thanks PayPal!

 

So I guess from my part it wasn’t clear that there is no protection the person the other end could have no developer experience yet they will be paid anyways and be paid in full.

Developers, Freelancers and Employers looking to hire freelance designers you should post jobs as a fixed priced job so you at least get some protection as their terms and conditions.

Had your own PPH nightmare get in touch?