Working with TypeKit

Posted 09 June 2010, 11:10 | by | | Perma-link

Something I meant to include in yesterday's post was around the steps I'd taken to get TypeKit working with the new GoogleApi WebFont Loader hosted version.

As I'm not using Google AJAX api  decided to just call the file directly, but I couldn't seem to get it to work using the docs on GoogleApi using the WebFontConfig object, however following the directions on the Git hub seemed to work better:

Call the webfont.js file:

<script type="text/javascript" 
        src="http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js"></script>

Then call load on the WebFont object:

  WebFont.load({
    typekit: { id: 'gjb1flq' }
    });

Passing in your TypeKit Id, (not the Kit Id as is implied in the docs).

To reduce the "Flicker of Unstyled Text" I then added the following to my CSS:

.wf-loading h1 { visibility: hidden; }
.wf-loading h2 { visibility: hidden; }
.wf-loading h3 { visibility: hidden; }
.wf-loading #logo p { visibility: hidden; }
.wf-loading code { visibility: hidden; }
.wf-loading pre { visibility: hidden; }
.wf-active { visibility: visible; }

Basically, as the web fonts are loading, the WebFont.js applies the .wf-loading style to html element, then once they are all in, applies the .wf-active style.

Rather than following the guidance on the git hub page (which suggests having the base state of the h1 hidden, and only showing it when the fonts are active) I've gone down this route, so if the script fails to load, or is turned off, the titles, code, etc will still show, albeit in their default fonts.

If I really wanted to I guess I could implement font based activation as well, using the .wf-adelle1adelle2-n7-active event.

Filed under: AJAX, Site Updates

Upgrading to ASP.NET 4.0

Posted 08 June 2010, 22:00 | by | | Perma-link

Bit of a Link List this one, but it's got a few nuggets in there too.

Thanks to my excellent web hosts LiquidSix Hosting who upgraded our servers within about a week of ASP.NET 4.0 coming out, I've been able to focus on pulling this site over to the latest platform.

There were various reasons for wanting to do this, from cleaner web.config files, the new <%: %> syntax for HTML encoding output, and indeed answering some of my issues with Entity Framework, but obviously the key reason is it's new and shiney Grin

The key issue I had with it was that I forgot to update one small part of my web.config:

<system.web>
    <httpRuntime requestValidationMode="2.0"/>
</system.web>

As I'd forgotten to set this, I was unable to use any markup when editing my blogs. Thankfully the error message tells you exactly what to set, so the fix was easy.

Other tweaks that also went live this week:

  • TypeKit have recently opensourced their code, and added an events system, which means that in FireFox the Flicker of Unstyled Text should be somewhat less intrusive.
  • The launch of DoodleStatic to host my static files (JS, CSS, images) a small, cookieless domain to speed up download of the site infrastructure.
  • Changing my calls to jQuery and Typkit's core libraries to ajax.GoogleApis.com, so your browser should be able to find them in its cache where other sites have used them.
  • Thanks to Damien Gaurd for spending the time to point out how to use the System.ServiceModel.Syndication namespaces in the context of an ASP.NET MVC site, something I'd been meaning to do for some time.

And a few other minor tweaks to keep things tidy.

Filed under: ASP.NET, ASP.NET MVC, Site Updates

Implementing Flickr.Net

Posted 26 April 2010, 15:27 | by | | Perma-link

Flickr.NET is a ".NET library for accessing the Flickr API". It's hosted on CodePlex, and (thankfully for me) updated to version 3.0 Beta just before I downloaded it.

I host all my images here on the site, provide RSS feeds of both the latest photos and each album, however beyond using a service like twitterfeed I'd not found a nice way to integrate it into other social networks - Facebook can only accept one blog feed directly and while Google Buzz picked up on the blog feed through the "Connected Sites" options (I guess through the WebMaster tools association) it only seems to accept the first feed it finds, so I thought if I post the images to Flickr as well, then there would be more integration for them.

So, after spending a little time nosing around the Flickr API docs, I went off and got the latest version of Flickr.NET, dropped it in my projects /bin folder, and added a reference to it.

The next step was to request an API key from Flickr - you'll want a different key for each application you produce as the query limits are per key - and then set up the application - you need to give it a description, pick your application type (Desktop, Web or Mobile) and then define how you're handling authentication, for a web application this means supplying a callback url - while testing http://localhost/ addresses (with port numbers if required) worked fine.

Flickr.NET comes with it's own configuration section that you could use to set up Flickr.NET:

<flickrNet apiKey="APIKEY" secret="SHAREDSECRET" cacheDisabled="true">

Which then allows you to call the library as:

var flickr = new FlickrNet.Flickr();

However, I found that this didn't play nicely in a Medium Trust environment (the main disadvantage of shared hosting), as the constructor threw an exception complaining that the it couldn't write to the cache location (even though it's configured to be disabled) - I'll admit now that I didn't look too hard into diagnosing this issue - I'm only submitting images, rather than downloading them, so I didn't feel much need for caching.

So I pulled the API Key and Shared Secret out into the main appSettings settings, and factored out the constructor into a simple factory class:

public class FlickrControl
{
  internal static Flickr GetFlickr() {
    // Disable Cache before calling anything else
    Flickr.CacheDisabled = true;

    // Create Flickr instance with simple constructor.
    var flickr = new Flickr(WebConfigurationManager.AppSettings["FlickrApi"],
                            WebConfigurationManager.AppSettings["FlickrSecret"]);

    return flickr;
  }
}

I then created two new controllers - one to handle the initial login request, and one to handle the response back from Flickr:

// User wants to authenticate with Flickr
public ActionResult Flickr() {
  // Store calling page in session to return the user to later
  Session["Referrer"] = null != Request.UrlReferrer ?
                       Request.UrlReferrer.PathAndQuery : string.Empty;

  // Get an instance of Flickr
  var flickr = FlickrControl.GetFlickr();

  // Redirect the user to Flickr authentication service
  // asking for Delete priviledges (so we can remove images).
  return new RedirectResult(flickr.AuthCalcWebUrl(AuthLevel.Delete));
}

// Flickr is returning a logged in user
public ActionResult FlickrReturn(string frob) {
  var flickr = FlickrControl.GetFlickr();

  // Generate an Auth Token from the "frob" returned from Flickr
  var auth = flickr.AuthGetToken(frob);

  // Store the authentication token for later use
  // ExternalSites.FlickrAuth is a static string value for finding this object.
  Session[ExternalSites.FlickrAuth] = auth;

  // See if we can find the users previous request to return them to.
  var referrer = Session["Referrer"] as string;

  if (!string.IsNullOrEmpty(referrer)) {
    // We found their previous page, bounce them back
    return new RedirectResult(referrer);
  }

  return RedirectToAction("Index");
}

Then when a user uploads, edits or deletes an image from the site, we check to see if they are authenticated with Flickr, and perform the same action on Flickr:

// Inside the Upload action
// Check to see if the user is Authenticated, and that they want to upload the image
if (null != Session[ExternalSites.FlickrAuth] && editPhoto.UploadToExternal) {
  var auth = Session[ExternalSites.FlickrAuth] as Auth;

  if (auth != null) {
    var flickr = FlickrControl.GetFlickr();
    // Add the user's auth token to our Flickr instance
    flickr.AuthToken = auth.Token;

    // See below
    FlickrControl.UploadImage(photo, flickr, ImageData.FileName);

    // Upload image adds the Flickr id to the photo, so we need to save
    // that to the image as well.
    m_PhotoRepository.Save();
  }
}

This is the method for uploading an image:

internal static void UploadImage(DoodlePhoto photo, Flickr flickr, string fileName) {
  if (null == photo.PhotoDetail) {
    // If editing a photo, the image won't be in memory, so needs to be loaded.
    photo.PhotoDetailReference.Load();
  }

  if (null == photo.Album) {
    // If editing a photo, the Album info won't be in memory, so needs to be loaded.
    photo.AlbumReference.Load();
  }

  // UploadPicture returns the id of the image on Flickr
  photo.FlickrId = flickr.UploadPicture(
              // Raw stream of image bytes
              new MemoryStream(photo.PhotoDetail.BytesOriginal.ToArray()),
              // might as well pass in string.Empty here,
              // not really needed as we're passing in a stream
              fileName,
              // The title that's displayed in Flickr
              photo.Caption,
              // The description that appears under the image in Flickr
              photo.Description,
              // The tag list
              photo.CollapsedTags,
              // Set the permissions on the Album - if it's public,
              // whether Family or Friends can see it.
              photo.Album.IsPublic, true, true,
              // What type of image it is (they are effectively all photos).
              ContentType.Photo,
              // I'm not uploading anything inappropriate
              SafetyLevel.Safe,
              // What search level should be used
              photo.Album.IsPublic
                ? HiddenFromSearch.Visible
                : HiddenFromSearch.Hidden);

  // Once the image is uploaded, we need to add it to the a photo set as well.
  addPhotoToPhotoset(photo, flickr);
}

To update an existing image on Flickr I use the following method:

internal static void UpdateImage(DoodlePhoto photo, Flickr flickr) {
  // Update the metadata for an existing image
  flickr.PhotosSetMeta(photo.FlickrId, photo.Caption, photo.Description);
  // Replace all tags for an existing image
  flickr.PhotosSetTags(photo.FlickrId, photo.CollapsedTags);

  // Check to see if we've created this photoset already
  if (string.IsNullOrEmpty(photo.Album.FlickrPhotoSetId)) {
    // Photo set doesn't exist, therefore we can safetly add the image
    addPhotoToPhotoset(photo, flickr);
  } else {
    // Photoset already exists, so we need to check if the image is
    // not already in the photoset, and add it if needed
    if (!photoSetContainsPhoto(photo, flickr)) {
      addPhotoToPhotoset(photo, flickr);
    }
  }
}

To add a photo to a photoset:

private static void addPhotoToPhotoset(DoodlePhoto photo, Flickr flickr) {
  // Check to see if we've already created a photoset
  if (String.IsNullOrEmpty(photo.Album.FlickrPhotoSetId)) {
    // Create a new Photoset, with the Photo's FlickrId as the Primary Photo Id
    var photoset = flickr.PhotosetsCreate(photo.Album.Caption,
                                          photo.Album.Description,
                                          photo.FlickrId);

    // Store the Photoset Id with the Album.
    photo.Album.FlickrPhotoSetId = photoset.PhotosetId;
  } else {
    // Simply add the Photo to the Photoset.
    flickr.PhotosetsAddPhoto(photo.Album.FlickrPhotoSetId, photo.FlickrId);
  }
}

Finally, the code to check if a Photo already exists in a PhotoSet:

private static bool photoSetContainsPhoto(DoodlePhoto photo, Flickr flickr) {
  // Get the details of the photos in a Photoset, will become an issue
  // only when I add more than 500 images to a Photoset!
  var photos = flickr.PhotosetsGetPhotos(photo.Album.FlickrPhotoSetId);
  // Return true if the photo's Id appears in the list.
  return null != photos.SingleOrDefault(p => p.PhotoId == photo.FlickrId);
}

Deleting a photo from Flickr is a trivial exercise, simply requiring a call to PhotosDelete with the Flickr Id of the photo.

All in all, I found this to be very clean, and fairly intutive - especially if you take the time to read the API as you go.

Filed under: .Net, ASP.NET MVC, Site Updates

Implementing DataAnnotations

Posted 26 March 2010, 00:29 | by | | Perma-link

Update: As of Entity Framework 4.1, the MinLengthAttribute is no longer needed as it's now part of the framework, but the length is the second parameter in the string format argument list, with the field name in position {0}. 06/06/2011

As I hinted at in my post "Upgrading to ASP.NET MVC 2.0", one of the main remaining changes I wanted to make after upgrading to version 2.0 was to move to using the DataAnnotations that gained first class support.

I've now done this, which meant that I've removed all the code based on the Nerd Dinner validation patterns, and switched to using the Validation Attributes classes.

I started with the basic RequiredAttribute to ensure that all required fields had a value, but then wanted to do a bit more - on my Contact form for example wanted to ensure that the name supplied was at least 3 characters long, that the email address used my rather more rigorous regex than ScottGu's, and that the message was at least 10 characters long.

A simple answer was to use the RegularExpressionAttribute, with settings such as @".{3,}" for the minimum length, and the lengthy pattern for emails, however I wasn't happy with this for a number of reasons:

  1. I wanted to use the "minimum length" validator on multiple properties, with different minimum lengths but very similar error messages including details of the length.
  2. Because my email pattern is compiled using string.Format to make it more manageable, I can't use it in a attribute as it needs to be a "compile time constant".

I also wanted something similar to the IntegerValidator in the Configuration namespace that allows me to specify just a MinValue, rather than the Range Validator, where I needed to supply an arbitrary high value to meet my needs.

As I'm not yet using the .NET 4 framework, I don't have access to the CustomValidatorAttribute, that's no bad thing in my mind, as I'm not a big fan of the "Magic String" form of configuration that I would need to use.

To that end I've created three validators:

public class IsEmailAttribute : RegularExpressionAttribute
{}
public class MinValueAttribute : ValidationAttribute
{}
public class MinLengthAttribute : ValidationAttribute
{}

IsEmailAttribute just supplies the email pattern (built in a read only property) to the base constructor, while MinValueAttribute and MinLengthAttribute both implement the IsValid method, and override the FormatErrorMessage to enable me to include the set value of the validator.

The full code for the MinLengthAttribute is:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class MinLengthAttribute : ValidationAttribute
{
  public int MinLength { get; set; }

  public MinLengthAttribute(int minLength) {
    MinLength = minLength;
  }

  public override bool IsValid(object value) {
    bool isValid = false;

    var stringValue = value as string;

    if (null != stringValue
        && MinLength <= stringValue.Length) {
      isValid = true;
    }

    return isValid;
  }

  public override string FormatErrorMessage(string name){
    return string.Format(ErrorMessageString, MinLength, name);
  }
}

The one big problem I've had with doing it this way is that the automation of the client-side validation doesn't work, but as it doesn't plug into the Validation Summary area, this is no great loss, as I'd have to redesign my forms more than I'd like to implement that.

The other major change I had to make to my code was to move to more of a ViewModel pattern - on some of my admin screens I was taking a FormsCollection rather than an explicit model which allowed me to have a photo form with text boxes for Caption, Order and Tags (which holds a comma seperated list of tags), but this doesn't map nicely to the Photo model, where Tags is a collection of Tag models. Writing a ViewModel for editing photos, all the annotations wired up nicely, and gave me much better control of what was being sent to the view.

One thing that was needed uploading photos that I did need to do was handle the case of a user not including an image - thankfully, the previous code set me in good stead:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult UploadPhoto(int id,
                                EditPhoto editPhoto,
                HttpPostedFileBase ImageData) {
  ViewData["Title"] = "Create Photo";
  ViewData["Message"] = "Upload a new photo";

  if (ModelState.IsValid
      && null != ImageData
      && ImageData.ContentLength != 0) {
    // Persist to database
    // Return appropriate view based on Request.IsAjaxRequest
  }

  if (null == ImageData || ImageData.ContentLength != 0) {
    // ImageData is missing
    ModelState.AddModelError("ImageData", "You must supply a photo.");
  }

  // Return appropriate view based on Request.IsAjaxRequest
}

So we can still add additional errors to the ModelState property, allowing us to highlight additional fields that aren't part of the ViewModel.

Where I haven't created a ViewModel, I've used the MetadataTypeAttribute on the partial classes created for the Entity Framework classes:

[MetadataType(typeof(AlbumMetadata))]
public partial class Album {
  public string Description {
    get {
      return string.Format(
        "Photos from the album \"{0}\" on http://www.doodle.co.uk/Albums.aspx/{1}/{2}",
        Caption, AlbumId, Caption.CreateSlug());
    }
  }
}

public class AlbumMetadata {
  [Required(ErrorMessage = "You must supply a caption that is at least 3 characters long.")]
  [MinLength(3, ErrorMessage = "The caption must be at least {0} characters long.")]
  public string Caption { get; set; }
}

Next time: Implementing FlickrNet.

Filed under: .Net, Entity Framework, Site Updates

Upgrading to ASP.NET MVC 2.0

Posted 17 March 2010, 19:46 | by | | Perma-link

As you might have seen, ASP.NET MVC 2.0 was released this week at MIX along with a handy tool to help upgrade your solutions, so I decided to take the plunge and see how much effort I'd need to go through to upgrade. It turns out not much.

I also managed to move my admin section off to an area, which meant I could finally have a URL structure that allowed me to have duplicate controllers for blogs and albums, which is something I'd wanted since I built them (as opposed to an AdminBlogs controller and an AdminAlbums controller, and it meant that the Tabs controller, used for autocomplete on the photo admin screens, could be nicely grouped with them as well).

The only outstanding piece of work now is to move to using Data Annotations for my form validation, which is where the bulk of the pain I'd encountered lay.

Basically, I'd taken a similar approach as the validation pattern in Nerd Dinner (pp. 37f): I'd created a view model that implemented the following IValidatable interface:

public interface IValidatable
{
  bool IsValid { get; }
  IEnumerable<RuleViolation> GetRuleViolations();
  void OnValidate();
}

I initially created an abstract BaseValidater class, but of course you can't have multiple inheritance in C#, so this only worked for my non-entity classes… The RuleViolation is identical to the Nerd Dinner one.

So for each View Model I created an enumerable Errors with yields for each possible error, for example (line breaks added to long strings for formatting):

public IEnumerable<RuleViolation> GetRuleViolations() {
  if (string.IsNullOrEmpty(PostTitle) || 3 > PostTitle.Trim().Length) {
    yield return new RuleViolation("The title is required.", "PostTitle");
  }

  if (3 > PostPath.Trim().Length || !PostPath.IsUrlPart()) {
    yield return new RuleViolation("The path is required, and should only " +
                        "contain alpha-numerics and hyphens.", "PostPath");
  }

  if (50 > PostBody.Trim().Length) {
    yield return new RuleViolation("The post body is required, and should " +
                        "be longer than 50 characters", "PostBody");
  }
}

And coupled this with the following implementation (tweaked from Nerd Dinner - I prefer using .Any() to .Count() as that only checks to see if there are any values, rather than enumerating over the entire set - not an issue for in memory objects like this, but something to consider with databases Wink):

public bool IsValid {
  get { return (!GetRuleViolations().Any()); }
}

Then in the controller I went with the following code to handle errors:

if (blog.IsValid)
{
  [...]
  return [...]
}

foreach (RuleViolation violation in photo.GetRuleViolations()) {
  ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
  ModelState.SetModelValue(violation.PropertyName,
             new ValueProviderResult(
                   ValueProvider[violation.PropertyName].AttemptedValue,
                   collection[violation.PropertyName],
                   System.Globalization.CultureInfo.CurrentCulture));
}

The ValueProvider in 1.0 was an IDictionary but this has changed in 2.0 to be an IValueProvider, which means that you can't just iterate through without knowing what the form items were called (which I wasn't, but others are) so my code changed to:

foreach (RuleViolation violation in photo.GetRuleViolations()) {
  ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
  ModelState.SetModelValue(violation.PropertyName,
           new ValueProviderResult(
                 ValueProvider.GetValue(violation.PropertyName).AttemptedValue,
                 collection[violation.PropertyName],
                 System.Globalization.CultureInfo.CurrentCulture));
}

The only other issue I had was how this was all going to play on IIS 6, but that turned out to be very easy in the RegisterArea method:

public override void RegisterArea(AreaRegistrationContext context) {
  context.MapRoute(
        "Admin_default",
        "Admin.aspx/{controller}/{action}/{id}",
        new { action = "Index", id = UrlParameter.Optional }
  );
}

All in all, very easy and smooth.

Filed under: ASP.NET MVC, Site Updates

I'd just like to thank...

Posted 26 November 2009, 19:32 | by | | Perma-link

As you may have noticed, I've had a bit of a change on the site - I rolled out the new look and feel back in September, before I'd put in place all the authoring controls, but as you can tell, I've been able to upload new photos recently, and I now have the blog authoring interface back in place as well.

Thanks go out to:

Now that this is all up and running again (other than a few tweaks here and there), hopefully we'll be publishing more content - starting with some posts on how I've found working with ASP.NET MVC, why I moved from LINQ to SQL to Entity Framework, and why that fixed the problems I was having, but introduced new ones that were harder to fix (I need to write that soon, before EF4 is released in March and makes this obsolete).

It's good to be back! Grin

Filed under: Site Updates

Web 2.0 Goodness!

Posted 02 May 2008, 01:02 | by | | Perma-link

Wow, look at me entering the new web dawn already!

I've finally implemented some Web 2.0 goodness, there's now some tagging on this site - go check out the Albums if you don't believe me ;), clearly I've not finished tagging everything - and Zach's name is going to be the biggest thing on the page by the time I've finished - he's in nearly every picture we've taken since December 2004!

However, like the blogs, they aren't truely web 2.0, as they aren't user generated, but then, that's what our SEO guys tell us to do as well...

On top of that, it's all been powered up with Linq to Sql goodness, more posts on my trials and tribulations with that to follow (things like Enumerating over your results from Stored Procs; How to get a single entity from a Linq Query; etc) - in case you hadn't noticed, the albums are based (loosely now) on the Album Component of the Personal Web Site Starter Kit - there was so much dubious code in there (that's still in the recently updated, Linqified one) that I've cleaned out - not closing the files it's opened from the file system etc - I thought these were supposed to be "Best Practice Showcases" as well as "Hey, look what you can do" type things?

I've also got to get back to the MCMS and .Net 3.5 series now the site has finally launched - I also need to add to that a post on How to get MCMS development started under Visual Studio 2008.

Clearly, I'm going to be busy...

Filed under: Plans, Site Updates

This is a test

Posted 16 October 2007, 22:50 | by | | Perma-link

This is a test, please ignore it.

Basically, we should now have pingback functionality here, and to prove it, here's a link I made earlier:
Facebook Blog Import

Filed under: Site Updates

Fixed the css

Posted 08 February 2007, 13:09 | by | | Perma-link

Ok, the last thing I needed to do was address the issue of the columns where short posts needed some extra line breaks to ensure that the Footer appeared below the navigation.

Well, thanks to A List Apart's latest article Multi-Column Layouts Climb Out of the Box, it looks like I might have fixed it! Thanks Alan.

Filed under: Site Updates