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?

 

Using Rsync to backup an Ubuntu server to Synology NAS

This is a short guide on how to set up rsync on a schedule, to backup and create snapshots of any remote server that allows SSH access to your local Synology NAS. It assumes you have some knowledge of using the terminal and SSH.

1. Set up a user.

First I would recommend setting up a separate user in Synology as this allows you to enable things like speed limit controls for the user, but you could do this process under the root/admin user if you wanted.

2. Enable SFTP Access

Via the control panel File Services > FTP > SFTP > Enable SFTP service, then open up a terminal and try SSH into your NAS e.g ssh [email protected] or ssh [email protected], enter your password and you should be logged in, if you aren’t or nothing happens check you have port 22 open on your firewall and that it’s forwarded to your NAS.

3. Once logged in edit the passwd file

 vi /etc/passwd 

If your not familiar with VI check a guide here http://www.lagmonster.org/docs/vi.html or use your cursor to go down to the last line (which should be your newly added user) and enter “A” This will take you to the end of the line, you can then delete /sbin/nologin and replace with /bin/sh it should then look like this:
backup

 

When you’re finished editing press ESC and then type :x, press ENTER.

I would also recommend changing the home directory (/var/services… as pictured above) to a different folder that sits in the root e.g /volume1/web-backup and adding the new user full access to this folder. As I had problems using a folder inside /homes so from here on in I’m assuming you have changed it to a folder in the root e.g /volume1/web-backup.

Allow access to a different folder with:

 chown newuser:users /volume1/web-backup 

4. Login as your newly created user

 su - new_user 

If you get any of the following messages

su: can't chdir to home directory '/volume1/web-backup'
su: can't run /sbin/sh: No such file or directory

The first is a permissions error, make sure that you have chown’ed the relevant folder , second is that you forgot to add /bin/sh onto the user both are covered in step 3

5. Add the SSH key to the remote server

Create the key, accept the default file location and press enter when it prompts for a password

 ssh-keygen -t rsa 

Copy the key to the remote server

 cat ~/.ssh/id_rsa.pub | ssh [email protected] "mkdir -p ~/.ssh && cat >>  ~/.ssh/authorized_keys" 

Now try SSH into your remote server and it shouldn’t prompt for a password, if you get an error about permissions here’s how to set the correct permissions. .ssh directory permissions to be 700 (drwx——) and the public key (.pub file) to be 644 (-rw-r–r–). Your private key (id_rsa) should be 600 (-rw——-).

 chmod /volume1/web-backup/.ssh 700;chmod /volume1/web-backup/.ssh/id_rsa.pub 644;chmod /volume1/web-backup/.ssh/id_rsa 600 

6. Schedule the backup and backup structure

I created 3 folders inside my local backup folder e.g

web-backup/www
web-backup/mysql
web-backup/snapshots

First try a test rsync, don’t worry this is a dry run as in it won’t actually copy anything.

 rsync --delete --stats -zav --dry-run [email protected]:/var/www/ /volume1/web-backup/www 

This should output something like:

Number of files: 212521
Number of files transferred: 71
Total file size: 8981539430 bytes
Total transferred file size: 265780 bytes
Literal data: 0 bytes
Matched data: 0 bytes
File list size: 5047457
File list generation time: 0.001 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 171658
Total bytes received: 5300913

sent 171658 bytes  received 5300913 bytes  142144.70 bytes/sec
total size is 8981539430  speedup is 1641.19 (DRY RUN)

If you get any error like this:

 rsync: failed to set times on "/volume1/web-backup/www/.": Operation not permitted (1) 

It’s a permission problem check the folder your trying to write to the local folder on your NAS has the owner as the user your running rsync under in this article “newuser” is the owner of web-backup.

If all this runs smoothly you need need to create two files in the root of your web-backup folder.

web-backup/rsync.sh
web-backup/snapshot.sh

Upload these files via the admin GUI or however you wish with the following contents

rsync.sh

rsync --exclude /cache --delete --stats -zav [email protected]:/var/www/ /volume1/web-backup/www
rsync --delete --stats -zav [email protected]:/backups /volume1/web-backup/mysql

In my case I store my MySQL backups in a seperate location so there’s a second command to get those too.

snapshot.sh

 cd /volume1/web-backup/www; tar cvf /volume1/web-backup/snapshots/snapshot-$(date +%Y-%m-%d).tgz *
find /volume1/web-backup/snapshots -mtime +120 -type f -exec rm -f '{}' \;

This creates a .tar archive of the /www directory and puts it in snapshots. It also checks for archives older than 120 days and removes those. You could add another snapshot for your mysql data.

7. Add the scheduled task

Finally simply add the tasks in Tasks > Task Scheduler make sure they run under the user your created at the start of this tutorial.

task

Missing something from this article? Please let me know and I will add it to the article.

Magento Review Generator

Adding fake reviews to your site is in the long run a bad idea, it can not only damage your site’s reputation but it’s bad practice. That said I have developed a module that will seed Magento with reviews which could be based on a specific criteria, it does this by creating a unique combination of words to create a readable sentence.

Not only this but it can add the reviews over time at a pace that is SEO friendly and organic.

You might think Google could easily foil such a system but in reality there is such a huge combination of sentences that it would take some genius algorithm to work out that these where being added programmatically in combination with a random time sequence I don’t see how Google would know the difference as long as it’s not overdone.

Getting back on topic the idea of this module is just to seed, marginally boost or test you product ratings for example adding a minimum of 1 or 2 reviews per product to get you started.

Get in touch if this is something you need on your ecommerce store.

You can now buy this script from my shop here

Magento price not saving multi scope/store

Just ran into this issue on a multi store view website in the admin backend, it seems because the product price scope is global unless you have a module to override this functionality the price should only have one attribute value, and each time you save the product in any store view you are essentially saving one price to store 0 the admin store.

If you used an importer or some other process to import your products you may find that there are incorrectly two records in the catalog_product_entity_decimal table

entity_tbl

You can look at the data via this query, replacing ATTRIBUTE_ID with your product ID

SELECT * FROM catalog_product_entity_decimal WHERE attribute_id = ATTRIBUTE_ID

How to fix?

In my store the price attribute is 75 it may differ though, there should only be one entry for store_id 0. If you now delete that row you will be able to save the product price. You can delete all these values in one line. (please backup your database first.)

DELETE FROM magento_db.catalog_product_entity_decimal WHERE attribute_id = 75 AND store_id = 2

Where store_id is the additional store entry and magento_db is your database name.

I’m Joel a magento developer/freelancer based in St Albans

I’m a developer for Magento, I have developed many custom modules for my clients, some of my work includes:

  • Banner Management
  • Product display grid allowing the user to control the homepage inserting blocks of video,products,reviews,offers etc
  • Landing page builder tool – making it easy to create many pages at once around several different keywords
  • Price checking module – Automatically checks google shopping for defined competitor prices, matches pricing and updates the products via Google’s Merchant API in realtime.
  • Customisation of SOLR Bridge plugin
  • WordPress Integration
  • Magento Custom Theming
  • Custom PayPal response for error messages
  • Cardinal Commerce 3DS integration
  • Trade only configuration
  • Removing shipping and payment steps from the checkout
  • Extending Magento’s REST API and building a custom REST API
  • Integrating Loyalty points system, custom events to update MailChimp
  • MailChimp Integration
  • Social discount module – offering discounts at the checkout for social actions
  • Custom email templates
  • Integrating Amazon Payment module

I’m based in the London, St Albans, Watford area and available for a consultation, primarily I prefer to work by email.

Configuring Magento for Trade only

Magento never fails to amaze me in it’s flexibility and it’s ability to configure the site for almost any business objective. One popular choice might be a trade only site to use it as a platform for wholesalers or partners to purchase large quantities of goods.

trade

When thinking about this configuration usually you would want some of the below:

 

– Independent store per customer

– Customer can only access the catalog once logged in

– Remove registration or have approval only

– Prices only visible to logged in members

– Sell the same SKU to different customers at different prices

– Remove checkout payment option

– Restrict address and delivery address alterations

 

Independent store per customer

Magento does support multiple store’s per website, so a new store view can be set up per customer, but to get this to work as intended as Magento allows the customer to login to any store, which you may want to restrict. You can add a store attribute to the customer profile and then check this attribute upon login and redirect the customer to the correct site, no matter where they log in at.

Customer logged in Catalog

This is pretty straightforward again adding a check in the header to see if the customer is logged in, otherwise redirect to the login page.

Remove registration

You may want to disable registration if your using the API to update Magento or have an approval process , there is a free module on Magento Connect that allows approval. It’s also worth disabling persistent login in the configuration so there is only one login page to edit.

Prices only visible to logged in members

As above , if you wanted the site to remain accessible to the public but just hide the pricing, a simple customer session check will do the job.

Customer groups for different pricing

Supported out of the box, although if your running multiple stores this probably becomes redundant.

Remove checkout payment option

This is a little bit more tricky, and involves editing several files there’s a guide on doing just that here there are some changes needed for 1.9.1 though that aren’t covered in that article.

Restrict address and delivery address alterations

You will most likely want to restrict address changes as these are orders on account, here you can set the input/selects to disabled.

 

Thinking about starting a trade only Magento website and require a consultant? Please get in touch.

Developer Shortcut – Bash script menu to connect to servers via SSH

Managing various servers can get quite repetitive entering various IP address’s or even remembering them in the first place! Lets solve that with a simple bash script depending on wether your doing this from a server or on your local environment the location of your bash_profile may be different but the steps are the same.

Your profile is usually in /home/users/yourname/.bash_profile

nano /home/users/yourname/.bash_profile

Open up this file and add a new line at the bottom

alias connect='bash /Users/yourname/menu.sh'

What this is doing is saying when I type connect in the terminal run menu.sh, you could change this word to another word of your choice.

Change the location of menu.sh to suit your needs, save and close.

Make a new file called /Users/yourname/menu.sh and inside put:

#!/bin/bash
# Bash SSH Menu

PS3='Please enter your choice: '
options=("mysql" "web"  "Quit")
select opt in "${options[@]}"
do
    case $opt in
        "web")
            echo "Connecting to web 10.0.0.1"
            ssh [email protected]
            break
            ;;
        "mysql")
            echo "Connecting to mysql 10.0.0.2"
            ssh [email protected]
            break
            ;;
"Quit")
            break
            ;;
        *) echo invalid option;;
    esac
done

Here we are setting up the menu options in the first line, in this case mysql and web which refer to my web and mysql server but these could be named whatever you like, the next part is similar to a switch statement in PHP, for each case print that we are connecting and then SSH to the IP. Finally a quit option and a default option to handle invalid requests.

If you use this in conjunction with SSH keys it will save a lot of time.