Wednesday, March 01, 2023

Prestashop 1.7x / 8.x "NAN" cannot be interpreted as a number

No comments:

\src\Core\Cart\CartRuleCalculator.php, function applyCartRule has the same line 2x. This line will generate a NAN if you have a free product via a cart rule in your basket, and another cart rule at the same time.

$discountAmountTaxExcl = $discountAmountTaxIncl / (1 + $taxRate)

The following code will work around the problem.

$discountAmountTaxExcl = ($discountAmountTaxIncl > 0) ? 
$discountAmountTaxIncl / (1 + $taxRate) : 0
;

Tuesday, December 20, 2022

Correos Tracking

No comments:

For anyone else that needs to access Correos tracking, you can start with their API documentation (WS Canonico interface), ask your Correos rep for a user name and a password to the API (this step took several days), and then use something like the below to get the status.

$username = 'bob';
$password = 'pass';
$tracking = 'PQ43B404AA015110128001N';

$client = new GuzzleHttp\Client();
$result = $client->get(
"https://localizador.correos.es/canonico/eventos_envio_servicio_auth/$tracking?codIdioma=ES&indUltEvento=S",
['auth' => [$username, $password]]);
$body = json_decode($result->getBody());

Wednesday, January 25, 2017

PHPStorm Xdebug Error Code 206

3 comments:
Short version: if stepping stops working in PHPStorm with Xdebug, and your Xdebug error log reports error 206 (see below), try deleting your watches.

I was happily coding away yesterday evening on our site LentillasSi (found a few uninterrupted hours... tough to do lately!), interspersed with tracing sessions where I checked and contemplated every line of code. This is a habit I picked up after reading (and rereading, and rereading) Steve Maguire's excellent "Writing Sold Code" (which I now see has a 2nd edition).

Then, disaster... the debugger stopped working. I could "break at first line", I could set a break point and hit it, the code ran fine without the debugger... but I could not step - the script would stop dead when I tried. Windows 10 anniversary edition, PHPStorm 2016.3.2, php_xdebug-2.4.1-7.0-vc14.dll, and XAMPP. This is not the first time this has happened to me, and I remember that my problems disappeared without me knowing why before, so I was determined to write down everything I did till I found a solutions.

After looking for the answer online, I did several things:
  • Reboot
  • Changed the debugging port (both in PHPStorm and Xdebug)
  • Reinstalled Xdebug (the wizard suggesting a new version, 2.5.0-7.0-vc14)
  • Add the extra lines from this Valk's answer on stackoverflow (previously, I only had zend_extension, xdebug.remote_enable, xdebug.remote_host, and xdebug.remote_port set), adjusting the error log path for my machine.

Once I restarted Apache and tried again, in the Xdebug log file, I found
response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="eval" transaction_id="22" status="break" reason="ok">

At first, I was confused (reason "ok"?!? wtf?), but then, for some reason, "eval" made me think of watches. I deleted all my watches (duh! Without first writing down what they were), and the debugger started working.

Wednesday, October 05, 2016

PrestaShop: Adding Fields to Admin Area Lists

No comments:
When clients (or accountants ;->) call with questions about a particular order, they don't always have the order id or order reference numbers. In these cases, it's handy to be able to search by other fields.

The PrestaShop override mechanism make it easy to add fields to admin area lists without changing the core code. For example, to add lookup by Invoice #, Phone Number, and Postal Code to the orders list in the PresaShop Admin area, I only had to create the following file: /override/controllers/admin/AdminOrdersController.php

NOTE: Be sure to delete /cache/class_index.php after adding your new class!

<?php
class AdminOrdersController extends AdminOrdersControllerCore
{
    public function __construct()
    {
        parent::__construct();

        $this->fields_list['invoice_number'] = array(
            'title' => $this->l('Invoice #'),
            'align' => 'text-center',
            'class' => 'fixed-width-xs'
        );
        $this->fields_list['phone_mobile'] = array(
            'title' => $this->l('Phone Number')
        );
        $this->fields_list['postcode'] = array(
            'title' => $this->l('Zip/Postal code')
        );
    }
}

Wednesday, July 27, 2016

CentOS7 + PHP 5.6 + APCu

No comments:
To install APCu on Linux (fully updated CentOS7 box), I read on various places on the web that I needed:

yum install php-pecl-apcu

Which gave me the error

Error: php56u-common conflicts with php-common-5.4.16-36.1.el7_2.1.x86_64

So I searched yum

sudo yum list \*apcu\*

...and came up with a command that worked....

sudo yum install php56u-pecl-apcu

Thursday, May 26, 2016

PrestaShop 1.6.x Front Module for non-CMS Page

No comments:
Ever want to add a totally new front page to PrestaShop, but found that a CMS page was not enough? I needed to create a totally new page, with a form that could submit to itself, and I did not find info about how to do this in PrestaShop 1.6.

Based on the idea of a module for doing this which I found in a PrestaShop 1.5 article, I did the following...

  • Use the PrestaShop module generator to create a new module
  • Inside the module folder, create folders /controllers/front/
  • Inside /controllers/front/, create a php file that describes the action for the controller
  • Name the controller class {modulename}{action}ModuleFrontController (extends ModuleFrontController). E.g. module changepassword, controller file changepassword.php would be
    class changepasswordchangepasswordModuleFrontController extends ModuleFrontController 
  • I BELIEVE case is unimportant, but make it case sensitive just in case.
  • If the customer needs to be logged in to use the action, then set the following at the beginning of the class before adding functions to your ModuleFrontController
    public $auth = true;
  • Put your template in /modules/{your_module}/views/templates/front/{your_template_name}.tpl
  • In your ModuleFrontController::initContent() function, do
    parent::initContent(); $this->setTemplate('{your_template_name}.tpl');
  • If you are submitting to self, you form action (in the tpl file) should be
    action="{$link->getModuleLink('{modulename}', '{action}')|escape:'html':'UTF-8'}"  
  • If you need to define smarty values based on the submitted form, use $this->context->smarty->assign in the init function of your ModuleFrontController.
  • Define a URL for the form in admin area -> preferences -> SEO & URLs.

Sunday, March 27, 2016

Full List of AdWords URL Parameters for Tracking Templates

No comments:
AdWords has added the ability to add parameters to all your URLs via Shared Library -> URL
options -> Tracking Template, which was a very welcome upgrade to those of us who tag all our URLs. However, I struggled to find a complete list of parameters available to me for use in the Tracking Template, and decided to make my own list and share it.

However, when investigating the creation of the table, I found a Google page "Set up tracking with ValueTrack parameters", which has a complete list of all the values and give good explanations. To find the list, you will need to expand the sections in "Available ValueTrack parameters" to see the values, but the list seems complete.

Thursday, March 24, 2016

In Search of the Lost MailChimp API v3 PHP Wrapper

No comments:
After spending some time deciding "MailChimp or some other service", for various reasons I decided to stay with MailChimp, especially since it's free for me at the current time. I dug into the docs, and immediately detect that MailChimp has moved from v2 of their API to the new v3, as of the spring of 2015. Since v2 will be turned off at the end of 2016, v3 was the logical choice.

Is there a wrapper? Let's see https://apidocs.mailchimp.com/api/downloads/ lists wrappers for a bunch of languages... but no mention of wrappers for v3.

When I reached out to MailChimp, they said:
"Because API v3.0 recently reached feature parity [emphasis mine] with previous versions, we haven't been able to set up any resources to promoting wrappers API 3.0. With said, our developers are working on improving our API 3.0 experience for our users, and if you'd like to check in on any feature updates or news relating to 3.0, please be sure to head to our Road map page below and sign up to receive our API announcements.
Going forward, while we don't have any documentations of various 3.0 wrappers, github would be a good resource to find any v3.0 wrappers that can be used."

Since Drew McLellan has updated his PHP API wrapper to v3, and his component is mentioned on the MailChimp site (listed as a v2 component), and since his component has more stars and downloads on packaist.org, I will go with that wrapper to start.

Wednesday, March 16, 2016

MySQL Split / Explode

No comments:
Note to self: Although MySQL does not have a split / explode type function, it DOES have a built SUBSTRING_INDEX function that can be used in some circumstances. E.g. if you have a field with value '12345-right', you can get the left part with SUBSTRING_INDEX(value, '-', 1), and the right part with SUBSTRING_INDEX(value, '-', -1).

In general, I agree with Melchior Blausand's rant on StackOverflow: that I shouldn't be looking for explode, I should be looking at a better way to store my data. If you are reading this, think about storing differently rather than using a hack like this.

Tuesday, March 15, 2016

TPV Virtual, BBVA, y PrestaShop

No comments:
En diciembre, escribí sobre mi uso del TPV de ING en mi tienda online. Funcionaba bien, pero faltaba la posibilidad de quitar 3D Secure (Verified by Visa, SecureCode, etc.) en pedidos seguros, y la opción de "Pago en 1 clic" (clicktopay).

Desde entonces, he cambiado a un TPV de BBVA. Sale ligeramente mas barato que el TPV de ING (creo que la commission es 0,48%, y recibo el dinero dentro de 72 horas), per mas importante, ahora tengo control de 3D Secure, y la opción de "Pago en 1 clic". Mi agente de BBVA entendía exactamente lo que estaba buscando, y necesitaba relativamente poco tiempo en tener todo en marcha.

También he cambiado al modulo RedSys de idnovate. El modulo tiene muchas opciones, y David siempre responde rápido a mis preguntas.

Da igual que banco estas utilizando, si esta a través de RedSys no olvides a utilizar la opción de "AutoReturn".

Sunday, February 14, 2016

Missing sqlite.dll when launching Navicat

No comments:
Note to self: "Missing required library sqlite.dll, 998" when starting Navicat: I have no idea what caused it, and reinstalling did not fix it, but this article let me practice my (pathetic) Germany, and gave me the solution to the problem - restarting the computer fixed it.
Posting since the majority of sites that talked about the problems did not include a solution.

Wednesday, February 10, 2016

PrestaShop Gamification / Merchant Expertise SQL Errors

No comments:
Quick Note: I added an override to the DbCore class yesterday after discovering that malformed queries fail silently. For example:

<?php
$resource = Db::getInstance()->query(
      "select * frommmmmmmmmmm ps_orders");
while ($data_row = 
      Db::getInstance()->nextRow($resource)) {
   //Do Important Stuff
}

Would simply fail to work, without logging anything.

[Yes, I hear you, I should have checked the value of $resource before starting the while loop, and I agree. However, note that the official PrestaShop docs say that query "Will not return a boolean; instead returns a database resource that you can use with other DB class methods, such as nextRow()". [Ed: Not true! query returns false on SQL syntax errors]]

[I have _PS_MODE_DEV_ on while developing, which catches the error in the example, but my problem was with code that I accidentally typed two letters when my focus was on the wrong window before committing. And yes, I hear you, I should have noticed that when doing my git commit.]

Once I am happy with my override, and the associated iterator I added to make code like the above cleaner and safer, I will blog about the classes. However, in the meantime, I can tell you that my override sends DB errors to the PHP error log, and that the log this morning was filled with stack crawls of SQL errors from the gamification module. I've verified that these errors exist in a virgin install of PrestaShop, and I will use these instructions (thanks, Oskar!) to disable the module.

I also reported the errors.

Tuesday, February 02, 2016

My 5 Most Read Tech Articles

No comments:
January was a "bad" month... my tech blog, which I use mostly to talk to myself, got more non-paid traffic than our e-commerce site. Trends say this is a very temporary situation (and let's hope that's true!), but it was a bit depressing anyway.

To celebrate, I've included the top 5 read articles from this blog for the month of January, 2016.

  1. PrestaShop 1.5/1.6 and the Google Tag Manager (May, 2015)
    Makes sense, I was surprised a module like this isn't included in PrestaShop by default. A surprising number of site are now using it.
  2. PHPStorm + xdebug + ScotchBox 2.0 (Jul, 2015)
    This came out of my summer love affair with Vagrant / ScotchBox, which I moved away from due to Windows 10 problems.
  3. PayPal Auto Return with PrestaShop (Nov, 2015)
    This goes hand in hand with the PrestaShop GTM module. It's needed to insure transactions get tracked.
  4. Enable the PrestaShop 1.6 Smarty Debug Console (Jul, 2015)
    Again, I was surprised this even needed to be researched... it should be easily enabled.
  5. Programmatically Importing Product with Images in Magento (Jun, 2009)
    The article that would not die. I don't know if it's still applicable, but for sure it's still read. I think I will add a warning to it to make sure people realize it was written centuries ago.
My favorite blog article in January, excluding my blog of course :->, came from L3's Peter O'Neil. Understanding your Website Visitors: Prospects vs Customers is a must have for your Google Analytics setup. I have to work though all the technical articles on his blog, there are others that look juicy, too.

Wednesday, January 13, 2016

PHPMailer SMTP in Prestashop

No comments:
I was using the Mail::Send static function in PrestaShop to send some messages with attachments, but PrestaShop was "decorating" the emails with an attached shop logo image. It also did not allow multiple BCCs.

Rather than override the Mail::Send function, I decided to use Composer to install an updated library to provide flexibility both now and in the future.

As many people have commented, PrestaShop uses an outdated version of SwiftMailer. My initial idea was to simply install a current version in my composer directory (leaving PrestaShop alone), but first I took a look at what libraries people recommended. PHPMailer and SwiftMailer are both very popular (and both apparently work just fine), but PHPMailer seems to be more popular. A comment that I read indicating that debugging PHPMailer was MUCH easier than debugging SwiftMailer, which lead me to decided on PHPMailer.

I used my old article to guide my PHPMailer installation, and then used my configured SMTP params to send email. Worked like a charm... multiple BCCs and attachments included. Below is the SMTP setup using the PrestaShop config. Note that this ONLY works if you set "Set my own SMTP parameters (for advanced users ONLY)" with valid SMTP values in the "Advanced Parameter" / "E-Mails" section of your PrestaShop admin area.

<?php
require(_PS_ROOT_DIR_ . '{location of your autoload file}/autoload.php');
use PHPMailer\PHPMailer\PHPMailer;

function getConfiguredPHPMailer()
{
    $configuration = Configuration::getMultiple(array (
        'PS_MAIL_METHOD',
        'PS_MAIL_SERVER',
        'PS_MAIL_USER',
        'PS_MAIL_PASSWD',
        'PS_MAIL_SMTP_ENCRYPTION',
        'PS_MAIL_SMTP_PORT'
    ), null, null, Context::getContext()->shop->id);

    // Returns immediatly if emails are deactivated
    if ($configuration['PS_MAIL_METHOD'] == 3) {
        return null;
    } else {
        if ($configuration['PS_MAIL_METHOD'] != 2) {
            @mail('you@yoursite.com', 'ERROR: Code only configured for SMTP',
                'Code only works with PrestaShop configured for SMTP (with PS_MAIL_METHOD == 2)');
            return null;
        }
    }

    $mail = new PHPMailer;
    $mail->isSMTP();                                 // Set mailer to use SMTP
    $mail->Host = $configuration['PS_MAIL_SERVER'];  // Specify SMTP server
    $mail->SMTPAuth = true;                          // Enable SMTP authentication
    $mail->Username = $configuration['PS_MAIL_USER'];// SMTP username
    $mail->Password = $configuration['PS_MAIL_PASSWD'];// SMTP password
    $mail->SMTPSecure = $configuration['PS_MAIL_SMTP_ENCRYPTION'];// Enable encryption
    $mail->Port = $configuration['PS_MAIL_SMTP_PORT'];// TCP port to connect to

    return $mail;
}

Friday, January 08, 2016

Buttons in Emails

No comments:
I was just checking my inbox to see how people do buttons in emails, and I found a lot of broken buttons - HTML emails which use links to images that GMail did not load by default. Reading this long post lead me to Button.cm, which gave me a nice, image-free button.

Thursday, January 07, 2016

Comprar Online

No comments:
Curioso... "comprar online" esta MUCHO mas utilizado en busquedas Google que "comprar por internet".

Monday, January 04, 2016

No comments:
Another "note to self": In PrestaShop, "partial refunds" are stored in the ps_order_slip table. You need to sum the shipping and products totals to get the full amount of the refund.

Monday, December 14, 2015

Lentillas ¡Sí!

No comments:
¡¡LentillasSi ya está aquí!! 
¿Usas lentillas? ¿Te salen muy caras y las estiras más de la cuenta? No sufras más, nosotros te vendemos lentillas de reemplazo, es decir, tus mismas lentillas pero a precios de internet. ¿No eres habitual de las compras online? ¿No sabes qué modelo usas? Contáctenos por email: Susana@LentillasSi.es o por teléfono a nuestro número gratuito 900 834 479 y te indicamos.
Además, disponemos de óptica colegiada en plantilla para resolver todas las dudas que puedan surgir.
Síguenos y podrás participar en nuestros concursos y llevarte tus lentillas gratis, enterarte de nuestras ofertas y ¡muchas cosas más!





LentillasSi - Venta de Lentillas Online

Wednesday, December 09, 2015

TPV Virtual, ING Direct, y PrestaShop

No comments:
Soy muy "fan" de ING Direct. Tengo mi cuenta corriente allí desde 2005, y una hipoteca allí. No dudaba en abrir una cuenta negocios con ellos a abrir un nuevo S.L. este año, y también utiliza su TPV virtual.

En general, todo ha sido muy fácil (aún que entre pipas y flautas, tienes que planear con 1 o 2 semanas entre pedir el TPV y empezar a cobrar... ellos primero necesitan unos días para comprobar tu negocio, después pruebas, etc.). Hacen los pagos a través de RedSýs, y RedSýs tiene un modulo gratis para PrestaShop. No pagas por un modulo... los que cobran no te dan nada mas de lo que tiene RedSýs gratis.

El soporte tanto de ING como RedSýs esta muy bien, ofrecen también IUPay, y ahora mismo es lo que estoy utilizando para nuestra tienda.

Pero...

Cuando programaba para los sitios webs www.visiondirect.co.uk, www.lensbase.es, y www.lenshome.es, teníamos 2 opciones a través de PayPoint (que es como RedSýs) que ING no ofrece:

  • "Pago en 1 clic" (clicktopay): después de la primera vez que el cliente paga con su tarjeta, puede hacer futuro pagos con un solo clic, sin la necesidad de entrar todo los datos de su tarjeta. (Ds_Merchant_Identifier)
  • La posibilidad de quitar 3D Secure (Verified by Visa, SecureCode, etc.) en ventas que sabemos que son seguros. (Ds_SecurePayment)
Las dos opciones lo hacia mas simple para el usuario a la hora de pagar, que subía el porcentaje de visitantes que compraba algo. Ahora estoy en búsqueda de un banco con precios razonables que tienen estas opciones. Publicaré todos los resultados cuando los tengo.

Total: si quieres un TPV virtual rápido, barato, y seguro, ING Direct no es mal opción.

Tuesday, December 01, 2015

Redsýs "Auto Return" with PrestaShop

No comments:
A couple of weeks ago, I wrote about my experiences with PayPal Auto Return, and commented that I had the same problem with Redsýs. Redsýs to the rescue... they pointed out that if I entered in my account at https://canales.redsys.es/, click on "Comercios" (sorry, their control panel is in Spanish), Edit, and look for the option "Parámetros en las URLs" (parameters in the URLs). There, they have the option "SI,sin mostrar recibo Redsys" (yes, without showing Redsýs receipt). Choosing "SI,sin mostrar recibo Redsys" will get you Auto Return.

Their auto return IS a bit clumsy... it seems to go through 2 other pages first. However, the first time the client sees that the payment worked (or failed) is on my website instead of somewhere else. This ensures that my Google Tag Manager tags fire correctly.

Thursday, November 26, 2015

PrestaShop Delivery Slip Table

No comments:
This is basically a note to myself: I want a bit crazy searching for where the "delivery slips" were stored in the PrestaShop database. It turns out that there is no "delivery slip" table, rather the delivery slip ID is actually part of the ps_order_invoice table (field delivery_number). The value is filled out whenever an order is set to "Shipped" or "Processing in progress".

Which basically means that PrestaShop is not set up for making multiple shipments on the same order. Not very handy, especially for B2B orders.

Wednesday, November 25, 2015

Composer Packages in PrestaShop (pre-1.7.x)

No comments:
Yesterday, I needed a Composer component for use in my PrestaShop application (I could have used sFTP in PHP in a number of different ways, but after reading the excellent book Modern PHP, I of course wanted a Composer solution to my problem). From reading the PrestaShop Developer's Blog, I knew that version 1.7.x will have Composer integrated into the program, so I needed to set it up in such a way as to avoid future problems.

To do this, I created a "composer" folder deep within my shop (but available from everywhere in case I needed future components), along with a "composer/vendor" folder. I created a composer/composer.json file that looked like this...

{
    "require": {
    }
}

I then set up Composer support in PHPStorm, chose Tools->Composer->Add Composer Dependency, and installed the phpseclib/phpseclib 2.0.0 package. PHPStorm think of everything ;->

My code was then able to use the phpseclib by requiring {full path}/composer/vendor/autoload.php, and creating a new \phpseclib\Net\SFTP class. From there, things just worked.

I then added the composer/vendor folder to .gitignore (as recommended by getcomposer.org), and added, committed, and pushed composer.json and composer.lock (whether to add composer.lock or not is a controversial issue... YMMV).

After reading these two excellent articles, it's clear that composer.lock SHOULD be added to git for my circumstances.

On my Linux server, I pulled the latest code. I found that Composer was already installed, so I navigated to the new /composer directory, and ran sudo composer update install (update when you have not checked in composer.lock, install when you have. Install obeys composer.lock). My new package was added to the Linux server, and the code worked out of the box.

For efficiency, I may decide to adapt this in PrestaShop 1.7.x, but I believe I will be able to use this as-is in future PrestaShop versions if needed.

Tired of using an old SwiftMailer version in PrestaShop? Want to get better logging?  What easier date time handling? With Composer and it's packages, you've got it, plus a lot more.

Wednesday, November 18, 2015

PayPal Auto Return with PrestaShop

No comments:
Kim from Colillas Branding (whose English was so good that I was surprised to see he was Spanish) asked today how to get PayPal to return directly to the website instead of first showing a "confirm payment" page. Returning to the site insures that you have a chance to show your tracking code, as the extra step that PayPal puts in can keep that from happening.

I googled and found the PayPal Auto Return feature, which looked like exactly what I needed. However, the location described in the above link does not exist in my PayPal account (I needed to look under Profile -> Account Settings -> Selling Options -> Website Preferences).

There I could turn on Auto Return, but I couldn't find what the return URL should be set to. There are questions on the PrestaShop forum asking what the return URL needs to be, but no useful replies.

So, time to "use the force.... read the source". In the PayPal module (the official PrestaShop one from 1.6.1.2), they set PAYPAL_RETURN_LINK to /modules/paypal/paypal_login/paypal_login_token.php. I added my domain to the front of that (i.e. I set "Return URL" in PayPal to http://www.mysite.com/modules/paypal/paypal_login/paypal_login_token.php), and it works... I was immediately sent to my shop on payment, the order confirmation page was shown (I'll need to look for that template in the PayPal module and customize it... another item for my to do list), the Google Tag Manager code got called, and the order is marked as paid PrestaShop. Bingo.

Now if I only knew how to do the same with RedSys for my ING payments... Kim! Help! 

Tuesday, November 17, 2015

Contributing to PrestaShop

No comments:
Ever since I started using open source software, I've gotten a lot of help in the various support forums, and try to help others in the forums whenever possible in return.

However, when I posted my "forgotten password" change for PrestaShop, and someone from Bellini Services suggest that I submit it as a pull request to the PrestaShop team, I thought "Yea, well, actually, why is it that I've never contributed to an open source project?" Maybe now is the time to start.

After reading the PrestaShop guide for making pull requests, I decided to wait until after work some day when I wasn't expected home for dinner. However, late yesterday I found a quick one line bug fix in the core PrestaShop code that looked like a good place to get my feet wet.

Following the guide was quicker and easier than I thought, and I had my pull request up in no time. First thing this morning, Maxime from the PrestaShop core code team pointed out that I had actually used the wrong branch for my pull request (I used 1.6 instead of 1.6.1.x), which was simple to fix. Maxime also indicated they would update the documentation to avoid future problems like this. Actually, if I had read this article, I could have saved myself some time.

He also indicated that a new forgotten password feature was already checked into the development branch. This one appears to be pretty much the same as what I coded, so I will skip sending it as a pull request.

All in all, I was surprised at how easy contributing is (thanks to the miracle of git), and how quickly PrestaShop (or at least Maxime) responded. Bottom line: contributing to PrestaShop is easy. I would encourage anyone with the talent to take it up as a hobby 

Wednesday, November 11, 2015

htaccess Tester - Way Cool

No comments:
This is cool... a great htaccess tester from a company called "Made With Love". Looks like the Belgians are not as dumb and clunky as all my Dutch friends say they are ;->

When I lived in Holland, the Dutch busting on the Belgians always reminded me of my favorite Philly DJ's comment (heating up the Philadelphia - Pittsburgh rivalry): "Pittsburgh--where the sky is yellow and brown and the plants are as smart as the people".

Tuesday, November 10, 2015

PrestaShop: Improved Customer Password Recovery

No comments:
Ever since I started using PrestaShop, I hated the password recovery method: send email with link, clicking on link sends you a new password via email.

For me, this method has various problems:
  • Two emails to the client are needed. Not all emails arrive instantly, so this can slow down a customer that wants to do business with you.
  • A password is being sent as plain text to the user.
  • The password auto-generated by PrestaShop is hard for the user to remember.
  • The customer's secure key is sent out as plain text (it's the "token" parameter in the URL of the first email).
Therefore, I decided to change the "forgotten password" feature. My goal was to send ONE email to the user, which directs them to a page to change their password.

The link which allows the password must be unique to the user, can only be used once, and additionally will only work if used within some arbitrary amount of time... let's say one hour.

FORGOT YOUR PASSWORD?


Looking at the code, I decided that a complete override of the PasswordController was needed. I did this as an override. I created the file /override/controllers/front/PasswordController.php, with public function postProcess, and deleted the /cache/class_index.php to make sure my overridden controller gets called.

I also extensively changed my theme's password.tpl to handle the new method. In addition, the text of the template, the error message, and the email needed to be changed to reflect the new implementation.

In order to limit the tokens to 1 hour, I needed to add a new MySQL table.

CREATE TABLE `customer_extra_info` (
  `id_customer` int(10) unsigned NOT NULL,
  `datetime_password_request` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `token_password_request` varchar(32) NOT NULL DEFAULT '-1',
  PRIMARY KEY (`id_customer`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

I plan to use this table for more customer specific info, which explains it's "non-specific" name.


Why I switched from Mandrill to Mailgun for PrestaShop

No comments:
I was working on an override of PrestaShop's "forgotten password" feature (article to arrive here shortly) when I ran into a problem... the "forgotten password" email that I was sending to my Gmail account via Mandrill from my RackSpace Cloud server was taking up to 10 minutes to arrive in my Gmail account! 10 minutes may be fine for sending newsletters, but if a customer is in the middle of the checkout process and has to wait 10 minutes for us to respond to his request for help on a forgotten email, we've lost the client.

I started by checking my Mandrill account. I have the free account (up to 12,000 emails a month for free), since I only use it for transactional emails. DKIM and SPF were working, domain was verified, reputation excellent... I saw nothing there that indicated that there was a problem. Their knowledge base had no info on the problem, and since I have a free account, they don't offer support (and I can't blame them ;->).

When reading about the problem, I saw that other Mandrill users were having the same problem. I also saw that Mailgun (owned by RackSpace) offered 50,000 free emails per month for RackSpace cloud users, and thought I would give it a whirl.

After setting up the DNS records and getting the domain verified, I realized that I had PrestaShop configured to send emails via the PHP mail function, and then had Mandrill set up via Postfix. I would assume that is slower than using SMTP directly (which means that my problem may have been a configuration problem), but since I had come this far (and since Mailgun has more free emails per month), I decided to set up Mailgun as my PrestaShop SMTP server.

My tests with Mailgun were mostly arriving in my Gmail account within a couple of seconds, with one taking almost 30 seconds, but it looked acceptable. Gmail's DKIM and SPF authentication tests all passed.

For the moment, I'll stay with Mailgun. I will keep an eye on delivery times, and switch back to Mandrill if I have problems.

Monday, November 02, 2015

How to Make a Copy of the Prestashop 1.6 Bank wire Module

No comments:
In order to make a copy of the Bank wire module (making a Bank transfer module, in my case), I did the following.
  • Make a copy of the \modules\bankwire\ folder and replace the name "wire" in all the file names to "transfer"
  • Search and replace (case sensitive) all instances of "wire" in the \modules\bankwire\ to "transfer"
  • Make copies of bankwire files in \mail and switch the names of the files, and the contents, from wire to transfer.
  • Make a copy of the bankwire folder in \{your_theme}\modules\, and rename the folder banktransfer
  • Search and replace (case sensitive) all instances of "wire" in the files in the \{your_theme}\modules\bankwire\ directory to "transfer"
  • Delete all files (EXCEPT index.php) from /{your_theme}/modules/banktransfer/translations/
  • Add PS_OS_BANKTRANSFER (I used value 99) to the configuration table, imitating PS_OS_BANKWIRE
  • Add values to order_state and order_state_lang imitating bankwire, using the same id_order_state value as PS_OS_BANKWIRE (99 in my case)

Friday, October 30, 2015

PrestaShop 1.6 Shopping Cart Sorting

No comments:
By default, PrestaShop orders the items in the shopping cart by the datetime they were added (order by date_add, id_product, id_product_attributes, from \classes\Cart.php). However, it drove me nuts that changing the quantity of the items in the cart changes their ps_cart_products::date_add, thereby causing the sorting order in the cart to change! Since the normal functioning of the code updates the basket with Ajax, it's usually not noticed by the visitor, but my code (for various reasons) actually performs a full update, which causes the items to jump visibly in the basket.

Although the default PrestaShop sorting is perfectly fine for the majority of users, I have seen people asking about how to change the sort order, and see the questions either unanswered or with answers that change the core code (something I try to avoid).

To "correct" this, I first created the following table:

CREATE TABLE `ps_cart_product_sorting` (
  `id_cart` int(10) unsigned NOT NULL,
  `id_product` int(10) unsigned NOT NULL,
  `original_date_add` datetime NOT NULL,
  UNIQUE KEY `ix_cart_product` (`id_cart`,`id_product`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

I then overrode \classes\Cart.php (by creating \overrides\classes\Cart.php), function getSummaryDetails. I first call the base class to return the $summary_details array. I then add the original date add to each product, and finally sort the array by the original date add.

<?php

function summary_compare (array $one, array $two)
{
    return strcmp ($one['original_date_add'], $two['original_date_add']);
}

class Cart extends CartCore
{
    //----------------------------------------------------------------------------
    public function getSummaryDetails($id_lang = null, $refresh = false)
    {
        $summary_details = parent::getSummaryDetails($id_lang, $refresh);
        if (isset ($summary_details['products'])) {
            for ($jj = 0; $jj < count($summary_details['products']); $jj++) {
                $summary_details['products'][$jj]['original_date_add'] =
                    $this->getOriginalDateAdd(
                        $summary_details['products'][$jj]['id_product'],
                        $summary_details['products'][$jj]['date_add']);
            }

            //Rather than letting prestashop sort by last modified (which moves things
            //around in the cart when changing qty there), sort by the original add_date.
            //Note that prestashop modifies cart_products::date_add every time the qty
            // changes, setting it to currente time.
            uasort($summary_details['products'], 'summary_compare');
        }

        return $summary_details;
    }

    //----------------------------------------------------------------------------
    protected function getOriginalDateAdd($id_product, $date_add)
    {
        $original_date_add = Db::getInstance()->getValue("
            select
                original_date_add
            from
                ps_cart_product_sorting
            where
                id_cart = {$this->id}
                and id_product = $id_product");

        if (!$original_date_add) {
            $original_date_add = $date_add;
            Db::getInstance()->insert('ps_cart_product_sorting', array (
                'id_cart' => $this->id,
                'id_product' => $id_product,
                'original_date_add' => $original_date_add
            ));
        }
        return $original_date_add;
    }
}

Finally, I added the sorting function

<?php
function summary_compare (array $one, array $two)
{
    return strcmp ($one['original_date_add'], $two['original_date_add']);
}

Wednesday, September 16, 2015

NimbleText

No comments:
As a programmer, you always run into situations where you need to process raw text into usable data. If you're a regex genius, or have a lot of free time, you can always write code to handle this. Or you can quickly get results with NimbleText.

It has about a 5 minute learning curve, and for me, "it just works". Highly recommended.

Yet another jewel I first read about on Scott Hanselman's Ultimate Developer and Power Users Tool List for Windows.

Thursday, September 10, 2015

PrestaShop /img dir Backup to S3

No comments:

I have my PrestaShop code plus all my modifications saved in BitBucket and my database backups in S3 (I keep last 7 days, Monday from the last 5 weeks, monthly and yearly backups on S3). However, I didn't want to back up my PrestaShop /img dir in either of the above ways... I don't need img history, just an rsync of the /img folder to S3.


Searching for solutions led me to S3Tools and the like... open source or paid wrappers to bring S3 to the command line. I was more inclined to write something myself... I'm not real happy putting my S3 keys in programs like this.

Then I found AWS CLI from Amazon. It just works! I did a pip install, filled in my credentials, and 2 minutes later had a differential backup working from Linux to S3. Testing with changes, additions, and deletions all worked as expected.

Once tested, I added the below to my nightly cron backup script...

aws s3 sync /var/www/html/img s3://my_prestashop_bucket/img

Friday, August 28, 2015

New GIT Repository -> BitBucket -> Windows

No comments:
Just added my WordPress code to a Bitbucket Git repository. As always when I want to init a new project, I first head over to gitignore.io to quickly get a decent .gitignore file. If you use the service, and your starting from Linux, the tutorial video is worth watching before you start.

After setting up the .gitignore file, in Linux, the following will create your repository...

sudo git init
sudo git add .
sudo git commit -m 'initial commit'

After that, create a new empty repository in Bitbucket, select the "I have an existing project" link, and follow the instructions to your new repository on Linux added to the empty repository on Bitbucket.

Sidenote: when I switched from Mercurial to Git (Mercurial was fine for me... just wanted to get my feet wet with Git), my idea was to use GitHub. However, being in a startup, with a small team, I decided to keep some Euros in my bank account and use Bitbucket, which is free for up to 5 users. I used Bitbucket with Mercurial, so I was already familiar with it, and I'm very happy to stay there.

Next, since I develop on Windows (10), I needed to get the code locally. To do that, I use SourceTree, which makes cloning the repository on Windows a no-brainer.

Friday, August 21, 2015

Quickly Toggle MySQL Logging

No comments:
MySQL logging is a lifesaver when trying to understand applications at a low level. However, since I use it infrequently, I always have to consult the Google doc I have with the details on how to toggle the setting, and where to find the output.

For Windows, I've switched to a BAT file to speed up the process (which I call from SlickRun).

BEFORE using the script, you will want to have the following in your MySQL config file (in Laragon, use the right button menu -> mysql -> my.ini) in the [mysqld] section.

log-output=FILE
general_log_file = "C:/whatever/mylog.txt"
general_log      = OFF

@echo off
set /p state="Set MySQL Logging state ON or OFF: "
c:\xampp\mysql\bin\mysql -u "root" -e "SET GLOBAL general_log = '%state%';
%SystemRoot%\explorer.exe "C:\whatever"

A better alternative is to output to a table. In your MySQL config file (in Laragon, use the right button menu -> mysql -> my.ini) in the [mysqld] section, add

log-output=TABLE (see docs (MySQL 5.7))
general_log      = OFF

Then your script would be

@echo off
set /p state="Set MySQL Logging state ON or OFF: "
c:\xampp\mysql\bin\mysql -u "root" -e "SET GLOBAL general_log = '%state%';

You will find the data in the mysql.general_log table. What I do to make is easier to grok is to create a table with queries that I want to ignore (e.g. the standard PrestaShop code at the beginning of a page load).

create table mysql.general_log_ignores
(
argument_to_ignore mediumblob null
);

and then select only the interesting queries with


select 
       event_time, 
       convert(argument using utf8)
from 
    general_log gl 
    left join general_log_ignores gli on gl.argument = gli.argument_to_ignore
where
    gli.argument_to_ignore is null
order by 
    gl.event_time;