<?xml version="1.0"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>N-webdesign blog</title>
		<link>http://www.n-webdesign.co.uk/blog/</link>
		<atom:link href="http://www.n-webdesign.co.uk/blog/" rel="self" type="application/rss+xml" />
		<description></description>

		
		<item>
			<title>NopCommerce 2.3 (MVC) alternative product gallery</title>
			<link>http://www.n-webdesign.co.uk/nopcommerce-2-3-mvc-alternative-product-gallery/</link>
			<description>&lt;p&gt;I had a request recently to change the thumbnail selections in the product detail screen from a popup (using Slimbox2) to an option that changes the main image when clicking a thumbnail.&lt;/p&gt;&lt;p&gt;This is pretty standard fayre and you can do the ajax stuff either by writing your own image swap script or using an existing gallery plugin.&lt;/p&gt;&lt;p&gt;I opted for changing from Slimbox to Galleriffic. This makes it really easy to do, gives a ton of options and is pretty easy to do.&lt;/p&gt;&lt;p&gt;Ok, on with the show...&lt;/p&gt;&lt;p&gt;This is probably a reasonably good guide to extending or changing NopCommerce 2+ (MVC) code, which is probably something a lot of people will be trying to do.&lt;/p&gt;&lt;p&gt;you'll need a working version of NopCommerce with source code and Galleriffic. Nop is &lt;a href=&quot;http://nopcommerce.com/&quot;&gt;here&lt;/a&gt;, Galleriffic is &lt;a href=&quot;http://www.twospy.com/galleriffic//&quot;&gt;here&lt;/a&gt;. JQuery is already in Nop, so no need to worry about that.&lt;/p&gt;&lt;p&gt;First thing to do is find where the existing image pop-ups are generated?&lt;/p&gt;&lt;p&gt;If you look in ProductTemplates.VariantsInGrid.cshtml, you'll see a reference to _ProductDetailsPictures in a render call. This is probably where we need to look.&lt;/p&gt;&lt;p&gt;In _ProductDetailsPictures.cshtml, we see several really cool things.&lt;/p&gt;&lt;p&gt;A reference to AddScriptParts() with a call to Slimbox (Great, we now know where to inject our JQuery/Plugin stuff)&lt;br /&gt;A reference to the default picture in a div (@Model.DefaultPictureModel etc)&lt;br /&gt;A set of thumbnails in a table&lt;/p&gt;&lt;p&gt;So we know that Galleriffic [in our example] needs a reference to JQuery, itself and an initialiser. It also needs a div to target with the selected image (or the default) and a set of thunbs in a list with the correct #id.&lt;/p&gt;&lt;p&gt;JQuery is loaded already for this view.&lt;/p&gt;&lt;p&gt;Copy the Galleriffic script into the scripts folder (in Nop.Web).&lt;/p&gt;&lt;p&gt;Now, create an initialiser script in the scripts folder.  Call it something like 'gallery.init.js'. Add the inititaliser as per Galleriffic. Mine looks like this:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;jQuery(document).ready(function($) {&lt;br /&gt;    var gallery = $('#thumbs').galleriffic({&lt;br /&gt;//        delay:                     3000, // in milliseconds&lt;br /&gt;//        numThumbs:                 20, // The number of thumbnails to show page&lt;br /&gt;//        preloadAhead:              40, // Set to -1 to preload all images&lt;br /&gt;//        enableTopPager:            false,&lt;br /&gt;//        enableBottomPager:         true,&lt;br /&gt;//        maxPagesToShow:            7,  // The maximum number of pages to display in either the top or bottom pager&lt;br /&gt;        imageContainerSel:         '.picture', // The CSS selector for the element within which the main slideshow image should be rendered&lt;br /&gt;        controlsContainerSel:      '#controls', // The CSS selector for the element within which the slideshow controls should be rendered&lt;br /&gt;        // captionContainerSel:       '', // The CSS selector for the element within which the captions should be rendered&lt;br /&gt;        // loadingContainerSel:       '', // The CSS selector for the element within which should be shown when an image is loading&lt;br /&gt;        renderSSControls:          false, // Specifies whether the slideshow's Play and Pause links should be rendered&lt;br /&gt;        renderNavControls:         false, // Specifies whether the slideshow's Next and Previous links should be rendered&lt;br /&gt;//        playLinkText:              'Play',&lt;br /&gt;//        pauseLinkText:             'Pause',&lt;br /&gt;//        prevLinkText:              'Previous',&lt;br /&gt;//        nextLinkText:              'Next',&lt;br /&gt;//        nextPageLinkText:          'Next ›',&lt;br /&gt;//        prevPageLinkText:          '‹ Prev',&lt;br /&gt;//        enableHistory:             false, // Specifies whether the url's hash and the browser's history cache should update when the current slideshow image changes&lt;br /&gt;//        enableKeyboardNavigation:  true, // Specifies whether keyboard navigation is enabled&lt;br /&gt;        autoStart:                 false, // Specifies whether the slideshow should be playing or paused when the page first loads&lt;br /&gt;        syncTransitions:           false, // Specifies whether the out and in transitions occur simultaneously or distinctly&lt;br /&gt;        defaultTransitionDuration: 1000, // If using the default transitions, specifies the duration of the transitions&lt;br /&gt;        onSlideChange:             undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... }&lt;br /&gt;        onTransitionOut:           undefined, // accepts a delegate like such: function(slide, caption, isSync, callback) { ... }&lt;br /&gt;        onTransitionIn:            undefined, // accepts a delegate like such: function(slide, caption, isSync) { ... }&lt;br /&gt;        onPageTransitionOut:       undefined, // accepts a delegate like such: function(callback) { ... }&lt;br /&gt;        onPageTransitionIn:        undefined, // accepts a delegate like such: function() { ... }&lt;br /&gt;        onImageAdded:              undefined, // accepts a delegate like such: function(imageData, $li) { ... }&lt;br /&gt;        onImageRemoved:            undefined  // accepts a delegate like such: function(imageData, $li) { ... }&lt;br /&gt;    });&lt;br /&gt;});&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;(This shows all the available options with any unused ones commented out, so it's a bit messy here).&lt;/p&gt;&lt;p&gt;Now in the _ProductDetailsPictures.cshtml file, comment out or delete the reference to Slimbox.js and add these 2 references:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;@{&lt;br /&gt;    Html.AddScriptParts(@Url.Content(&quot;~/Scripts/jquery.galleriffic.js&quot;));&lt;br /&gt;}&lt;br /&gt;@{&lt;br /&gt;    Html.AddScriptParts(@Url.Content(&quot;~/Scripts/gallery.init.js&quot;));&lt;br /&gt;}&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;If you like, you can now F5 and check it all runs, and the 2 files are showing in the source.&lt;/p&gt;&lt;p&gt;We now need to add one thing to the 'model', so we can get a uniform display of product images, irrespective of size. Here's what I mean and why.&lt;/p&gt;&lt;p&gt;In the standard Slimbox presentation, it loads the default image into the picture div, which it gets from '@Model.DefaultPictureModel.FullSizeImageUrl'. &lt;/p&gt;&lt;p&gt;This formats the image based on 'Product detail image size' in Media Settings, which is in the DefaultPictureModel, which we don't have available in our PictureModel.&lt;/p&gt;&lt;p&gt;To fix this, we need to add a new field to the PictureModel which we can use in the controller to define a set of picture URLs that are set to the correct [and uniform] size. &lt;/p&gt;&lt;p&gt;This means that we can upload images of different sizes into the product details, but we can always rely on them being re-sampled to whatever our 'picture' div is set to, so we don't get unruly behaviour if someone uploads a 800 x 800 pixel image. We always get in bounced down to out defined setting.&lt;/p&gt;&lt;p&gt;Open PictureModel.cs [in Nop.Web/Models/Media. Add the line 'public string ProductImageURL { get; set; }' with all the other getters &amp;amp; setters. This just gives us a place to define a URL specifically for our uniform images.&lt;/p&gt;&lt;p&gt;(This really should be done as an extended partial class, so we retain the base code intact. It's cool if you want to do it that way, this is just a bit easier to manage for now).&lt;/p&gt;&lt;p&gt;Next, we need to amend the controller to add data into our new ProductImageURL. In theory, we could add a whole new controller for this, but just changing the existing one works. Open CatalogueController.cs (it's in Controllers) and find the ActionResult for Product (about line 1165).&lt;/p&gt;&lt;p&gt;The bit we need to amend is actually in the 'PrepareProductDetailsPageModel(product);' method, so navigate to there. Scroll down to the 'pictures' section, and there are 2 things it does. One is set the DefaultPictureModel and the other is to create a list of PictureModels based on the product model pictures.&lt;/p&gt;&lt;p&gt;Just add the line:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;ProductImageURL = _pictureService.GetPictureUrl(picture, _mediaSetting.ProductDetailsPictureSize),&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Into the model.PictureModels.add() call. Don't forget the trailing comma! This will give us a URL, with the correct picture size, to use as a reference in our thumbs list.&lt;/p&gt;&lt;p&gt;Now we need to remove the default picture reference from the picture div, plus create a list of thumbs in _ProductDetailsPictures.cshtml. &lt;/p&gt;&lt;p&gt;So comment out or remove the contents of the picture div, so it now looks like this:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;div class=&quot;picture&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;[EDIT: You may want to call this div prod-picture, as I just noticed another div called picture if you add related products. Whatever you call it, don't forget to re-name the target 'imageContainerSel:' var in the gallery init script!]&lt;/p&gt;&lt;p&gt;This tells the initialiser where to put the images when we select them. You'll note it is regerenced in the initialiser script.&lt;/p&gt;&lt;p&gt;Now comment out or remove the table contining the thmbs and replace with:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;div id=&quot;thumbs&quot;&amp;gt;&lt;br /&gt;    @if (Model.PictureModels.Count &amp;gt; 0)&lt;br /&gt;    {&lt;br /&gt;        &amp;lt;ul class=&quot;thumbs noscript&quot;&amp;gt;&lt;br /&gt;                @foreach (var picture in Model.PictureModels)&lt;br /&gt;                {&lt;br /&gt;                    &amp;lt;li&amp;gt;&lt;br /&gt;                        &amp;lt;a &lt;br /&gt;                            href=&quot;@picture.ProductImageURL&quot; &lt;br /&gt;                            rel=&quot;lightbox-p&quot; &lt;br /&gt;                            title=&quot;@Model.Name&quot; &lt;br /&gt;                            class=&quot;thumb&quot;&amp;gt;&lt;br /&gt;                            &amp;lt;img src=&quot;@picture.ImageUrl&quot; alt=&quot;@picture.AlternateText&quot; title=&quot;@picture.Title&quot; /&amp;gt;&lt;br /&gt;                        &amp;lt;/a&amp;gt;&lt;br /&gt;                    &amp;lt;/li&amp;gt;&lt;br /&gt;                }&lt;br /&gt;        &amp;lt;/ul&amp;gt;&lt;br /&gt;    }&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;This creates a div #thumbs that Galleriffic needs to find its content. You'll need to do some CSS to format the list how you like it.&lt;/p&gt;&lt;p&gt;Not, we've also changed the code above to '@if (Model.PictureModels.Count &amp;gt; 0)'. In the original, it only showed thumbnails if we had 2 or more images, but in this version, we need to show all the thumbs.&lt;/p&gt;&lt;p&gt;So there you have it. A pretty simple alternative to Slimbox2.&lt;/p&gt;&lt;p&gt;Enjoy :)&lt;/p&gt;</description>
			<pubDate>Tue, 10 Jan 2012 16:30:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/nopcommerce-2-3-mvc-alternative-product-gallery/</guid>
		</item>
		
		<item>
			<title>Gallery module for NopCommerce 1.90</title>
			<link>http://www.n-webdesign.co.uk/gallery-module-for-nopcommerce-1-9/</link>
			<description>&lt;p&gt;I've updated our Gallery for NopCommerce to a 1.90 version.&lt;/p&gt;&lt;p&gt;It's basically the same thing as the earlier version, but works with 1.90.&lt;/p&gt;&lt;p&gt;Files are available on &lt;a href=&quot;https://sourceforge.net/projects/nopgallery190v1/&quot;&gt;Sourceforge&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you have any questions, comments or spot any omissions, please let us know.&lt;/p&gt;&lt;p&gt;Enjoy/&lt;/p&gt;</description>
			<pubDate>Tue, 20 Dec 2011 11:35:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/gallery-module-for-nopcommerce-1-9/</guid>
		</item>
		
		<item>
			<title>NopGallery - Update</title>
			<link>http://www.n-webdesign.co.uk/nopgallery-update/</link>
			<description>&lt;p&gt;I've added the BETA source for a NopCommerce popup gallery to &lt;a href=&quot;https://sourceforge.net/projects/nopgallerybeta/&quot;&gt;Sourceforge&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;It's basically the sode from the previous blog article.&lt;/p&gt;&lt;p&gt;Note, it's only written for &amp;lt; 1.7. A newer version (with more gallery providers) may turn up some day :)&lt;/p&gt;&lt;p&gt;/enjoy.&lt;/p&gt;</description>
			<pubDate>Fri, 28 Oct 2011 10:22:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/nopgallery-update/</guid>
		</item>
		
		<item>
			<title>Better Google Base [Taxonomy] in nopCommerce 1.9</title>
			<link>http://www.n-webdesign.co.uk/better-google-base-taxonomy-in-nopcommerce-1/</link>
			<description>&lt;p&gt;The time has come for me to add the taxomomy details to a nopCommerce 1.9 build. It wasn't in 1.9 by default, but [on the face of it] it seems pretty easy to add stuff in 1.8/1.9 with the introduction of the Entity Framework library, which makes naming convention important, but makes new objects and entities easier, once you figure out a few things.&lt;/p&gt;&lt;p&gt;This isn't a tutorial on ASP.NET or c#.&lt;/p&gt;&lt;p&gt;This isn't a how-to or turorial on Entity Frameworks.&lt;/p&gt;&lt;p&gt;This isn't a discussion of Google Merchant Centre, Taxonomy or why it's important.&lt;/p&gt;&lt;p&gt;This isn't a 'how to extend nopCommerce' tutorial per se, but you could use this example to extend it yourself for your own objects.&lt;/p&gt;&lt;p&gt;If you aren't familiar with c#, Entity Framework stuff and the workings of a .NET application, you probably need to take your time with this. I'm not going to hold your hand through things that aren't related to the broad strokes of this process. And remember, don't break what you can't fix, so back everything up first, so if it goes awry [and it probably will if you are new to all this] you can restore and start again.&lt;/p&gt;&lt;p&gt;This will be followed [hopefully next week] with a 1.9 version of the popup gallery, which will also be extended to allow 2 options for the gallery. One will be to show in a slimbox2 [LightBox] style, the other will be to display as an easySlider1.7 'scrolling' affair that I've used in nopCommerce 1.* with great effect. But more on that next week.&lt;/p&gt;&lt;p&gt;This article will show you how to add Taxonomy to products in nopCommerce 1.9 [and 1.8 with a few extra requirements for EF].&lt;/p&gt;&lt;p&gt;Here are a few notes on how we want the implementation to work:&lt;/p&gt;&lt;p&gt;We will download and create a 'taxonomy' table to store all out items. We'll grab the data itself from Google and do an import (or in my case, a copy from another project that I have it in).&lt;/p&gt;&lt;p&gt;Once we have the data, we want to be able to add our own categories/entries to it, so we want a new menu item [I'm putting it in products] to link to the list/add/edit/delete pages for taxonomy.&lt;/p&gt;&lt;p&gt;In the earlier version, I had set it to have a single selectable taxonomy with an additional textbox to manually add the extra text, but this is a bit restrictive, so I'm making it so you can select as many entries as you like for the product, and also as mentioned above, to add new taxonomy details fo your own to select from a drop down.&lt;/p&gt;&lt;p&gt;So we also need a custom property adding to product and a table for ProductID &amp;lt;-&amp;gt; GoogleTaxonomyID linking. This just means we can have as many Taxonomy categories per product and the pivot/link table stores them all, so we have less messing around in the core model. We just add an entry with ProductID and GoogleTaxonomyID, then retrieve any List&amp;lt;&amp;gt; by ProductID in the main product object.&lt;/p&gt;&lt;p&gt;So, first things first. Get the Google Taxonomy list and create a table from it in your nopCommerce database. See &lt;a href=&quot;http://www.n-webdesign.co.uk/better-google-base-in-nopcommerce/&quot;&gt;this&lt;/a&gt; post for how to download and set up the taxonomy table.&lt;/p&gt;&lt;p&gt;Ok, now we need to add our objects to the solution. I'm adding a sub-folder in products, called GoogleProductTypes. It kinda makes sense being in there.&lt;/p&gt;&lt;p&gt;Once added, you can add a Class called GoogleProduct.cs and add the ID(int) and Name(string) fields to it, using the same names as the corresponding fields in the Taxonomy table. This is fairly important, as the Entity Framework uses the call and datatable names to match objects. You can call one apples and one oranges, but it just means you have to edit the EF diagram manually, which is a bit pointless if you just name stuff the same from the off.&lt;/p&gt;&lt;p&gt;Your class should look something like:&lt;/p&gt;&lt;p&gt;//######################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Linq;&lt;br /&gt;using System.Text;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes&lt;br /&gt;{&lt;br /&gt;    public partial class GoogleProduct : BaseEntity&lt;br /&gt;    {&lt;/p&gt;&lt;p&gt;        #region Properties&lt;/p&gt;&lt;p&gt;        public int GoogleProductTypeID { get; set; }&lt;br /&gt;        public string GoogleProductType { get; set; }&lt;/p&gt;&lt;p&gt;        #endregion&lt;/p&gt;&lt;p&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//#######################################&lt;/p&gt;&lt;p&gt;Next, open the Entity Framework diagram, and right click, then select update from the database. This will add the new taxonomy table to the model. You will need to edit the imported object, as it will come in called nop_GoogleProductType or similar, and we want it to be called 'GoogleProduct' to match our class name.&lt;/p&gt;&lt;p&gt;Once we have this set, we can now add out Service [manager] class and the interface. So, create a new interface in our GoogleProductTypes folder, next to our GooglePRoduct.cs and call it IGoogleProductService.cs.&lt;/p&gt;&lt;p&gt;We will be building this Interface up as we add new metthods but for now, we only need all the Taxonomy items in a list, so add: List&amp;lt;GoogleProduct&amp;gt; GetAllGoogleProducts();, so the Interface looks like...&lt;/p&gt;&lt;p&gt;//#######################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes&lt;br /&gt;{&lt;br /&gt;    public partial interface IGoogleProductService&lt;br /&gt;    {&lt;br /&gt;        #region Methods&lt;/p&gt;&lt;p&gt;        List&amp;lt;GoogleProduct&amp;gt; GetAllGoogleProducts();&lt;/p&gt;&lt;p&gt;        #endregion&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//#######################################&lt;/p&gt;&lt;p&gt;Ok, now the actual class, called GoogleProductService as follows...&lt;/p&gt;&lt;p&gt;//#######################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Linq;&lt;br /&gt;using System.Text;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Caching;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Data;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes&lt;br /&gt;{&lt;br /&gt;    public partial class GoogleProductService : IGoogleProductService&lt;br /&gt;    {&lt;/p&gt;&lt;p&gt;        #region Fields&lt;/p&gt;&lt;p&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Object context&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        private readonly NopObjectContext _context;&lt;/p&gt;&lt;p&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Cache manager&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        private readonly ICacheManager _cacheManager;&lt;/p&gt;&lt;p&gt;        #endregion&lt;/p&gt;&lt;p&gt;        #region Ctor&lt;/p&gt;&lt;p&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Ctor&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;param name=&quot;context&quot;&amp;gt;Object context&amp;lt;/param&amp;gt;&lt;br /&gt;        public GoogleProductService(NopObjectContext context)&lt;br /&gt;        {&lt;br /&gt;            this._context = context;&lt;br /&gt;            this._cacheManager = new NopRequestCache();&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        #endregion&lt;/p&gt;&lt;p&gt;        #region Methods&lt;/p&gt;&lt;p&gt;        public List&amp;lt;GoogleProduct&amp;gt; GetAllGoogleProducts()&lt;br /&gt;        {&lt;br /&gt;            var query = from p in _context.GoogleProduct&lt;br /&gt;                        orderby p.GoogleProductType&lt;br /&gt;                        select p;&lt;br /&gt;            var products = query.ToList();&lt;br /&gt;            return products;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        #endregion&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//#######################################&lt;/p&gt;&lt;p&gt;For now, we have a single method, GetAllGoogleProducts() that just does that. We are keeping it simple and building up from this slowly, so we know it all works as we go.&lt;/p&gt;&lt;p&gt;Now we want to add a new set of pages to admin, so we can manage these Taxonomy entries. We do this by adding a menu to admin.sitemap and then a set of pages, based on the current structure, to show, add, amend and delete items.&lt;/p&gt;&lt;p&gt;Open the admin sitemap, and in the catalog section under Manufacturers, add a line for Google Product Types. Just copy the manufacturers one and edit it.&lt;/p&gt;&lt;p&gt;I've pointed mine at ~/administration/googleproducttypes.aspx, so we now need to add the page. This is just a normal admin page, so go and add it now. make sure it uses the admin master page, then add a user control called GoogleProductTypes.ascx and add it to the page.&lt;/p&gt;&lt;p&gt;Now these pages need to use all the same base classes as the other pages and controls, so just copy any inheritance from other pages. I normally just pick a set of pages that is closest to the structure of what I' adding, so in this case I've copied the logic from the Manufacturers.aspx/ascx to give me my method prototypes and inheritance.&lt;/p&gt;&lt;p&gt;Once you've created the page and user control, just add the GoogleProductTypes.ascx control to the page. We'll now plumb in all the data and stuff.&lt;/p&gt;&lt;p&gt;First, add 'using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;' to BaseNopUserControl.cs, then add the new service. Like:&lt;/p&gt;&lt;p&gt;        public IGoogleProductService GoogleProductService&lt;br /&gt;        {&lt;br /&gt;            get { return IoC.Resolve&amp;lt;IGoogleProductService&amp;gt;(); }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;This goes in with the other services. It just allows us to use tons of built-in libraries and methods without invocating the classes ourselves, as they are already in the scope.&lt;/p&gt;&lt;p&gt;Once this is done, we can reference our GoogleProductService directly from the user control class. The ascx file will look like this:&lt;/p&gt;&lt;p&gt;//##############################################&lt;/p&gt;&lt;p&gt;&amp;lt;%@ Control Language=&quot;C#&quot; AutoEventWireup=&quot;true&quot; CodeBehind=&quot;GoogleProductTypes.ascx.cs&quot; Inherits=&quot;NopSolutions.NopCommerce.Web.Administration.Modules.GoogleProductTypes&quot; %&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;lt;div class=&quot;section-header&quot;&amp;gt;&lt;br /&gt;    &amp;lt;div class=&quot;title&quot;&amp;gt;&lt;br /&gt;        &amp;lt;img src=&quot;Common/ico-catalog.png&quot; alt=&quot;&amp;lt;%=GetLocaleResourceString(&quot;Admin.Manufacturers.Title&quot;)%&amp;gt;&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;%=GetLocaleResourceString(&quot;Admin.GoogleProductTypes.Title&quot;)%&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div class=&quot;options&quot;&amp;gt;&lt;br /&gt;        &amp;lt;input type=&quot;button&quot; onclick=&quot;location.href='GoogleProductTypesAdd.aspx'&quot; value=&quot;&amp;lt;%=GetLocaleResourceString(&quot;Admin.GoogleProductTypes.AddButton.Text&quot;)%&amp;gt;&quot;&lt;br /&gt;            id=&quot;btnAddNew&quot; class=&quot;adminButtonBlue&quot; title=&quot;&amp;lt;%=GetLocaleResourceString(&quot;Admin.GoogleProductTypes.AddButton.Tooltip&quot;)%&amp;gt;&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;table class=&quot;adminContent&quot;&amp;gt;&lt;br /&gt;    &amp;lt;asp:GridView ID=&quot;gvGoogleProductTypes&quot; runat=&quot;server&quot; AutoGenerateColumns=&quot;False&quot; Width=&quot;100%&quot;&lt;br /&gt;    OnPageIndexChanging=&quot;gvGoogleProductTypes_PageIndexChanging&quot; AllowPaging=&quot;true&quot; PageSize=&quot;15&quot;&amp;gt;&lt;br /&gt;        &amp;lt;Columns&amp;gt;&lt;br /&gt;            &amp;lt;asp:TemplateField HeaderText=&quot;&amp;lt;% $NopResources:Admin.GoogleProductTypes.Name %&amp;gt;&quot; ItemStyle-Width=&quot;65%&quot;&amp;gt;&lt;br /&gt;                &amp;lt;ItemTemplate&amp;gt;&lt;br /&gt;                    &amp;lt;%#Server.HtmlEncode(Eval(&quot;GoogleProductType&quot;).ToString())%&amp;gt;&lt;br /&gt;                &amp;lt;/ItemTemplate&amp;gt;&lt;br /&gt;            &amp;lt;/asp:TemplateField&amp;gt;&lt;br /&gt;            &amp;lt;asp:TemplateField HeaderText=&quot;&amp;lt;% $NopResources:Admin.GoogleProductTypes.Edit %&amp;gt;&quot; HeaderStyle-HorizontalAlign=&quot;Center&quot;&lt;br /&gt;                ItemStyle-Width=&quot;20%&quot; ItemStyle-HorizontalAlign=&quot;Center&quot;&amp;gt;&lt;br /&gt;                &amp;lt;ItemTemplate&amp;gt;&lt;br /&gt;                    &amp;lt;a href=&quot;GoogleProductTypesDetails.aspx?GoogleProductTypeID=&amp;lt;%#Eval(&quot;GoogleProductTypeId&quot;)%&amp;gt;&quot; title=&quot;&amp;lt;%#GetLocaleResourceString(&quot;Admin.GoogleProductTypes.Edit.Tooltip&quot;)%&amp;gt;&quot;&amp;gt;&lt;br /&gt;                        &amp;lt;%#GetLocaleResourceString(&quot;Admin.GoogleProductTypes.Edit&quot;)%&amp;gt;&lt;br /&gt;                    &amp;lt;/a&amp;gt;&lt;br /&gt;                &amp;lt;/ItemTemplate&amp;gt;&lt;br /&gt;            &amp;lt;/asp:TemplateField&amp;gt;&lt;br /&gt;        &amp;lt;/Columns&amp;gt;&lt;br /&gt;    &amp;lt;PagerSettings PageButtonCount=&quot;50&quot; Position=&quot;TopAndBottom&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/asp:GridView&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;/p&gt;&lt;p&gt;//##############################################&lt;/p&gt;&lt;p&gt;And the code behind (.cs) file will look like this:&lt;/p&gt;&lt;p&gt;//##############################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections;&lt;br /&gt;using System.Configuration;&lt;br /&gt;using System.Data;&lt;br /&gt;using System.IO;&lt;br /&gt;using System.Text;&lt;br /&gt;using System.Web;&lt;br /&gt;using System.Web.Security;&lt;br /&gt;using System.Web.UI;&lt;br /&gt;using System.Web.UI.HtmlControls;&lt;br /&gt;using System.Web.UI.WebControls;&lt;br /&gt;using System.Web.UI.WebControls.WebParts;&lt;br /&gt;using System.Xml;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.ExportImport;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Manufacturers;&lt;br /&gt;using NopSolutions.NopCommerce.Common.Utils;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.Web.Administration.Modules&lt;br /&gt;{&lt;br /&gt;    public partial class GoogleProductTypes : BaseNopAdministrationUserControl&lt;br /&gt;    {&lt;br /&gt;        protected void Page_Load(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (!Page.IsPostBack)&lt;br /&gt;            {&lt;br /&gt;                BindGrid();&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected void BindGrid()&lt;br /&gt;        {&lt;br /&gt;            var googleproducttypes = this.GoogleProductService.GetAllGoogleProducts();&lt;br /&gt;            gvGoogleProductTypes.DataSource = googleproducttypes;&lt;br /&gt;            gvGoogleProductTypes.DataBind();&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected void gvGoogleProductTypes_PageIndexChanging(object sender, GridViewPageEventArgs e)&lt;br /&gt;        {&lt;br /&gt;            gvGoogleProductTypes.PageIndex = e.NewPageIndex;&lt;br /&gt;            BindGrid();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//##############################################&lt;/p&gt;&lt;p&gt;Now if you F5, the app will run (this is good) but you'll get an error when you try to load the GoogleProductTypes page in admin. This is because you need to add an entry in the UnityDependancyResolver.cs file. This is in BusinessLogic/Infrastructure. Add the following line 'using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;' to the using directive and add this to the page in the obvious place:&lt;/p&gt;&lt;p&gt;container.RegisterType&amp;lt;IGoogleProductService, GoogleProductService&amp;gt;(new UnityPerExecutionContextLifetimeManager());&lt;/p&gt;&lt;p&gt;Sorry about that. Please bear in mind, for speed rather than clarity, I'm adding this to a build as I write it up, so please bear with me. I'll provide all code in a file at the end, so don't panic about missing anything.&lt;/p&gt;&lt;p&gt;Ok, now F5 and navigate to administration, then the new section in Products, you'll see a big list of taxonomy details, ready to edit. You'll need to add localisation tokens etc, but the basics are there.&lt;/p&gt;&lt;p&gt;So what now?&lt;/p&gt;&lt;p&gt;Well, we need an add, an edit and a update for this bit, and then a way of adding a list of taxonomy names to products.&lt;/p&gt;&lt;p&gt;Ok, so the add facility. We need a page called 'GoogleProductTypesAdd.aspx'. Add this and a corresponding 'GoogleProductTypesInfo.ascx' user control. Do the same as we did with the main page and control, using a reference page. Again, I'm going to use Manufacturers, as that is a simple(ish) object. The only difference is, I'm adding an info control directly for add/edit, rather than as a control within the ad control, as on the Manufacturers usage. This is because we only want a really simple single textbox, no tabs or complex content.&lt;/p&gt;&lt;p&gt;GoogleProductTypesAdd.aspx is pretty simple. It just has the  GoogleProductTypesInfo.ascx user control in it. The GoogleProductTypesInfo.ascx control looks as follows:&lt;/p&gt;&lt;p&gt;//#######################################&lt;/p&gt;&lt;p&gt;&amp;lt;%@ Control Language=&quot;C#&quot; AutoEventWireup=&quot;true&quot; CodeBehind=&quot;GoogleProductTypesInfo.ascx.cs&quot; Inherits=&quot;NopSolutions.NopCommerce.Web.Administration.Modules.GoogleProductTypesInfo&quot; %&amp;gt;&lt;br /&gt;&amp;lt;%@ Register TagPrefix=&quot;nopCommerce&quot; TagName=&quot;SimpleTextBox&quot; Src=&quot;SimpleTextBox.ascx&quot; %&amp;gt;&lt;br /&gt;&amp;lt;%@ Register TagPrefix=&quot;nopCommerce&quot; TagName=&quot;ToolTipLabel&quot; Src=&quot;ToolTipLabelControl.ascx&quot; %&amp;gt;&lt;br /&gt;&amp;lt;div class=&quot;section-header&quot;&amp;gt;&lt;br /&gt;    &amp;lt;div class=&quot;title&quot;&amp;gt;&lt;br /&gt;        &amp;lt;img src=&quot;Common/ico-catalog.png&quot; alt=&quot;&amp;lt;%=GetLocaleResourceString(&quot;Admin.GoogleProductTypesAdd.Title&quot;)%&amp;gt;&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;%=GetLocaleResourceString(&quot;Admin.GoogleProductTypesAdd.Title&quot;)%&amp;gt;&amp;lt;a href=&quot;Manufacturers.aspx&quot;&lt;br /&gt;            title=&quot;&amp;lt;%=GetLocaleResourceString(&quot;Admin.ManufacturerAdd.BackToManufacturers&quot;)%&amp;gt;&quot;&amp;gt;&lt;br /&gt;            (&amp;lt;%=GetLocaleResourceString(&quot;Admin.GoogleProductTypesAdd.BackToManufacturers&quot;)%&amp;gt;)&amp;lt;/a&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div class=&quot;options&quot;&amp;gt;&lt;br /&gt;        &amp;lt;asp:Button ID=&quot;SaveButton&quot; runat=&quot;server&quot; Text=&quot;&amp;lt;% $NopResources:Admin.ManufacturerAdd.SaveButton.Text %&amp;gt;&quot;&lt;br /&gt;            CssClass=&quot;adminButtonBlue&quot; OnClick=&quot;SaveButton_Click&quot; ToolTip=&quot;&amp;lt;% $NopResources:Admin.ManufacturerAdd.SaveButton.Tooltip %&amp;gt;&quot; /&amp;gt;&lt;/p&gt;&lt;p&gt;        &amp;lt;asp:Button ID=&quot;SaveAndStayButton&quot; runat=&quot;server&quot; CssClass=&quot;adminButtonBlue&quot; Text=&quot;&amp;lt;% $NopResources:Admin.ManufacturerAdd.SaveAndStayButton.Text %&amp;gt;&quot;&lt;br /&gt;            OnClick=&quot;SaveAndStayButton_Click&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;table class=&quot;adminContent&quot;&amp;gt;&lt;br /&gt;    &amp;lt;tr&amp;gt;&lt;br /&gt;        &amp;lt;td class=&quot;adminTitle&quot;&amp;gt;&lt;br /&gt;            &amp;lt;nopCommerce:ToolTipLabel runat=&quot;server&quot; ID=&quot;lblName&quot; Text=&quot;&amp;lt;% $NopResources:Admin.GoogleProductTypesInfo.Name %&amp;gt;&quot;&lt;br /&gt;                ToolTip=&quot;&amp;lt;% $NopResources:Admin.GoogleProductTypesInfo.Name.Tooltip %&amp;gt;&quot; ToolTipImage=&quot;~/Administration/Common/ico-help.gif&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;/td&amp;gt;&lt;br /&gt;        &amp;lt;td class=&quot;adminData&quot;&amp;gt;&lt;br /&gt;            &amp;lt;nopCommerce:SimpleTextBox runat=&quot;server&quot; ID=&quot;txtName&quot; CssClass=&quot;adminInput&quot; ErrorMessage=&quot;&amp;lt;% $NopResources:Admin.GoogleProductTypesInfo.Name.ErrorMessage %&amp;gt;&quot;&amp;gt;&lt;br /&gt;            &amp;lt;/nopCommerce:SimpleTextBox&amp;gt;&lt;br /&gt;        &amp;lt;/td&amp;gt;&lt;br /&gt;    &amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;And the GoogleProductTypesInfo.ascx.cs code behind is like:&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections;&lt;br /&gt;using System.Configuration;&lt;br /&gt;using System.Data;&lt;br /&gt;using System.Web;&lt;br /&gt;using System.Web.Security;&lt;br /&gt;using System.Web.UI;&lt;br /&gt;using System.Web.UI.HtmlControls;&lt;br /&gt;using System.Web.UI.WebControls;&lt;br /&gt;using System.Web.UI.WebControls.WebParts;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Manufacturers;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Media;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Products;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Templates;&lt;br /&gt;using NopSolutions.NopCommerce.Common.Utils;&lt;br /&gt;using NopSolutions.NopCommerce.Web.Administration.Modules;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.Web.Administration.Modules&lt;br /&gt;{&lt;br /&gt;    public partial class GoogleProductTypesInfo : BaseNopAdministrationUserControl&lt;br /&gt;    {&lt;br /&gt;        private void BindData()&lt;br /&gt;        {&lt;br /&gt;            var googleproducttype = this.GoogleProductService.GetGoogleProduct(this.GoogleProductTypeId);&lt;/p&gt;&lt;p&gt;            if (googleproducttype != null)&lt;br /&gt;            {&lt;br /&gt;                this.txtName.Text = googleproducttype.GoogleProductType;&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected void Page_Load(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (!Page.IsPostBack)&lt;br /&gt;            {&lt;br /&gt;                this.BindData();&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected override void OnPreRender(EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            BindJQuery();&lt;br /&gt;            BindJQueryIdTabs();&lt;/p&gt;&lt;p&gt;            base.OnPreRender(e);&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected void SaveButton_Click(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (Page.IsValid)&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    GoogleProduct googleproduct = SaveInfo();&lt;br /&gt;                    Response.Redirect(&quot;googleproducttypes.aspx&quot;);&lt;br /&gt;                }&lt;br /&gt;                catch (Exception exc)&lt;br /&gt;                {&lt;br /&gt;                    ProcessException(exc);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected void SaveAndStayButton_Click(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (Page.IsValid)&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    GoogleProduct googleproduct = SaveInfo();&lt;br /&gt;                    Response.Redirect(&quot;GoogleProductTypesAdd.aspx?GoogleProductTypeID=&quot; + googleproduct.GoogleProductTypeID);&lt;br /&gt;                }&lt;br /&gt;                catch (Exception exc)&lt;br /&gt;                {&lt;br /&gt;                    ProcessException(exc);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        public GoogleProduct SaveInfo()&lt;br /&gt;        {&lt;br /&gt;            var googleproducttype = this.GoogleProductService.GetGoogleProduct(this.GoogleProductTypeId);&lt;/p&gt;&lt;p&gt;            if (googleproducttype != null)&lt;br /&gt;            {&lt;/p&gt;&lt;p&gt;                googleproducttype.GoogleProductType = txtName.Text;&lt;br /&gt;                this.GoogleProductService.UpdateGoogleProductType(googleproducttype);&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                googleproducttype = new GoogleProduct()&lt;br /&gt;                {&lt;br /&gt;                    GoogleProductType = txtName.Text,&lt;br /&gt;                };&lt;br /&gt;                this.GoogleProductService.AddGoogleProductType(googleproducttype);&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;            return googleproducttype;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        public int GoogleProductTypeId&lt;br /&gt;        {&lt;br /&gt;            get&lt;br /&gt;            {&lt;br /&gt;                return CommonHelper.QueryStringInt(&quot;GoogleProductTypeId&quot;);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;So now, if we F5 and check it all out, we can add and edit new taxonomy entries here.&lt;/p&gt;&lt;p&gt;We might need to re-visit this bit to add better filtering and searching (like the options available in Localisation) but we'll worry about that later.&lt;/p&gt;&lt;p&gt;At this moment, we have about half of the structure we need for the complete implementation. Hopefully you've got here error free or have been able to fix anything that has arisen. It's pretty likely that you'll have some errors with the entity framework and other bits of the model process, but as a tip, make sure all your data tables have a primary key and an identity field, that the naming convention between the database and the classes we are adding in consistent and that you take small steps, especially when you are learning new stuff like Entity Framework models. &lt;/p&gt;&lt;p&gt;Ok, on to stage 2. Adding the mappings for google product types to our products.&lt;/p&gt;&lt;p&gt;We are going to add a mapping table in the database, with a ProductID and a GoogleProductTypeID, a new tab to the 'Products' section and some nifty selection options to add/remove entries with ease.&lt;/p&gt;&lt;p&gt;I considered either a dropdown selector with an add new, or a checkbox list stylee thing as in the categories tab, but aas there are nearly 4000 taxonomies, it was a no brainer. We will be adding a list of already available items, a dropdown for adding new ones and a linkbutton to delete existing ones.&lt;/p&gt;&lt;p&gt;This should be [in theory] fairly painless with the EF stuff, but here goes. (I've had a lot of trouble with it so far, as I tend to fight stuff like this, rather than accept it knws best :)).&lt;/p&gt;&lt;p&gt;So first, add the table to the database. &lt;/p&gt;&lt;p&gt;//################################&lt;/p&gt;&lt;p&gt;USE [my-database]&lt;br /&gt;GO&lt;/p&gt;&lt;p&gt;/****** Object:  Table [dbo].[Nop_GoogleProductTypeMapping]    Script Date: 05/26/2011 16:38:38 ******/&lt;br /&gt;SET ANSI_NULLS ON&lt;br /&gt;GO&lt;/p&gt;&lt;p&gt;SET QUOTED_IDENTIFIER ON&lt;br /&gt;GO&lt;/p&gt;&lt;p&gt;CREATE TABLE [dbo].[Nop_GoogleProductTypeMapping](&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;[GoogleProductTypeMappingID] [int] IDENTITY(1,1) NOT NULL,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;[GoogleProductTypeID] [int] NULL,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;[ProductID] [int] NULL,&lt;br /&gt; CONSTRAINT [PK_GoogleProductTypeMapping] PRIMARY KEY CLUSTERED &lt;br /&gt;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;[GoogleProductTypeMappingID] ASC&lt;br /&gt;)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]&lt;br /&gt;) ON [PRIMARY]&lt;/p&gt;&lt;p&gt;GO&lt;/p&gt;&lt;p&gt;//########################################  &lt;/p&gt;&lt;p&gt;Next, add a GoogleProductTypeMapping.cs file to our GoogleProductTypes folder, like so:&lt;/p&gt;&lt;p&gt;//########################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Linq;&lt;br /&gt;using System.Text;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes&lt;br /&gt;{&lt;br /&gt;    public partial class GoogleProductTypeMapping : BaseEntity&lt;br /&gt;    {&lt;/p&gt;&lt;p&gt;        #region Properties&lt;/p&gt;&lt;p&gt;        public int GoogleProductTypeMappingID { get; set; }&lt;br /&gt;        public int GoogleProductTypeID { get; set; }&lt;br /&gt;        public int ProductID { get; set; }&lt;/p&gt;&lt;p&gt;        #endregion&lt;/p&gt;&lt;p&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;...then add these new methods to the IGoogleProductService.cs file:&lt;/p&gt;&lt;p&gt;        void AddGoogleProductToProduct(GoogleProductTypeMapping googleproducttypemapping);&lt;/p&gt;&lt;p&gt;        void RemoveGoogleProductFromProduct(int ProductID, int GoogleProductTypeID);&lt;/p&gt;&lt;p&gt;        GoogleProductTypeMapping GetGoogleProductTypeMapping(int ProductID, int GoogleProductTypeID);&lt;/p&gt;&lt;p&gt;        GoogleProductTypeMapping GetGoogleProductMapping(int GoogleProductTypeMappingID);&lt;/p&gt;&lt;p&gt;        List&amp;lt;GoogleProductTypeMapping&amp;gt; GetGoogleProductTypeMapping(int ProductID);&lt;/p&gt;&lt;p&gt;...and also add them to the GoogleProductService.cs file as follows...&lt;/p&gt;&lt;p&gt;//######################################&lt;/p&gt;&lt;p&gt;        public GoogleProductTypeMapping GetGoogleProductTypeMapping(int ProductID, int GoogleProductTypeID)&lt;br /&gt;        {&lt;br /&gt;            var query = from m in _context.GoogleProductTypeMapping&lt;br /&gt;                        where m.ProductID == ProductID &amp;amp;&amp;amp; m.GoogleProductTypeID == GoogleProductTypeID&lt;br /&gt;                        select m;&lt;br /&gt;            var mapping = query.SingleOrDefault();&lt;/p&gt;&lt;p&gt;            return mapping;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        public GoogleProductTypeMapping GetGoogleProductMapping(int GoogleProductTypeMappingID)&lt;br /&gt;        {&lt;br /&gt;            var query = from m in _context.GoogleProductTypeMapping&lt;br /&gt;                        where m.GoogleProductTypeMappingID == GoogleProductTypeMappingID&lt;br /&gt;                        select m;&lt;br /&gt;            var mapping = query.SingleOrDefault();&lt;/p&gt;&lt;p&gt;            return mapping;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        public void AddGoogleProductToProduct(GoogleProductTypeMapping googleproducttypemapping)&lt;br /&gt;        {&lt;/p&gt;&lt;p&gt;            _context.GoogleProductTypeMapping.AddObject(googleproducttypemapping);&lt;br /&gt;            _context.SaveChanges();&lt;/p&gt;&lt;p&gt;        }&lt;/p&gt;&lt;p&gt;        public void RemoveGoogleProductFromProduct(int ProductID, int GoogleProductTypeID)&lt;br /&gt;        {&lt;br /&gt;            var googlemapping = GetGoogleProductTypeMapping(ProductID, GoogleProductTypeID);&lt;br /&gt;            if (googlemapping == null)&lt;br /&gt;                return;&lt;/p&gt;&lt;p&gt;            if (!_context.IsAttached(googlemapping))&lt;br /&gt;                _context.GoogleProductTypeMapping.Attach(googlemapping);&lt;br /&gt;            _context.DeleteObject(googlemapping);&lt;br /&gt;            _context.SaveChanges();&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        public List&amp;lt;GoogleProductTypeMapping&amp;gt; GetGoogleProductTypeMapping(int ProductID)&lt;br /&gt;        {&lt;br /&gt;            var query = from m in _context.GoogleProductTypeMapping&lt;br /&gt;                        where m.ProductID == ProductID&lt;br /&gt;                        select m;&lt;br /&gt;            var mapping = query.ToList();&lt;br /&gt;            return mapping;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;Finally, do the 'update from database' on the NopModel.edmx file. Remember to rename the new mapping table to match the class we've just added.&lt;/p&gt;&lt;p&gt;We also need to expose the list in our Products.cs class, so in our extensions folder in Nop.BusinessLogic [make one if you haven't go one], extend our partial products class like so:&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Linq;&lt;br /&gt;using System.Text;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.BusinessLogic.Products&lt;br /&gt;{&lt;br /&gt;    public partial class Product : BaseEntity&lt;br /&gt;    {&lt;br /&gt;        public List&amp;lt;GoogleProduct&amp;gt; GoogleProducts&lt;br /&gt;        {&lt;br /&gt;            get&lt;br /&gt;            {&lt;br /&gt;                return IoC.Resolve&amp;lt;IGoogleProductService&amp;gt;().GetAllGoogleProductsByProduct(this.ProductId);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;This just adds a property of type List of GoogleProducts, so we can reference them in the code later on.&lt;/p&gt;&lt;p&gt;Now we can add the section to the 'Products' tabs in the admin section.&lt;/p&gt;&lt;p&gt;In administration/modules, add a user control called 'ProductGoogleMappings.ascx'. It looks something like:&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;&amp;lt;%@ Control Language=&quot;C#&quot; AutoEventWireup=&quot;true&quot; CodeBehind=&quot;ProductGoogleMappings.ascx.cs&quot; Inherits=&quot;NopSolutions.NopCommerce.Web.Administration.Modules.ProductGoogleMappings&quot; %&amp;gt;&lt;br /&gt;&amp;lt;%@ Register TagPrefix=&quot;nopCommerce&quot; TagName=&quot;ToolTipLabel&quot; Src=&quot;ToolTipLabelControl.ascx&quot; %&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;lt;asp:Panel runat=&quot;server&quot; ID=&quot;pnlData&quot;&amp;gt;&lt;br /&gt;    &amp;lt;asp:GridView ID=&quot;gvwGoogleMappings&quot; runat=&quot;server&quot; AutoGenerateColumns=&quot;false&quot; DataKeyNames=&quot;GoogleProductTypeMappingID&quot;&lt;br /&gt;        OnRowDeleting=&quot;gvwGoogleMappings_RowDeleting&quot; Width=&quot;100%&quot;&amp;gt;&lt;br /&gt;        &amp;lt;Columns&amp;gt;&lt;br /&gt;            &amp;lt;asp:TemplateField HeaderText=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.GoogleMapping %&amp;gt;&quot;&amp;gt;&lt;br /&gt;                &amp;lt;ItemTemplate&amp;gt;&lt;br /&gt;                    &amp;lt;%#Server.HtmlEncode(Eval(&quot;GoogleProduct.GoogleProductType&quot;).ToString())%&amp;gt;&lt;br /&gt;                &amp;lt;/ItemTemplate&amp;gt;&lt;br /&gt;            &amp;lt;/asp:TemplateField&amp;gt;&lt;br /&gt;            &amp;lt;asp:TemplateField HeaderText=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.Delete %&amp;gt;&quot;&lt;br /&gt;                HeaderStyle-HorizontalAlign=&quot;Center&quot; ItemStyle-HorizontalAlign=&quot;Center&quot;&amp;gt;&lt;br /&gt;                &amp;lt;ItemTemplate&amp;gt;&lt;br /&gt;                    &amp;lt;asp:Button ID=&quot;btnDeleteGoogleMappings&quot; runat=&quot;server&quot; CssClass=&quot;adminButton&quot; Text=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.Delete %&amp;gt;&quot;&lt;br /&gt;                        CausesValidation=&quot;false&quot; CommandName=&quot;Delete&quot; CommandArgument='&amp;lt;%# Bind(&quot;GoogleProductTypeMappingID&quot;) %&amp;gt;' ToolTip=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.Delete.Tooltip %&amp;gt;&quot; /&amp;gt;&lt;br /&gt;                &amp;lt;/ItemTemplate&amp;gt;&lt;br /&gt;            &amp;lt;/asp:TemplateField&amp;gt;&lt;br /&gt;        &amp;lt;/Columns&amp;gt;&lt;br /&gt;    &amp;lt;/asp:GridView&amp;gt;&lt;br /&gt;    &amp;lt;p&amp;gt;&lt;br /&gt;        &amp;lt;strong&amp;gt;&lt;br /&gt;            &amp;lt;%=GetLocaleResourceString(&quot;Admin.ProductGoogleMappings.AddNewGoogleMapping&quot;)%&amp;gt;&lt;br /&gt;        &amp;lt;/strong&amp;gt;&lt;br /&gt;    &amp;lt;/p&amp;gt;&lt;br /&gt;    &amp;lt;table class=&quot;adminContent&quot;&amp;gt;&lt;br /&gt;        &amp;lt;tr&amp;gt;&lt;br /&gt;            &amp;lt;td class=&quot;adminTitle&quot;&amp;gt;&lt;br /&gt;                &amp;lt;nopCommerce:ToolTipLabel runat=&quot;server&quot; ID=&quot;lblSelectPicture1&quot; Text=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappingss.SelectMappings %&amp;gt;&quot;&lt;br /&gt;                    ToolTip=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.SelectGoogleMappings.Tooltip %&amp;gt;&quot; ToolTipImage=&quot;~/Administration/Common/ico-help.gif&quot; /&amp;gt;&lt;br /&gt;            &amp;lt;/td&amp;gt;&lt;br /&gt;            &amp;lt;td class=&quot;adminData&quot;&amp;gt;&lt;br /&gt;                &amp;lt;asp:DropDownList ID=&quot;ddlGoogleMappings&quot; CssClass=&quot;adminInput&quot; runat=&quot;server&quot;&lt;br /&gt;                    ToolTip=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.DropDownList %&amp;gt;&quot; /&amp;gt;&lt;br /&gt;            &amp;lt;/td&amp;gt;&lt;br /&gt;        &amp;lt;/tr&amp;gt;&lt;br /&gt;        &amp;lt;tr&amp;gt;&lt;br /&gt;            &amp;lt;td colspan=&quot;2&quot; align=&quot;left&quot;&amp;gt;&lt;br /&gt;                &amp;lt;asp:Button runat=&quot;server&quot; ID=&quot;btnAddGoogleMapping&quot; CssClass=&quot;adminButton&quot; Text=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.Add.Text %&amp;gt;&quot;&lt;br /&gt;                    ToolTip=&quot;&amp;lt;% $NopResources:Admin.ProductGoogleMappings.Add.Tooltip %&amp;gt;&quot; &lt;br /&gt;                    onclick=&quot;btnAddGoogleMapping_Click&quot; /&amp;gt;&lt;br /&gt;            &amp;lt;/td&amp;gt;&lt;br /&gt;        &amp;lt;/tr&amp;gt;&lt;br /&gt;    &amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/asp:Panel&amp;gt;&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;...and the 'ProductGoogleMappings.ascx.cs' code behind file as follows:&lt;/p&gt;&lt;p&gt;//#####################################&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Configuration;&lt;br /&gt;using System.Data;&lt;br /&gt;using System.Web;&lt;br /&gt;using System.Web.Security;&lt;br /&gt;using System.Web.UI;&lt;br /&gt;using System.Web.UI.HtmlControls;&lt;br /&gt;using System.Web.UI.WebControls;&lt;br /&gt;using System.Web.UI.WebControls.WebParts;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Categories;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Manufacturers;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Media;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Products;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Products.Specs;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Templates;&lt;br /&gt;using NopSolutions.NopCommerce.Common.Utils;&lt;br /&gt;using NopSolutions.NopCommerce.Web.Administration.Modules;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Infrastructure;&lt;br /&gt;using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.Web.Administration.Modules&lt;br /&gt;{&lt;br /&gt;    public partial class ProductGoogleMappings : BaseNopAdministrationUserControl&lt;br /&gt;    {&lt;br /&gt;        private void BindData()&lt;br /&gt;        {&lt;br /&gt;            var googlemapping = this.GoogleProductService.GetGoogleProductTypeMapping(this.ProductId);&lt;br /&gt;            if (googlemapping != null)&lt;br /&gt;            {&lt;br /&gt;                pnlData.Visible = true;&lt;/p&gt;&lt;p&gt;                gvwGoogleMappings.Visible = true;&lt;br /&gt;                gvwGoogleMappings.DataSource = googlemapping;&lt;br /&gt;                gvwGoogleMappings.DataBind();&lt;/p&gt;&lt;p&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                pnlData.Visible = false;&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        private void FillDropDown()&lt;br /&gt;        {&lt;br /&gt;            var googlemapping = this.GoogleProductService.GetAllGoogleProducts();&lt;br /&gt;            ddlGoogleMappings.DataSource = googlemapping;&lt;br /&gt;            ddlGoogleMappings.DataTextField = &quot;GoogleProductType&quot;;&lt;br /&gt;            ddlGoogleMappings.DataValueField = &quot;GoogleProductTypeID&quot;;&lt;br /&gt;            ddlGoogleMappings.DataBind();&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected void Page_Load(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            if (!Page.IsPostBack)&lt;br /&gt;            {&lt;br /&gt;                this.BindData();&lt;br /&gt;                this.FillDropDown();&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        public void SaveInfo()&lt;br /&gt;        {&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        protected void gvwGoogleMappings_RowDeleting(object sender, GridViewDeleteEventArgs e)&lt;br /&gt;        {&lt;br /&gt;            int GoogleProductTypeMappingID = (int)gvwGoogleMappings.DataKeys[e.RowIndex][&quot;GoogleProductTypeMappingID&quot;];&lt;br /&gt;            GoogleProductTypeMapping GoogleMapping = this.GoogleProductService.GetGoogleProductTypeMapping(ProductId, GoogleProductTypeMappingID);&lt;br /&gt;            if (GoogleMapping != null)&lt;br /&gt;            {&lt;br /&gt;                this.GoogleProductService.RemoveGoogleProductFromProduct(ProductId, GoogleProductTypeMappingID);&lt;br /&gt;                BindData();&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;        }&lt;/p&gt;&lt;p&gt;        protected void btnAddGoogleMapping_Click(object sender, EventArgs e)&lt;br /&gt;        {&lt;br /&gt;            GoogleProductTypeMapping googlemapping = new GoogleProductTypeMapping();&lt;/p&gt;&lt;p&gt;            googlemapping.ProductID = ProductId;&lt;br /&gt;            googlemapping.GoogleProductTypeID = Convert.ToInt16(ddlGoogleMappings.SelectedValue);&lt;/p&gt;&lt;p&gt;            this.GoogleProductService.AddGoogleProductToProduct(googlemapping);&lt;/p&gt;&lt;p&gt;            BindData();&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;        public int ProductId&lt;br /&gt;        {&lt;br /&gt;            get&lt;br /&gt;            {&lt;br /&gt;                return CommonHelper.QueryStringInt(&quot;ProductId&quot;);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;//#####################################.&lt;/p&gt;&lt;p&gt;Then, in the 'ProductDetails.ascx', add a reference to the new user control like:&lt;/p&gt;&lt;p&gt;&amp;lt;%@ Register TagPrefix=&quot;nopCommerce&quot; TagName=&quot;GoogleMappings&quot; Src=&quot;~/Administration/Modules/ProductGoogleMappings.ascx&quot; %&amp;gt;&lt;/p&gt;&lt;p&gt;...and add a new tab to the ajax tabb control:&lt;/p&gt;&lt;p&gt;    &amp;lt;ajaxToolkit:TabPanel runat=&quot;server&quot; ID=&quot;pnlGoogleMappings&quot; HeaderText=&quot;&amp;lt;% $NopResources:Admin.ProductDetails.GoogleMappings %&amp;gt;&quot;&amp;gt;&lt;br /&gt;        &amp;lt;ContentTemplate&amp;gt;&lt;br /&gt;            &amp;lt;nopCommerce:GoogleMappings ID=&quot;ctrlGoogleMappings&quot; runat=&quot;server&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;/ContentTemplate&amp;gt;&lt;br /&gt;    &amp;lt;/ajaxToolkit:TabPanel&amp;gt;&lt;/p&gt;&lt;p&gt;That should be pretty straightforward. Now F5 again, login, navigate to a product and you should see a new tab with our newly added list [as yet unpopulated] of google product types and an add option.&lt;/p&gt;&lt;p&gt;I've glossed over some of the minor points of adding the logic and mechanics to the pages, but you can see whaer we add the google products list to the drop down and where we commit the add/update via the entity framework stuff.&lt;/p&gt;&lt;p&gt;Ok, so now we have a list of google product types, a way of adding them and managing them in products, and a way of exposing this information in the code.&lt;/p&gt;&lt;p&gt;All that is left now is to add them to the Froogle.ashx file.&lt;/p&gt;&lt;p&gt;So, open the FroogleService.cs file (Found in PromotionProviders/Nop.Froogle].&lt;/p&gt;&lt;p&gt;Now we have the data in our Product class ready to roll, we can be quite creative with how we use it, but I'm keeping it simple for this implementation.&lt;/p&gt;&lt;p&gt;We willl basically iterate through the 'GoogleProducts' List in the Product class and add  this to the xml feed data. So first, add a reference to the Google Products library:&lt;/p&gt;&lt;p&gt;using NopSolutions.NopCommerce.BusinessLogic.Products.GoogleProductTypes;&lt;/p&gt;&lt;p&gt;Then add the following in the code at around line 97, just after the comments about google product type attributes.&lt;/p&gt;&lt;p&gt;string sout = &quot;&quot;;&lt;/p&gt;&lt;p&gt;                        foreach (GoogleProduct c in product.GoogleProducts)&lt;br /&gt;                        {&lt;br /&gt;                            if (sout.Length &amp;gt; 0)&lt;br /&gt;                            {&lt;br /&gt;                                sout += &quot;, &quot;;&lt;br /&gt;                            }&lt;/p&gt;&lt;p&gt;                            sout += &quot;\&quot;&quot; + c.GoogleProductType + &quot;\&quot;&quot;;&lt;br /&gt;                        }&lt;/p&gt;&lt;p&gt;                        if (sout.Length &amp;gt; 0)&lt;br /&gt;                        {&lt;br /&gt;                            writer.WriteStartElement(&quot;g&quot;, &quot;product_type&quot;, googleBaseNamespace);&lt;br /&gt;                            writer.WriteCData(sout);&lt;br /&gt;                            writer.WriteFullEndElement();&lt;br /&gt;                        }&lt;br /&gt;.&lt;br /&gt;This simply adds a list of taxonomy entries to the file.&lt;/p&gt;&lt;p&gt;And that's it. We're done.&lt;/p&gt;&lt;p&gt;Please be aware that this will probably have a few errors in it or the odd think I've missed, so please let me know if you spot anything and I'll amend it. Once I've got it in production somewhere, I'll add the complete code to SourceForge (which will be a lot easier for you!). If you have any other comments or suggestions, please let me know.&lt;/p&gt;&lt;p&gt;Enjoy. ed/&lt;/p&gt;</description>
			<pubDate>Wed, 25 May 2011 16:28:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/better-google-base-taxonomy-in-nopcommerce-1/</guid>
		</item>
		
		<item>
			<title>Better Google Base in nopcommerce</title>
			<link>http://www.n-webdesign.co.uk/better-google-base-in-nopcommerce/</link>
			<description>&lt;p&gt;Following a read of a topic on the nopCommerce forum, and my own research into changes at Google for base/Merchant Centre, I've come to a few conclusions about the feed from nopCommerce and looking at ways to improve it.&lt;/p&gt;&lt;p&gt;I'm paralleling this blog with the work on a nopCommerce 1.6 site, but it should be fairly close to the requirements for earlier versions. I will then be applying it to a nopCommerce 1.8 site, which is similar in structure, but has several library changes and has introduced a lot more interfaces and other things (as .NET 4/VS2010 allows) that may confuse non-developers, so I'll do some notes on that when I get round to completing it.&lt;/p&gt;&lt;p&gt;The intention of this addition is to not be too fancy, make it meaningful and get it working in a few hours. You can really go to town on the features and functionality here, but all I'm really interested in doing is integrating a set of values that can be selected and applied to a category, then applied to a base feed. There will be loads of extras to add, like multiple product_types per category. There will be better places to put things and better ways to do this, but as long as it fits to nopCommerce programming style and gets info into Google base per product, that's enough for now.&lt;/p&gt;&lt;p&gt;It's likely that for v1.9, this will all be added to nopCommerce by default (they seem to be pretty good at adding things to new versions that I've added to older ones!) as IMHO, froogle/base is one of the most important aspects of selling products on-line.&lt;/p&gt;&lt;p&gt;Now, on with the show.&lt;/p&gt;&lt;p&gt;Get the taxonomy types download from &lt;a href=&quot;http://www.google.com/support/merchants/bin/answer.py?hl=en&amp;amp;answer=160081/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I got the text version, as I'm only going to add the data as rows, like 'Arts &amp;amp; Entertainment &amp;gt; Collectibles &amp;gt; Sports Collectibles &amp;gt; Sports Trading Cards &amp;gt; Soccer Trading Cards'  in a single field.&lt;/p&gt;&lt;p&gt;Ok, lets import the data. I'm using SQLServer 2008 r2, which has a nice DTS (those were the days) style import feature. Right click on the nop db and select 'Tasks &amp;gt; Import Data'.&lt;/p&gt;&lt;p&gt;Select the file to import and just follow the wizard. I called the new table [Nop_GoogleProductTypes] but call it what you like.. If you have a problem with importing the text, just rename it as csv , open/save as excel (so you get a single column sheet) and import this. You'll also need to add an ID field to the table, and set this as the Identity field.&lt;/p&gt;&lt;p&gt;Ok, so we have the product_types. What is the best way to add these to the products and where is the best place to put the data?&lt;/p&gt;&lt;p&gt;My thoughts are that this should sit at the category level. This seems the most appropriate way to make the setting specific without being a nightmare to manage. And it's probably the most logical, but since when was that important :o&lt;/p&gt;&lt;p&gt;If you haven't extended a class object in nopCommerce before, it's a bit scary. Once you have got familiar with the structure of the code, it's really not that bad (apart from product variant attribute combinations, but that's another story!) and is mostly uniform in structure, so you get used to it pretty quickly.&lt;/p&gt;&lt;p&gt;What we are going to do, is add an identifier property for the product type (the ID of the newly imported product_type) to the category class, then add a custom property which will require a new but very simple object called 'GoogleProductType' or similar, then add the update/add fields to the existing nopCommerce admin area. We don't really need to add any manager classes for the new product types, but it'll need a collection object. If we add it to an existing class folder, we'll also avoid having to add a new provider.&lt;/p&gt;&lt;p&gt;So with this in mind, I'm going to add this to the categories folder. It's probably the best place, as it is only related to [in myinterpretation] categories.&lt;/p&gt;&lt;p&gt;Navigate to Solution &amp;gt; Libraries &amp;gt; Nop.BusinessLogic &amp;gt; Categories, right click on the categories folder and select Add &amp;gt; New Item. Add a new class called 'GoogleProductType.cs'.&lt;/p&gt;&lt;p&gt;Change the class definition to: public partial class GoogleProductTypes : BaseEntity. For completeness, I like to add a summary and regions as per nopCommerce standards, but it won't make the program work any better, so it isn't essential you do to.&lt;/p&gt;&lt;p&gt;Add a class initialiser inside the class definition.&lt;/p&gt;&lt;p&gt;        public GoogleProductTypes()&lt;br /&gt;        {&lt;/p&gt;&lt;p&gt;        }&lt;/p&gt;&lt;p&gt;Add 2 public properties, one for GoogleProductTypeID and one for GoogleProductTypeName, so your completed calss looks like:&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.BusinessLogic.Categories&lt;br /&gt;{    &lt;br /&gt;    /// &amp;lt;summary&amp;gt;&lt;br /&gt;    /// Represents a GoogleProductType&lt;br /&gt;    /// &amp;lt;/summary&amp;gt;&lt;br /&gt;    public partial class GoogleProductTypes : BaseEntity&lt;br /&gt;    {&lt;br /&gt;        #region Ctor&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Creates a new instance of the Category class&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public GoogleProductTypes()&lt;br /&gt;        {&lt;/p&gt;&lt;p&gt;        }&lt;br /&gt;        #endregion&lt;/p&gt;&lt;p&gt;        #region Properties&lt;/p&gt;&lt;p&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Gets or sets the GoogleProductType identifier&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public int GoogleProductTypeId { get; set; }&lt;/p&gt;&lt;p&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Gets or sets the GoogleProductType name&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public string GoogleProductTypeName { get; set; }&lt;/p&gt;&lt;p&gt;        #endregion&lt;/p&gt;&lt;p&gt;        #region Custom Properties&lt;/p&gt;&lt;p&gt;        #endregion&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;Now we need a collection class. Create a new class as above, called 'GoogleProductTypeCollection.cs'.&lt;/p&gt;&lt;p&gt;Collection classes are pretty boring these days, so just make it look like:&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.BusinessLogic.Categories&lt;br /&gt;{&lt;br /&gt;    /// &amp;lt;summary&amp;gt;&lt;br /&gt;    /// Represents a GoogleProductType collection&lt;br /&gt;    /// &amp;lt;/summary&amp;gt;&lt;br /&gt;    public partial class GoogleProductTypeCollection : BaseEntityCollection&amp;lt;GoogleProductTypes&amp;gt;&lt;br /&gt;    {&lt;/p&gt;&lt;p&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;For the interested amongst you, the magic in these collections is done by inheriting from BaseEntityCollection and passing the object type. All the list add/remove stuff that does the work is referenced directly from in there.&lt;/p&gt;&lt;p&gt;Now this is the bit that probably makes most people who try to extend nopCommerce give up. It's the way nopCommerce references objects of type by expressing them in several places, in a seemingly confusing manner.&lt;/p&gt;&lt;p&gt;What we have done so far is create a partial class (and a collection for it) which means we will be defining other bits of this class structure elsewhere. See[url= &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/wa80x488%28v=vs.80%29.aspx&quot;&gt;http://msdn.microsoft.com/en-us/library/wa80x488%28v=vs.80%29.aspx&lt;/a&gt;]this for a better explanation than I can give.&lt;/p&gt;&lt;p&gt;Next, we go to Nop.DataAccess &amp;gt; Categories and add a 'DBGoogleProductType.cs' class. This is pretty much identical to the class we added to Categories, so make sure it looks like:&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.DataAccess.Categories&lt;br /&gt;{&lt;br /&gt;    /// &amp;lt;summary&amp;gt;&lt;br /&gt;    /// Represents a GoogleProductType&lt;br /&gt;    /// &amp;lt;/summary&amp;gt;&lt;br /&gt;    public partial class DBGoogleProductType : BaseDBEntity&lt;br /&gt;    {&lt;br /&gt;        #region Ctor&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Creates a new instance of the Category class&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public DBGoogleProductType()&lt;br /&gt;        {&lt;/p&gt;&lt;p&gt;        }&lt;br /&gt;        #endregion&lt;/p&gt;&lt;p&gt;        #region Properties&lt;/p&gt;&lt;p&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Gets or sets the GoogleProductType identifier&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public int GoogleProductTypeId { get; set; }&lt;/p&gt;&lt;p&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Gets or sets the GoogleProductType name&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        public string GoogleProductTypeName { get; set; }&lt;/p&gt;&lt;p&gt;        #endregion&lt;br /&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;Next add a collection for this, called 'DBGoogleProductTypeCollection.cs'. It'll look like:&lt;/p&gt;&lt;p&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;/p&gt;&lt;p&gt;namespace NopSolutions.NopCommerce.DataAccess.Categories&lt;br /&gt;{&lt;br /&gt;    /// &amp;lt;summary&amp;gt;&lt;br /&gt;    /// Represents a GoogleProductType collection&lt;br /&gt;    /// &amp;lt;/summary&amp;gt;&lt;br /&gt;    public partial class DBGoogleProductTypeCollection : BaseDBEntityCollection&amp;lt;DBGoogleProductType&amp;gt;&lt;br /&gt;    {&lt;/p&gt;&lt;p&gt;    }&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;Next, we need to add our product type to the category. This will actually give you a good idea of how nopCommerce objects knit together, as well as allowing you to do something useful with it.&lt;/p&gt;&lt;p&gt;Open the Category.cs file and add:&lt;/p&gt;&lt;p&gt;public int GoogleProductType { get; set; }&lt;/p&gt;&lt;p&gt;public string GoogleProductTypeText { get; set; }&lt;/p&gt;&lt;p&gt;To the properties region at the end. The reason for 2 fields is that we wand to get the id of the google product_type, but also to be able to add a free-form value of our own to append.&lt;/p&gt;&lt;p&gt;Now add the same 2 fileds to the DBCategory.cs' file, in the same place.&lt;/p&gt;&lt;p&gt;Now we need  to add the 'custom' GoogleProductType object to the category. This might not seem necessary now, but when we use the category later in the actual feed, we'll be thankful we did. There is a fair bit of seemingly wierd code to add, but it's all necessary, so please bear with me. &lt;/p&gt;&lt;p&gt;If you have done this before, you'll know it's a bit of a wild journey through some odd places to add wierd code.&lt;/p&gt;&lt;p&gt;First, we need to add a method or two in the CategoryManager.css class. We need to get all the Google Product Types for adding to a drop down for selection, and also to return a single one for a category to call as a custom property. &lt;/p&gt;&lt;p&gt;Open DBCategoryProvider.cs and add:&lt;/p&gt;&lt;p&gt;public abstract DBGoogleProductType GetGoogleProductType(int GoogleProductTypeID); &lt;/p&gt;&lt;p&gt;...at the end of the methods already in there.&lt;/p&gt;&lt;p&gt;In CategoryManager.cs, add a method with the same name, like:&lt;/p&gt;&lt;p&gt;        public static GoogleProductType GetGoogleProductType(int GoogleProductTypeID)&lt;br /&gt;        {&lt;br /&gt;            var dbitem = DBProviderManager&amp;lt;DBCategoryProvider&amp;gt;.Provider.GetGoogleProductType(GoogleProductTypeID);&lt;br /&gt;            var productType = DBMapping(dbitem);&lt;/p&gt;&lt;p&gt;// More code to go here...&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;This will give a few errors, but we'll deal with them as we go.&lt;/p&gt;&lt;p&gt;Now we need to add a DBMapping to tell the program which DB field matches which object property.&lt;/p&gt;&lt;p&gt;So at the end of the #region Utilities section, add a dbmapping like:&lt;/p&gt;&lt;p&gt;        private static GoogleProductType DBMapping(DBGoogleProductType dbItem)&lt;br /&gt;        {&lt;br /&gt;            if (dbItem == null)&lt;br /&gt;                return null;&lt;/p&gt;&lt;p&gt;            var item = new GoogleProductType();&lt;br /&gt;            item.GoogleProductTypeId = dbItem.GoogleProductTypeId;&lt;br /&gt;            item.GoogleProductTypeName = dbItem.GoogleProductTypeName;&lt;/p&gt;&lt;p&gt;            return item;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;This matches fields in the partial classes together later on when we have data from the db ready to build our object.&lt;/p&gt;&lt;p&gt;Next (I know, but please be patient!) we need to add the method signature from theDBCategoryProvider to the SQLCategoryProvider.cs file (Found in Nop.DataAccess.SQLServer).&lt;/p&gt;&lt;p&gt;        public override DBGoogleProductType GetGoogleProductType(int GoogleProductTypeID)&lt;br /&gt;        {&lt;br /&gt;            DBGoogleProductType item = null;&lt;br /&gt;            Database db = NopSqlDataHelper.CreateConnection(_sqlConnectionString);&lt;br /&gt;            DbCommand dbCommand = db.GetStoredProcCommand(&quot;Nop_GoogleProductTypeGet&quot;);&lt;br /&gt;            using (IDataReader dataReader = db.ExecuteReader(dbCommand))&lt;br /&gt;            {&lt;br /&gt;                if (dataReader.Read())&lt;br /&gt;                {&lt;br /&gt;                    item = GetGoogleProductTypeFromReader(dataReader);&lt;br /&gt;                }&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;            return item;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;Then add a method to get the data from the reader:&lt;/p&gt;&lt;p&gt;        private DBGoogleProductType GetGoogleProductTypeFromReader(IDataReader dataReader)&lt;br /&gt;        {&lt;br /&gt;            var item = new DBGoogleProductType();&lt;br /&gt;            item.GoogleProductTypeId = NopSqlDataHelper.GetInt(dataReader, &quot;GoogleProductTypeID&quot;);&lt;br /&gt;            item.GoogleProductTypeName = NopSqlDataHelper.GetString(dataReader, &quot;GoogleProductType&quot;);&lt;br /&gt;            return item;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;In Category.cs, add:&lt;/p&gt;&lt;p&gt;        public GoogleProductType GoogleProductType&lt;br /&gt;        {&lt;br /&gt;            get&lt;br /&gt;            {&lt;br /&gt;                return CategoryManager.GetGoogleProductType(this.GoogleProductTypeID);&lt;br /&gt;            }&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;Which will get the underlying object from the product type id.&lt;/p&gt;&lt;p&gt;Now we need to reflect this new information in the database for categories, and add fields to the presentation layer in the admin section.&lt;/p&gt;&lt;p&gt;So, in Nop_Categories in the database, add 2 new fields. One called something like GoogleProductTypeID, type INT, one called GoogleProductType, type NVARCHAR(255). This will allow us to add a default Google Product Type, and also append our own bits to the taxonomy.&lt;/p&gt;&lt;p&gt;We now need to add methods to return the GoogleProductTypes in a collection that we can add to our Category Admin page(s).&lt;/p&gt;&lt;p&gt;To add the methods to get a collection of GooglePRoductTypes, we need to duplicate the same process as we added for a single object, so in the CategoryManager.cs file, add a new method below the GetGoogleProductType() method we created earlier, like this:&lt;/p&gt;&lt;p&gt;        public static GoogleProductTypeCollection GetGoogleProductTypes()&lt;br /&gt;        {&lt;br /&gt;            var dbCollection = DBProviderManager&amp;lt;DBCategoryProvider&amp;gt;.Provider.GetGoogleProductTypes();&lt;br /&gt;            var productTypeCategoryCollection = DBMapping(dbCollection);&lt;/p&gt;&lt;p&gt;            return productTypeCategoryCollection;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;You'll get erros, but don't worry, we'll fix them now.&lt;/p&gt;&lt;p&gt;In DBCategoryProvider.cs, add the following abstract method definition at the end, just after the one we added earlier.&lt;/p&gt;&lt;p&gt;public abstract DBGoogleProductTypeCollection GetGoogleProductTypes();&lt;/p&gt;&lt;p&gt;Now back to the CategoryManager class, we need to add a DBMapping for the collection, so just below the GoogleProductType DBMapping we added earlier, add:&lt;/p&gt;&lt;p&gt;        private static GoogleProductTypeCollection DBMapping(DBGoogleProductTypeCollection dbCollection)&lt;br /&gt;        {&lt;br /&gt;            if (dbCollection == null)&lt;br /&gt;                return null;&lt;/p&gt;&lt;p&gt;            var collection = new GoogleProductTypeCollection();&lt;br /&gt;            foreach (var dbItem in dbCollection)&lt;br /&gt;            {&lt;br /&gt;                var item = DBMapping(dbItem);&lt;br /&gt;                collection.Add(item);&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;            return collection;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;Ok, so far, so good. We have fixed the errors, so all the methods match correctly. We added the method definition to the DBCategoryProvider.cs file, so we now need to add the actual method to the SQLCategoryProvider.cs file to get the stuff out of the DB and into the app.&lt;/p&gt;&lt;p&gt;Now create another stored proc called something like Nop_GoogleProductTypesGet and then add this method at the end of the SQLCategoryProvider.cs file:&lt;/p&gt;&lt;p&gt;public override DBGoogleProductTypeCollection GetGoogleProductTypes()&lt;br /&gt;        {&lt;br /&gt;            var result = new DBGoogleProductTypeCollection();&lt;br /&gt;            Database db = NopSqlDataHelper.CreateConnection(_sqlConnectionString);&lt;br /&gt;            DbCommand dbCommand = db.GetStoredProcCommand(&quot;Nop_GoogleProductTypesGet&quot;);&lt;/p&gt;&lt;p&gt;            using (IDataReader dataReader = db.ExecuteReader(dbCommand))&lt;br /&gt;            {&lt;br /&gt;                while (dataReader.Read())&lt;br /&gt;                {&lt;br /&gt;                    var item = GetGoogleProductTypeFromReader(dataReader);&lt;br /&gt;                    result.Add(item);&lt;br /&gt;                }&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;            return result;&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;(Note: I'm not adding the stored proc details here. They are pretty simple selects, so you should be able to figure out what to do with them).&lt;/p&gt;&lt;p&gt;Ok, time for a deep breath and a think about what we have done so far.&lt;/p&gt;&lt;p&gt;We've added a new table to the db for Google Product Types, added new fields to our Category object, added a new object type to represent GoogleProductTypes, made it all nopCommerce stylee and prepared some methods to retrieve and use this new info.&lt;/p&gt;&lt;p&gt;Ok, so let's do some actual work that people can see. (I often explain the iceberg principle, but it never makes sense to anyone!).&lt;/p&gt;&lt;p&gt;I'm going to add the 2 new fields to the SEO panel in the Category Details page. We could put it elsewhere, make our own panel or add it somehere odd, but the SEO panel works for me.&lt;/p&gt;&lt;p&gt;If you check in CategoryDetails.ascx, you'll see we need to edit CategorySEO.ascx. So open this in the editor.&lt;/p&gt;&lt;p&gt;In the table where the field values are listed, add 2 new rows, one for the dropdown and one for the text field. You should add all the localisation tokens etc, but I've essentially got 2 new controls. A DropDownList called ddlGoogleProductType and a textbox called txtGoogleProductType.&lt;/p&gt;&lt;p&gt;Now press F7 to see the codebehind.&lt;/p&gt;&lt;p&gt;We are interested in 3 things here. Getting the new data fields out of the DB and into the controls, filling the dropdownlist and writing data back in.&lt;/p&gt;&lt;p&gt;NOTE: There are references to Localised content here, but for the purpose of this document, I'm going to ignore them and just make amends to the SaveInfo() method. The localisation functionality is important to nopCommerce, but for now (as a proof of concept) I'm keeping it simple.&lt;/p&gt;&lt;p&gt;Now add:&lt;/p&gt;&lt;p&gt;                this.txtGoogleProductType.Text = category.GoogleProductTypeText;&lt;/p&gt;&lt;p&gt;To the BindData() method, after the other calls to fill TextBoxes.&lt;/p&gt;&lt;p&gt;We'll be adding the dropdownlist stuff too now. Add this method to the CategorySEO.cs file:&lt;/p&gt;&lt;p&gt;        protected void FillDropDown()&lt;br /&gt;        {&lt;br /&gt;            ddlGoogleProductType.DataSource = CategoryManager.GetGoogleProductTypes();&lt;br /&gt;            ddlGoogleProductType.DataTextField = &quot;GoogleProductTypeName&quot;;&lt;br /&gt;            ddlGoogleProductType.DataValueField = &quot;GoogleProductTypeID&quot;;&lt;br /&gt;            ddlGoogleProductType.DataBind();&lt;br /&gt;        }&lt;/p&gt;&lt;p&gt;Now add FillDropDown(); to the BindData() method. We will now 'find' the selected product type (if available).&lt;/p&gt;&lt;p&gt;Next, add:&lt;/p&gt;&lt;p&gt;                ddlGoogleProductType.Items.Insert(0, &quot;&quot;);&lt;/p&gt;&lt;p&gt;...to the BindData() method. This allows us to use 0 as the type (And we could also add a default value of zero to the GoogleProductID field in Nop_Category in the database). &lt;/p&gt;&lt;p&gt;Now add:&lt;/p&gt;&lt;p&gt;                if (category.GoogleProductTypeID != null)&lt;br /&gt;                {&lt;br /&gt;                    if (category.GoogleProductTypeID &amp;gt; 0)&lt;br /&gt;                    {&lt;br /&gt;                        ddlGoogleProductType.Items.FindByValue(category.GoogleProductTypeID.ToString()).Selected = true;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;/p&gt;&lt;p&gt;...to select the stored value. (It'll be null at the moment, because we've not added the field to the DBMapping for Category. It should be in the stored proc, because nopCommerce generally uses SELECT *.&lt;/p&gt;&lt;p&gt;So if we have a look, we see this uses GetCategoryByID() from the Category Manager.cs file, so we need to update the GetCategoryFromReader() method by adding:&lt;/p&gt;&lt;p&gt;            item.GoogleProductType = NopSqlDataHelper.GetInt(dataReader, &quot;GoogleProductTypeID&quot;);&lt;br /&gt;            item.GoogleProductTypeText = NopSqlDataHelper.GetString(dataReader, &quot;GoogleProductType&quot;);&lt;/p&gt;&lt;p&gt;This will add the field values to the object.&lt;/p&gt;&lt;p&gt;EDIT: Some SP's aren't using SELECT *, so we need to add our new fields to them. The ones I've updated are:&lt;/p&gt;&lt;p&gt;[Nop_CategoryLoadAll]&lt;br /&gt;[Nop_CategoryLoadByPrimaryKey]&lt;/p&gt;&lt;p&gt;I've just added the new field names to the selects. Namely:&lt;br /&gt;,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;c.GoogleProductType,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;c.GoogleProductTypeID&lt;/p&gt;&lt;p&gt;Ok, now in the SaveInfo() method, add 2 new parameters to the UpdateCategory() method call, ', ddlGoogleProductType.SelectedValue, txtGoogleProductType.Text'.&lt;/p&gt;&lt;p&gt;Navigate to the method in CategoryManager and add the new parameters to the list, eg: ', int GoogleProductTypeID, string GoogleProductType'.&lt;/p&gt;&lt;p&gt;Then in the method, add them to the Provider.UpdateCategory() method, like ', GoogleProductTypeID, GoogleProductType'.&lt;/p&gt;&lt;p&gt;Now open DBCategoryProvider.cs and update the UpdateCategory() method by adding ', int GoogleProductTypeID, string GoogleProductType' to the parameters.&lt;/p&gt;&lt;p&gt;Next, we need to edit the UpdateCategory() method in SQLCategoryProvider.cs. Again, we add the 2 new parameters, like: ', int GoogleProductTypeID, string GoogleProductType'.&lt;/p&gt;&lt;p&gt;We then add 2 new parameters to the stored procedure parameter list and also to the stored proc itself. Add this to the UpdateCategoty() method:&lt;/p&gt;&lt;p&gt;            db.AddInParameter(dbCommand, &quot;GoogleProductTypeID&quot;, DbType.Int32, GoogleProductTypeID);&lt;br /&gt;            db.AddInParameter(dbCommand, &quot;GoogleProductType&quot;, DbType.String, GoogleProductType);&lt;/p&gt;&lt;p&gt;Then add the 2 perameters and inserts in the stored procedure. (Nop_CategoryUpdate).&lt;/p&gt;&lt;p&gt;You'll also need to update the references to the UpdateCategory() method in MarkCategoryAsDeleted() and RemoveCategoryPicture(). Both of these are in the CategoryManager class. And also SaveInfo() in the CategoryInfo.ascx.cs file.&lt;/p&gt;&lt;p&gt;I also noticed I'd missed adding:&lt;/p&gt;&lt;p&gt;            item.GoogleProductTypeID = dbItem.GoogleProductType;&lt;br /&gt;            item.GoogleProductTypeText = dbItem.GoogleProductTypeText;&lt;/p&gt;&lt;p&gt;to the Category DBMapping in CategoryManager(), so it was adding ok, but not returning. Ooops. &lt;/p&gt;&lt;p&gt;I also missed adding the parameter to GetGoogleProductType() in SQL CategoryProvider. Add &lt;/p&gt;&lt;p&gt;            db.AddInParameter(dbCommand, &quot;GoogleProductTypeID&quot;, DbType.Int32, GoogleProductTypeID);&lt;/p&gt;&lt;p&gt;Under the DBCommand dbcommand = statemnt.&lt;/p&gt;&lt;p&gt;Ok, so now, if you run the app (and fix a few likely errors that occur!) you can add a Google Product Type to a category.&lt;/p&gt;&lt;p&gt;Now, [and at last, the point of all this!] we can add this information to our froogle feed.&lt;/p&gt;&lt;p&gt;We see a few nuances and other things we now need to think about. A product has one or many categories, so we need to add them all to this feed.&lt;/p&gt;&lt;p&gt;For now, I've added this to the FroogleService.cs file, just after the commented out reference to product_type:&lt;/p&gt;&lt;p&gt;                        string sout = &quot;&quot;;&lt;/p&gt;&lt;p&gt;                        foreach (ProductCategory c in product.ProductCategories)&lt;br /&gt;                        {&lt;br /&gt;                            if (c.Category.GoogleProductTypeID &amp;gt; 0)&lt;br /&gt;                            {&lt;br /&gt;                                if (sout.Length &amp;gt; 0)&lt;br /&gt;                                {&lt;br /&gt;                                    sout += &quot;, &quot;;&lt;br /&gt;                                }&lt;br /&gt;                                sout += &quot;\&quot;&quot; + c.Category.GoogleProductType.GoogleProductTypeName + &quot; &quot; + c.Category.GoogleProductTypeText + &quot;\&quot;&quot;;&lt;br /&gt;                            }&lt;br /&gt;                        }&lt;/p&gt;&lt;p&gt;                        if (sout.Length &amp;gt; 0)&lt;br /&gt;                        {&lt;br /&gt;                            writer.WriteStartElement(&quot;g&quot;, &quot;product_type&quot;, googleBaseNamespace);&lt;br /&gt;                            writer.WriteCData(sout);&lt;br /&gt;                            writer.WriteFullEndElement(); // g:brand&lt;br /&gt;                        }&lt;/p&gt;&lt;p&gt;This basically builds a list of comma-separated values, in quotes, as per the spec, to account for multiple categories.&lt;/p&gt;&lt;p&gt;Hopefully, this will be helpful to someone, or at least give a leg-up when adding it to your site.&lt;/p&gt;&lt;p&gt;It is by no means a complete solution and in reality, it needs a lot more functionality and features before it's commercial strength.&lt;/p&gt;&lt;p&gt;This works ok for me, but I accept you may have problems, or I've made some [very likely] typos and omissions, so please ask if you need help.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;&lt;p&gt;/ed.&lt;/p&gt;</description>
			<pubDate>Thu, 03 Feb 2011 12:00:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/better-google-base-in-nopcommerce/</guid>
		</item>
		
		<item>
			<title>User-friendly pop-up image gallery for NopCommerce</title>
			<link>http://www.n-webdesign.co.uk/user-friendly-pop-up-image-gallery-for-nopcommerce/</link>
			<description>&lt;p&gt;&lt;strong&gt;The problem...&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I have done several 'hacks' to nopCommerce to add galleries to pages. These normally involve a new page, a load of custom code and a very inflexible editing system for the CMS user.&lt;/p&gt;&lt;p&gt;This isn't a problem with nopCommerce, but the 'Topics' section (which is used to edit CMS style page content) is good but basic.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The solution&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;That's pretty easy [in theory]. Create something that lets you add some simple markup to a page, then the necessary Slimbox jQuery stuff, then somewhere to add the image files and then a widget/whoozip/thingymajig to knit it all together.&lt;/p&gt;&lt;p&gt;In practice, it's still pretty easy, but requires some thought and the scary prospect of adding some custom classes, objects and database stuff to nopCommerce.&lt;/p&gt;&lt;p&gt;The best part of all this is, it is written following nop style code, using 2 new tables in the db and about 10 new stored procs,so it'll be really easy to upgrade and it is written to run inside the Content Management section of nop admin so it'll be right where editors need it.&lt;/p&gt;&lt;p&gt;Actually, the best part is that once you have added images, which is just like adding an image to a product, the only thing you need to do is add &lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;div id=&quot;MyGalleryName&quot;  class=&quot;gallery&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;&lt;/div&gt; to the source in the topic and it adds the gallery right there.&lt;/p&gt;&lt;p&gt;Actually, it's not that scary really. Here's what I've done...&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Breaking eggs...&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Ok, we need a custom gallery class to store galleries, pics, titles and stuff.&lt;/p&gt;&lt;p&gt;The admin screens to manage it all.&lt;/p&gt;&lt;p&gt;We need a gallery popup/markup script to do the magic. (Slimbox in our case, but this could be adopted to anything JQuery/Prototype based really?).&lt;/p&gt;&lt;p&gt;We need some glue to fix it all together.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The bad news&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Well, kinda bad news. I haven't got the gallery totally production ready, so you're going to have to wait a while for the full how-to, source files and changes.&lt;/p&gt;&lt;p&gt;I have got the gallery working in a live site that is soon to get Galleried up, so to get your juices running, here are some screenshots of the gallery in use.  &lt;/p&gt;&lt;p&gt;&lt;strong&gt;The gallery editor, sitting happily in the Content menu...&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://www.n-webdesign.co.uk/assets/Uploads/BlogFiles/galleries-1.png&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;The gallery listing. This will show as many galleries as you add...&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://www.n-webdesign.co.uk/assets/Uploads/BlogFiles/galleries-2.png&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Editing a single galley will show the images that are in the gallery. These will be cycled through as per SlimBox...&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://www.n-webdesign.co.uk/assets/Uploads/BlogFiles/galleries-3.png&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Here's a gallery item, shown on a topic page. Remember, 1 line of markup to add!&lt;/strong&gt; &lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://www.n-webdesign.co.uk/assets/Uploads/BlogFiles/galleries-4.png&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;This shows the SlimBox rendering on the gallery image.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;http://www.n-webdesign.co.uk/assets/Uploads/BlogFiles/galleries-5.png&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;There are things to do...&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;As usual, test it. Then test it some more.&lt;/p&gt;&lt;p&gt;Document it and refine it a little.&lt;/p&gt;&lt;p&gt;This is in v1.6. Check it in v1.8&lt;/p&gt;&lt;p&gt;How do you add multiple galleries to a page? I dunno? I'll try it and see what happens. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;If you are interested in using the gallery on your site, please let me know. I have a tendancy to make things work, then leave them as-is, which works for me, but can be a pain for other people. Shame me into writing these things up properly!&lt;/strong&gt;&lt;/p&gt;</description>
			<pubDate>Wed, 17 Nov 2010 12:19:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/user-friendly-pop-up-image-gallery-for-nopcommerce/</guid>
		</item>
		
		<item>
			<title>Adding a twitter feed to nop with twittertable </title>
			<link>http://www.n-webdesign.co.uk/adding-a-twitter-feed-to-nop-with-twittertable/</link>
			<description>&lt;p&gt;One great thing about writing software for the internet is the availability of really cool libraries and apps, often for free.&lt;/p&gt;&lt;p&gt;I've just added the really simple and effective &lt;a href=&quot;http://theodin.co.uk/blog/javascript/tweetable-jquery-plugin.html&quot;&gt;Tweetable&lt;/a&gt; JQuery plugin to a nop build. It's so simple (mainly due to nop, JQuery and Tweetable) that you can be up and running with it in minutes.&lt;/p&gt;&lt;p&gt;Although I am talking about nop and .NET here, this can be added to anything really easily, it's just that I've added it to a nop build and many nop newbies struggle to get their head round adding bespoke content, so hopefully it will be useful for them to get something meaningful up on their site.&lt;/p&gt;&lt;p&gt;Ok, you need 3 things. A twitter account (actually, you don't log in or anything, so in theory, you could grab anyone's tweets), a nop site and the Tweetable files.&lt;/p&gt;&lt;p&gt;So, first things first.&lt;/p&gt;&lt;p&gt;Tweetable works by grabbing ajax data from twitter and making a list which it puts in a div. Just a normal UL/LI html list in a normal div.&lt;/p&gt;&lt;p&gt;This is great, because it means you can create a user control with hardly any markup that can go almost anythere on the page. It makes it easy to control the layout in CSS and in theory, you could have several tweet feeds on a page, all in different sizes and shapes. you wouldn't really do this, but you might want to show this in different places on several pages.&lt;/p&gt;&lt;p&gt;So for flexibility, add a new user control called Tweetable.ascx or whatever you like. In nop, it's best to add it in the Modules folder for consistency, but on other .NET builds, add it wherever you like.&lt;/p&gt;&lt;p&gt;No if you are using nop, open the Tweetable.ascx.cs file and inherit from BaseNopUsercontrol instead of the default System.Web.UserControl.&lt;/p&gt;&lt;p&gt;Next, we need to add the Tweetable JS file and JQuery references.&lt;/p&gt;&lt;p&gt;JQuery is used a lot in nop, so just if check it's referenced in the site. I find that Ctrl+U in Firefox, then F3 and type jquery in the find will show it it's there.&lt;/p&gt;&lt;p&gt;Now copy the jquery.tweetable.js file into your Scripts folder or wherever you normally store Javascript files.&lt;/p&gt;&lt;p&gt;In the Tweetable.ascx.cs file, add a ClientScriptInclude to the page inside the Page_Load() method. Something like:&lt;/p&gt;&lt;p&gt;if (!Page.ClientScript.IsClientScriptIncludeRegistered(this.GetType(), &quot;Tweetable&quot;))&lt;br /&gt;            {&lt;br /&gt;                Page.ClientScript.RegisterClientScriptInclude(this.GetType(), &quot;Tweetable&quot;, &quot;../Scripts/jquery.tweetable.js&quot;);&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;Obviously change the target to match your script location.&lt;/p&gt;&lt;p&gt;If you need to add JQuery too, just add a reference before the above code, something like:&lt;/p&gt;&lt;p&gt;if (!Page.ClientScript.IsClientScriptIncludeRegistered(this.GetType(), &quot;JQuery&quot;))&lt;br /&gt;            {&lt;br /&gt;                Page.ClientScript.RegisterClientScriptInclude(this.GetType(), &quot;JQuery&quot;, &quot;../Scripts/jquery-1.4.min.js&quot;);&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;This will make the necessary JQuery things available to the page.&lt;/p&gt;&lt;p&gt;Now we need to add the invocation and the target div for our tweets.&lt;/p&gt;&lt;p&gt;In the Tweetable.ascx file, add a script under the &amp;lt;%@ Control %&amp;gt; directive as follows.&lt;/p&gt;&lt;p&gt;&amp;lt;script&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;$(function(){&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $('#tweets').tweetable({username: 'nwebdesign', time: true, limit: 3, replies: false});&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;});&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;&lt;p&gt;This is pretty self explanatory, but basically this is calling the nwebdesign tweets, and getting 3 of them. You can get as many or as few as you like.&lt;/p&gt;&lt;p&gt;We then add a target div for Tweetable to fill. Add this directly under the script invocation as follows:&lt;/p&gt;&lt;p&gt;&amp;lt;div id=&quot;tweets&quot;&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;&lt;p&gt;Once this is done, you can simply drag Tweetable.ascx from the Solution Explorer directly in to a page (or just type the include and control tag in manually) and you are ready to roll.&lt;/p&gt;&lt;p&gt;Once it is all working, you can copy the styles from the css file provided with Tweetable and edit it for your needs or just write your own styles.&lt;/p&gt;&lt;p&gt;If you want it to have a little more flexibility and take advantage of settings in nop, you can move the invocation script into the .ascx.cs file and build the javascript call dynamically.&lt;/p&gt;&lt;p&gt;Tweetable references 4 JSON style values: username:, time:, limit:, replies:.&lt;/p&gt;&lt;p&gt;Add 4 entries in the settings table:&lt;/p&gt;&lt;p&gt;Tweetable.TweetUser (The twitter user)&lt;br /&gt;Tweetable.TweetTime (Set true/false whether we show time)&lt;br /&gt;Tweetable.TweetLimit (The number of tweets to show)&lt;br /&gt;Tweetable.TweetReplies (Set true/false whether we show replies)&lt;/p&gt;&lt;p&gt;These are all set as strings, as we will be using them in a StringBuilder(). &lt;/p&gt;&lt;p&gt;Now we can create a StringBuilder object and build the javascript we need and add it in a ClientScriptInclude as we did with JQuery and the Tweetable plugin.&lt;/p&gt;&lt;p&gt;In the Tweetable.ascx.cs file, add this in the Page_Load() method following the JQuery and Tweetable includes:&lt;/p&gt;&lt;p&gt;if (!Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), &quot;Tweets&quot;))&lt;br /&gt;            {&lt;br /&gt;                StringBuilder sb = new StringBuilder();&lt;/p&gt;&lt;p&gt;                sb.Append(&quot;&amp;lt;script&amp;gt;&quot;);&lt;br /&gt;                sb.Append(&quot;$(function(){&quot;);&lt;br /&gt;                sb.Append(&quot;$('#tweets').tweetable({username: '&quot; + SettingManager.GetSettingByName(&quot;Tweetable.TweetUser&quot;).Value + &quot;', time: &quot; + SettingManager.GetSettingByName(&quot;Tweetable.TweetTime&quot;).Value + &quot;, limit: &quot; + SettingManager.GetSettingByName(&quot;Tweetable.TweetLimit&quot;).Value + &quot;, replies: &quot; + SettingManager.GetSettingByName(&quot;Tweetable.TweetReplies&quot;).Value + &quot;});&quot;);&lt;br /&gt;                sb.Append(&quot;});&quot;);&lt;br /&gt;                sb.Append(&quot;&amp;lt;/script&amp;gt;&quot;);&lt;/p&gt;&lt;p&gt;                string s = sb.ToString();&lt;/p&gt;&lt;p&gt;                Page.ClientScript.RegisterClientScriptBlock(this.GetType(), &quot;Tweets&quot;, s, false);&lt;br /&gt;            }&lt;/p&gt;&lt;p&gt;This just builds the Tweetable invocation script dynamically and adds it to the markup.&lt;/p&gt;&lt;p&gt;Remember to remove the hard coded script from the Tweetable.ascx file. Also note that the settings for true/false will need to be lowercase, as they are used by JS.&lt;/p&gt;&lt;p&gt;That's it. Pretty simple, but a very useful plugin.&lt;/p&gt;&lt;p&gt;Have fun...&lt;/p&gt;</description>
			<pubDate>Fri, 08 Oct 2010 17:32:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/adding-a-twitter-feed-to-nop-with-twittertable/</guid>
		</item>
		
		<item>
			<title>ePDQ intergration for nopCommerce part 2</title>
			<link>http://www.n-webdesign.co.uk/epdq-intergration-for-nopcommerce-part/</link>
			<description>&lt;p&gt;&lt;strong&gt;Why this second blog?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Since adding my speculative blog post about there not being an ePDQ payment module available for nopCommerce, and me having to write one, I've had a few requests from people for the code.&lt;/p&gt;&lt;p&gt;Please note there is at least one commercial offering out there, so if you aren't a developer, want timely support or need some guaranteed level of maintenance, have a look at their offering. If you just want a minimal module with scant instructions and are happy to roll your sleeves up, then I have added a project on &lt;a href=&quot;https://sourceforge.net/projects/epdq-nopcomm/&quot;&gt;SourceForge&lt;/a&gt; so you can get the files.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Why do this when there is already a version out there?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Simply put, I didn't want to pay for it. Nop makes it pretty easy to add new payment modules to the system so I thought I'd just get on with it.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Why is your version being release for free?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;There are two reasons. &lt;/p&gt;&lt;p&gt;One. I don't plan on spending the time to make it a 'commercial' product. &lt;/p&gt;&lt;p&gt;Two. Nop Commerce is open source, I have used it on many projects and I'd like to maintain the spirit of community by putting omething back in.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Things to be aware of with my ePDQ module&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;This has been developed on a nop 1.5 install. I have a 1.6 site I can test it on, but it should work without too many issues on older builds. i've not used &amp;gt; 1.6 yet, so I am unaware if the payment module structure (on which this relies) is different. &lt;/p&gt;&lt;p&gt;It has been 'ported' from another similar module, so potentially has some left over bits and bobs in there.&lt;/p&gt;&lt;p&gt;It is still considered a 'beta' project.&lt;/p&gt;&lt;p&gt;Some data, settings or functionality may be missing simply because I didn't need it.&lt;/p&gt;&lt;p&gt;The structure of the module follows nopCommerce styles and implemetation methods for payment modules. As such, it has all the admin options in the 'Payment Method' details screen etc.&lt;/p&gt;&lt;p&gt;We accept no responsibility for loss or problems you may incur with you using this modules or following the suggested implementation process in this blog. The module is intended as a demonstration of integrating an ePDQ payment module in to nopCommerce.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;On with the show&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Some prerequisites:&lt;/p&gt;&lt;p&gt;An ePDQ account.&lt;br /&gt;A nop commerce web site.&lt;/p&gt;&lt;p&gt;Adding the module and setting it all up:&lt;/p&gt;&lt;p&gt;Step 1:&lt;/p&gt;&lt;p&gt;Configure your ePDQ CPI details. I won't be covering this here, so make sure you understand what needs to be done. Full details can be found in the integration guide &lt;a href=&quot;http://www.barclaycard.co.uk/business/existing-customers/activate-epdq-cpi/&quot;&gt; from Barclays&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Step 2:&lt;/p&gt;&lt;p&gt;Download the sample ePDQ module files from &lt;a href=&quot;https://sourceforge.net/projects/epdq-nopcomm/&quot;&gt;SourceForge&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Unzip the files somewhere, ready to copy into the project.&lt;/p&gt;&lt;p&gt;Step 3:&lt;/p&gt;&lt;p&gt;Copy the Necesse.Payment.ePDQ folder from the ZiP file into the \Project\Payment folder. &lt;/p&gt;&lt;p&gt;In the solution explorer, find the payment projects folder, select add existing project and select the ePDQ module project.&lt;/p&gt;&lt;p&gt;Step 4:&lt;/p&gt;&lt;p&gt;From the ZiP file contents, copy the ePDQ folder from 'Administration/Payment' into the same folder in the nopCommerce store. (Or whatever you've called your site root folder in the solution).&lt;/p&gt;&lt;p&gt;This contains the admin config pages for the module. You should use 'Show all files' from the solution explorer and add these files to the project.&lt;/p&gt;&lt;p&gt;Step 5:&lt;/p&gt;&lt;p&gt;From the ZiP file, copy the ePDQ folder directly into the root of the web site. Add the files to the project as above.&lt;/p&gt;&lt;p&gt;These are the files that ePDQ uses to update payment status back to nop. If you don't set this option in ePDQ, they aren't needed. NOTE: the reason these files are in a folder is that ePDQ requires a digest protected file to post back payment statuses. It's all in the manual! For nop/Windows, this means adding a user to the Web Server and setting prmissions in IIS (NOT NT) for basic challenge/response on this folder.&lt;/p&gt;&lt;p&gt;Step 6:&lt;/p&gt;&lt;p&gt;From the ZiP, copy the Templates/Payment/ePDQ folder into the same path (Templates/Payment) inside nopCommerceStore (or whatever you've called your site).&lt;/p&gt;&lt;p&gt;Step 7:&lt;/p&gt;&lt;p&gt;Add a reference to the ePDQ module in the nopCommerce Store.&lt;/p&gt;&lt;p&gt;Run nopCommerce and make sure it all builds.&lt;/p&gt;&lt;p&gt;You can do this next bit in DEV up to a point (trying it in debug first is recommended), or just deploy the updated files to the web server and set it up there. In dev, ePDQ wil throw an invalid url error for localhost, even if you set it in the CPI admin tool.&lt;/p&gt;&lt;p&gt;Sign in to nop admin and navigate to the Configuration/Payments/Payment Methods page.&lt;/p&gt;&lt;p&gt;Select 'Add New'.&lt;/p&gt;&lt;p&gt;Fill in the form as required. The important settings and values are:&lt;/p&gt;&lt;p&gt;Configuration template path: Payment\ePDQ\ConfigurePaymentMethod.ascx&lt;/p&gt;&lt;p&gt;User template path: ~\Templates\Payment\ePDQ\PaymentModule.ascx&lt;/p&gt;&lt;p&gt;Class name: Necesse.Payment.ePDQ.ePDQPaymentProcessor,Necesse.Payment.ePDQ&lt;/p&gt;&lt;p&gt;System keyword: EpDQ&lt;/p&gt;&lt;p&gt;Obviously set it to active and I just added ePDq for the display name and name values.&lt;/p&gt;&lt;p&gt;Once you have added this, select 'save' and you will now see a 'Configuration' tab in the module admin.&lt;/p&gt;&lt;p&gt;These are all settings that you need to define or get from ePDQ with the exception of 'Use test mode:'. This is simply a settings flag that writes the response from ePDQ to the nop log. It is handy for debugging and testing writeback actually happens. As you know, ePDQ is a bit 'underfacilitated' for us developers.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;I think that's it?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I think that's everything. As I mentioned, this is a 'beta' module that has been tested only by me, so if you find anything awry or need something I've missed, just let me know. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;Things to check when troubleshooting&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;In theory all the files are in the ZiP, my instructions are correct and my basic testing has covered all the reasonable bases.&lt;/p&gt;&lt;p&gt;If your site builds, I would imagine the majority of errors you may get will be in the ePDQ setup, but as convoluted and odd as it is, it's all in the manual. It's just really easy to miss something.&lt;/p&gt;&lt;p&gt;Have fun...&lt;/p&gt;</description>
			<pubDate>Thu, 07 Oct 2010 16:20:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/epdq-intergration-for-nopcommerce-part/</guid>
		</item>
		
		<item>
			<title>Social media is a toddler, apparently...</title>
			<link>http://www.n-webdesign.co.uk/social-media-is-a-toddler-apparently/</link>
			<description>&lt;p&gt;According to this &lt;a href=&quot;http://www.bbc.co.uk/news/business-11450923/&quot;&gt;great bbc article&lt;/a&gt;, businesses are slow to react to, don't understand or underestimate the power of social networking and media.&lt;/p&gt;&lt;p&gt;According to the article, a leading retailer heard about technical issues from journalists who had seen details on twitter! Why should we be suprised. There have been several well publicised outbursts from top [over] rated sportsmen lately about being dropped from teams and other issues with team selections for rugby games being 'leaked' unintentionally by players. Corrie, amongst others, have banned Twitter from sets. Where will it end?&lt;/p&gt;&lt;p&gt;So what does this mean for businesses, especially small businesses looking to use social networking as a marketing tool?&lt;/p&gt;&lt;p&gt;Twitter, FaceBook, YouTUBE and blogs (shall we call them social media tools from now on?) might be 'toddlers' but they are immensely powerful and useful business marketing tools. Used correctly, in a meaningful and structured way, within a marketing strategy, they can be immensely powerful and beneficial to businesses. Used incorrectly or without consideration, they can be an embarrassing and costly waste of time.&lt;/p&gt;&lt;p&gt;So how can we avoid, as small to medium business, getting egg on our social media faces?&lt;/p&gt;&lt;p&gt;The key to using social media correctly is in understanding the media, how it reflects our business, how it communicates to our stakeholders and most specifically how marketing works as a method of communication.&lt;/p&gt;&lt;p&gt;It has been suggested that 'marketeers need to completely change their techniques to make social media work'. i disagree, although it is important to understand how these new areas of communication media are acting as a conduit from businesses to consumers. It is, in essence, no different than the transition in marketing and advertising from newspaper to radio, radio to television and so on. For example, radio became a great new land of prospects for proactive marketing people, but we still spend on newspaper ads. The same is true of FaceBook, Twitter or whichever social media tool takes your fancy. The big change, and advantage, is the removal of the anonymity of a straight ad or marketing channel. Now the customer can bite back. That's where the power lies.&lt;/p&gt;&lt;p&gt;Like all things in this [marketing] business, it's very much a numbers game and it's sometimes difficult to decide on the chicken or the egg? You might have a great writing style and fill your tweets with humorous, insightful comment every 10 minutes, but if you aren't being read, it's just wasting your time. As we said, it's about understanding the media and working out what will work for you. &lt;/p&gt;&lt;p&gt;For example, you might have a new site, so work on building a blog and news rich content to grow the brand and drive up traffic up, then invest time in Twitter to build a buzz and gain a meaningful dialogue with clients and followers,&lt;/p&gt;&lt;p&gt;So you can see that by knowing what your starting point is, understanding the tools that are available and when it is appropriate to introduce new methods of communication, you can get the most from your time and efforts.&lt;/p&gt;&lt;p&gt;More mature or established businesses can use Twitter and other social media tools as hooks on their already busy websites. Moving direct communication with clients from email or chat apps to Twitter will leverage the vogue appeal of Twitter and allow your sales team to keep in touch with clients old and new. &lt;/p&gt;&lt;p&gt;So in summary, don't ditch the ads, email campaigns or SEO. Just take some time to know and understand each new or emerging social media tool and see how it can work for you and your business. As with all marketing, a little imagination, objectivity and time will go a long way. See how other businesses and institutions are introducing these new communication streams to get an idea on what could work for you.&lt;/p&gt;</description>
			<pubDate>Mon, 04 Oct 2010 17:19:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/social-media-is-a-toddler-apparently/</guid>
		</item>
		
		<item>
			<title>2 week freeze on dev work</title>
			<link>http://www.n-webdesign.co.uk/2-week-freeze-on-dev-work/</link>
			<description>&lt;p&gt;N Web Design and N Go will be freezing development work from 18th September 2010 until  4th October 2010.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;What does this mean?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Well, we will be working on things as normal, but because of some commitment requirements for those 2 weeks, we won't be doing any roll outs or updates within these 2 dates.&lt;/p&gt;&lt;p&gt;We will be supporting you services, systems and applications as normal during this time, so don't worry.&lt;/p&gt;&lt;p&gt;If you have any questions, please get in touch.&lt;/p&gt;&lt;p&gt;Thanks.&lt;/p&gt;</description>
			<pubDate>Wed, 01 Sep 2010 15:23:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/2-week-freeze-on-dev-work/</guid>
		</item>
		
		<item>
			<title>ePDQ intergration for nopCommerce</title>
			<link>http://www.n-webdesign.co.uk/epdq-intergration-for-nopcommerce/</link>
			<description>&lt;p&gt;One of the applications we use a lot is NopCommerce.&lt;/p&gt;&lt;p&gt;The reasons why are pretty obvious if you've used it or seen what it can do.&lt;/p&gt;&lt;p&gt;On a recent e-commerce project based around nopCommerce (1.6 on .NET 3.5) I needed to use ePDQ for the payment gateway.&lt;/p&gt;&lt;p&gt;This is all cool stuff, because I've done a lot in nopCommerce and also have done a few ePDQ integrations on bespoke dev in the past.&lt;/p&gt;&lt;p&gt;Another great reason for using nopCommerce is the community support, which is pretty big, normally helpful and good at sharing code. In actual fact nop themselves seem to do a pretty regular job of integrating common payment gateways and other items into the base build from user contributed code, so I was pretty confident of seeing ePDQ in there, or at least a project on GitHub or similar for it.&lt;/p&gt;&lt;p&gt;I was amazed that no one had actually contributed one to the project, but there were a few 'call for quote' options where people had written one and were offering it commercially.&lt;/p&gt;&lt;p&gt;Payment gateways in general and nop in particular are pretty easy to work with, so I wasn't going to pay for an option. If you are limited by time, want something of commercial strength or aren't really a .NET developer, the  commercial offerings should be a good option, although I know noting about price, support or implementation, so check out the nopCommerce forum if you need details.&lt;/p&gt;&lt;p&gt;So, how do I go about adding ePDQ to nop?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Understanding ePDQ&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;First things first, you need to understand the payment gateway, what it expects and what it returns.&lt;/p&gt;&lt;p&gt;I had a little refresher on ePDQ and it all came flooding back.&lt;/p&gt;&lt;p&gt;Payment gateways work in one of several ways. The differences are normally in how they authenticate and secure your data, with writebacks all generally taking a familiar approach.&lt;/p&gt;&lt;p&gt;With ePDQ, you need to generate a secure post, send it to the encryption service on the gateway, along with a few other variables, then send the returned data to the actual gateway where the user puts in their card details.&lt;/p&gt;&lt;p&gt;Once payment has been made, a return url is used to update the order locally (in nopCommerce) and the user gets a nice redirect back to your site.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Understanding nopCommerce&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;NopCommerce is easy to use because it has a lot of in-built functionality, is well written with proper interfaces and inheritance (that most people seem to not bother with, but should) and [IMHO] has a nice usable project structure in the solution.&lt;/p&gt;&lt;p&gt;What makes nopCommerce scary as hell at first, is that it is quite huge. If you take a little time to understand how it all knits, it's pretty easy to just figure out the smaller pieces of code that do what you need to do.&lt;/p&gt;&lt;p&gt;So, nopCommerce payment types all have some big similarities that allow us to copy and complete the small difference that make up the payment methods.&lt;/p&gt;&lt;p&gt;All the payment methods live in their own project with a payment processor or two, have a configuration user control in the admin section of the store and a template in the templates section.&lt;/p&gt;&lt;p&gt;These are supplemented by response scripts that take the returned data from the gateway, update the order and do whatever you need to with the response.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Making a new payment type, eg: ePDQ&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;This is simply a matter or adding each required project, user control and writeback file based on the closest existing payment type.&lt;/p&gt;&lt;p&gt;For ePDQ, I have based my version on the SagePay processor.&lt;/p&gt;&lt;p&gt;The new payment type needs to inherit from IPaymentMethod and include all the required methods. &lt;/p&gt;&lt;p&gt;The ProcessPayment() and PostProcessPayment() methods do all the work. (I assume these are called from the checkout process once the payment method is selected. See, interfaces at work!).&lt;/p&gt;&lt;p&gt;My implementation creates a RemotePost object, adds some post fields, calls a local method that build the encrypted post fields via a WebClient() using a mix of data from the Order class and settings in the Settings management store (which are reflected in the payment configuration file in the Administration section of the store), then fires it all off to the gateway.&lt;/p&gt;&lt;p&gt;I've added most of the meaningful variables to the ePDQ configuration user control, and also added a TestMode setting, which takes the writeback values from the gateway and writes them to a log file.&lt;/p&gt;&lt;p&gt;One of the slighly annoying aspects of ePDQ is its lack of a test gateway or simulator, such as PagePay offers. Thinking around this for testing takes a bit of consideration, but if we take the rough process as follows:&lt;/p&gt;&lt;p&gt;Build an order string&lt;br /&gt;Send to ePDQ  &lt;br /&gt;Get encrypted data back from ePDQ&lt;br /&gt;Send the form post to the gateway with encrypted data enclosed&lt;br /&gt;Get a writeback from ePDQ with status&lt;br /&gt;Display a happy message to the user&lt;/p&gt;&lt;p&gt;We can see 3 or 4 meaningful places where data would be useful for debugging, so if necessary we could add logs for each step of the process. One of the big problems with debugging payment gateways is always the inability to return to a Localhost page. Having meaningful debug data will help.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;So, where's the solution?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Good question. I'm writing it now, so it's not ready to be used in my projects or anyone else's just yet.&lt;/p&gt;&lt;p&gt;I'll probably offer it as a sample download and do a more in depth how-to at the same time.&lt;/p&gt;&lt;p&gt;:)&lt;/p&gt;</description>
			<pubDate>Mon, 23 Aug 2010 15:29:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/epdq-intergration-for-nopcommerce/</guid>
		</item>
		
		<item>
			<title>An AJAX calendar, QTip,  .NET and JSON</title>
			<link>http://www.n-webdesign.co.uk/an-ajax-calendar-qtip-net-and-json/</link>
			<description>&lt;p&gt;I recently needed to update a calendar on an old(ish) .NET 2.0 site.&lt;/p&gt;&lt;p&gt;It worked ok, but was based on the .NET calendar control and was showing its age.&lt;/p&gt;&lt;p&gt;I love writing cool new things, but I hate re-inventing the wheel, so I had a look at what was around in JQuery that migh do it?&lt;/p&gt;&lt;p&gt;For decent usability, I needed:&lt;/p&gt;&lt;p&gt;Rendering by month&lt;br /&gt;Multiple events per day&lt;br /&gt;No changes to the current event data&lt;br /&gt;Something that can popup a div with more details and a link&lt;/p&gt;&lt;p&gt;There are a number of candidates for the calendar, but I selected &lt;a href=&quot;http://arshaw.com/fullcalendar/&quot;&gt;fullCalendar&lt;/a&gt; as the most appropriate.&lt;/p&gt;&lt;p&gt;This looks pretty nice out of the box and has a load of callbacks for stuff so that I could hook in some click events for &lt;a href=&quot;http://craigsworks.com/projects/qtip//&quot;&gt;QTip&lt;/a&gt; and also had a really simple events model that would take a JSON feed. &lt;/p&gt;&lt;p&gt;This made it really easy to create data from the existing classes via a json parser that outputs data to a resource handler (.ashx) file for consumption by the calendar.&lt;/p&gt;&lt;p&gt;It was also quite straighforward to create some AJAX data for QTip, again in another Resource Handler (.ashx) file.&lt;/p&gt;&lt;p&gt;The demos and instructions for fullCalendar and QTip give you all you need to mash the two together, but there were a few things I struggled with.&lt;/p&gt;&lt;p&gt;QTip 1.01 like JQuery 1.3 but not 1.4. FullCalendar doesn't seem to care. &lt;a href=&quot;http://github.com/dustmoo/qtip&quot;&gt;There is a version  based on 1.0.1&lt;/a&gt; that uses 1.4 that fixed a load of errors in IE.&lt;/p&gt;&lt;p&gt;Actually, that was the only real issue.&lt;/p&gt;&lt;p&gt;A note on using a Resource Handler (.ashx) for ajax calls, rather than a, aspx page.&lt;/p&gt;&lt;p&gt;A while back, I found that calling AJAX data through Prototype where headers, body and html tags were added caused some issues with IE. &lt;/p&gt;&lt;p&gt;Using a .ashx file means you can do a simple Response.Write/Output  which just sends raw data to the browser, which does half of AJAX's job for it, but stops IE playing up. This may have been specific to my implementation at the time, but .NET sends too much junk to the screen anyway, so I like the uncluttered rendering you get from a .ashx file.  It's great for sending XML data out too.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;In summary&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;You can save time by spending a little time on checking things out.&lt;/p&gt;&lt;p&gt;What is old may not be worthless.&lt;/p&gt;&lt;p&gt;It's always possible to get things working better without changing the underlying data or management of it, just the presentation.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Keep them wanting more&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;When this new calendar goes live, I'll post a link to it.&lt;/p&gt;&lt;p&gt;I'll also do a better 'how-to' so you can mash a calendar, a rollover and old data together in .NET.&lt;/p&gt;</description>
			<pubDate>Wed, 11 Aug 2010 16:41:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/an-ajax-calendar-qtip-net-and-json/</guid>
		</item>
		
		<item>
			<title>Google AdWords, KeyWords and FWords</title>
			<link>http://www.n-webdesign.co.uk/google-adwords-keywords-and-fwords/</link>
			<description>&lt;p&gt;I get asked a lot about helping people with AdWords, keywords and managing their pay-per-click account.&lt;/p&gt;&lt;p&gt;Some people haven't got the time or the knowledge or the manpower to do it themselves, so they happily let other companies get involved.&lt;/p&gt;&lt;p&gt;Some people see pay-per-click as expensive and decide to reduce the overhead and manage it themselves. This is normally ok in principle, but if you've used AdWords, you'll know that it is far from setting a budget, creating an ad and clicking 'enabled'.&lt;/p&gt;&lt;p&gt;So, what is AdWords and PPC advertising? Well we probably already know. It's the process of paying a search engine [although we are talking mainly about the big player here, namely Google] to show your ad in sponsored links when someone searches for a keyword you have chosen. There is a lot more to it than that, but you get the idea.&lt;/p&gt;&lt;p&gt;That seems pretty straightforward. You choose keywords, and Google have a really simple keyword generator inside the AdWords app, you set a budget and you're selling. How cool is that. Except, it is normally followed by Google bursting your proud bubble and telling you your keywords are poor, your bids are too low and there are 100 other better keywords that will increase your chances of success.&lt;/p&gt;&lt;p&gt;Ok, so what does this mean? Does it mean I don't know what I'm doing and should get an expert on the case? Should I give up entirely? Well, no, it doesn't mean that at all. What Google is doing is trying to help you promote your web site better, smarter and more efficiently.&lt;/p&gt;&lt;p&gt;Here's what is going on.&lt;/p&gt;&lt;p&gt;You have a web site. It may or may not be optimised for keywords. You have 2 options.&lt;/p&gt;&lt;p&gt;You grab all the keywords you think match what your site is all about and add them, create an ad and get going.&lt;br /&gt;You ask Google to get keywords for you from a page on your site, create an add and get going. &lt;/p&gt;&lt;p&gt;You wait a day or so, because AdWords stats aren't real time. You see what is happening.&lt;/p&gt;&lt;p&gt;It rarely makes great reading first time round. Even with a pretty well optimised site, you'll still get some criticism about your keywords and where the ad points.&lt;/p&gt;&lt;p&gt;So you have keywords, a budget, an ad. That's it isn't it?&lt;/p&gt;&lt;p&gt;No. It's just a toe in the water in reality.&lt;/p&gt;&lt;p&gt;Let's get serious about AdWords. It is all about choosing the keywords that match your business or product, testing them in real time and looking at the reports. You need to really understand what is coming back and how it relates to the keywords, your ads and the landing page you have chosen.&lt;/p&gt;&lt;p&gt;I'm sorry guys. Effective pay-per-click is all about analysis, number crunching and targetted keywords. It is time consuming and takes organisation and, like most marketing, it is trial and error, sometimes learning some harsh lessons. You need to be reflective, self critical and no too sensetive. After all, it's just a machine telling you that you [might] suck. Not a real person.&lt;/p&gt;&lt;p&gt;Understanding, at least superficially, what AdWords are doing is important. It is also useful to understand what is happening when you create an ad and how it is received.&lt;/p&gt;&lt;p&gt;Google is a search engine. It loves and respect meaningful content and information about a subject, based on a keyword. Although it's a commercial entity, it includes this ethos when processing pay-per-click.&lt;/p&gt;&lt;p&gt;The process of keyword -&amp;gt; ad -&amp;gt; web page is very simple to understand but very significant in how the keyword relates to the content.&lt;/p&gt;&lt;p&gt;A well chosen keyword that relates to a relevant page, based on Googles database ranking of course, means less cost per click, a better position in sponsored ads and a more effective pay-per-click performance.&lt;/p&gt;&lt;p&gt;So if there ever was a magic bullet or killer technique for better PPC performancee, this is it. Make sure keywords are relevant to the pages the ad points at. Simple. Actually, it may not be that simple. &lt;/p&gt;&lt;p&gt;A totally un-optimised site will still work in a pay-per-click environment, it will just cost more per click and will struggle to get high up in the searches it does qualify for. In some cases, theclicker will wonder what they are doing on your web site because it isn't close to what they were looking for? That isn't good.&lt;/p&gt;&lt;p&gt;Let's look at a simple example.&lt;/p&gt;&lt;p&gt;You sell widgets, gadgets and oozits.&lt;/p&gt;&lt;p&gt;The three best keywords are widgets, gadgets and oozits.&lt;/p&gt;&lt;p&gt;These are 3 different things but you sell them all, so they are relevant and as such, you have 3 web pages, one dedicated to each product.&lt;/p&gt;&lt;p&gt;We assume each is reasonably optimised for the target keyword.&lt;/p&gt;&lt;p&gt;Ok, what next. Well, you choose keywords related to each, but this now means that it is difficult to be precise and target all three effectively with one ad or page, so we deivide the keywords into 'sets'.&lt;/p&gt;&lt;p&gt;So we now have a set of widget keyowords, gadget and oozit ones too.&lt;/p&gt;&lt;p&gt;This means we need at least 3 ads but actally, it prompts us to consider 3 campaigns.&lt;/p&gt;&lt;p&gt;So we now have a widget a gadget and an oozit campaign. We can be more specific an target our keywords dorectly, more effectively and  manage them better. We can also look at different ads for each campaign.&lt;/p&gt;&lt;p&gt;Let's think about widgets. We know from experience that people want different things and as such, search for different widget related keywords. Knowing our industry and looking at the keyword stats, we know the 3 best options are: 'cheap widgets', 'long lasting widgets' and just plain old 'widgets'.&lt;/p&gt;&lt;p&gt;We can now create three ads, select the keywords most appropriate to each ad and ensure we have better targetted and more effective keywords.  We can word our ads accordingly, so when people enter the keyword, they see it in the ad title.&lt;/p&gt;&lt;p&gt;In reality, we could have tens of ads with tens of words each, but the principle is the same. Thought, understanding, analysis and objectivity make AdWords more efficient and therefore mor cost effective. Once you have analysed, optimised and structured your AdWords campains in collaboration with a better set of landing pages, managing them will become far easier and small adjustments will return bigger benefits.  &lt;/p&gt;&lt;p&gt;That's all pretty clear now, but there are also obvious advantages elsewhere if we read between the lines.&lt;/p&gt;&lt;p&gt;First off, the keywords you have chosen for pay-per-click are also the keywords that you would like appear in natural searches for. Yep, you've got it. Google AdWords are giving you a rankings leg-up and also telling you which keywords you need to improve on in your web site. Time for some optimisation methinks. Now you can see why a good CMS is such a hot app for businesses.&lt;/p&gt;&lt;p&gt;Secondly, with pay-per-click, you only pay for a click. You'll notice in your stats that you get anywhere from .001% to 10% click throughs. At 1%, this is 100 impressions per click. This means your name and brand has been exposed 100 times for your £1 or whatever your CPC is. Ask your marketing guys about brand exposure.  It takes someone seeing your brand about 30 times before they notice it in a conscious way. Pay-per-click is helping and, with good keywords, it's the right people seeing your name. Again, this makes the ad all important. They need to see certain things there, at least subconsciously.&lt;/p&gt;&lt;p&gt;So you are going to rip it all up and start again. What do you need to do?&lt;/p&gt;&lt;p&gt;Well, I'd say give us a call on 0161 298 3816 and relax, but there isn't any reason why you can't get meaningful advantages, both directly by click throughs and indirectl by improved page content, by doing it yourself. Here are a few tips:&lt;/p&gt;&lt;p&gt;1. Choose the keywords you want and let Google expand on the related ones. They know what is relevant and important. They respond to billions of searches every day. (I dread to think how big their databases are!).&lt;/p&gt;&lt;p&gt;2. Start small and organised. Blanket keywords will get impressions, but will soon burn up your budget on meaningless and ineffective exposure.&lt;/p&gt;&lt;p&gt;3. Organise keywords into 'sets' that relate well and target keyword relevant pages. Avoid 'carpet bombing'. Remember, AdWords will tell us when we need to work on a few things.&lt;/p&gt;&lt;p&gt;4.  Use campaigns to help organise 'sets' more efficiently and effectively. Remember, different keywords need to point at keyword relevant pages. If you don't have them, create them. (See, a good CMS is again invaluable).&lt;/p&gt;&lt;p&gt;5. Read the results, the mesages and the status. This is easier in a well organised account.&lt;/p&gt;&lt;p&gt;6. Be self critical and prepare to adjust page content regularly, sometimes daily. (You already know, a good CMS...).&lt;/p&gt;&lt;p&gt;7. Build up keywords slowly and consider competition and monthly search volumes when choosing.&lt;/p&gt;&lt;p&gt;8. Hack out the dead wood regularly. This could be ads or keywords. Lots of impressions, no click throughs means the ad isn't working. Sort it out. &lt;/p&gt;&lt;p&gt;9. Use your keywords to constantly improve your web site and expand your content regularly. Yes, a CMS is perfect for this. Go on, you know you want one!&lt;/p&gt;&lt;p&gt;10. Add Google Analytics and monitor click throughs, bounce rates, new visitors and trends. That's another blog for another time, but the basics will show you how effective your campaigns are.&lt;/p&gt;&lt;p&gt;So there you have it. Far from exhaustive, but hopefully it'll give you some food for thought and help you get more from pay-per-click and even accidentally optimise your web site in the process.&lt;/p&gt;&lt;p&gt;Getting better keywords and pay per click management helps bring people to your web site but you still have much to do once they get there. I feel another blog coming on...&lt;/p&gt;&lt;p&gt;I know Google AdWords can be a little like the cockpit of the space shuttle to most people (I don't think many astronaughts read my blogs) but it can be quite easy, if time consming, to manage once you have an objective.&lt;/p&gt;&lt;p&gt;It will take time and will involve lots of work and analysis [PPC Management and SEO isn't expensive for nothing] but the 'feel good' at the end will be woth it.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;&lt;p&gt;Ed/&lt;/p&gt;</description>
			<pubDate>Wed, 09 Dec 2009 11:21:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/google-adwords-keywords-and-fwords/</guid>
		</item>
		
		<item>
			<title>Get going with Google Froogle...</title>
			<link>http://www.n-webdesign.co.uk/get-going-with-google-froogle/</link>
			<description>&lt;p&gt;It's probably more well known as Google Base or Google Shopping, but Froogle is still a hugely underused sales tool.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;So, if you don't know, what IS Google Shopping/Base/Froogle?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;It's essentially a way of you telling Google what you sell, so they can show your products when people search for them.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;So why is it so good?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Froogle is good because you can tell Google what you want to sell on-line, and they list it on page 1 of their searches for any relevant search keyword or phrase that matches your product name or description.&lt;/p&gt;&lt;p&gt;I told you it was good, didn't I.&lt;/p&gt;&lt;p&gt;And best of all, it's free. So hang on, it's free and is a fast-track to page 1 listings, why isn't everyone doing it?&lt;/p&gt;&lt;p&gt;I think it is underused because :&lt;/p&gt;&lt;p&gt;1. People don't know about it&lt;br /&gt;2. People don't tell their clients about using it because they don't know about it or can't manage data for it&lt;br /&gt;3. It can be a bit time consuming to manage and uses a complicated Google schema for datafeeds&lt;/p&gt;&lt;p&gt;Well, I've debunked reason 1 &amp;amp; 2. Now you know about it, you should be getting pretty excited abut what it can do to help you.&lt;/p&gt;&lt;p&gt;So just what is involved with a Google Base listing?&lt;/p&gt;&lt;p&gt;You need a Googleaccount. They are free, so get one now.&lt;/p&gt;&lt;p&gt;You need to activate the business solutions base option. Again, this is free so you can go right ahead and get working.&lt;/p&gt;&lt;p&gt;Once you have a Google Base account set up, there are 3 ways you can manage your Google Base items.&lt;/p&gt;&lt;p&gt;1. Type them in manually&lt;br /&gt;2. Upload an XML file with the product data in&lt;br /&gt;3. Point Google at a live RSS/XML feed of your data&lt;/p&gt;&lt;p&gt;There is a time limit of 30 days before feed data expires, so you need to manage the information at least once a month. Any changes to prices and other important information wineed to be done mnually unless you have an automated feed.&lt;/p&gt;&lt;p&gt;The specifics of formatting your internal data in the required format are not within the scope of this blog, but once you have a Google account, you can get all the tips and tricks, as well as the full API specs from the business knowledgebase.&lt;/p&gt;&lt;p&gt;If you only have a small number of products, managing them directly through the Google Base console wil be fine.&lt;/p&gt;&lt;p&gt;If you have a lot of items, you probably want to automate an extract so you can upload or link directly to a feed.&lt;/p&gt;&lt;p&gt;Many E-Commerce applicaions have a Google Base export facility.&lt;/p&gt;&lt;p&gt;We have a .NET library that automates an RSS Feed from any data. Once it's stable, we'll be providing a download and documentation for our asp.net Google Base Module.&lt;/p&gt;&lt;p&gt;Well, that's it. Go get some Google Base if you haven't already.&lt;/p&gt;&lt;p&gt;Ed/&lt;/p&gt;</description>
			<pubDate>Mon, 07 Dec 2009 15:33:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/get-going-with-google-froogle/</guid>
		</item>
		
		<item>
			<title>Surf the [Google] Wave...</title>
			<link>http://www.n-webdesign.co.uk/surf-the-google-wave/</link>
			<description>&lt;p&gt;I'm a bit of a cynic. No, I really am.&lt;/p&gt;&lt;p&gt;I also like really cool and useful new things, including gadgets and trinkets but especially software. And if it's in the clouds, even better...&lt;/p&gt;&lt;p&gt;I hate it when people come up with really obvious ideas, like Google have with Wave, because I get all bitter and twisted about why I didn't think of that.&lt;/p&gt;&lt;p&gt;Ok, I shouldn't beat myself up about it. After all, Google have an [almost] endless pot of resources, including the all important time and money. But this is just a simple mash up of a few aged concepts, a snappy title and a little sprinkle of the, ah yes, the endless Google marketing resource. See the cynic kicked n there.&lt;/p&gt;&lt;p&gt;I'm not sure this would get anywhere close to the heights it will achieve without the Google brand, but it's still a great idea and will be a really useful tool for networking. &lt;/p&gt;&lt;p&gt;So what exactly is Google Wave and why is it going to be good?&lt;/p&gt;&lt;p&gt;Well, Google Wave is a piece of software that runs in a browser, that allows more than one person to  collaborate on a document that can include pictures, text, movies and, well, almost anything electronic.&lt;/p&gt;&lt;p&gt;Imagine MSN, Facebook, Twitter, Skype IM, well, it's like all those, except different.&lt;/p&gt;&lt;p&gt;It's different because it's been crafted and shaped to focus on, errmmm, well, focus. All communication and social networking style apps, like those mentioned above, have some kind of access permission. So does Google Wave. With Twitter you follow, MSN etc, you invite contacts and waffle about things, with Facebook, you just pop in and out and see other people's washing.&lt;/p&gt;&lt;p&gt;My take on social networking is that Twitter is someones opinion, rant or observation that others can read if they want to. That's cool. MSN et al are quite intimate, one to one, friends in the living room, chatting. Again, a great app and indeed the grandfather of IM/Social Networking. Facebook is great at helpin you organise your life a little better. It's quite irreverant, has fun things to do while you are there, so you hang  around some more, bui essentially, a waste of time. Albeit a cool one.&lt;/p&gt;&lt;p&gt;So is Google Wave just another Social Networking app? Well, yes and no. It may well mature and grow into something as vast as Facebook and is probably as snappy as Twitter, although it is wrapped in that clean but  rather sterile Google style GUI. What Google Wave has is that focus. There I go again. What I mean by that is, it is structured so precisely and finely tuned with exactly the right amount of meaningful functionality that it is almost perfect for any Social Networking task. It is also really well suited to collaborative brainstorming and teamwork. That is where I will be using it most.&lt;/p&gt;&lt;p&gt;Yes, you can distribute invites to your 21st birthday party, with maps and everything, but for me, it's going to save many trees because I won't need to 'brainstorm' with post it notes on a wall any more.&lt;/p&gt;&lt;p&gt;I've only had a very brief look at Google Wave, which is in Beta at the moment anyway, and I have no idea which direction Google will want it to go.&lt;/p&gt;&lt;p&gt;The 'product positioning' part of my brain, just next to the bit that likes motorbikes and Halo on the '360, says it will gather as much meaningful information as it can before launch, decide which demographic it is most popular with and steer gently in that direction without really lowering its appeal to other users and groups. This cynic feels his 'product positioning' centre may be moving slowly towards the bit at the back, next to canal boats and gardener's question time.&lt;/p&gt;&lt;p&gt;Read about Google Wave at: &lt;a href=&quot;https://wave.google.com/wave/&quot;&gt;https://wave.google.com/wave/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;&lt;p&gt;Ed/&lt;/p&gt;</description>
			<pubDate>Wed, 18 Nov 2009 09:33:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/surf-the-google-wave/</guid>
		</item>
		
		<item>
			<title>Choosing the right CMS</title>
			<link>http://www.n-webdesign.co.uk/choosing-the-right-cms/</link>
			<description>&lt;p&gt;This is a question you will see a lot on the web.&lt;/p&gt;&lt;p&gt;Ask a question like 'What is the best CMS?' and 101 people will give you 101 different answers.&lt;/p&gt;&lt;p&gt;The really simple answer is ask someone to provide one for you. If you want to do it yourself or utilise internal resources, that's cool too. Read on...&lt;/p&gt;&lt;p&gt;CMS applications and frameworks are divided into 2 distinct categories. Those that are free or open source and those that are paid for. Some offerings are free to use, but may charge for the source code. There are very few real differences between free and commercial CMS applications, other than the level of support they offer, the development opportunities available to the user and the cost.  &lt;/p&gt;&lt;p&gt;I have checked out and evaluated many of the more popular offerings, most of which are really good. I do have my favourites and have strongly promoted several that cover both sides of the technology divide, differ in complexity and allow bespoke development without too many constraints.&lt;/p&gt;&lt;p&gt;I'm not going to tell you which one to use. After all, there is a champion for each and every available open source CMS so I don't need to get involved.&lt;/p&gt;&lt;p&gt;So how do you go about choosing one?&lt;/p&gt;&lt;p&gt;First of all, you need to ask yourself a few basic questions.&lt;/p&gt;&lt;p&gt;Asking which is the best CMS is like asking which in the best car or musical instrument. It all depends on what you are going to do with it. It mght well be a beutiful 1950's Sunburst Les Paul, but it's useless in the brass section.&lt;/p&gt;&lt;p&gt;So, in order to help you decide, you need to ask yourself:&lt;/p&gt;&lt;p&gt;Who will be administrating my CMS?&lt;br /&gt;Where am I hosting it?&lt;br /&gt;Who will be updating it?&lt;br /&gt;What content will it need to have?&lt;br /&gt;Will pages be added regularly?&lt;br /&gt;Which technology path am I following?&lt;br /&gt;How soon do I need to be up and running?&lt;/p&gt;&lt;p&gt;So why these questions?&lt;/p&gt;&lt;p&gt;Well, [in no particular order] you need to assess your level of competance or the level of competance of the person who will be installing, configuring and skinning the site. &lt;/p&gt;&lt;p&gt;Trust me, there is a steep learning curve with content management systems. &lt;/p&gt;&lt;p&gt;If you are a programmer or have a good knowledge of html/css and computers, you'll probably get to intermediate pretty quickly, so don't be put off by something that at first glance looks totally unfathomable. Just expect a few long, frustrating evenings.&lt;/p&gt;&lt;p&gt;If you are a developer or run web sites already, you'll probably have a pre-determined group of applications in mind. Yes, I'm talking about Microsoft v Linux. Or more specifically, .NET/MSSQL or PHP/MySql.&lt;/p&gt;&lt;p&gt;Ask this question 4 or 5 years ago, this would have be a no-brainer. If you wanted an open source anything, it was all PHP based. There were a few asp offerings, but they were nowhere near the maturity of the PHP based apps. Now that has all changed.&lt;/p&gt;&lt;p&gt;With a different attitude to open source software and with asp.net becoming more mature, we are seeing some really useful asp.net based CMS frameworks out there.&lt;/p&gt;&lt;p&gt;So, decide which technology works best for you. This is considering templating, hosting, administration, integration and most of all, comfort. After all, whatever you choose, it only sends out html, so as long as you are comfortable with the environment, that's a good choice.&lt;/p&gt;&lt;p&gt;Because many CMS applications are now quite mature, they offer so many things out of the box. You should get most things you need, including friendly urls, blogs, news, galleries and a lots more. This means you should be able to cater for most basic web site requirements by simply installing the cms, setting it up and skinning it.&lt;/p&gt;&lt;p&gt;If you or your clients want something a little bit extra, you need to consider a few more things.&lt;/p&gt;&lt;p&gt;You now start to see why your technology choice is important. If you need something out or the scope of the base install, modules or extensions, you'll probably need to roll your sleeves up and get stuck in with some dev work.&lt;/p&gt;&lt;p&gt;At this point, it's important to understand how your chosen CMS will react to changes. Can you work in modules or extensions without changing the underlying application? If not, you are slowly moving away from the project dev tree, which can mean you may be isolated and restricted when appling changes and updates. This is certainly a real consideration for users wishing to extend cms frameworks. Why is moving away from the base application a bad thing? Well, it means you lose the simplicity of upgrading based on the original source, which means you may miss important things like bug fixes, security enhancements and most of all, features and functionality. You can still make it all work, it'll just take some work to patch it all together.   &lt;/p&gt;&lt;p&gt;So, we've highlighted some of the things for consideration. Here are the tips to help when choosing a CMS.&lt;/p&gt;&lt;p&gt;1. Make sure the chosen framework has everything, within reason, that you need to work for your site.&lt;/p&gt;&lt;p&gt;2. A good sign is an active group of contributors and good help resources, reflecting a well maintained application.&lt;/p&gt;&lt;p&gt;3. Check web sites that are using the CMS. Do they all look the same or are they truly expressive?&lt;/p&gt;&lt;p&gt;4. Play with the admin section. Does it make sense? Will the least experienced user be able to use it?&lt;/p&gt;&lt;p&gt;5. Can you add things to the CMS without changing the original code? Does it support modular development if I need to add to the site?&lt;/p&gt;&lt;p&gt;6. Will the site give me tools for SEO?&lt;/p&gt;&lt;p&gt;So there you have it. Far from a completely exhaustive list but hopefully there are some helpful things to consider.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;</description>
			<pubDate>Fri, 23 Oct 2009 10:52:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/choosing-the-right-cms/</guid>
		</item>
		
		<item>
			<title>Code snippets: A really easy javascript accessibility text size selector </title>
			<link>http://www.n-webdesign.co.uk/code-snippets-a-really-easy-javascript-accessibility-text-size-selector/</link>
			<description>&lt;p&gt;&lt;strong&gt;A client asked me recently about upgrades to a web application. One of the required upgrades was to add text sizing.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;If you don't know what this is, it's just putting some options in place to allow the web site visitor to choose which font size they use to view your web site. You can see this example in action over there &amp;gt;&amp;gt;&amp;gt;&lt;/p&gt;&lt;p&gt;Many modern browsers let you do this, but adding the option to your web site alows the visitor easy access to the functionality.&lt;/p&gt;&lt;p&gt;So, we sat down and I discussed the presentation with the designer.&lt;/p&gt;&lt;p&gt;I suggested a little paragraph in the sidebar that said something like:&lt;/p&gt;&lt;p&gt;Text size: a b c.&lt;/p&gt;&lt;p&gt;We decided against the +/- option, as it can get a bit stupid and I think 3 presented sizes are enough.&lt;/p&gt;&lt;p&gt;I thought a little bit more about how to implement this, the requirements being:&lt;/p&gt;&lt;p&gt;Simple&lt;br /&gt;Re-usable&lt;br /&gt;Client side&lt;/p&gt;&lt;p&gt;So, the decision was to offer 3 links, one for each size option, set a cookie to remember the selection next time, and using javascript/css only.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Here's how we did it.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;We use some prototype functions to help with events and element manipulation, so you need to get rototype and reference it in your html page.&lt;/p&gt;&lt;p&gt;You can download and read about prototype at &lt;a href=&quot;http://www.prototypejs.org/&quot;&gt;http://www.prototypejs.org/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Reference it in your html page like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;lt;script src=&quot;prototype.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;So, you can make a test web page, referencing prototype, using the code below:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&amp;gt;&lt;br /&gt;&amp;lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt;A simple font re-sizer using prototype and js&amp;lt;/title&amp;gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;script src=&quot;prototype.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;Some content?&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Ok, so far, so good. Next we need to create our own javascript handler file and add some other stuff, such as cookie handling that I don't think prototype does? (but it might. I have never looked).&lt;/p&gt;&lt;p&gt;So, our file will need to have an event listener to check for the page loading. Create a file called something like resize.js. It will look something like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;Event.observe(window, 'load', function() &lt;br /&gt;{  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;some-function-call-here();&lt;br /&gt;});&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;You can also add a reference to this .js file in the html file by adding:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;script src=&quot;resize.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;...to the head of the html example.&lt;/p&gt;&lt;p&gt;Next, we need to create a function that sets the font size based on a passed value. So, we add something like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;function setFontSize(size)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Set the font size here...&lt;br /&gt;}&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;So how do we change the global font size?&lt;/p&gt;&lt;p&gt;I decided that adding something like 'style=&quot;font-size:0.9em;&quot;' in the body or main wrapper tag would work and also let us use some of prototype's great functionality to handle setting the style.&lt;/p&gt;&lt;p&gt;Now we are going to use prototype's $() method to help us, so we need to add something to the setFontSize function, so it uses the size passed in, like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;function setFontSize(size)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sSize = size+&quot;px&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;$('app').style.fontSize = sSize;&lt;br /&gt;}&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;This uses pixels, but you could use ems too (this site now uses this example based around ems). &lt;/p&gt;&lt;p&gt;Note we use $('app') to reference something that we set the style of. This is simply a shortcut, provided by prototype, to replace the getElementById() javascript function.&lt;/p&gt;&lt;p&gt;For our example, this means we are applying this style to an element in the dom called 'app', so we need to point it out in the htmlfor prototyp to find by giving something an id=&quot;app&quot; attribute.&lt;/p&gt;&lt;p&gt;You can target anything in the html, but the body tag or an outer wrapper div will work best. If, for any reason, your body tag already has an id for something else, just use that in the function call instead of 'app'.&lt;/p&gt;&lt;p&gt;Either way, the body tag in your html should look something like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;body id=&quot;app&quot;&amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;So if we now update the main event handler to call the resizing function, we now have:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;Event.observe(window, 'load', function() &lt;br /&gt;{  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;setFontSize(12);&lt;br /&gt;});&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;If you open the .html file in a browser, you should see some text and no errors. You can now see if the javascript we have added works. Simply change the parameter in the function call inside the event listener to something like 25. The text hould be huge. If it is, we know it's ok so far.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Great, that worked first time for me too.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Now we have done that, we have a few issues we need to resolve.&lt;/p&gt;&lt;p&gt;We need to add something to allow a resize to be done manually and also to 'remember' the size change.&lt;/p&gt;&lt;p&gt;Let's do the manual re-size first.&lt;/p&gt;&lt;p&gt;This is really simple.&lt;/p&gt;&lt;p&gt;In the body of the html file, add a paragraph like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;p&amp;gt;Text: &amp;lt;a href=&quot;#&quot; style=&quot;font-size:15px;&quot; onclick=&quot;setFontSize(15);&quot;&amp;gt;15&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;/p&gt;&lt;/div&gt; &lt;/p&gt;&lt;p&gt;Now save and reload the html file. Click the link, the font size changes. Cool.&lt;/p&gt;&lt;p&gt;You can use your own formatting and presentation, but here is a nice sample, offering 3 size options. Remember, you can use as many as you like in your own apps:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;p&amp;gt;&lt;br /&gt;Text:size:  &amp;lt;a href=&quot;#&quot; style=&quot;font-size:13px;&quot; onclick=&quot;setFontSize(13);&quot;&amp;gt;13&amp;lt;/a&amp;gt; &amp;lt;a href=&quot;#&quot; style=&quot;font-size:15px;&quot; onclick=&quot;setFontSize(15);&quot;&amp;gt;15&amp;lt;/a&amp;gt; &amp;lt;a href=&quot;#&quot; style=&quot;font-size:18px;&quot; onclick=&quot;setFontSize(18);&quot;&amp;gt;18&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/p&amp;gt;&lt;/p&gt;&lt;/div&gt; &lt;/p&gt;&lt;p&gt;Try it out. You get 3 options and it is updated immediately when you select. Excellent.&lt;/p&gt;&lt;p&gt;Ok, but each time we reload the page, it defaults back to the base font size, so we need to use cookies to check for any previous size selection and also to store any subsequent selections.&lt;/p&gt;&lt;p&gt;Here are some javascript cookie handlers. add them to your resize.js file above the current content:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;function createCookie(name,value,days) &lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (days) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var date = new Date();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;date.setTime(date.getTime()+(days*24*60*60*1000));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var expires = &quot;; expires=&quot;+date.toGMTString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;else &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var expires = &quot;&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.cookie = name+&quot;=&quot;+value+expires+&quot;; path=/&quot;;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;function readCookie(name) &lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var nameEQ = name + &quot;=&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ca = document.cookie.split(';');&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;for(var i=0;i &amp;lt; ca.length;i++) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var c = ca&lt;em&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (c.charAt(0)==' ') c = c.substring(1,c.length);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (c.indexOf(nameEQ) == 0) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return c.substring(nameEQ.length,c.length);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;return null;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;function eraseCookie(name) &lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;createCookie(name,&quot;&quot;,-1);&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;&lt;/em&gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;I'm not going to cover the cookie handling code apart from saying it allows us to check for and set a cookie using javascript.&lt;/p&gt;&lt;p&gt;We now need to do 2 things.&lt;/p&gt;&lt;p&gt;Check for any existing resize cookie and use the value if it's there.&lt;br /&gt;Add or update the cookie when we call the resize function.&lt;/p&gt;&lt;p&gt;We will do the first requirement by checking for a valid cookie in the load event handler.&lt;/p&gt;&lt;p&gt;By adding an if/else statement, we can check for cookies and fall back on the default value if there isn't one. Change your resize.js so that the Event.observe handler looks like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;if (readCookie('resize') == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setFontSize(12);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var size = readCookie('resize');&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setFontSize(size);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Now we have the ability to check for a cookie and use it, we need to add that little extra bit that sets the cookie for us for next time. Edit the setFontSize() function so it looks like:&lt;/p&gt;&lt;p&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;function setFontSize(size)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sSize = size+&quot;px&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;$('app').style.fontSize = sSize;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;createCookie('resize', size, 30);&lt;br /&gt;}&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Now reload the page and test it. You should have 3 opions to resize text which are remembered next time a visitor accesses the page&lt;/p&gt;&lt;p&gt;Here are the 2 pages we used in full.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;resize.html&lt;/strong&gt;&lt;br /&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;&amp;lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&amp;gt;&lt;br /&gt;&amp;lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt;A simple font re-sizer using prototype and js&amp;lt;/title&amp;gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;script src=&quot;prototype.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;script src=&quot;resize.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;lt;body id=&quot;app&quot;&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;&lt;br /&gt;Text:size:  &amp;lt;a href=&quot;#&quot; style=&quot;font-size:13px;&quot; onclick=&quot;setFontSize(13);&quot;&amp;gt;13&amp;lt;/a&amp;gt; &amp;lt;a href=&quot;#&quot; style=&quot;font-size:18px;&quot; onclick=&quot;setFontSize(18);&quot;&amp;gt;18&amp;lt;/a&amp;gt; &amp;lt;a href=&quot;#&quot; style=&quot;font-size:24px;&quot; onclick=&quot;setFontSize(24);&quot;&amp;gt;24&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;Some content?&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/p&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;resize.js&lt;/strong&gt;&lt;br /&gt;&lt;div class=&quot;codesnippet&quot;&gt;&lt;p&gt;function createCookie(name,value,days) &lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (days) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var date = new Date();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;date.setTime(date.getTime()+(days*24*60*60*1000));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var expires = &quot;; expires=&quot;+date.toGMTString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;else &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var expires = &quot;&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;document.cookie = name+&quot;=&quot;+value+expires+&quot;; path=/&quot;;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;function readCookie(name) &lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var nameEQ = name + &quot;=&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var ca = document.cookie.split(';');&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;for(var i=0;i &amp;lt; ca.length;i++) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var c = ca&lt;em&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (c.charAt(0)==' ') c = c.substring(1,c.length);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (c.indexOf(nameEQ) == 0) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return c.substring(nameEQ.length,c.length);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;return null;&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;function eraseCookie(name) &lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;createCookie(name,&quot;&quot;,-1);&lt;br /&gt;}&lt;/p&gt;&lt;p&gt;Event.observe(window, 'load', function() &lt;br /&gt;{  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (readCookie('resize') == null)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setFontSize(12);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var size = readCookie('resize');&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setFontSize(size);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;});&lt;/p&gt;&lt;p&gt;function setFontSize(size)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;var sSize = size+&quot;px&quot;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;$('app').style.fontSize = sSize;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;createCookie('resize', size, 30);&lt;br /&gt;}&lt;br /&gt;&lt;/em&gt;&lt;/p&gt;&lt;/div&gt; &lt;/p&gt;&lt;p&gt;And that's it. Simple text re-sizing in the client using client side javascript which can be used in almost any web site, application and project.&lt;/p&gt;&lt;p&gt;It has been tested and works ok in IE7+ and Firefox but should also be fine in other versions plus Safari/Opera/IceWeasel etc.&lt;/p&gt;&lt;p&gt;Thanks for reading.&lt;/p&gt;&lt;p&gt;/ed.&lt;/p&gt;</description>
			<pubDate>Wed, 14 Oct 2009 17:14:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/code-snippets-a-really-easy-javascript-accessibility-text-size-selector/</guid>
		</item>
		
		<item>
			<title>Make sure you ask for what you want</title>
			<link>http://www.n-webdesign.co.uk/make-sure-you-ask-for-what-you-want/</link>
			<description>&lt;p&gt;This is a cautionary tale.&lt;/p&gt;&lt;p&gt;When somebody does something for you, make sure that you ask them for everything you want and need. &lt;/p&gt;&lt;p&gt;In a perfect world, anybody who uses a product or service should be able to rely on the provider to ensure they are getting everything they will need. &lt;/p&gt;&lt;p&gt;The provider should be able to fill in the gaps between the consumers' knowledge and the required skills, knowledge or experience required to get the most from their contract or purchase without actually knowing everything they are likely to need up front. &lt;/p&gt;&lt;p&gt;Unfortunately, this doesn't seem to happen as often as it should.&lt;/p&gt;&lt;p&gt;...and who are the losers? Well, our clients for one, but I am also frustrated by the damage it does to this unregulated and largely fragmented industry we work in.&lt;/p&gt;&lt;p&gt;I will be a little more specific.&lt;/p&gt;&lt;p&gt;I'm not really talking about products we sell but about services and what we need to provide to our clients to fill in the knowledge gaps.&lt;/p&gt;&lt;p&gt;I'm not talking about the 'This electric drill you sold me, nobody mentioned I needed electricity...' kind of scenario, but in cases where a client instructs you to create or build a web site, there is far more to this than just providing the html/css and walking away, no matter how good your work is. Clients deserve more from us. More clarity, more details and most of all, we should work harder for their trust.&lt;/p&gt;&lt;p&gt;Now I am being slightly unfair, as this isn't entirely the providers fault. Clients should be more insistent on getting information and explanations about anything they are unsure of. &lt;/p&gt;&lt;p&gt;So what are the common pitfalls in these situations?&lt;/p&gt;&lt;p&gt;The most common issue arises from ownership. This can be domains, hosting accounts, source code or nything else that is covered by copyright or intellectual property rights.&lt;/p&gt;&lt;p&gt;The finer points of ownership are beyond the scope of this post, but simply put, if you buy a domain, you own it. Not the person who holds its TAG or the reseller who sold it to you. You should ensure and insist that the domin is registered in your name or that of the entity you purchased it for.&lt;/p&gt;&lt;p&gt;The same goes for hosting. For the agreed period of your contract with the hosting company, you 'own' that part of the internet. You are permitted within the law, the hosting company's policy and the services the ISP provides, to do with it as you will. This includes access to any part that you would usualy and rightly expect.&lt;/p&gt;&lt;p&gt;Problems with hosting occur when the people who have done your web site suggest they will 'host it for you' or as an 'included' part of the job. This is fine, provided they manage it for you or extend to you the same access that you would normally require from a service provider. Some times, companies don't let you have access to hosting accounts because it is their policy. Again, as long as you know this and they are prepared to add analytics, give you stats or do anything else you might normally do with hosting, that's fine.&lt;/p&gt;&lt;p&gt;The biggest issue people have is with ownership of the code or website scripting. Again this is something outside the scope of this post and if you are in a situation where IP or copyright is in contention, you must consult a legal professional.&lt;/p&gt;&lt;p&gt;For ownership of a web site or source code, as a rule of thumb, if you instruct somebody to do work on your behalf, they are essentially creating something at your behest that will be passed to you as a finished product. To ensure there is no ambiguity or argument over the ownership of a web site or source code, you must ask the provider directly about this. It is often a good idea to receive a copy of the finished work on cd.&lt;/p&gt;&lt;p&gt;There are some exceptions to this of course. &lt;/p&gt;&lt;p&gt;Sometimes providers will use open source applications released under license into the public domain as the basis of a project, as we sometimes do. &lt;/p&gt;&lt;p&gt;This application and source code is as much yours as it is theirs, but you will need to check what restrictions are imposed on the templates and additional work carried out by the developer.   &lt;/p&gt;&lt;p&gt;You may have work carried out that is based on a commercial application, such as Immediacy. In this case, you will not have any rights to the source code, but will most likely own the 'design' that is created for you. Again, ensure that you ask.&lt;/p&gt;&lt;p&gt;If I were a cynic, I would say that many companies attempt to tie you in with difficult, unclear or unmentioned clauses. If you don't insist on what you require before you start a project, it is unlikely that you will be able to get your requirements met once it has finished.&lt;/p&gt;&lt;p&gt;If you employ an I.T. development contractor, a freelance designer or copywriter, all the work they do on your behalf retains your intellectual property rights. This is normally stipulated by the terms of a contract. So if you have a company that does work for you that involves html, css or programming source, make sure that you know where you stand. &lt;/p&gt;&lt;p&gt;Some companies will charge more if you want the source code. Some will not want to or not be able to provide it. This isn't necessarilly a bad thing, as long as you are aware of this before you instruct them.  &lt;/p&gt;&lt;p&gt;Don't get me wrong. There are some really great companies out there [like us. sic], lots of good ones too, but there are a small number who, either through ignorance, idleness or sheer  over familiarity just gloss over the things that might be more significant to the client, who is blissfully unaware of it waiting to bite them in the proverbial in a few months time.&lt;/p&gt;&lt;p&gt;Most web design &amp;amp; development companies are open and clear about most of these things, including copyrights and ownership of source code. &lt;br /&gt;Most web design &amp;amp; development companies will happily talk about the technology and libraries they are using.  &lt;br /&gt;Most web design &amp;amp; development companies will offer advice on your needs and will mention many things that you had not realised.&lt;/p&gt;&lt;p&gt;And remember,  Make sure you ask for what you want...&lt;/p&gt;</description>
			<pubDate>Thu, 01 Oct 2009 12:46:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/make-sure-you-ask-for-what-you-want/</guid>
		</item>
		
		<item>
			<title>Cloud computing</title>
			<link>http://www.n-webdesign.co.uk/cloud-computing/</link>
			<description>&lt;p&gt;When you have been around for any length of time, you see many things being re-used, re-invented or re-invigorated.&lt;/p&gt;&lt;p&gt;Whether this is Kurasawa films into westerns, Shakespeare into contemporary dance spectaculars or some computer trend into , errrrmmm, another trend with a different name but essentially the same logical process, it's clear that what goes around comes around. And around. And around.&lt;/p&gt;&lt;p&gt;Is there truly anything that is new? Well, probably not a great deal. In technology, we get bigger, faster and lighter things, but essentially,there is input, processing and output.&lt;/p&gt;&lt;p&gt;All the really exciting things happen in the way we use these advances in technology. How we communicate to the user, where we store the data and where it is accessed from. Take, for example. cloud computing.&lt;/p&gt;&lt;p&gt;I know it's been around a while, but what is it and why is it so cool and why has technology suddenly allowed it to exist.&lt;/p&gt;&lt;p&gt;Actually, it's been around for almost 40 years, is quite a good idea and is just really a fancy name for remote applications.&lt;/p&gt;&lt;p&gt;It is cooler now, because of the changes in technology that have given us faster internet, cheap disk space, faster processors, Ajax, usable browsers and most importantly of all, huge corporations that only work in the on-line world.&lt;/p&gt;&lt;p&gt;So what exactly is cloud computing? It is essentially the storage and processing of documents and applications on a remote server, that can be opened, edited, manipulated and changed on line, normally through a web interface that runs in a browser.&lt;/p&gt;&lt;p&gt;But hold on, isn't that just like a mainframe used to work? Well, essentially, yes.&lt;/p&gt;&lt;p&gt;In the old days when you had to wear brown corduroys, have a beard and look like an extra from an OU program to work with computers, unless you had a big computer in a university somewhere, computers were really quite feeble. Something with the power of my mobile phone was as big as a house.&lt;/p&gt;&lt;p&gt;Necessity, being the mother of invention, made some bright sparks come up with X Windows, which was a way of using a small computer to connect to a large computer in a different romm, building or continent, which you could use to do your number crunching. This was essentially the idea behind cloud computing. &lt;/p&gt;&lt;p&gt;This is also the thing that led to mainframes, thin client systems, AS/400 minicomputers [ironically, often really big] remote desktops and a whole host of usefule distributed processing that made it easier to centralise and share data, processes and documents.&lt;/p&gt;&lt;p&gt;These days, we are rarely short of processing power, but we are often short of time.&lt;/p&gt;&lt;p&gt;The advantages of cloud computing as we see it today, with Google Docs and Microsoft Office Online, is the ability to share information, irrespective of setup geographical or time differences.&lt;/p&gt;&lt;p&gt;If you have a browser, you have Google Docs. You don't need to wait for the IT department to come and install some office software. You can write your report in a hotel room in Hawaii while MIS in Scunthorpe add the figures and the MD in Timbuktoo approves it.&lt;/p&gt;&lt;p&gt;Of course you can just e-mail copies around. But versioning then becomes an issue. This really is an incredibly useful interpretation of an old idea.&lt;/p&gt;&lt;p&gt;If, like me, you use several computers, it is a breeze to centralise your documents knowing they aren't on the other PC or on your forgotten USB stick.&lt;/p&gt;&lt;p&gt;Of course cloud computing isn't just online office applications. In reality, any shared web application belongs to the family, although we've just called it by other names. With more bespoke applications being written as browser based rather then desktop software, we really are already deep in the clouds. What's that you say, is this new thin client thing really taking off...?&lt;/p&gt;&lt;p&gt;If you haven't used something like Google Docs or stored files in something like &lt;a href=&quot;http://skydrive.live.com/&quot;&gt;http://skydrive.live.com/&lt;/a&gt; , you really should try it and see how it makes working smarter, faster and easier.&lt;/p&gt;</description>
			<pubDate>Thu, 17 Sep 2009 12:35:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/cloud-computing/</guid>
		</item>
		
		<item>
			<title>Now I'll just see how it looks in IE6...</title>
			<link>http://www.n-webdesign.co.uk/now-i-ll-just-see-how-it-looks-in-ie/</link>
			<description>&lt;p&gt;You create this really cool CSS/Ajax/html layout. I mean really nice, clean, neat and tidy. It looks great in FireFox, Safari and IE7 is almost perfect too. Happy days.&lt;/p&gt;&lt;p&gt;...until you hear thos dreaded words. 'How does it look in IE6?'.&lt;/p&gt;&lt;p&gt;Now before you start thinking, 'here we go again, Ed and his hatred of IE6', I don't hate IE6. I just think it's like having a steam driven car. Fantasti in its day. Revolutionary. Ground breaking. But 100 years out of date.&lt;/p&gt;&lt;p&gt;The example in question is at &lt;a href=&quot;http://www.hit.org.uk/training/&quot;&gt;http://www.hit.org.uk/training/&lt;/a&gt;. In the middle column, we've added some neat css flyouts. We had them working absolutely fine in Firefox, Safari and IE7+. Check it in IE6. Nada. Nothing. No show. What's a programmer/designer/disparaging soul to do?&lt;/p&gt;&lt;p&gt;The original css had been hacked around from another recent triumph at &lt;a href=&quot;http://www.express-safety.co.uk/&quot;&gt;http://www.express-safety.co.uk&lt;/a&gt; where we added some nice pure css menus. And they looked cool. &lt;/p&gt;&lt;p&gt;So when the guys at HIT said they wanted to make their training section 'a bit sexier', I just had to suggest using a flyout menu, similar to the one we added to Express Safety.&lt;/p&gt;&lt;p&gt;The problm they were facing was that there was just too much information, which meant losing some of it below the fold with 3 sections.&lt;/p&gt;&lt;p&gt;We chatted about it for a while and came up with a 'tabbed' section layout and because the course listings needed to show a lot of info at this point,  rather than dropping down, we decided flying out to te left would be the best option.&lt;/p&gt;&lt;p&gt;Cue a nice [but properly invalid] markup, ome tricky css and hey presto, we had a winner. &lt;/p&gt;&lt;p&gt;...until IE6 arrived that is.&lt;/p&gt;&lt;p&gt;The problem is, many of HIT's site visitors still use IE6 (maybe 25%, which is above the average these days)  so we needed to make things work in IE6 too.&lt;/p&gt;&lt;p&gt;Ok, problem 1. They just didn't show in IE6. Not to worry. Just do the old 'hide the &amp;lt;/a&amp;gt; and wrap it all in a table' trick. Still no show. Hmm. Ok, try a few things and position:absolute cures it.&lt;/p&gt;&lt;p&gt;Now we have a problem in Firefo et al. It uses a centred layout, so we can't set a -neg left and top to position the flyout.  Ok, knock up a little javascript to calculte and set the left offset from the screen size. Sorted. It worked in IE once we found the proper call for screen width.&lt;/p&gt;&lt;p&gt;More tweaks later, like adding hug amounts of negative margins for IE6 and we get close, but that's good enough for me.&lt;/p&gt;&lt;p&gt;So why am I telling you this? Well, it took a day to do the that worked in IE7+, as well as all the other browsers, then about 3 days to get it looking acceptable [it looks nothing like it should] in IE6. IR6 is causing people to compromoise or ignore layout issues and the solution is to upgrade as soon as you can.&lt;/p&gt;</description>
			<pubDate>Tue, 08 Sep 2009 17:03:00 +0000</pubDate>
			
			
			<guid>http://www.n-webdesign.co.uk/now-i-ll-just-see-how-it-looks-in-ie/</guid>
		</item>
		

	</channel>
</rss>
