Sunday, 29 August 2010

Tip: Configuration settings on your website

Just a little tip today, but one I've had to use on all the N2 work I've done.

For most sites I've had to do, I've had to add general configuration settings that can be access by any page. The current N2 site has these - if you right click/edit on your homepage and then click on the "Site" tab, you'll see a few there such as 404settings and the like.

I like to put all my config values in here as it's then all in the one place, but accessing them is a bit of a pain. If you try to access them by N2.Find.StartPage then the object returned is a ContentItem, not a StartPage.

To get round this, I create a static class and have a static method to return any property in the configuration settings:

public static StartPage SiteConfigurationSettings
{
        get
        {
                StartPage sp = N2.Find.StartPage as StartPage;
                if (sp == null) 
                {
                        throw new Exception("Cannot find the N2 Start page");
                }
                else
                {
                        return sp;
                }

        }
}

Now you can then access this property across the site in code as well as allow the end users to be able to update the value!

This is just a simple bare-bones example, but of course, you can cache this property too :)

Tuesday, 24 August 2010

Creating a page with a drop down list selectable value

In the past I had to create a page for a client which contained a list of cars and one of the requirements was that they would want to create pages on the site to show the types of cars that they sell. This information included:-

  • The car body shapes (eg hatchback, estate, small van)
  • The year the car registration plate
  • The colour of the car
You get the idea. Now, we could of course allow free text to allow the user to enter this information and N2 will display this quite easily with <%=CurrentItem.CarBodyShape%> or similar. However, it's quite easy to make typing mistakes and it becomes harder to write a method later to return all red cars, or all cars registered in 2002 for example.

So, we decided that we would use drop down lists where possible so that data entry errors would be reduced. Simple? It should be straightforward, but first we need to look at how N2CMS properties work. If you look at a simple textfield:

        [EditableTextBox("Host Name", 72, ContainerName = MiscArea)]
        public virtual string HostName
        {
            get { return (string)(GetDetail("HostName") ?? string.Empty); }
            set { SetDetail("HostName", value); }
        }

You can see that "EditableTextBox" attribute tells N2CMS that when you want to edit or display this property it's a TextBox.We can change this easily to EditableFreeTextArea and get a text area.

However, for our drop down list it's not that straightforward. For my list, I want to add values in like red, blue, silver and black but how do we specify them? The answer is we create our own attribute first, then add it to our Item.

Load up Visual Studio and create a new class in your Items folder called CarShapeAttribute.cs like this:


I like to add "Attribute" to the end of my class names when I am creating attributes, but it's purely personal choice!

Ok, the next thing we're going to do is create the class. Copy and paste this code into your new CarShapeAttribute.cs class:-

#region Namespaces
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using N2;
#endregion

namespace MyProject.Items
{

    public class CarShapeAttribute : N2.Details.AbstractEditableAttribute
    {
        public override void UpdateEditor(ContentItem item, Control editor)
        {
            // Update the drop down list with the value from the ContentItem
            DropDownList ddl = editor as DropDownList;
            if (ddl != null)
            {
                ddl.SelectedValue = item[this.Name].ToString();
                if (ddl.Items.FindByValue(item[this.Name].ToString()) != null)
                {
                    ddl.Items.FindByValue(item[this.Name].ToString()).Selected = true;
                }
            }

        }
        public override bool UpdateItem(ContentItem item, Control editor)
        {            
            // Get the drop downlist in the editor control 
            // Maybe you can add a check that ddl is not null afterwards!
            DropDownList ddl = (DropDownList)editor;

            // Get the item ID from the drop downlist
            // NB this could just as easily be a string value
            // If it was a string you would do this instead
            // string itemID = ddl.SelectedValue; 
            int itemID = int.Parse(ddl.SelectedValue);

            // Set the value on the "bag" to save
            item[this.Name] = itemID;

            // Return true if the item changed (and needs to be saved)
            return true;
        }
        protected override Control AddEditor(Control container)
        {
            // Create your drop down list here and populate it with values - nothing fancy here!
            DropDownList ddl = new DropDownList();
            ddl.Items.Add(new ListItem("Not applicable", "0"));
            ddl.Items.Add(new ListItem("Hatchback", "1"));
            ddl.Items.Add(new ListItem("5 door", "2"));
            ddl.Items.Add(new ListItem("4 Door", "3"));
            ddl.Items.Add(new ListItem("Small van", "4"));
            ddl.Items.Add(new ListItem("Sports car", "5"));
            container.Controls.Add(ddl);
            return ddl;

        }
        public override Control AddTo(Control container)
        {
            return base.AddTo(container);
        }
    }
}

Next we need to create a Page and associated Item that we can edit.

On your Visual Studio PROJECT, right click and click on Add > New Item and you should be able to see you've got your N2 Page Template like this:-




I've called my page Car.aspx.

Once you've clicked ok, look in your "UI" folder and you'll see the template has created a Car.aspx page for you and in your Items folder you've got a CarPage.cs file! Great! That's saved us some boring config work!

Open up your Item/Car.cs file and paste this in:-

using N2;
using N2.Web;
using N2.Details;
using MyProject.Items;

namespace N2.Items
{
    /// <summary>
    /// This class represents the data transfer object that encapsulates 
    /// the information used by the template.
    /// </summary>
    [PageDefinition("CarPage", TemplateUrl = "~/UI/Car.aspx")]
    [WithEditableTitle, WithEditableName]
    public class CarPage : ContentItem
    {
        [EditableFreeTextArea("Text", 100)]
        public virtual string Text
        {
            get { return (string)(GetDetail("Text") ?? string.Empty); }
            set { SetDetail("Text", value, string.Empty); }
        }

        // This is our car shape attribute!
        [CarShapeAttribute(Title = "Car shape", SortOrder = 110)]
        public virtual int CarShape
        {
            get { return (int)(GetDetail("CarShape", 0)); }
            set { SetDetail("CarShape", value, 0); }
        }

    }
}

And in your UI/CarPage.aspx page, paste this in:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Car.aspx.cs" Inherits="N2.UI.Car" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Car</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
<n2:Display PropertyName="Text" runat="server" />
<p>The car shape selected is:</p>
<p><b><%=CurrentItem.CarShape %></b></p>
    </div>
    </form>
</body>
</html>

Now save it all, check it compiles and hit Control and F5 to run it. When it loads up, right click on the root page and select "new" and you should see the option to create a new CarPage!

Great! Click on it and you'll now see your page where you can enter a title and select a drop down list of the car shape:


Save this and your page will be displayed:

The car shape is 5?! WHAT!! Well, it is in N2CMS as we're storing it as an integer! 

The good news is that the display value of the car can change - we're not storing the "Sports car" value, but the ID so you can easily change this and also add new values. 

To display the correct "friendly name", we'll need to add a property onto our Items/Car.cs class. So now paste this into your class:

using N2;
using N2.Web;
using N2.Details;
using MyProject.Items;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace N2.Items
{
    /// <summary>
    /// This class represents the data transfer object that encapsulates 
    /// the information used by the template.
    /// </summary>
    [PageDefinition("CarPage", TemplateUrl = "~/UI/Car.aspx")]
    [WithEditableTitle, WithEditableName]
    public class CarPage : ContentItem
    {
        [EditableFreeTextArea("Text", 100)]
        public virtual string Text
        {
            get { return (string)(GetDetail("Text") ?? string.Empty); }
            set { SetDetail("Text", value, string.Empty); }
        }

        // This is our car shape attribute!
        [CarShapeAttribute(Title = "Car shape", SortOrder = 110)]
        public virtual int CarShape
        {
            get { return (int)(GetDetail("CarShape", 0)); }
            set { SetDetail("CarShape", value, 0); }
        }

        /// <summary>
        /// This method takes in the id of a car shape and returns its friendly name
        /// </summary>
        /// <param name="id">The c</param>
        /// <returns></returns>
        public string CarShapeText
        {
            get
            {
                // Create an instance of the attribute class
                CarShapeAttribute attr = new CarShapeAttribute();
                
                // Pass in a temporary control to get back our drop down list
                Control c = new Control();
                DropDownList list = (DropDownList)attr.AddTo(c);
                if (list == null)
                {
                    return "Not found!";
                }

                // Return the value!
                return list.Items.FindByValue(CarShape.ToString()).Text;
            }
        }

    }
}

Now, when you compile and then reload your page you should see this:



You can see the principle here of how we can now go on to create other attributes for other parts of the car. You may also want to create an "attributes" folder to put all your attribute classes in so it doesn't get too cluttered.

What is now VERY powerful is that we can do search for cars by this attribute type to create search pages - I'll cover this at a later date though!

Simple! Hope this helps!

Wednesday, 18 August 2010

Using N2CMS with MySQL

As I have mentioned before in an earlier blog, when I am developing locally I like to use SQL Server 2008 primarily as it's the one I have most experience with. However, some of my clients can only use MySql.

To complicate matters further, I've recently had an order come through to build a site which will have to use the ASP.NET membership with MySql which is something I've not done before.

I'd recommend reading these blog posts first:

Both were a big help in explaining what to do. 

However, I had to do a few tweaks first to get it working. Nothing too difficult but again it's the kind of thing which can eat time. So, if you want to get your N2CMS site working with ASP.NET Membership and MySql, here's my two minute guide:-

  • Add a reference to the MySql.Web.Dll which you will find in "c:\Program Files\MySql\MySql Connector Net 6.2.3\Assemblies"
  • Change the N2CMS connectionstring to point to your MySql database (for this example I am just going to leave the of the connection string as "N2CMS" which is the default one)
  • Replace your membership, roles and profiles nodes in the web.config with this:-


    <membership defaultProvider="MySqlMembershipProvider">
      <providers>
        <clear/>
        <add name="MySqlMembershipProvider"
        type="MySql.Web.Security.MySQLMembershipProvider, MySql.Web, Version=6.2.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"
        connectionStringName="N2CMS"
        enablePasswordRetrieval="false"
        enablePasswordReset="true"
        requiresQuestionAndAnswer="false"
        requiresUniqueEmail="true"
        passwordFormat="Hashed"
        maxInvalidPasswordAttempts="5"
        minRequiredPasswordLength="6"
        minRequiredNonalphanumericCharacters="0"
        passwordAttemptWindow="10"

        applicationName="/"
        autogenerateschema="true"/>
      </providers>
    </membership>

    <roleManager enabled="true" defaultProvider="MySqlRoleProvider">
      <providers>
        <clear />
        <add connectionStringName="N2CMS"
        applicationName="/"
        name="MySqlRoleProvider"
        type="MySql.Web.Security.MySQLRoleProvider, MySql.Web, Version=6.2.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"
        autogenerateschema="true"/>
      </providers>
    </roleManager>

    <profile defaultProvider="MySqlProfileProvider">
      <providers>
        <clear/>
        <add
        type="MySql.Web.Profile.MySqlProfileProvider, MySql.Web, Version=6.2.3.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"
        name="MySqlProfileProvider"
        applicationName="/"
        connectionStringName="N2CMS"
        autogenerateschema="true"/>
      </providers>
    </profile>

If you are wondering how I got the version and PublicKeyToken, right click on the MySql.Web.dll in the solution explorer and click properties - this will give you the version. To get the PublicKeyToken, you will need to look in the c:\windows\microsoft.net\framework\v2.0.xxx\config\machine.config file and search for "MySql".
If you now run N2 you will get the installer and be able to install a new site! Note you can also manage the providers, members, roles etc from within Visual Studio with the ASP.NET Configuration tool. 

Once you've done this you may need to alter the rules on passwords etc, but that is beyond this two minute guide!

Hope this helps!

Tuesday, 17 August 2010

How to reorder pages in N2CMS

This is just a little tip, but one which I missed.

If you want to move pages up and down through the N2CMS hierarchy, there's two small buttons at the top left of the page which can do this for you. Select your page, then click on one of these and your page will move!


Run several N2CMS websites on one database server

One of the things that I have found is that out of the box, N2CMS will create your tables as:-
  • n2item
  • n2ContentItem etc.
Now, for some smaller clients of ours they can't afford to have a dedicated database server. On some of the cheaper hosts you can one database but then you'd need to create separate n2cms tables for each client.

It's dead simple to do this. If you had ClientA and ClientB you would want your tables to be something like:
  • ClientA_Item
  • ClientA_ContentItem and 
  • ClientB_Item
  • ClientB_ContentItem
To do this, look in your web.config and there's an element that you need to ammend

<!-- Other flavours: SqlServer2005, SqlServer2000, MySql, SqLite, SqlCe, Firebird -->
<database connectionStringName="N2CMS" flavour="AutoDetect" caching="true" cacheProviderClass="NHibernate.Caches.SysCache2.SysCacheProvider, NHibernate.Caches.SysCache2" tablePrefix="TheNameOfYourClient_">
<hibernateProperties>
<add name="hbm2ddl.keywords" value="none" />
</hibernateProperties>
</database>

Voila! You can now run several sites on one database!

Note: I always add a "_" after the name of the client as it makes the tables in the database look a little neater!

Data import and moving databases

When I am developing, I have a local setup which consists of a laptop to do the development work on and a separate database server. I prefer to develop with SQL server for a number of reasons, primarily it's the one I've got most exposure to and I know it more than any other database.

However, some of my clients are using MySQL database so at first it may seem that there's some data migration that would need to be done to move the data from SQL Server to MySQL.

Before I knew about this little "trick", I'd spent money on software to convert the databases (and most didn't quite work either) but there's a couple of reasons that I now realise why I don't need to have done this.

Firstly, when n2cms is saving data to the database it uses NHiberate to do it. This is great news as it hides the database implementation - I call save on an object and under the n2cms hood it calls NHibernate to save the object to the database. Luckily, to change databases it's a simple change in the web.config and it also allows us to use other database providers such as SQL Server, SQL Lite or MySql.

Secondly, n2cms allows you to export your data from one n2cms website so you can import it into another. This is completely database agnostic as well!

This is something I missed at first, but if you look at the top of the screen you'll see an "Export" button.


Click this and you'll now get the option to import or export data. Export your data and save it somewhere on your disk.


If you are exporting your site to move to another server, just click "Export these items".

Ok, so we've exported the data to our local disk and we now need to to migrate the data to the new server.

Go back into your web.config and change the connection string to your new database server.Start up your n2cms site and you will get the install page.

Install as usual until step 4 and select the "Advanced options" at the bottom of the page.

Select your exported file and n2 will then recreate your site on the new server!

Simple, but absolute genius!

How to set up your N2CMS and Web Forms development environment

One of the areas I found a little tricky at first was to set up N2CMS so I could actually run it. I wasn't quite sure where to start, what batch files to run etc. This guide will help you download the N2CMS Web Forms Template software, set up Visual Studio with the snippets and get you running N2CMS with a SQLLite database.

This guide is purely to get you started. It's not going to get into any details, just a step by step guide to getting you ready to be able to follow some of my other guides later on.


Also, I am assuming you will be using Visual Studio 2008 as currently I believe there is issues with Visual Studio 2010.

First of all, go to www.n2cms.com and then go to the downloads page and download the "N2 CMS 2.0 ASP.NET WebForms Templates Pack" as highlighted here:-







Next, extract the archive out. I am going to extract this archive into my "d:\n2cms" folder. From now on, I will assume you will have done the same. You should now see something like this in your d:\n2cms folder:-



Because we're going to run this demo using SQL Lite, you will need to copy the two files in the "Libraries" folder into the N2CMS/bin folder. If it asks about copy and replacing, just click ok. If you don't do this, you will get an error when you try to run the site at the end of this guide!

Now, the next stage is to set up Visual Studio 2008 so that we can use the properties and page template options. I have found these invaluable when developing N2CMS sites and I'd strongly suggest you use them. To be honest, although setting up the properties isn't easy there's a couple of "gotchas" which are simple to miss. If you use these, it's just far, far easier to develop.

Ok, if you look inside the "Snippets" folder you'll see a few files that start with "n2" like


Copy these files into your "\Documents\Visual Studio 2008\Code Snippets\Visual C#\My Code Snippets" folder on your C: drive. When you develop a page in N2CMS, you'll be able to type "n2prop" then press tab and the code will be generated for you.

The next thing is to set up the page and item templates. In the "Snippets" folder there are two zip files that you need to copy to your "\Documents\Visual Studio 2008\Templates\ItemTemplates\Visual C#" folder:-


You can ignore the  "N2 Page Controllers" snippet as that is for the ASP.NET MVC project.

Right, now we've done the background work, go do d:\n2cms\n2cms and double click on the N2.Templates.sln file. Or you can open this from within Visual Studio 2008.




Once it's loaded you should see something like this now in your Solution Explorer:-




If you now press "Control and F5" together to run N2, you should then be able to see the N2 homepage!






Note: If you get an error saying "The IDbCommand and IDbConnection implementation in the assembly System.Data.SQLite could not be found." then you've forgotten to copy the MySQL files from the library folder. I nearly always do this when I start a new project!

Wednesday, 11 August 2010

Welcome

Welcome to my new blog which is aiming to provide some clear instructions for those developers who are looking to develop with the superb N2CMS platform.

One of the complaints about N2CMS is that there's a lack of clear documentation and also that the instructions and tutorials aren't clear. I'll be honest and say that over the last year that I've been using it, I've found a few hurdles in the way but once you've mastered them you'll see just how easy it is to make pretty impressive content managed websites. I'm going to try where possible to use screenshots from Visual Studio, real code samples and not really delve too much into the "under the hood" code. The aim will be clear examples wherever possible to help solve "real world" problems that I've found.

I'm going to try to dump everything I've picked up in the last year into this blog. I daresay some of it you'll know, some of it might not be 100% correct but I hope there's a few tips that you will be able to pick up on and find useful.

Cheers!

I'm going to assume that readers will at least know some ASP.NET and C# and have Visual Studio 2008.