Schubert's Blog

A biker and technology enthusiast's
view of the world

CSS Getting links to look like buttons

On Mapze.com, I've had places where I wanted to give the user a HTML link and a button, side-by-side, but they look nothing alike. For example, at the bottom of the 'Create a New Event' page, the user can click a link that took them back to their home page or a button to add a new event. Although, through the magic of CSS, it is possible to get the both of them to look identical. In Mapze.com Beta 1, I let this be, but, in Beta 2, I had to find a way to improve the overall page and website appeal and this was one of the major changes.

A normal HTML link tag looks like

<a href="/">Back to Home page</a>

and a button tag looks like

<button type="button">Add an event</button>

giving us:

Back to Home page

Now, for some CSS. Start by adding a border of 1 pixel, solid black to the style attribute of both the link and button. This will give both the tags a border and make them look like a custom styled button. This giving us:

Back to Home page

Then add a color value to both the style attributes. This will give the text in both the link and button the same color. I'll use hex codes #034AF3, as this is one of the primary colors of the Mapze.com Beta 2 color scheme. This gives us:

Back to Home page

Next give the link and buttons padding, so that they fill up and the border is not sticking to the text. I've used 5 pixels all round. This give us:

Back to Home page

Finally, give the both of them a background color, I've used white. On the link remove the text decoration. And on the button, set the cursor type to pointer, this will give the user a pointer icon when they hover over the button. And I had to set the font size to 1em to get the button to size up, this might not be necessary and depends on the site design. All Done! The final button and link look like:

Back to Home page

And the final markup for the link tag looks like:

<a href="/" 
style="border: 1px solid black;
color: #034af3;
padding: 5px;
background-color: #ffffff;
text-decoration: none;"
>Back to Home page</a>

and the button looks like

<button type="button" 
style="border: 1px solid black;
color: #034af3;
padding: 5px;
background-color: #ffffff;
font-size: 1em;
cursor: pointer;"
>Add an event</button>

Even though I've used the style attribute here, on a professional site I would recommend using the class tag and move all the styles into a CSS file. Usually followed by using a tool like CSSTidy to clean and compress the CSS file. I'm always open to hearing suggestion, on how to make CSS more effective and cross-browser complaint. Look forward to hearing from some design gurus and novices like myself.

By Schubert on 05 September 2010 12:43

Categories: Designing | Tags: , ,

Permalink | Comments (0)

Submit to DotNetKicks...

Mapze.com and TDD & Mocks Part 2

In this blog entry I present two of the four test cases I've used to test the base business logic class from Mapze.com Beta 1. I've used the NUnit testing framework, as it was the simplest to implement with Visual Web Developer 2008. And Rhino Mock to pass in fake anonymous and logged in users to the base class. The code for the base class being tested here, is covered in the previous blog, so head over there if you haven't read it yet. On with the testing, first create fake accounts:

[TestFixture]
public class BlBaseText
{
//Faking a normal user account
Mock<IIdentity> FakeLoggedInSpereIIdentity;
//Faking an admin account
  Mock<IIdentity> FakeLoggedInMapzeAdminIIdentity;
//Faking an anonmymous account
  Mock<IIdentity> FakeAnonIIdentity;
...

In the NUnit setup function, I initialze the fake accounts.

  ...
[TestFixtureSetUp] public void Setup() { //Setting up the user account SPere
FakeLoggedInSpereIIdentity = new Mock<IIdentity>(); FakeLoggedInSpereIIdentity.Setup(i => i.IsAuthenticated).Returns(true).Verifiable(); FakeLoggedInSpereIIdentity.Setup(i => i.Name).Returns("SPere").Verifiable(); //Setting up the admin account MapzeAdmin
FakeLoggedInMapzeAdminIIdentity = new Mock<IIdentity>(); FakeLoggedInMapzeAdminIIdentity.Setup(i => i.IsAuthenticated).Returns(true).Verifiable(); FakeLoggedInMapzeAdminIIdentity.Setup(i => i.Name).Returns("MapzeAdmin").Verifiable(); //Setting up the anonymous account
FakeAnonIIdentity = new Mock<IIdentity>(); FakeAnonIIdentity.Setup(i => i.IsAuthenticated).Returns(false).Verifiable(); FakeAnonIIdentity.Setup(i => i.Name).Returns("").Verifiable(); }
...

Now that we have fake accounts, we can go ahead and setup the acutal test. The four test cases are:

1. The user is authenticated and their username exist in the database. This is the normal scenario and should be the case 90% of the time.

  ...
[Test]
public void ConstructorTest_UserIsAuthenticateAndValidUserName() { //Inject the fake user SPere
BlBase.FakeIIdentity = FakeLoggedInSpereIIdentity.Object; //The MVC state dictionary to store error and messages
ModelStateDictionary dictionary = new ModelStateDictionary(); //Initialise the base business logic class
BlBase blbase = new BlBase(dictionary, new L2SUserService()); //Make sure the current user variable is an instance of the IUser interface
Assert.IsInstanceOf<IUser>(blbase.currentUser); //Make sure the current user id is 1
Assert.AreEqual(1, blbase.currentUser.Id); //Make sure the user name is "SPere"
Assert.AreEqual("SPere", blbase.currentUser.UserName); //And finally make sure there were no errors or messages
Assert.AreEqual(0, dictionary.Count); }
...

2. The user is authenticated but the username does not exist in the database. Most likely cause of this is the user delete their account, but their authentication cookie is still valid as they were not logged out.

  ...
[Test] public void ConstructorTest_IsAuthenticateAndNotValidUserName() { //Create a fake account "NotValidUsername"
var FakeIIdentity = new Mock<IIdentity>();
FakeIIdentity.Setup(i => i.IsAuthenticated).Returns(true).Verifiable();
FakeIIdentity.Setup(i => i.Name).Returns("NotValidUserName").Verifiable();

//Inject the fake user "NotValidUsername"
BlBase.FakeIIdentity = FakeIIdentity.Object;
//The MVC state dictionary to store error and messages
ModelStateDictionary dictionary = new ModelStateDictionary();

//Initialise the base business logic class
BlBase blbase = new BlBase(dictionary, new L2SUserService());

//Make sure the current user variable is null
Assert.IsNull(blbase.currentUser);
//Make sure there is an error in the model state dictionary
Assert.AreEqual(1, dictionary.Count);
//Make sure the Severe Error key is present in the dictionary
Assert.AreEqual("SevereError", dictionary.Keys.FirstOrDefault());
//Make sure the message value is 1, so we can show generic severe error
//message and log the user out
Assert.AreEqual("1", dictionary["SevereError"].Errors.First().ErrorMessage);
}
}

To prevent this blog from turning into the longest blog post on the internet, I've not posted test 3 and 4, but they are similar. 3. was to check the scenario when the user is authenticated but is suspended and 4. is the scenario when the user is not authenticated. Hope this helps someone. As always, I'm open to suggestion and sharing best practices when it comes to testing, coding, designing and motorbiking Laughing.

Mapze.com and TDD & Mocks Part 1

This is my first foray into the world of writing code to test other code and have to admit, I enjoyed it. Ever since, I have tried to use TDD in every project, even if it only means testing a subset of the desired functionality. The finalized Mapze.com Beta 1 website had 490 test cases, 243 to test the Business logic layer, 121 to test the MVC Controllers and 126 to test the helper classes. The only disadvantage of TDD I've found so far is it almost doubles the development time. But the confidence of knowing that the website can cope with all the test cases, thought up during and after development, outweighs the cons.

In this blog entry, I'll discuss the BlBase class, Mapze.com's base business logic class and cover how I've gone about using NUnit and Rhino Mocks to test it in the next blog entry. Having decided to use LinqToSql as the Data Access Layer, all the business logic classes will need an instance of the data context. They will also need an instance of the current user to carry out selects, inserts and updates against. Making the BlBase class look like:

public class BlBase
{
//Current user variable, all business logic code have access to the current user
public IUser currentUser;
//The datacontext initialised once per request
public RedStartDataContext currentDataContext;

//Only for testing, inject a fake user identity
public static IIdentity FakeIUser;
//Modelstate dictionary, all business logic code can store
//informative and error messages here
public ModelStateDictionary currentModelState;
...

The BlBase class has one constructor and no methods. And this constructor has code to intialise the current user and data context variable. The constructor looks like:

  ...
//Constructor to initialise the user and data context
public BlBase(ModelStateDictionary ModelState, IUserService UserService)
{
//save the model state and initialise the data context
currentModelState = ModelState;
currentDataContext = new RedStartDataContext();

//Get the current user

//A little trick here: when this code is live
//HttpContext.Current will never be null. When
//NUnit call this code the FakeIUser will be used
//instead. Mapze Beta 2 has a cleaner implementation.
IIdentity identity;
if (HttpContext.Current != null)
identity = HttpContext.Current.User.Identity;
else
identity = FakeIUser;

//is the request authenticated
if (identity.IsAuthenticated)
{
//get the user from db
currentUser = UserService.Get(identity.Name);

//Make sure there is a user with the identity name
//also make sure the user account is not suspended.
//The 1 in the ModelError is picked up by a custom
//MVC error trapping filter and logs out the user.
if(currentUser == null || currentUser.Suspended)
{
currentModelState.AddModelError("SevereError", 1);
}
}
}
}

I believe most social networking and user interactive website will have a similar base class for all their business logic classes to inherit from. Making sure this class works correctly is of utmost importance, especially to business that rely on their user base for sales and feedback. The next blog entry shows how I've used NUnit and Rhino Mocks to test this class.

Zipping with C#

In order to save on bandwidth and improve the user experience, by making the route files faster to download, I needed a way to compress files. This was one of the goals for Mapze Beta 1 but considering future improvement, I wanted Mapze.com to be able to uncompress uploaded route files too. Allowing users to upload GPX, ITN, CSV or multiple route files by zipping them. Mapze.com would than unzip the route files on server and process each file like normal route files.

The soution : the GZipStream class

The .Net Framework comes with the System.IO.Compression namespace which has an interesting class called GZipStream. At first I though job done. The namespace and class name clearly indicated a class that can handle compressing and uncompressing streams. Plus, using a class in the .Net Framework has the advantage of knowing it will be supported in future releases, if not, an alternative will be provided. It will be installed with the Framework, so one less assembly to be concerned with at deployment time and if there are any bugs or issues, Microsoft will take care of it.

Turns out the GZipStream class can only handle .gz files, well known formats like zip and tar are not support. Another important point was it did not support hierarchical or directory level compression, or at least did not make the task easy. I needed the ability to read and create zip files that could contain multiple files or at least files at one directory level deep. Considering the above two points, it was time to search for other solutions. A quick google brought up sharpziplib library. It supported Zip, BZip2 and GZip format and was written in C# for the .Net Framework. Bliss.

The solution: the SharpZipLib

The final zipping code snippet looks like:

bool GetZippedITNFile (string SaveToFilePath, int ITNFileCount)
{
//Create a Zip file
ZipOutputStream zipstream = new ZipOutputStream(File.Create(SaveToFilePath));
//Set the compression level
zipstream.SetLevel(9);

//Temporary variable to indicate a new zip file entry
ZipEntry tempZipEntry;
//Temporary variable to store the file contents
string tempITNFileContents;
//Temporary variable to encode the contents before adding it to the zip stream
UTF8Encoding enc = new UTF8Encoding();

for(int currentFileNo = 1 ; currentFileNo <= ITNFileCount; currentFileNo++)
{
//Create a zip entry and set its filename, i.e. Route-1.itn, Route-2.itn, etc
tempZipEntry = new ZipEntry(string.Format("Route-{0}.itn", currentFileNo));
//Set other file properties
tempZipEntry.DateTime = DateTime.Now;
tempZipEntry.Comment = string.Format("Route file {0}", currentFileNo);

//Add the zip entry to the stream. Indicates that any
// characters / bytes written to the stream are now a part of a new zip file
zipstream.PutNewEntry(tempZipEntry)
//Get the file contents to zip
tempITNFileContents = GetITNFileContents(currentFileNo);
//UTF8 encode the contents and add it to the zip stream
zipstream.Write(enc.getBytes(tempITNFileContents), 0, tempITNFileContents.Length);
}

//Flush any buffered contents to the file
zipstream.Flush();
//Close the file
zipstream.Close();
}

The out come will be a .ZIP file that can be opened with well known zipping softwares like WinRAR, WinZip, 7Zip, etc. The .Zip file will containing .ITN files and the ITN file will be named Route-1.itn, Route-2.itn, Route-3.itn, etc

By Schubert on 03 January 2010 22:52

Categories: Development | Tags: , , , ,

Permalink | Comments (0)

Submit to DotNetKicks...

Mapze.com Beta 1

I wanted a challenge, a website to work on from ideas and concepts, all the way to deploying the final product on an internet accessible server and getting public feedback.

The solution: Mapze.com

Mapze.com is an event sharing website, to keep it short and simple. Although, its much more than that. The first Beta focused on bikers like myself. Besides the usual register, update my details and update my password page, the user should be able to plan a route online using Google Maps and upload a route in format like GPX, ITN, CSV, etc. They should also be able to download the routes in various GPS friendly formats (at least the well know devices) and share their planned rideout dates, details and route experiences in one central location. All very interesting, as I've never developed any of the above from scratch or seen anything like it on the web.

Technologies of choice: having worked on ASP.Net for over 3 year now, I wanted the developement process to be a challenge too. The ASP.Net MVC web framework being new and promising, served the purpose well. It allowed for cleaner SEO friendly urls and gave me full control over the HTML, prefect for learning some CSS at the same time. Having more exposure to the C# language over VB.Net, I decided to use C# 3.0 and taking advantage of the new LINQ To SQL ORM tool for the database access layer. Since Mapze.com needed to save GPS locations and needed to do searches and calculation based on global co-ordinate, like find rideouts planned within 5 miles of London, I decided to take advantage of the new spartial data types and function in MSSQL 2008.

The above technologies formed the base of Mapze Beta 1. The other technologies involved were Google Maps, so users could plan and amend their routes online. JQuery and Javascript to handle AJAX calls to google geo services and presenting them on the map. XML Parsing for reading GPX (XML) files. NUnit and Rhino mocks to follow the TDD style of progamming. MSSQL Full text indexing to search through event keywords. .Net Multilingual support, to make the website accessible in various language. Elmah and Log4Net to gracefully handle any error and warning on the website. DiscountASP.Net for hosting the website. And finally RapidSSL for the SSL certificate, for when users login and update their password.

Like most project I've worked on for clients and personal ones, I came away with some interesting lesson and a revised list of Do's and Don't's for future projects. These are a few lesson learnt from the first Beta:

  • Setting up SSL on IIS was a lot easier than I had initally though
  • CSS is a powerful display formatting language
  • Route formats are inconsistant, GPX being the most flexible and feature rich format
  • the .net build in Zipping classes are good, but ... library is better

Taking on the full software development life-cycle for even a simple concept website like Mapze.com, definitely involved a lot of creativity, architectural and scability considerations ... and a lot of hard work.

Screen shots of the final product:

Mapze.com Beta 2 is live! Click here for more ...