- In /config/defines.inc.php, changed _PS_MODE_DEV_ to true.
- In /config/smarty.config.inc.php, after the assignment of the $smarty variable, add the following code:
if (defined('_PS_MODE_DEV_') && _PS_MODE_DEV_)
$smarty->debugging = true; - In the template (.tpl file) you want to see debug info, add the following line at the very end of the template
{debug} - MAKE SURE there is a return after {debug}
- In the back office / advanced parameters / performance, click the "Clear Cache" button at the top right.
- In the URL that you are testing, add the param SMARTY_DEBUG (e.g. http://ls/index.php?id_product=57&controller=product&SMARTY_DEBUG).
- Make sure that you are not getting a pop-up blocked by your browser. Once you allow the popup, you will get your console.
- If you are still having problems, set the "PS_SMARTY_CONSOLE" value in the configuration table to 1.


Wednesday, July 08, 2015
Enable the PrestaShop 1.6 Smarty Debug Console
Thursday, July 02, 2015
PHPStorm Autocomplete for PrestaShop Classes
Julien Bourdeau has the solution, PhpStorm-PrestaShop-Autocomplete. Quick install and just works. Thanks, Julien!
Ah! It's "Validate::isFloat".
Wednesday, July 01, 2015
PrestaShop Admin Area Hook Error Reporting
I can only tell you 100% for sure that this works for the actionProductUpdate hook, but I imagine it's valid everywhere. Here's a quick snippet to show you the general idea....
public function hookActionProductUpdate($params)
{
if (!Validate::isInt (Tools::getValue('pwr_start')))
{
$this->context->controller->errors[] = "Ya screwed up!";
}
}
There are 3 main things you want to take note of:
- Use the Validate class to validate your input data
- Use Tools::getValue instead of directly pulling things from $_GET / $_POST. This does some light purification of the input data.
- If you want to stop the submit of the form, add the error to the controller's error list. Unfortunately, this is only reported at the top of the form.
Sunday, May 31, 2015
Download a RackSpace Server Image
NOTE: Be aware that, at least with a CentOS 7 Cloud Server, the image you download with the following method is ONLY compatible with XenServer, a Type 1 Hypervisor. It simply did not work with VirtualBox. I saw one report that you can load it with VirtualBox, but Rackspace themselves says it will not work and suggests this method instead.
I got my Rackspace cloud API token, and used curl from my Rackspace server to do the following:
- Get export endpoint (which is based on your server region) and authentication token using these docs. The export endpoint is the "cloudImages" "publicURL", and the authentication token is the "access" "token" "id".
curl -X POST https://auth.api.rackspacecloud.com/v2.0/tokens -d '{ "auth":{ "RAX-KSKEY:apiKeyCredentials":{ "username":"RACKSPACE_USER_NAME", "apiKey":"API_TOKEN" } } }' -H "Content-type: application/json" |python -m json.tool - Export your account and authentication token (NOT your API token).
export account="RACKSPACE_USER_NAME"
export token="AUTHENTICATION_TOKEN" - Combine your endpoint URL with the calls from this article to get your list of images.
curl -s https://lon.images.api.rackspacecloud.com/v2/images -H "X-Auth-Token: $token" |python -m json.tool - Asynchronously export the image your interested in (check the "image" "name" field to find the one you want) by using the image "id" and the name of you Cloud Files container. If you don't have a Cloud Files container (I didn't), you have to sign up for it to get exported images.
curl -s https://lon.images.api.rackspacecloud.com/v2/tasks -X POST -d '{"type": "export","input":{"image_uuid": "IMAGE_ID","receiving_swift_container": "YOUR_CLOUD_FILE_CONTAINER"}}' -H "Content-Type: application/json" -H "X-Auth-Token: $token" |python -m json.tool - You can now simply wait for your image to appear in your cloud file container, or you can actively check the status of you export job by using the id returned by the export command.
curl -s https://lon.images.api.rackspacecloud.com/v2/tasks/EXPORT_JOB_ID -H "X-Auth-Token: $token" |python -m json.tool - If all goes well, you can pick up your image from the cloud file container. Mine took about 5 to 10 minutes to appear.
Friday, May 29, 2015
Adjustable Height Desks - cheaper version
Ever since I started reading about it years ago, the idea of an adjustable height desk really intrigued me. Back when I wanted to be a writer, I remember reading about Hemingway using a standing desk, and I've since learned that luminaries such as Ben Franklin and Winston Churchill did the same.I badly wanted to switch both myself and my staff to adjustable height desks, but could never convince myself to pay the cost. However, in 2012, Susana was able to buy motor driven adjustable height tables for everyone at 443 EUR each from http://www.schaefershop-industrie.es/. These tables have been great for sitting less, and also for breaking up the day.
Standing can be overused (standing for long periods of time can cause varicose veins), and I could not find any long term, detailed studies that back up claims that switching between sitting and standing is healthier and makes you more productive (the standard claims), but I do like having the option to switch between both, and I DO feel that it makes me healthier and more productive.
When I was on my flight back from Düsseldorf yesterday via Air Berlin, I saw an ad for Veridesk that I wish I had seen back before we bought our desks. If you want the benefits of a standing desk without as much cost, then Veridesk might be a useful alternative.Hmm... looking at their site, maybe I should look into getting a standing mat....
Monday, May 18, 2015
PuTTY - Script Output in Different Color
How about if I changed the color of the output? This article turned me onto the basic method, which had to be fine tuned due to my use of the zenburn color theme. Also, I found that, for some reasons, the color macros that work on the command line were not working in the scripts. Doesn't matter... I can now start and end scripts with commands to change color, and get the output in a different color....
So, a shell script (myscript.sh) like this...
#!/bin/bash
echo -e "\e[0;32m----------------------------------------------------"
echo "My Script Output"
echo -e "----------------------------------------------------\e[0m"
Will output the following (assuming you are using the zenburn color theme)....
[root@local ~] ~/myscript.sh
----------------------------------------------------
My Script Output
----------------------------------------------------
[root@local ~]
Friday, May 15, 2015
CentOS 7 Hostname Resetting on Reboot - SOLVED
Problem
Solution
- Backup /etc/hosts, /etc/sysconfig/network, /etc/sysconfig/network-scripts/ifcfg-eth0 and /etc/cloud/cloud.cfg
- execute "hostnamectl set-hostname yourdomain.com"
- execute "hostnamectl set-hostname --static yourdomain.com"
- Change old hostname to yourdomain.com in /etc/hosts
- In /etc/sysconfig/network, add
HOSTNAME=yourdomain.com - In /etc/sysconfig/network-scripts/ifcfg-eth0, add
HOSTNAME=yourdomain.com
DHCP_HOSTNAME=yourdomain.com - in /etc/cloud/cloud.cfg, set
preserve_hostname=true - Reboot
- hostname and hostname -f should now correctly give yourdomain.com
Wednesday, May 13, 2015
Windows PuTTY Tips
- Make a .reg file with your color scheme and font to easily apply it to new sessions (since the default dark blue folder names on black background is just a pain in the butt). Just add the name of the session and run the .reg file. I use the zenburn color theme with a larger font.
I also change my LineCodePage to UTF-8, which is necessary on CentOS7 - ymmv.
Here is a gist of my customized zenburn. - F6 gets you the tilde character in putty (useful for non-US keyboards), which is the path to your home directory.
- nano ~/.bashrc opens a file where you can define aliases to make your work easier. Of course, this will only be available on the machine you are working on, but if you work on the machine a lot, it's well worth the time to make aliases. Much, much more can be added... search bashrc some day when you're bored
- I like: alias dir="ls -CFhal"
- Also, alias bashrc="nano ~/.bashrc && source ~/.bashrc && { echo -e 'Success--'; cat ~/.bashrc; } | mail -s 'bashrc backup' 'you@yourdomain.com'". This open bashrc for editing, puts any changes you make live, and emails you the script so you have a backup. Like, wow!
- Add scripts to bashrc, which are available from the command line. To backup a file by sending it to an email address (always a good idea before you make any changes to files via the shell), add this to .bashrc
bufile() {
file_to_backup=$1
{ cat $file_to_backup; } | mail -s "'$file_to_backup' Backup" 'you@yourdomain.com'
}
...and then call it from the shell
bufile index.php
Thursday, May 07, 2015
PrestaShop 1.5/1.6 and the Google Tag Manager
Adding Page View Tracking
Most PrestaShop themes use one header.tpl for all pages on the site. This post assumes that is the case with your theme... if not, apply everything about header.tpl to all your headers.- Set up a GTM (Google Tag Manager) container, etc., and copy out the GTM snippet
- open header.tpl
- Look for the body tag
- IMMEDIATELY after the end of the body tag, add...
{literal}
[your GTM snippet]
{/literal}
e.g.
- NOTE FROM 2018: This article is rather old, and the newest GTM installation code from Google also talks about adding code right after the "head" tag, which could also be done in the header.tpl file. Thanks to Oliver Gerber for pointing that out.
- Add a tracking tag to GTM
- Preview and debug your GTM code.
- Publish the GTM container
Adding Transaction (Conversion) Tracking
IMPORTANT: For this code to fire, your payment method modules need to be calling PrestaShop after payment. E.g. here are the changes I needed to make to get this to happen with PayPal. As far as I can tell, this "problem" exists for both my module, and for the PrestaShop Google Analytics module.
- Install the PrestaShop module "Data Layer Module" (instructions for installing a PrestaShop module can be found in the PrestaShop Documentation). This is simply a copy of the ganalytics module with lots of code removed and analytics JavaScript adapted to the GTM. It's a VERY basic module that any PHP coder can understand and expand on.
- In GTM, add a new Google Analytics transaction tag... named something like "PrestaShop Conversion"
- Add a firing rule (a.k.a. a Trigger) named e.g. "Order Confirmation" with values {{event}} equals prestashop_order_confirmation (prestashop_order_confirmation is an event that I trigger in the Data Layer Module) for the conversion tag.

- Preview and debug your conversion tracking.
- Publish the GTM container
Adding Site Speed Tracking
By default, GTM and GA track your site speed, but using only a 5% sampling rate. That's perfect if your Amazon, but many sites need 100% sampling to get good data. Here's how to get this up.
- In GTM, open your page view tracking tag
- In the "Fields to Set" section, add a new field called "siteSpeedSampleRate"
- Set the value to 100 (= 100%)
- Preview and debug your GTM code.
- Publish the GTM container
Notes
- Always push when using the data layer! See Simo Ahava's article on the subject. Actually, read all of Simo Ahava's articles... he's a great resource.
- This code was written for a site which only has PayPal as a payment option. Theoretically, it should work for other gateways (it's not at all PayPal specific). I would be interested in hearing of your results if you use this with other payment gateways.
Friday, March 06, 2015
Spidering and Parsing
For my purposes, PHPCrawl "just worked". I'll post again if I find something better, but so far, no need to look.
Rather than regexing the page contents, I wanted to to query the HTML. There are an AMAZING number of options, and the first two I tried "just broke" (one was Simple HTML DOM, and the other I'm not sure), since the pages I'm trying to parse are rather complicated.
Simple HTML DOM also had the additional disadvantage of being dead slow.
I am now working with the DOMDocument class, based on the comments on this excellent stack overflow post. So far, so good.
Update: This article by Ersin Kandemir was helpful as is, but additionally sent me to the XPath Helper Chrome extension (by Adam Sadovsky), which was also a big help (hint: not only does it show you xpath commands, it also lets you test your own commands on the current page). Thanks guys!
Friday, June 05, 2009
Gmail imap connect and seen/unseen
Formatting: I still don't have a good method of formatting for blogger. For this post, I used "Convert Special Characters into HTML Entities" by Stanley Shilov (Thanks!), switching to "Edit HTML" in Blogger, putting Stanley's optput between "pre" open and close tags, and then switching back to "compose" mode to clean up the output. There's GOT to be a better way... if anyone knows of any, please yell.
$mbox = imap_open ("{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX", "your@gmail.com", "your");
$numMessages = imap_num_msg($mbox);
for ($jj = $numMessages; $jj > 0; $jj--) { //always have to go backwards, since message ID is dynamic
$header = imap_header ($mbox, $jj);
if ('U' == $header->Unseen) { //This message is unread
$body = '';
$struct = imap_fetchstructure($mbox, $jj);
if (!empty ($struct->parts)) {
$numParts = count ($struct->parts);
for ($kk = 0; $kk < $numParts; $kk++) {
$part = $struct->parts[$kk];
if ('PLAIN' == $part->subtype) {
$body = imap_fetchbody($mbox, $jj, $kk + 1);
}
}
} else {
$body = imap_body ($mbox, $jj);
}
imap_setflag_full($mbox, $jj, "\\Seen"); // mark as read
}
}
imap_close($mbox);
Thursday, June 04, 2009
Permissions for Magento Upgrade
Wednesday, June 03, 2009
Zend Studio for Eclipse autocompletion hinting
/**
*
* @var Mage_Catalog_Model_Product
*/
var $product;
Debugging Magento: Vista, XAMPP, & Zend Studio for Eclipse
Tuesday, June 02, 2009
Programmatically Importing Product with Images in Magento
It took me quite a while to figure out how to import products into Magento including images. The long version is below, the final results are here for those that need a quick fix, with thanks to articles from Darryl Adie (to show me 95% of the solution) and tza79 (who showed me how to set the visibility, a.k.a. mediaAttribute). Start with the code from Darryl, and then add the following...
//This call is needed since the media gallery is null for a newly created product.
$product->setMediaGallery (array('images'=>array (), 'values'=>array ()));
$product->addImageToMediaGallery ($fullImagePath, array ('image'), false, false);
$product->addImageToMediaGallery ($fullSmallImagePath, array ('small_image'), false, false);
$product->addImageToMediaGallery ($fullThumbnailPath, array ('thumbnail'), false, false);
----------------------------Long, boring version:
$product->load(6); $mediaGallery = $product->getMediaGallery(); print_r ($mediaGallery);Hmmm, it's nothing more than an array of arrays. Why not make it myself using setMediaGallery? Bingo!
Friday, February 27, 2009
Displaying Google Search Params
Tuesday, February 10, 2009
Wednesday, July 16, 2008
Download from remote CVS
* Server machine (user where you want access, eg: mister_big)
* Create a keypair private/public key on client machine to avoid retype the password every time (on peter machine)
Go to .ssh folder and type:
ssh-keygen -t dsa
Select Emtpy pass-phrase.
2 files had been created. id_dsa (private key) and id_dsa.pub (public key)
* You need add the id_dsa.pub content into authorized_key2 file on server machine (server machine is the computer where you want to access, in this case mister_big)
* Copy id_dsa.pub into server machine. Eg: via FTP. Note: You need move id_dsa.pub to root folder to access file via FTP since .ssh folder is not listed on FTP clients.
* On mister_big machine, move id_dsa.pub into .ssh directory (.ssh folder is a directory placed on root)
* Merge the new key (WARNING: I saw a lot comands to make this but for me only works fine this option)
mv authorized_keys2 authorized_keys2.bak // It's a backup
cp id_dsa.pub authorized_keys2 // Overwrite current authorized_keys2
cat authorized_keys2.bak >> authorized_keys2 // Use a backup to merge files
chmod 600 authorized_keys2 // Restore permissions
Go again to client machine (peter machine) and type:
ssh-agent /bin/bash
ssh-add
And next, you can download code without type password anymore:
export CVS_RSH=ssh
cvs -d:ext:mister_big@yourdomain.com/cvs checkout
Tuesday, June 10, 2008
Enums
//First, make sure any old files are cleaned up.
System.IO.File.Delete(m_strOutFile);
//Ask FM to generate the new file
Utils.Tools.RunFMScript("Shipments.fp5", "TourlineExport");
Utils.Tools.WaitForFMGenerateFile(m_strOutFile, 120);
System.IO.TextReader reader = null;
try
{
if (System.IO.File.Exists(m_strOutFile))
{
reader = System.IO.File.OpenText(m_strOutFile);
while (reader.Peek() > -1) {
string strLine = reader.ReadLine();
if (strLine.Length > 0 && '"' == strLine[0])
strLine = strLine.Substring(1);
if (strLine.Length > 0 && '"' == strLine[strLine.Length - 1])
strLine = strLine.Substring(0, strLine.Length - 1);
string[] splitter = { "\",\"" };
string[] shipmentLines = strLine.Split(splitter, StringSplitOptions.None);
To index into shipmentLines, I started doing the following...
const int k_shipmentID = 0;
const int k_invoiceID = 1;
const int k_title = 2;
const int k_firstNames = 3;
const int k_surname = 4;
...but quickly realized that it was hard to maintain (if the needed fields change, or the number of fields change - whatever). Therefore, I started using enums
enum FMFields
{
ShipmentID, InvoiceID, Title, FirstNames, Surname, Street, Street2,
Town, City, County, Country, PostalCode, StrUnfoInfo, InvoiceTotal, ScannedStatus,
DateScanned
};
Much nicer, and easier to maintain - and less typing to boot! Note that I use the .NET convention of capitalizing both the enum name and the names of the members.
Thursday, March 13, 2008
Position Preferences in AdWords
However, I found 2x problems. The first was that I found no way, besides going in one keyword at a time, to adjust this. Nothing in the web interface, and nothing in the AdWords Editor. So, before sending an email asking Susana to do this across all our campaigns, I started reading about people using it. Apparently, there are a lot of circumstances where it simply doesn't work at all, and Google even specifically says that this option is only a "suggestion", not a guarantee.
Well, since we always rank well anyway, I decided to skip the work.
Thursday, February 21, 2008
Products Not Showing - MySQL 5
Monday, November 19, 2007
Missing data in XML (format FMPXMLRESULT)
<COL>
<DATA>Value1</DATA>
<DATA>Value2</DATA>
<DATA>Value3</DATA>
<DATA>Value4</DATA>
</COL>
<COL>
<DATA>240</DATA>
<DATA />
<DATA>6</DATA>
<DATA>6</DATA>
</COL>
(...)
if (currentReader.NodeType == XmlNodeType.Text)
{
if (isRowToRetrieve)
{
rowInfo[columnIndex] = reader.Value;
if (columnIndex == ProdIDPos)
{
//Values capture
valuesArray[valuesIndex++] = currentReader.Value;
}
else if (columnIndex == QuantityPos)
{
//Quantities capture
quantitiesArray[quantitiesIndex++] = currentReader.Value;
}
else if{(...)}
else{(...)}
}
}
(...)
if ((lastElementName == "DATA") && (currentReader.Name == "DATA") && (lastNodeType != XmlNodeType.Text))
{
Log("Quantity for "+ valuesArray[quantitiesIndex] +" is missing");
}
Then the key item here is the consideration that, between a XML ElementName and EndElementName, a XML NodeType Text is expected
Wednesday, October 17, 2007
Localhost FTP problems
However, today, I just could not get FTPPuts to work. I was getting an Access Denied error, and had no clue what the problem was. After uninstalling and reinstalling, checking params in the server, in my Filezilla, and in my FTP code, I was still clueless.
OK, WarFTP out, IIS back in - and the same exact error! A "critical transfer error" 550, permissions. It was not IIS or WarFTP that was the problem, but the lack of write permissions for the FTP user in NTFS. Letting "everybody" write to my FTP directory fixed the problem.
Bye bye WarFTP - you've served me well - but I'll stay with IIS now till I have further problems ;->
Friday, October 12, 2007
Abandoned Carts on Credit Card Page
Note that "never coming back" might also indicate that they failed SecPay's pre-bank checks - SecPay does not indicate to the merchants if clients did not come back due to the pre-bank checks or because of abandoning the cart. However, it appears that the percentage that fails the pre-bank check is small compared to people who simply do not complete the order.
4.5% is actually good compared to the other web shops that I talked to. One reported 8% (SECPage with 3D turned on), another said 4-8% was "normal" (Secure Trading), and a 3rd report 10% (PayPal).
What to do when the users never come back? We send out an email after 2 hours, and then again after 10 days, with a link to help the user complete the order. In this way, we're able to get almost one third of users to complete their order. One shop (with few orders of much higher totals) follows up with phone calls to all the abandoned carts, and gets a very large percentage (well over 50%) to finish their order.
Thursday, August 02, 2007
FileMaker Auto-Login
I borrowed and stole ideas from my Google searches (starting with EnumWindow C#), and used the trusty old Spy++ application (part of Visual Studio Tools, and something that I haven't fired up in years), to get 90% of the way there. When I was at 90% and trying to set the password text in the edit box, I found Using P/Invoke to Automate Database Signon, which would have saved me quite a bit of time if I had found it earlier, and got me over the last 10%.
Here is the basic code. You can recreate this by creating a WindowsApplication project in VisualStudio 2005 and adding a timer component which fires as often as you need. Be sure to set the timer to "enabled", and hook it up to the timer1_Tick function.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public delegate bool FMCallBack(int hwnd, int lParam);
namespace FMAutoLogin
{
public partial class Form1 : Form
{
private const int WM_SETTEXT = 0x000C; //TextBox
private const int BM_CLICK = 0x00F5; //Button
[DllImport("user32.Dll")]
public static extern int EnumWindows(FMCallBack x, int y);
[DllImport("User32.Dll")]
public static extern void GetWindowText(int h, StringBuilder s, int nMaxCount);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd,
uint Msg, int wParam, string lParam);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
string lpszClass, string lpszWindow);
//----------------------------------------------------------------------
public Form1()
{
InitializeComponent();
}
//----------------------------------------------------------------------
private void timer1_Tick(object sender, EventArgs e)
{
EnumWindows(new FMCallBack(Form1.EnumWindowCallBack), 0);
}
//----------------------------------------------------------------------
private static bool EnumWindowCallBack(int hwnd, int lParam)
{
StringBuilder sb = new StringBuilder(1024);
GetWindowText((int)hwnd, sb, sb.Capacity);
if (sb.ToString().Contains(".fp5") &&
sb.ToString().Contains("File \""))
{
SetForegroundWindow((IntPtr) hwnd);
IntPtr editBox = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Edit", "");
SendMessage(editBox, WM_SETTEXT, 0, "your_password");
IntPtr okButton = FindWindowEx((IntPtr)hwnd, IntPtr.Zero, "Button", "OK");
SendMessage(okButton, BM_CLICK, 0, "");
}
return true;
}
}
}
Thursday, April 26, 2007
Taking Shop Offline
if ($_SERVER['REMOTE_ADDR'] != "80.28.198.60")
doRedirect("http://www.visiondirect.co.uk/checkoutChanges.txt");
?>
After adding (or removing) this, you need to remove compiled templates.
NOTE: SecPay cannot arrive successfully at the orderSuccess page if you have this code running.
NOTE2: Of course, you additionally have to add the file indicated by "doRedirect".
Wednesday, February 21, 2007
Count sales from Tuesday
select count(*) as mycount, left(datetime, 8) as mydate from {orders} where dayofweek(datetime) = 3 and referURL like "GL%" group by mydate order by mydate desc
Tuesday, January 30, 2007
Daily GreenLight Sales
select left (datetime,8) as mydate, left (datetime,4), mid(datetime, 5, 2), mid(datetime, 7, 2), count(*) as mycount from [OrdersTable] where datetime >= "20070101000000" and referURL like "GL|%" group by mydate order by mydate
Tuesday, December 19, 2006
Sort powers in MySQL
The way for sort ‘power’ it’s: ORDER BY `Power`+0
Strange… but works fine!!!
Thursday, December 07, 2006
MySQL Count Distinct
Spain 50
France 20
Germany 10
The correct way to do it is "select country, count(*) as mycount from xxx group by country order by mycount desc"
Hopefully, now that I have this, I'll never do a search on google looking for something so obvious ;->
Monday, November 27, 2006
JShop MySQL Query Performance
$theQuery = str_replace('\"', '\\\"', str_replace("'", "''", $sql_query));
if ($result == FALSE) {
$this->lastError = mysql_errno().": ".mysql_error();
mysql_query("insert into amex_dbPerf (descript, err, time) values ('$theQuery', $this->lastError, $totalTime)");
return FALSE;
} else {
mysql_query("insert into amex_dbPerf (descript, err, time) values ('$theQuery', '', $totalTime)");
return $result;
CREATE TABLE `amex_dbperf` (
`actionID` int(11) NOT NULL auto_increment,
`descript` text NOT NULL,
`err` varchar(128) NOT NULL default '',
`time` decimal(12,11) NOT NULL default '0.00000000000',
PRIMARY KEY (`actionID`)
)
PHP Timing
$startTime = $secs + $micros;
//Do Something
list($secs, $micros) = split(" ", microtime());
$totalTime = ($secs + $micros) - $startTime;
totalTime is a decimal with seconds
Tuesday, November 07, 2006
Visual Studio Regular Expressions Example
custNode["address1"].InnerText
...where custNode and address1 were variable, and I wanted to replace them with the more safe...
SafeInnerText (custNode, "address1")
To do this, I made one of my occasional ventures into regular expressions, and thought I would document it for future reference.
Find expression was this...
{<:i*>}{\[\"}{:i*}{\"\].InnerText}
The find is broken up into 4 "tags" (terminology from Visual Studio). I broke it up into multiple tags since I only need 2 parts of the found expression, not all of them.
Here is a rundown of the parts...
{<:i*>} (first tagged expression)
Finds custNode["address1"].InnerText
{} to indicate it's an expression
< indicates the beginning of a word
:i* to find the "custNode" part (Matches the expression ([a-zA-Z_$][a-zA-Z0-9_$]*))
> the end of the word
{\[\"} (second tagged expression
Finds custNode["address1"].InnerText
{} to indicate it's an expression
\[ (escaping special chars) to find the left bracket
\"(escaping special chars) to find the double quote
{:i*} (third tagged expression)
Finds custNode["address1"].InnerText
{} to indicate it's an expression
:i* to find the "address1" part (Matches the expression ([a-zA-Z_$][a-zA-Z0-9_$]*))
{\"\].InnerText} (fourth tagged expression)
Finds custNode["address1"].InnerText
{} to indicate it's an expression
\"(escaping special chars) to find the double quote
\[ (escaping special chars) to find the left bracket
.InnerText - literal
Now the replace...
SafeInnerText (\1, "\3")
\1 and \3 are replaced by the first and third tagged expressions.
NOTE: \0 gives you ALL the tagged expressions - in this case, it would be custNode["address1"].InnerText
Monday, September 25, 2006
Zend (javaw.exe) using 100% CPU
Tuesday, September 12, 2006
Generating Euro and Dollar Prices from Pounds
//Start: Price with VAT (e.g. EasySEPT, £18.00)
//Remove tax (VAT): £15.31
//Multiply by tax (VAT) rate: 15.31 * 119% = 18.22
//Multiply by exchange rate: 18.22 * 1.55 EUR = 28.24
//Round TO NEAREST DOLLAR (e.g. 28.2 = 28, 28.5 = 29): €28
I needed to apply this to both the $tableProducts, and a variation for $tableAdvancedPricing.
Checked into the LB source code.
Friday, July 14, 2006
myunhtmlentities
ERROR: Failed to send reminder 61871 sent for order 315956, customer 2326, email bob&helen@talktalk.net
Looking at the reorder reminder table showed me that the email address is retreived from the customer table. There, it is "correctly" stored as bob&helen@talktalk.net. I say "correctly", since JShop generally does store string HTML escaped, which to me is weird - but it's also a fact.
Solution: whenever getting things from tables in JShop, remember that the data is probably HTML escaped. When you need unescaped strings, you can use the myunhtmlentities (from visiondirect/admin/resources/includeBase.php. If you need this outside the admin area, move it to someplace accessible by the non-admin code.
Thursday, June 01, 2006
Simple Control.Invoke
using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
...
public class SafeInvokeHelper
{
static readonly ModuleBuilder builder;
static readonly AssemblyBuilder myAsmBuilder;
static readonly Hashtable methodLookup;
static SafeInvokeHelper()
{
AssemblyName name = new AssemblyName();
name.Name = "temp";
myAsmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
builder = myAsmBuilder.DefineDynamicModule("TempModule");
methodLookup = new Hashtable();
}
public static object Invoke(System.Windows.Forms.Control obj, string methodName, params object[] paramValues)
{
Delegate del = null;
string key = obj.GetType().Name + "." + methodName;
Type tp;
if (methodLookup.Contains(key))
tp = (Type)methodLookup[key];
else
{
Type[] paramList = new Type[obj.GetType().GetMethod(methodName).GetParameters().Length];
int n = 0;
foreach (ParameterInfo pi in obj.GetType().GetMethod(methodName).GetParameters()) paramList[n++] = pi.ParameterType;
TypeBuilder typeB = builder.DefineType("Del_" + obj.GetType().Name + "_" + methodName, TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.Public | TypeAttributes.Sealed, typeof(MulticastDelegate), PackingSize.Unspecified);
ConstructorBuilder conB = typeB.DefineConstructor(MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) });
conB.SetImplementationFlags(MethodImplAttributes.Runtime);
MethodBuilder mb = typeB.DefineMethod( "Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, obj.GetType().GetMethod(methodName).ReturnType, paramList );
mb.SetImplementationFlags( MethodImplAttributes.Runtime );
tp = typeB.CreateType();
methodLookup.Add(key, tp);
}
del = MulticastDelegate.CreateDelegate(tp, obj, methodName);
return obj.Invoke(del, paramValues);
}
}
Tuesday, May 23, 2006
FM Inventory Quantities
- "on hand" is "in box" minus any order lenses which have not yet been shipped. AKA would be "available for ordering".
- Newly added QtyCorrected is corrective qty used to adjust stock levels after checking inventory.
QUANTITY IN BOX: conversiontolenses + Total Bought - TOTAL QUANTITY SOLD TRANSFERED + QtyCorrected
Conversion to lenses: number
Total Bought: Initial Inventory Level + IQuantity Bought
Initial Inventory Level: number
IQuantity Bought: number
TOTAL QUANTITY SOLD TRANSFERED: QUANTITY TRABNSFERED SOLD + QUANTITY WHOLESALE TRABNSFERED SOLD
QUANTITY TRABNSFERED SOLD: number
QUANTITY WHOLESALE TRABNSFERED SOLD : number
Tuesday, May 16, 2006
Regediting ActiveSync Folder
Monday, May 08, 2006
GMail PHPed
Easy just didn't happen. I tried the FireFox GSpace Add On, and the shell extension GMail Drive, but they ONLY treat their own files (although both are cool tools to use a gmail account as a file store). They do this by using a special email title, and by storing the files as attachments.
Also tried PhpGmailDrive 0.3.2, but this only displays files which are attachments.
Finally, I hacked PhpGmailDrive's usage of libgmailer (after upgrading libgmailer to version 0.9 Beta 2), which is a PHP wrapper for the gmail API. The documentation is not the best, and I was never able to get more than the first 50 messages, but by applying a "label" to the gmail messages of interest, 50 emails at a time, I was able to get the following hacked code to download the emails as files...
if ($gm[$a]->connect()) { //
$gm[$a]->fetchBox(GM_LABEL, "fix", 0); // name of constants can be found in libgmailer.php
$snapshot = $gm[$a]->getSnapshot(GM_LABEL);
$lastidx = count($snapshot->box)-1; //
//$gf->TreeData .= "// ".count($snapshot->box).".\n";
//$gf->TreeData .= "// ".$snapshot->box_total.".\n";
//for ($c=0;$c<=$snapshot->box_total;$c+1)
//{
//$gm[$a]->fetchBox(GM_STANDARD, "inbox", $c); // name of constants can be found in libgmailer.php
//$snapshot = $gm[$a]->getSnapshot(GM_STANDARD);
//$lastidx = count($snapshot->box)-1;
foreach ((array)$snapshot->box as $item) { //
$gm[$a]->fetchBox(GM_CONVERSATION, $item["id"], 0); // name of constants can be found in libgmailer.php
$snapshot1 = $gm[$a]->getSnapshot(GM_CONVERSATION);
foreach ((array)$snapshot1->conv as $item1) {
$fh = fopen("C:\\down\\".$item1["subj"], "w");
$body = $item1["body"];
$body = str_replace(""", "\"", $body);
$body = str_replace("
$body = str_replace("
", "\r\n", $body);
$body = str_replace("
fwrite($fh, $body);
fclose($fh);
continue;
Thursday, April 13, 2006
Selenium IDE
php system call problems
system ("C:\\cool.exe C:\\coolfile.cfl", $returnVal);
simply does not work, and simply gives a returnVal of 1. Nothing in the php error log even with all errors turned on, nothing in the system log, and a "file not found" error returned by using some suggestions from the system documnentation page (something like echo system("(".$SysCmd." > /dev/null) 3>&1 1>&2 2>&3", $Ret);). I tried a million combinations, and they worked on the cmd line, but failed with system.
Solution?
system ("C:/cool.exe C:/coolfile.cfl", $returnVal);
My new rule is to always use forward slashes with system.
Wednesday, March 29, 2006
WebGains
You can print the invoices from their web site. Log in, and look for the "account statement" link on the lower left.
Tuesday, February 07, 2006
PHP - auto-create variables from query
select someField, someOtherField from somewhere
while ($rec = fetch) {
$someField = $rec["someField"];
$someOtherField = $rec["someOtherField"];
do something...
}
To save the steps of pulling the variables out into variables, I use the following...
$rociResult = $dbA->query ("select someField from $table where xxx");
if (TRUE == $rociResult) {
while ($rociRec = $dbA->fetch ($rociResult)) {
extract($rociRec, EXTR_PREFIX_ALL, "v");
echo $v_someField;
}
}
Friday, January 27, 2006
EasyPHP for Macintosh
Friday, January 06, 2006
Win2K DVD Decoder
Wednesday, December 21, 2005
Compiles that go to the client
1) do a checkin
2) Build a release version to send to the client
2) tag the state of the program (and any satellite programs) with the version number of the release version sent to the client (and program name, if a satellite version).
Example: you change SupplierChecker for SNR. When everything is working, you check in the code, build a release version (noting the version number), and send it to SNR. Immediately afterwards, you tag SupplierChecker and AmexCommon (which is also used by SupplierChecker) with the tag "SupplierChecker 1.0.xxxx.yyyy (where xxxx and yyyy are the other parts of the version number of the release version sent to SNR).
-------------------------
Why do we do this? When e.g. SNR calls and says he has a bug, we can check the EXACT code that he was using. Also, if you've started new changes, you can always go back to a version WITHOUT the new changes to quickly correct a bug.
Monday, December 05, 2005
Install 24U Simple Dialog Freeware Plugin
20060806 ED: New Instructions at \\server2003\Permanent\Software Depot\Plug-ins\24U Simple Dialog FM Plugin\readme.txt:
I BELIEVE that the only thing you need to do is copy the file 24uSimpleDialog.fmx to your \Program Files\FileMaker\FileMaker Developer 6\System folder. Either that, or run the install process in the "\\server2003\Permanent\Software Depot\Plug-ins\24U Simple Dialog FM Plugin\Old Contents and Instructions (not working)" folder and THEN copy in the fmx file.
I NEVER get this working on the first try. Please update these instructions if my first suggestion DOES work. Thanks.
Wednesday, November 30, 2005
Move Putty's stored data to a new Windows computer
FileMaker via ODBC Performance Problems
However, the big performance culprits were 2x summary fields, which accounted for about 93% of the slowdown. He was able to remove one field, since SNR indicated that it was totally unused. The other he converted to a global field, and introduced a “recalculate” button on the layout where the field was used – the button called a script to recalculate the field in accordance with the old summary field definition.
With around a half a dozen calculated fields, the updates took about 2 seconds, which is too slow, but fast enough for our needs.



