Sunday, May 31, 2015

Download a RackSpace Server Image

No comments:
I needed to download one of my Rackspace cloud backup images to my computer for use with VirtualBox. I assumed I would be able to simply download it, but to my surprise, that´s not possible. Rackspace suggested I use their API and their "getting started" tutorial, and upvote the request to add direct image download as a Rackspace feature, which I did.

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

No comments:

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

No comments:
I was writing a shell script with rather verbose output, and was getting confused with which output was from which invocation of the command. I started clearing the screen between commands, but sometimes forgot. There's got to be a better way....

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

No comments:

Problem

Setting the hostname was working without problems in CentOS 7 (including latest updates), but the hostname would revent back to the old hostname on every reboot. Nothing I read about worked, and I see lots of other people with the same problems (although RackSpace could not reproduce the problem on a newly spun up CentOS 7).

Solution

Assumes you're running as root. Otherwise, sudo the commands
  • 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

No comments:

  • 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

No comments:

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

Adding page view tracking is super simple. Adding conversion tracking requires a data layer, which I added with a module.
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

Speed of your website is fundamental to a good conversion rate. In addition, it effects your ranking in the serps. Monitoring your page speed is an important KPI.
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

No comments:
I needed to pull English product reviews from an English language version of a site and put them in Excel files for translation to Dutch, and rather than doing things one by one, I decided to code the work, resulting in this quick note on spidering and parsing in PHP.

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

1 comment:
Some quick notes on imapping gmail (notice the novalidate-cert part... might save you some headaches), marking messages as read, and also about formatting PHP code for blogger. NOTE: code below has error checking removed for clarity.

$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);
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.

Thursday, June 04, 2009

Permissions for Magento Upgrade

No comments:
Just updated my "live" and local servers to Magento ver. 1.3.2.1 via the admin area, "System" menu, "Magento Connect", "Magento Connect Manager". I needed to set permissions on the live server, and used Magento: Magento Connect Manager - Save Settings - Permissions and How to resolve the file permissions error in Magento Connect Manager?

I got a lot of permissions errors when setting the permissions on the command line, and also a couple (for index.php and .htaccess) when installing (will have to talk to NuBlue about how to update them next time). However, since the only change in the two files was stuff for the super-new compiled stuff, I feel that I can address that problem next time.

For my local server, I simply downloaded the latest version, deleted my "magento" DB, created a new empty one, and installed (checking back with Max Berndt's "Getting Started with Magento Ecommerce!" to make sure I didn't miss anything). To install French, I downloaded the French pack, downloading the "Full Package", and copying the two fr_FR folders to the appropriate folders in Magento.

Wednesday, June 03, 2009

Zend Studio for Eclipse autocompletion hinting

No comments:
I had problems getting auto-complete to work for my "product" member variable (of type Mage_Catalog_Model_Product) working in Zend Studio for Eclipse. I found the solution in this article by HanaDaddy. The below works for me... typing in $this->product-> now gives me auto complete.

/**
*
* @var Mage_Catalog_Model_Product
*/
var $product;

Debugging Magento: Vista, XAMPP, & Zend Studio for Eclipse

3 comments:
Here are some notes about what worked for me for being able to debug Magneto in Zend Studio for Eclipse using XAMPP on Vista.

For debugging with PDT & xDebug, use this excellent site. My php.ini for xDebug:
[XDebug]
zend_extension_ts = D:\xampp\php\ext\php_xdebug-2.1.0-5.2-vc6.dll
zend_debugger.allow_hosts=127.0.0.1/24, 192.168.20.107/108
xdebug.remote_enable=On
xdebug.remote_host=127.0.0.1/24, 192.168.20.107/108
xdebug.remote_port=8080
xdebug.remote_handler=dbgp

XAMPP
Download the Window's Installer version of XAMPP. I have all 4 programs (MySQL, Apache, FileZilla FTP Server, and Mercury SMTP server) all running as services that boot on startup. Make sure you have at least XAMPP version 1.7.1. Afterwards, follow the EXCELLENT Magento installation instructions by Max Berndt (thanks Max!).

ZEND DEBUGGER
I don't have full notes here, but I do remember that I had a lot of problems, and in the end used the zend debugger from the PDT even though I own a licensed version of Zend Studio for Eclipse. I downloaded org.zend.php.debug.debugger.win32.x86_5.2.15.v20081217.jar from http://downloads.zend.com/pdt/plugins/, and used 7zip to open the jar and extract ZendDebugger.dll from the resource/php5 directory, and copied it into C:\xampp\php\ext\. I then commented out all the lines from the [Zend], [XDebug], and [DEBUGGER] sections of the php.ini file. I then added the following 3 lines to the [DEBUGGER] section before rebooting Apache...
zend_extension_ts=C:\xampp\php\ext\ZendDebugger.dll
zend_debugger.allow_hosts=127.0.0.1/24, 192.168.20.25/39
zend_debugger.expose_remotely=always
I don't remember where I got the exact allow_hosts, but I do know that the default values that I had caused Apache to crash, and the above finally worked for me.
Here you might also want to refer to the article PHP Debug with Zend Debugger And Eclipse PDT Tutorial Part 1.

ZEND STUDIO FOR ECLIPSE
The following assumes that Magento can be reached from http://127.0.0.1/magento
- From the "Run" menu, select "Debug Configurations"
- On the left, right-click on "PHP Web Page" and select "New"
- Name: Magento
- Server Debugger: Zend Debugger
- PHP Server: Click on the "New" link
- Name: Magento Server, URL http://127.0.0.1 (do NOT use localhost - I don't have notes on this, but I'm sure that I had trouble accessing the admin area using local host, and switching to http://127.0.0.1 fixed the problem).
- Hit next, going to the Server Path Mapping, and "Add"
- Path on Server: the windows path to your magento dir, e.g. C:\Users\Ed\workspace\magento
- Path in Workspace: /magento
- Click Finish
- Now back in the "Debug Configurations" dialog, set file to magento/
- uncheck "Auto Generate (URL)", and set the path (first part of which is auto-generated) to /magento
- In the "common" tab, click "run" and "debug" for showing in the favorite menus.

Hopefully, the above will work for you...

Tuesday, June 02, 2009

Programmatically Importing Product with Images in Magento

6 comments:
Although written in 2009, this article is still consistently one of the best read articles on the site. Use it at your own risk. If someone knows for sure that it still works (or doesn't!), please let me know.
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:

I understood quickly with debugging (and with the help of Branko Ajzele's article) that the media gallery used in magento/app/code/core/Mage/Catalog/Model/Product.php was the object that I wanted to use, but for a newly created product, product->getMediaGalleryImages() returns NULL.

Product attribute "media_gallery_images" is only set in getMediaGalleryImages, when $product->getMediaGallery is already set, but $product->getMediaGallery is null for a newly created product - this is the cause of getMediaGalleryImages returning null.

product->setMediaGallery doesn't exist - but then again, neither does getMediaGallery (even though it's called in $product->getMediaGalleryImages)! What's going on here?!? This led me to several articles, which led to people talking about PHP5's Overloading. OK, maybe I should have known this already, but it was a cool discovery. However, it didn't help me solve my problem.

Here's where I lost a lot of time... I decided my best bet was to debug the admin area, to see how they create the media gallery. I was able to get Zend Studio for Eclipse to debug the admin area... a task which I will document shortly.

With the debugger, I could see that it was the ProductController that was creating the media gallery while loading the admin part, but EXACTLY where it was creating it, I was not able to find before it was time to go to dinner yesterday. In any case, if you want to debug this yourself, I can give you a hint: start in the "newAction" routine in /magento/app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php

Rather than going back to debugging the admin area today, I decided to take a good look at what an empty media gallery looks like, by creating a product in the Magento admin area, and then loading it with...
$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

No comments:
If I search for 'lensbase víüçñ' in google.es using internet explorer, and capture the 'q' parameter from $_SERVER["HTTP_REFERER"] in PHP, I get 'lensbase+v%C3%AD%C3%BC%C3%A7%C3%B1'. There may be an easy way to convert all the %xx back to something readable, but I did not find it (tried urldecode,  utf8_decode, iconv, and about 100 other things).

I found a urlRawDecode function that worked great on windows, but not on Linux.

I found JavaScript code here, but calling it was just another problem.

At the end, I store my data as the full gobblygook, and then "fix it" on the display. For display, I set my header to utf-8 using 
header('Content-type: text/html; charset=utf-8');
and call urldecode on the string. That fixes everything except the + symbol and, since it's just a quick and dirty check of queries from google, I just str_replace them with spaces.

Tuesday, February 10, 2009

ColorCop

No comments:

If you need a simply color picker utility, I'm very happy with ColorCop.

Wednesday, July 16, 2008

Download from remote CVS

No comments:
* Client machine (your current Linux user, eg: peter)
* 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

No comments:
For the Tourline email, I needed to parse an FM CVS Export file. First, I split up the fields with a "split" function...




//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

No comments:
While going over the settings from a German campaign, I stumbled upon the "position preferences" option. This allows you to specify to Google in which position or range of positions you want to appear in the paid search results. Great! I thought. Let's set up all the keywords to be in position 1-6, since going any lower is just a waste of time.

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

No comments:
If you find a new JShop install where products are not showing, it could be a JShop 1.2/1.3 incompatibility with MySQL5 - products not showing up is the major symptom of using MySQL5. To fix this, read the thread http://forums.jshopecommerce.com/showthread.php?t=2711. A copy of the file that needs to be merged to make it work is kept at \\server2003\Permanent\Projects\Details\Amexoptics\jss versions\MySQL5_Patch

Monday, November 19, 2007

Missing data in XML (format FMPXMLRESULT)

No comments:
We usually have XML data with some value missing, like in this example:



<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>



In order that the XML reader overlooks the data missing at the second position in a consistent way, the following condition can be used (the following code is "conceptual", which means that it is not the real one that is currently running):


(...)

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

1 comment:
In the past, I had problems using IIS's FTP to work. Therefore, I used WarFTP, which was an easy setup and worked fine.

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

No comments:
On one of our client's web shops using SecPay's SECPage interface to check out (without 3D turned on - 3D = MasterCard's 'SecureCode', and Visa’s 'Verified by Visa'), we have 4.5% of users that never come back from SecPay (marked "New" in JShop), and 0.5% whose credit cards fail.

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

No comments:
While working on automating our daily FileMaker -> SQLServer data dump, we needed a way to automatically log into FileMaker whenever it brought up the password dialog. When Felipe mentioned the problem this morning when we got our daily morning coffee at the bakery downstairs, I realized that it could be done with some old Win32 calls.

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

No comments:
CT recently had to take the shop offline, and I added the following to /templates/includes/top.html (and top_checkout.html) to make sure that we could use the shop, but not customers...

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

No comments:
Here is how I got a count of all Tuesday sales from GreenLight...
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

No comments:
When checking a drop in GreenLight sales, I used...

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

1 comment:
We are using the ORDER BY CAST (field AS SIGNED) clause to sort the results by ‘power’ field. But it’s wrong!!

The way for sort ‘power’ it’s: ORDER BY `Power`+0

Strange… but works fine!!!

Thursday, December 07, 2006

MySQL Count Distinct

No comments:
I'm always (stupidly) trying to do something like "select count(distinct(country))..." when I want so see some results like...
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

No comments:
NOTE: This quick and dirty hack (part of the insert function in /routines/dbAccess_mysql.php) kills the last ID - it would need to be made nice to work 100%.

$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

No comments:
list($secs, $micros) = split(" ", microtime());
$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

No comments:
I had a number of lines of code in the form...
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

1 comment:
Many thanks to Alberto Miranda who gave me the solution to a grave problem - Zend Studio 5.2.0 suddenly (without me changing anything that I'm aware of) started using close to 100% of CPU time (actually, the Zend process no - it was the javaw.exe process - but it was spawned by Zend). I used Alberto's solution (unchecking the "Use OS Look and Feel" option in the Zend preferences) and, after rebooting the computer and rebooting Zend (which was DEAD slow until it was fully launched) fixed the problem. Alberto, m'man, I owe you unas copas!

Tuesday, September 12, 2006

Generating Euro and Dollar Prices from Pounds

No comments:
JSHOP: I wrote a file called amex_setPrices.php for the Lensbase site (in the admin folder) which sets price according to the following formula:
//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

No comments:
I got a reorder reminder cron job error that said:
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

No comments:
Just read a great post by John Wood on dotnetjunkies.com called "SafeInvoke: Making GUI Thread Programming Easier in C#". It takes a lot of the drudgery out of the way by avoiding the direct typing of delegates. The code is here, and I include it here in case the link disappears.

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

No comments:
- "in box" is the number of physical boxes which actually exists
- "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

No comments:
In order to allow multiple pocket PCs to syncronize to the same folder, open RegEdit and go to HKEY_CURRENT_USER\Software\Microsoft\Windows CE Services\Partners\. There, each partnership defines it's shared folder in HKEY_CURRENT_USER\Software\Microsoft\Windows CE Services\Partners\[somenumber]\Services\Synchronization\Briefcase Path. Make sure all the Briefcase paths are set to the same folder. EXACT NAME IS IMPORTANT, so copy and paste the value from the partnership that you are interested in.

Monday, May 08, 2006

GMail PHPed

No comments:
When FMImporter is done importing the CSV files into FileMaker, it sends an email to a gmail account with the mail title the name of the CSV file as the title and the contents as the body. Today, it happened that I needed to reprocess 115 of these files, and wanted an easy way to turn the emails into files.

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);
$body = str_replace("
", "\r\n", $body);
$body = str_replace("
", "", $body);
fwrite($fh, $body);
fclose($fh);
continue;

Thursday, April 13, 2006

Selenium IDE

No comments:
When tracking down my "system" php call bug, I needed to register a user again and again and again. After the first dozen times or so, I looked for macros for FireFox, and found Selenium IDE, which did the trick for me. The only "trick" you need to know is that the record button is the little red one to the RIGHT of the toolbar. Otherwise, it's all pretty intuitive.

php system call problems

No comments:
Using something like...
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

No comments:
For WebGains, be sure to keep a min. of £100 balance in the account. This is required by their contract, which states "5.4 The Merchant shall ensure that there are always sufficient cleared funds in the Merchant Account to meet all of the liabilities of the Merchant. In the event that the funds in the Merchant Account fall below £100, WBG reserves the right to suspend the Services unless and until sufficient funds are provided.". Got this from Derek Grant [derek.grant@webgains.com] on Mon 16/01/2006 16:47 as enclosure.

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

No comments:
I'm often doing something really dumb like..

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

No comments:
If you want to set up a xAMP enviornment quickly, you use EasyPHP or one of the similar packages for Windows. But how about for Macintosh? Even though there are already parts of an xAMP setup there, it's MUCH quicker and easier to just set up MAMP, which will not interfer with any other xAMP components that you already have installed on your machine.

Friday, January 06, 2006

Win2K DVD Decoder

No comments:
A bit off topic - to get free DVD playback on Win2K, try DScaler 5 - check the "DScaler 5 - Alpha MPEG Filters" link.

Wednesday, December 21, 2005

Compiles that go to the client

No comments:
Whenever a version goes to a client, we NEED to

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

1 comment:
The freeware installer can be found on \\server2003\Permanent\Software Depot\Plug-ins. Drop the AutoInstaller folder and the 24uPlugInInstall.fp5 DB into C:\Program Files\FileMaker\FileMaker Developer 6\ folder, and run the auto installer. Unclick all but the "W" (Windows) version of the 24U SimpleDialog plug in, and hit the Update All button. That's it.

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

No comments:
To move putty's setting from one machine to another, use regedit to export a reg file of HKEY_CURRENT_USER\Software\SimonTatham\PuTTY, and install it on a new machine.

FileMaker via ODBC Performance Problems

No comments:
When Cristóbal was updating fields in Batch Line Items.fp5 (a FileMaker DB) via ODBC, each field update was taking about 30 seconds. A small part of the problem was the calculated fields, and if you ever need to maximize performance, you might want to look to see if it’s possible to remove them.
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.

Direct Link to Blogger Article

No comments:
If you need to link to a particular article in Blogger, surf to the blog in FireFox, right click, select “Web Developer” (this assumes you have it installed), “Information”, and “View Link Info”. There, you will be able to see all the direct links on the page.

Friday, November 25, 2005

ActiveSync - "Combine or Replace" Dialog

Just had a case (after a power outage) where we got a strange dialog from the Desktop PC running active sync:

The following information type on your mobile device has items that have not been syncronized with this computer before. "combine or replace" information type: files

Combine the items on my device with the items on this computer.
Replace the items on my device with the items on this computer.
Do not syncronize this information type at this time

Not really sure what happened, but it appears as if the file that coordinates the sync got messed up. Best article I read on it was at http://www.pocketpcfaq.com/raj/Combine-Replace.html, reproduced here in case the link eventually disappears:

ActiveSync - "Combine or Replace" Dialog
By Raj Pillai, Copyright 2003-2004
Version 1.24 Revised 5/11/2004

Click to Visit Pocket PC Talk

Introduction:

Every Pocket PC user encounters the Combine or Replace dialog box as shown below at some point of time although the Information Type might differ. The information below tries to explain why this dialog box is prompted and the scenario's when this dialog box can appear.



Why Does This Dialog Appear?

When you connect your Pocket PC for synchronization purpose ActiveSync compares the data on the Desktop and the device using something called as "store identifiers" along with time-stamps to determine if the information type has changed. If the identifiers do not match, then it tries to re-establish the mapping between the Desktop and the Pocket PC by displaying the above dialog box.

What Do The Options Indicate?

The "Information Type:" will list the type of information that ActiveSync has not mapped. The three options are self explanatory.

*

The 1st option will simply combine all the information (Pocket PC + Computer) on both the devices.
*

The 2nd option will copy all the information from the Computer to the Pocket PC. It will replace all the information stored on the Pocket PC.
*

The 3rd option does not synchronize the listed information types.

There is no option which will replace the data on your computer with the Pocket PC data. The closest way of doing it would be to use the 1st option and combine the data.

Why Did The Dialog Appear?

Lets take a look at the scenarios where this dialog box will appear:

*

When you synchronize your Pocket PC with data for the first time.
*

When you delete a partnership on the Desktop without deleting the information on the device, and then reconnects the device to create a new partnership.
*

When you try to synchronize with a different Outlook profile for the first time.
*

When you move the location of the default Outlook ".pst" file after establishing a partnership.
*

When you synchronize with two computers and perform a replace operation on one computer, and then synchronizes the device with a second computer.
*

When you synchronize after restoring the Pocket PC data from a backup file.
*

When you delete the repl.dat file, which stores the partnership information. This file can be found in the ActiveSync Profiles directory. The default path in Windows XP would be:

C:\Documents and Settings\<>>\Application Data\Microsoft\ActiveSync\Profiles\<<>>

The Combine or Replace Dialog Appears Every Time I Synchronize:

If you are synchronizing with only "one computer" and this dialog appears everytime you synchronize, then you might need to recreate the partnership. Due to some kind of corruption the store identifiers might getting affected at every synchronization and therefore are not able to map themselves correctly.

To delete a partnership:

1. Disconnect the Pocket PC from the computer.
2. In ActiveSync -> File -> Delete Partnership. Select whether you want to delete your synchronization folder as well.
3. Connect the device and follow the onscreen instructions to setup a new partnership.

If you are synchronizing with "two computers" and the dialog appears on a regular basis, then it appears that for some reason, the store identifiers for the selected information types is getting affected when you synchronize with the second desktop PC and therefore the first computer is unable to match the identifiers when it compares and pops up the Combine or Replace dialog.

Although the Combine option creates duplicates, as far as I know ActiveSync version 3.7.1 checks for duplicate entries as well and prompts you whether you would like to remove or keep them. But in some scenarios it is unable to identify the duplicates since the identifiers are different. If you have a lot of duplicate entries, then I recommend you use the free Outlook Duplicate Remover utility, written by Fr. Simon Rundell. It is available for download at the following site:

http://www.slipstick.com/addins/contacts.htm#dupes

You could verify if the problem is caused due to synchronization with a secondary PC by first synchronizing with either of your computer and then before synchronizing with the second computer, create a new Contact/ Appointment on your device and another set on your Desktop (all having different name or else a conflict will occur) and see if you are displayed the dialog. In this scenario the information will get updated automatically on both the devices without the C/R dialog since the store identifiers have not been affected.

In such a scenario, delete both the partnerships on the computer and form a new partnership. You could try the following approach:

Before you leave work, synchronize your Pocket PC and after synchronization is successful delete the partnership. Go to your home PC. Delete the partnership on the home PC as well before connecting. Now connect and then create a new partnership and you might be prompted with the Combine or Replace dialog once select combine since you want the data on your Pocket PC to reflect on your home computer as well. Once this is done, repeat the steps to create a new partnership on your work PC.

This information should help you understand the possible causes and how you could go about solving the recurring Combine or Replace dialog that appears whenever you try to synchronize with your computer.

Thursday, November 10, 2005

Getting CVS Commit emails from ModWest

No comments:
- Download CVS spam from http://www.badgers-in-foil.co.uk/projects/cvsspam/
- Upload the file to your CVS folder
- With the shell, decompress the tarball with tar -zxvf cvsspam???.tar.gz
- Rename the resulting folder "cvsspam"
- cd /cvs/CVSROOT
- chmod 777 commitinfo
- vi commitinfo, and add the line
ALL /usr/bin/ruby /cvs/cvsspam/record_lastdir.rb
- save the change
- chmod 444 commitinfo
- chmod 777 loginfo
- vi loginfo, and add the line
ALL /usr/bin/ruby /cvs/cvsspam/collect_diffs.rb --to me@mydomain.com %{sVv}
- save the loginfo
- chmod 444 loginfo
- make the directory /cvs/cvsspam/
- mv /cvs/cvsspam/cvsspam.conf /cvs/cvsspam
- ModWest's sendmail does not accept the -io option (see http://www.modwest.com/help/kb10-124.html) used by cvsspam. Remove the option from /cvs/cvsspam/cvsspam.rb
- To add more people to the send list, check the /cvs/cvsspam/cvsspam.conf file