Showcase

Showcase of tips and tricks that make programming easier. Some say that this is where I put everything that otherwise gets lost and re-created often...

A years worth of browser screen resolutions

A very non definitive list of browser screen resolution in terms of height, but possibly useful as it's from www.frenchpropertysearch.com which attracts a large cross section of users. Table is of course sortable by the headers.

x y count
1024 768 18762
1280 800 10964
1280 1024 6681
1440 900 4238
1680 1050 2472
800 600 2064
1920 1200 1343
1152 864 1212
1280 768 668
1300 2300 497
1280 960 392
1400 1050 272
1600 1200 221
1300 1300 215
1280 720 199
1024 600 147
1366 768 145
1280 854 116
1600 900 75
1360 768 70
320 396 66
1024 640 35
1152 720 30
1920 1080 29
1536 960 28
1344 840 27
800 480 24
200 100 23
640 480 19
2560 1600 18
969 768 17
2560 1024 17
1600 1024 16
1120 840 14
960 600 13
1024 1280 13
1152 870 13
240 320 12
1152 768 10
320 240 9
3840 1024 9
1280 994 8
236 320 7
819 614 7
1024 614 7
1600 1000 7
1024 738 6
1024 819 6
1440 960 6
832 624 5
2048 768 5
124 160 4
480 272 4
922 691 4
1000 1000 4
1200 1600 4
50 2000 3
124 96 3
240 218 3
480 320 3
768 1024 3
800 1038 3
900 1440 3
1224 768 3
1360 1024 3
1408 1126 3
1512 945 3
2048 1152 3
2048 1536 3
3072 768 3
176 220 2
236 300 2
236 400 2
720 576 2
800 352 2
1010 700 2
1010 708 2
1024 711 2
1024 720 2
1024 742 2
1050 1680 2
1152 900 2
1274 970 2
1280 600 2
1280 900 2
1440 852 2
1680 945 2
1836 1174 2
86 35 1
124 140 1
170 180 1
170 220 1
172 208 1
172 220 1
234 282 1
240 160 1
240 276 1
316 240 1
360 480 1
640 384 1
794 322 1
800 472 1
800 570 1
800 628 1
800 5000 1
848 480 1
853 683 1
896 717 1
980 720 1
981 613 1
990 693 1
990 714 1
998 701 1
1007 693 1
1008 710 1
1010 707 1
1024 767 1
1045 836 1
1056 792 1
1148 836 1
1152 922 1
1159 927 1
1168 847 1
1176 664 1
1192 668 1
1200 668 1
1200 1920 1
1203 671 1
1220 970 1
1224 691 1
1232 770 1
1268 951 1
1272 960 1
1272 964 1
1272 989 1
1274 962 1
1276 960 1
1280 820 1
1280 896 1
1280 964 1
1344 1008 1
1360 764 1
1360 765 1
1376 768 1
1400 1020 1
1408 880 1
1432 1122 1
1444 1130 1
1588 1200 1
1600 1050 1
1688 964 1
1920 1440 1
2304 864 1
2880 900 1
2960 1050 1
3072 1280 1
5120 1024 1

Generate SQL to identify duplicates

A small tool that javascript to generate SQL that will identify duplicated records in a table





Guru and User1

ASCII cartoon strip, because I can't draw.

Today Guru tackles the difficulties of choosing an architecture.

1
     .--------------------------------.
     |Guru, should I use Ruby or Java |
     |for web apps in the cloud?      |
     `------.------------------------------.
            |Well User1 it depends on your |
            |skillset and the requirements.|
            :-----------------------------.'
            |Analyse your requirements    |
            |perform some evaluations and |
            |come to a conclusion.        |
        ($) `-----------------------------'(#)
        /|\                                /|\
        / \                                / \
2      .-------------------------.
       |Can't you just tell me?  |
       `-------------------------'
                 .----------------------------.
                 |You need to do the analysis.|
                 `----------------------------'
          .---------------------------.
          |Don't be obstructive, we're|
          |all on the same team       |
          `---------------------------'
        ($)                               '(#)
        /|\                                /|\
        / \                                / \
 3  .----------------------------------.
    |Please tell me, I know you know...|
    `-------------.--------------------------.
                  |Do you like chilli sauce? |
     .--------------------.------------------'
     |Yeah I love it, why?|.---------------.
     `--------------------'| Use Ruby then |
         .---------------------------.-----'
         |That's not very analytical |
      ($)`---------------------------'  '(#)
      /|\                                /|\
      / \                                / \

4               .------------------------------.
                |My analysis shows that 68% of |
                |projects using Ruby are       |
                |successfully developed by a   |
                |team who love chilli sauce.   |
        .----------------.---------------------'
        |Ruby it is then |
        `----------------'
        .------------------------------.
        |Isn't there a more scientific |
        |way of doing this?            |
        `------------------.---------------------.
                           |You'd have thought...|
                           `---------------------'
        ($)                                     (#)
        /|\                                     /|\
        / \                                     / \


How to import a SQL Server .bak file into MySQL

The .BAK files from SQL server are in Microsoft Tape Format (MTF) ref: http://www.fpns.net/willy/msbackup.htm

The bak file will probably contain the LDF and MDF files that SQL server uses to store the database.

You will need to use SQL server to extract these. SQL Server Express is free and will do the job.

So, install SQL Server 2008 Express edition, use sqlcmd -S \SQLExpress (whilst logged in as administrator)

then issue the following command.

restore filelistonly from disk='c:\temp\mydbName-2009-09-29-v10.bak';
GO

This will list the contents of the backup - what you need is the first fields that tell you the logical names - one will be the actual database and the other the log file.

RESTORE DATABASE mydbName FROM disk='c:\temp\mydbName-2009-09-29-v10.bak'
WITH 
   MOVE 'mydbName' TO 'c:\temp\mydbName_data.mdf', 
   MOVE 'mydbName_log' TO 'c:\temp\mydbName_data.ldf';
GO

At this point you have extracted the database - then install Microsoft's "Sql Web Data Administrator". together with this export tool and you will have an SQL script that contains the database.

Reverse engineered AmiBack (Amiga) extraction utility.

This is really a quick hack to read AmiBack backups - I don't know if it works with floppy based backups - but probably the source below is most useful for understanding the format.

I used it succesfully to restore all of my backups that were stored on QIC Tape (45/150mb).

/*---------------------------------------------------------------------------
 *
 *      Title:          AmiBack extraction utility
 *
 *      File:           amiread.cpp
 *
 *      Date:           May 2005
 *
 *      Description:
 *          Reads/extracts files from a dumped amibackup format archive.
 *
 *      Rev     By              Description                            Date
 *      1.0     R.J.Harrison    Implement                             05-05-2005
 *
 *      Copyright © 2005 R.J.Harrison
 *---------------------------------------------------------------------------*/

#pragma warning(disable : 4786)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <direct.h>
#include <io.h>

#include <fcntl.h>
#include <vector>
#include <string>

#pragma pack(1)
typedef struct {
	char header[4]; // FHDR
	char name[108];// fname
	long aa; //???
	long filesize;
	long ee;
	long ff;
	long gg;
	long hh;
	char depth;
	char type;
	// content follows. (unless filesize 0);
}AmiHeader;

void check_path(const char *path)
{
	FILE *q;
	static char *ptr,*name, *end, tmp [200];
	
	strcpy (tmp,path);
	end = ptr = tmp+strlen(tmp);
	while (*--ptr && *ptr != '/');
	if(*ptr)
	{
		*++ptr = 0; /* Remove file name */
		name = ptr = tmp;
		do
		{
			while (*ptr && *ptr != '/') ptr++;
			if(*ptr)
			{
				*ptr = 0;
				if(!(q=fopen(name,"r")))
				{
					mkdir (name);
				}
				else
				{
					fclose (q);
				}
				*ptr++ = '/';
			}
			
		}while (*ptr && ptr < end);
	}
}

main()
{
	FILE *f = fopen("N:/Users/Richard/tape/Tapes/amiga_wb_work.amiback","rb");
	std::vector<std::string> dirz(100);
	#define BUFFER_SIZE 1000 /* k */
	int buffer_size = BUFFER_SIZE;
    buffer_size *= 1024;
    void *file_buffer = malloc(buffer_size + 10);
	
	if (!f)
	{
		fprintf (stderr, "Not found\n");
		exit(-1);
	}
	char hdr[512];
#define SWAP_LONG(v) (((v & 0xff000000) >> 24) | ((v & 0xff0000) >> 8) | ((v &  0xff00) << 8) | ((v & 0xff) << 24))
	fread(hdr,1,sizeof(hdr),f);
	printf ("%d\n",sizeof(AmiHeader));
	int fc=0;
	std::string fname;
chdir("C:/temp/amiga/");
	while(!feof(f))
	{
		AmiHeader fhdr;
		fread (&fhdr, 1, sizeof(fhdr),f);
		fhdr.filesize = SWAP_LONG(fhdr.filesize);
		if (fc == 9175)
			printf("%x\n", ftell(f));
		//? Colon in name is new device...
		if (strncmp(fhdr.header, "FHDR", 4))
			printf ("???Header???");
		printf ("%5d: %10d - ",fc++,fhdr.filesize);
		fname = std::string();
		switch (fhdr.type)
		{
		case 0x2:
			{
				char *s = strchr(fhdr.name, ':');
				if (s)
					*s = 0; // zoink colon from devname
				dirz[0] = fhdr.name;
			}
			break;
		case 0x0:
			dirz[fhdr.depth] = fhdr.name;
			break;
		default:
			for (size_t i = 0; i < fhdr.depth; i++)
			{
//				printf ("%s/",dirz[i].c_str());
				fname = fname + dirz[i] + "/";
			}
			break;
		}
		fname = fname + fhdr.name;
		printf ("%s\n",fname.c_str());
		if (fhdr.filesize)
		{
			bool list = false;
			int size = fhdr.filesize;
			int of;
			if(!list)
			{
				check_path(fname.c_str());
				of = open(fname.c_str(), O_BINARY | O_CREAT | O_WRONLY | O_TRUNC, 0x666);// need mode..hdr.mode);
				if(!of) fprintf(stderr,"unable to open %s\n", fname.c_str());
			}
			
			while(size >= buffer_size)
			{
				fread(file_buffer, 1, buffer_size, f);
				if(!list)
					write(of, file_buffer, buffer_size);
				size -= buffer_size;
			}
			if(size > 0)
			{
				fread(file_buffer, 1, size, f);
				if(!list )
					write(of, file_buffer, size);
			}
			if (!list )
				close(of);
//			if (fseek(f, fhdr.filesize, SEEK_CUR))
//			{
//				fprintf (stderr,"Seek failed\n");
//				exit(-1);
//			}
		}
		else
		{
			printf ("Directory\n");
			if (fhdr.type)
				printf ("Type %d\n", fhdr.type);
		}
		//ii[0] - 0x2 = device;
		//ii[0] - 0x0 = dir;
		//ii[0] - 0x1 = file?
	}
}


SQL server 2005. SQL to create table structure

The following isn't mine, it was written by David. I include it here for the usual reason.

select  'create table [' + so.name + '] (' + o.list + ')' + CASE WHEN tc.Constraint_Name IS NULL THEN '' ELSE 'ALTER TABLE ' + so.Name + ' ADD CONSTRAINT ' + tc.Constraint_Name  + ' PRIMARY KEY ' + ' (' + LEFT(j.List, Len(j.List)-1) + ')' END
from    sysobjects so
cross apply
    (SELECT 
        '  ['+column_name+'] ' + 
        data_type + case data_type
                when 'sql_variant' then ''
                when 'text' then ''
                when 'decimal' then '(' + cast(numeric_precision_radix as varchar) + ', ' + cast(numeric_scale as varchar) + ')'
                else coalesce('('+case when character_maximum_length = -1 then 'MAX' else cast(character_maximum_length as varchar) end +')','') end + ' ' +
        case when exists ( 
        select id from syscolumns
        where object_name(id)=so.name
        and name=column_name
        and columnproperty(id,name,'IsIdentity') = 1 
        ) then
        'IDENTITY(' + 
        cast(ident_seed(so.name) as varchar) + ',' + 
        cast(ident_incr(so.name) as varchar) + ')'
        else ''
        end + ' ' +
         (case when IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' + 
          case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT ELSE '' END + ', ' 

     from information_schema.columns where table_name = so.name
     order by ordinal_position
    FOR XML PATH('')) o (list)
left join
    information_schema.table_constraints tc
on  tc.Table_name               = so.Name
AND tc.Constraint_Type  = 'PRIMARY KEY'
cross apply
    (select '[' + Column_Name + '], '
     FROM       information_schema.key_column_usage kcu
     WHERE      kcu.Constraint_Name     = tc.Constraint_Name
     ORDER BY
        ORDINAL_POSITION
     FOR XML PATH('')) j (list)
where   xtype = 'U'
AND name        NOT IN ('dtproperties')

Simple batch file (CMD) for deploying a .NET aspx site from a SVN controlled directory.

What follows is in the quick hack category - I'm sure there is a much better of doing this - but this works and keeps my servers cleaner by not copying the .aspx.* files (e.g. .aspx.vb .aspx.rex) and the project files.

Before running this for the first time you will need to
svn co www-svn
net stop w3svc
svn update www-svn
rd/s www
xcopy/t/e/y www-svn www
xcopy/s/y www-svn\*.aspx www\
xcopy/s/y www-svn\*.ascx www\
xcopy/s/y www-svn\*.config www\
xcopy/s/y www-svn\bin\*.* www\bin\
xcopy/s/y www-svn\*.jpg www\
xcopy/s/y www-svn\*.png www\
xcopy/s/y www-svn\*.gif www\
xcopy/s/y www-svn\*.js www\
xcopy/s/y www-svn\*.css www\
xcopy/y www-svn\global*.* www\
net start w3svc

Simple method to make a site multi-lingual in PHP

Firstly do this from the start of the project - it's a lot easier and quicker than editing at the end.

Wherever you need text that is to be translated use the simple sequence ~PHnnnn~, where nnnn is a four digit number. It slows you down a little at first - however the benefits later on are massive - for one thing you can edit the English text and you therefore get a simple CMS for free.

For example:

    phrase_op("<h1>~PH2228~</h1>");

phrase_op is a simple routine that takes phrases from a database and replaces all of the tokens found.

/**
 * outputs a string changing all the ~PH####~ into the corresponding phrase
 */
function phrase_op($txt)
{
    preg_match_all("/\~PH[0-9][0-9][0-9][0-9]\~/",$txt,$regs,PREG_SET_ORDER);
        foreach($regs as $result) {
        $phc = substr($result[0],1,strlen($result[0])-2);
        $ph = get_phrase($phc);
        $txt = str_replace($result[0],$ph,$txt);
    }
    echo $txt;
}


function load_phrases()
{
    global $phrases;
    global $config;

    $phrases = array();

    $sql = "SELECT * FROM tb_phrases where iso='".mysql_real_escape_string($config['cur_lang'])."'";

    $result = mysql_query($sql) or die('exec_query failed: [ '.$sql.' ]'.mysql_error());

    while ($row = mysql_fetch_array($result,MYSQL_ASSOC))
    {
        $phrases[$row['phrase_code']] = $row['text'];
    }
}

All you need to do now is to create the sql to create the phrases, and a quick page to allow editing.

Useful regex's to verify email, alphanumeric, numeric (to ensure that an entire string matches)

Regex is a black art, with the usual rule being the smaller the better.

The following are a basic collection that I use quite often:

Using Wordpress 4.1.7, Woocommerce to create a furniture store

It’s been a while since I did any Wordpress development, but recently I was asked to build a straightforwards, clean, site to act as a shop front / ecommerce for a retailer of special and refurbished furniture.

As always with these sites, the URL caused a considerable discussion, until it was decided that zunika-interiors.co.uk fitted nicely into their target niche market.

The last site I built was using Wordpress 4.1.7 with Woocommerce, so I started off with a direct copy of this (it’s always easier to drop the whole lot onto the ftp server and then upgrade later. Mostly these days it’s a case of choosing your plugins wisely, and hopefully the theme will play along. Unfortunately the last site was using the excellent Barberry theme, but for a new site that’s not an option as I didn’t buy this plugin (it was the last client), and as always budgets are tight so I had to settle for the almost as good (and still excellent) mystile theme.

The logo was already done, so it was a case of setting up the catalogs, configuring the menus and sidebars, and overall I reckon that the effective development time was less than a day (in reality it was longer despite my best efforts to avoid problems with versioning I still managed to confuse myself, when eventually I just went back to my known good configuration of plugins and themes).

Wordpress, Woocommerce configuration and plugins

As with most things on my site I’m writing this down so that next time I don’t have to find my notes.

These are the plugins that I used

Theme niceness

It’s only really been a few years since I properly did a Wordpress site and I have to say that I’m impressed by all of the niceness that the mystile theme gives me.

It’s clean

It’s responsive.

Conclusion

Although the site development is finished there is still a lot of admin for the site owner to finish off (he’s got to go through his warehouse which I’ve seen and it’s full to the brim with some really good quality, solid, furniture. I suspect that this will take a while as the warehouse is quite dark and all of the pieces need to have good photographs to really show them off at their best). There was one piece that had a really lovely walnut veneer, that was hardly visible when I used my cheap and nasty camera (that’s attached to my cheap and nasty phone)). He’s got a good camera, and I hope his muscles are up to all of the hefting, but it’ll be worth it.

Until he’s got all of the furniture loaded the site will be quite bare (apart from my obligatory inclusion of an F-15 as a test product); but if anyone’s interested it’s here good quality furniture .