jQuery click and the onclick attribute

by Aaron Powell 27. August 2010 04:35

Note: This may be a bit of an example of “doing it wrong”, but I don’t care :P

Yesterday I was having a look at a bug for a client in which we have a search control on a page which does an AJAX search, reloading the whole page.
Obviously that was a bit of a problem, the dynamic search results were being trashed by the full postback.

It was a very basic form, with a text input box for keywords and a button which you could click if you wanted to do the search. When clicking the button everything was fine, but when hitting Enter in the textbox the whole page refreshed.
After doing a few tests I noticed something interesting, the AJAX was working, the search results were loaded client side but then immediately the page posted.

So I went on the hunt for the keyboard event which was on the textbox and I found this:

$(function() {
	$('.searchKeywords).keypress(function(e) {
		if(e.which == 13) {
	                e.preventDefault();
                	if ($(".searchKeywords").val() != "") {
        	            $(".KeywordSearchFilter").click();
	                }
		}
	});
});

Nothing odd there, that’s perfectly fine for handling the keypress event and then doing the search, so my next assumption is that the button is having a problem.

I started digging around to see if I could find where the buttons click event handler was being registered.
But no joy, nothing, nada, zip.
No jQuery click handler is being registered.

Ooooook… So I had a look at the source for the button itself and found this:

<input type="submit" name="..." value="Go" onclick="SearchHelper.searchByKeyword(null);;return false;WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("...", "", true, "SearchKeyword", "", false, false))" id="..." class="submit KeywordSearchFilter">

I’ve trimmed out the control names so it’s a bit easier to read

Crap, see the problem? The click events are registered in the onclick attribute of the button!

Well first off I was surprised that jQuery actually fired the onclick attributes events, but it is a problem that it wasn’t acknowledging the return false statement.

So I had 2 options in front of me:

1) Refactor the code so that it was registering the event using jQuery

2) Find some funky way to do this

 

Option 1 was not really viable, this search control is a super class of a common search control and I didn’t want to break any other parts of the site, sure our tests would cover it *cough cough :P* but it’s quite a large site and with only a few weeks left here I don’t want to cock anything up.

So it’s option 2, time to work out some funky way around it!
Since onclick is just an attribute, I thought “can I just access it and grab the methods out”? I don’t see why not, jQuery must be doing something like that itself.

And since it’s just a set of functions, could I invoke it?

Well it turns out that I could! And this is what I now have in the codebase:

$(function() {
	$('.searchKeywords).keypress(function(e) {
		if(e.which == 13) {
	                e.preventDefault();
                	if ($(".searchKeywords").val() != "") {
        	            $(".KeywordSearchFilter").attr('onclick')();
	                }
		}
	});
});

I tested this in IE8, Firefox and Chrome and it worked in all three browsers!

Hey, it may not be the best solution, but hey, it worked! :P

Categories: jQuery | JavaScript

Text casing and Examine

by Aaron Powell 23. August 2010 15:41

A few times I’ve seen questions posted on the Umbraco forums which ask how to deal with case insensitivity text with Examine, and it’s also something that we’ve had to handle a few times within our own company.

Here’s a scenario:

  • You have a site search
  • You use examine
  • You want to show the results looking exactly the same as it was before it went into Examine

If you’re running a standard install you’ll notice that the content always ends up lowercased!

This is a bit of a problem, page titles will be lowercase, body content will be lowercase, etc. Part of this will be due to a mistake in Examine, part of it is due to the design of Lucene.

In this article I’ll have a look at what you need to do to make it work as you’d expect.

First, some background

Before we dive directly into what to do to fix it you really should understand what is happening. If you don’t care feel free to skip over this bit though :P.

Searching is a tricky thing, and when searching the statement Examine == examine = false; To get around this searching is best done in a case insensitive manner. To make this work Examine did a forced lowercase of the content before it was pushed into Lucene.Net. This was to ensure that everything was exactly the same when it was searched against.
In hindsight this is not really a great idea, it really should be the responsibility of the Lucene Analyzer to handle this for you.

Many of the common Lucene.Net analyzers actually do automatic lowercasing of content, these analysers are:

  • StandardAnalyzer
  • StopAnalyzer
  • SimpleAnalyzer

So if you’re using the standard Examine config you’ll find yourself using the StandardAnalyzer and still have your content lowercased.

This means that there’s no need to Lucene to concern itself about case sensitivity when searching, everything is parsed by the analyzer (field terms and queries) and you’ll get more matches.

So how do I get around this?

Now that we’ve seen why all your content is generally lower case, how can we work with it in the original format and display it back to the UI?

Well we need some way in which we can have the field data stored without the analyzer screwing around with it.

Note: This doesn’t need to be done if you’re using an analyzer which doesn’t have a LowerCaseTokenizer or LowercaseFilter. If you’re using a different analyzer, like KeywordAnalyzer then this post wont cover what you’re after (since the KeywordAnalyzer isn’t lowercasing, you’re actually using an out-dated version of Examine, I recommend you grab the latest release :)). More information on Analyzers can be found at http://www.aaron-powell.com/lucene-analyzer

Luckily we’ve got some hooks into Examine to allow us to do what we need here, it’s in the form of an event on the Examine.LuceneEngine.Providers.LuceneIndexer, called DocumentWriting. Note that this event is on the LuceneIndexer, not the BaseIndexProvider. This event is Lucene.Net specific and not logical on the base class which is agnostic of any other framework.

What we can do with this event is interact directly with Lucene.Net while Examine is working with it.
You’ll need to have a bit of an understanding of how to work with a Lucene.Net Document (and for that I’d recommend having a read of this article from me: http://www.aaron-powell.com/documents-in-lucene-net), cuz what you’re able to do is play with Lucene.Net… Feel the power!

So we can attach the event handler the same way as you would do any other event in Umbraco, using an Action Handler:

public class UmbracoEvents : ApplicationBase
{
	public UmbracoEvents()
        {
            var indexer = (LuceneIndexer)ExamineManager.Instance.IndexProviderCollection["DefaultIndexer"];

            indexer.DocumentWriting +=new System.EventHandler(indexer_DocumentWriting);
        }
}

To do this we’ve got to cast the indexer so we’ve got the Lucene version to work with, then we’re attaching to our event handler. Let’s have a look at the event handler

void indexer_DocumentWriting(object sender, DocumentWritingEventArgs e)
{
	//grab out lucene document from the event arguments
	var doc = e.Document;

	//the e.Fields dictionary is all the fields which are about to be inserted into Lucene.Net
	//we'll grab out the "bodyContent" one, if there is one to be indexed
	if(e.Fields.ContainsKey("bodyContent")) 
	{
		string content = e.Fields["bodyContent"];
		//Give the field a name which you'll be able to easily remember
		//also, we're telling Lucene to just put this data in, nothing more
		doc.Add(new Field("__bodyContent", content, Field.Store.YES, Field.Index.NOT_ANALYZED));
	}
}

And that’s how you can push data in. I’d recommend that you do a conditional check to ensure that the property you’re looking for does exist in the Fields property of the event args, unless you’re 100% sure that it appears on all the objects which you’re indexing.

Lastly we need to display that on the UI, well it’s easy, rather accessing the bodyContent property of the SearchResults, use the __bodyContent and you’ll get your unanalyzed version.

Conclusion

Here we’ve looked at how we can use the Examine events to interact with the Lucene.Net Document. We’ve decided that we want to push in unanalyzed text, but you could use this idea to really tweak your Lucene.Net document. But really playing with the Document is not recommended unless you *really* know what you’re doing ;).

Categories: .Net | Examine | Umbraco

Paging with Examine

by Aaron Powell 18. August 2010 13:12

I’ve been asked this question a few times, how do you implement pagination in your Examine search results.

Well a fun-fact is that Lucene doesn’t have really good way to do this, it just involves skipping to the point that you want to get the results from.

Thanks to .NET and LINQ we have extension methods which handles that nicely, Skip and Take, and since that the search results are IEnumerable underneath they can be used. But there’s one problem, doing this will result in a bit of a performance hit, as your initial Skip would hydrate a bunch of entities from the underlying Lucene store, which is where you loose performance.

So we implemented our own version of Skip! So you can just use Skip and Take as standard, and they’ll be evaluated without loosing performance.

Here’s the code:

int pageNumber = string.IsNullOrEmpty(Request.QueryString["pageNum"]) ? 0 : int.Parse(Request.QueryString["pageNum"]);
int pageSize = 10; //this could be a config value or something

/* serach snipped */

var results = searcher.Search(...);

//And we'll just set a repeater with the results
Repeater.DataSource = results.Skip(pageNumber * pageSize).Take(pageSize);
Repeater.DataBind();

It's just that easy.

Categories: .Net | Examine | Umbraco

How to build a search query in Examine

by Aaron Powell 12. August 2010 12:14

Now that Examine is able to be used by a wider audience than Umbraco understanding how search works is possibly a bit more important ;).

So today while answering a question on the Umbraco forum I thought that what I was going on about is something that more people might want to hear about. And really, I do like the sound of my own (virtual) voice…

Understanding Lucene.Net from Examine

For this I’m going to be looking at the Lucene.Net implementation of Examine, and this is agnostic of whether you’re using UmbracoExamine or Examine.LuceneEngine.

To get started you should familiarize yourself a bit with the Lucene Query Parser Syntax, as that’s what we’re using internally to get the data back from Lucene.Net.

Also, we’re going to be working with the Fluent API for Examine, so fell free to read up on that here and here. With Examine we’ve made it easy to see what the Lucene query you’re building up is as you’re working with the Fluent API, in fact if you to a .ToString() call on the ISearchCriteria instance you’ll be able to see what you’re search query looks like (we’ve also got some other information in the result of that method call too).

So you can see what’s been generated, let’s build a query and dissect it:

var criteria = searcher.CreateSearchCriteria(IndexTypes.Content);
criteria = criteria.NodeName("Hello").And().NodeTypeAlias("world").Compile();

Console.WriteLine(criteria.ToString()); //+(+nodeName:Hello +nodeTypeAlias:world) +__IndexType:content

So this is what we've got, a total of three conditions… But wait, there’s only two conditions that I entered right?

Not quite, Examine has some smarts built into it around what you’re searching on and it’ll add that restriction on your behalf. This is so you don’t get results back from a different index type in your query. Note: If you don’t specify the IndexType then this condition wont be added for your.

Also, to ensure that all your entered queries don’t get killed by the IndexType (a problem in earlier Examine builds) we combine everything you enter into a GroupedAnd statement.

Let’s have a look at the parts we did request, and how they are comprised:

+nodeName:Hello +nodeTypeAlias:world

Ok, so what we've got here is our two conditions which we've added using Examine. Each is an AND (or in Lucene terminology SHOULD) and this is denoted by the +. Next we have a field name, in this case either nodeName or nodeTypeAlias. Youl’ll notice back in our Fluent API query we actually generated all of that using the build in methods, rather than having to use more magic strings. Next there is a : to indicate where the field name ends. Lastly we have the term which we’re going to search against, either Hello or world.

So essentially it’s built up of BOOLEAN_OPERATION&FieldName:Term (the & is so it’s slightly readable).

Conclusion

This was a brief look at how the Fluent API for Examine will turn your typed query into a Lucene query that is then searching.

With this knowledge you should be better able to design complex queries, use mixed conditionals and just plain go crazy.

Categories: .Net | Examine | Umbraco

Sydney Umbraco user meet-up

by Aaron Powell 14. July 2010 05:50

In the first week of August Umbraco trainer Peter Gregory will be in Sydney to run Level 1 and Level 2 training courses; we think this will be a great time for everyone to get together and talk Umbraco.

What to expect

TheFARM will host a fairly casual affair, mostly just a chance for those of you using Umbraco to get together and discuss ideas, implementations and anything else that comes to mind.

We’ll also do a few small talks including a look at Umbraco 4.5 which was released at CodeGarden 10 in June and a look at the architecture for Umbraco 5.

Core Developer of the Year Shannon Deminick, core developer Aaron Powell and 2009 MVP (Most Valued People) & Umbraco trainer Peter Gregory will be there to chat and answer your questions. If there’s anything in particular you’d like to know more about feel free to leave us a comment ahead of time.

Who should come?

Everyone!

If you’re taking the Level 1 and/ or Level 2 that week then come on down.
If you’re currently using Umbraco then come on down.
If you’re just plain interested in what the hell this Umbraco thing is then come on down!

When and where

Wednesday 4th August, 6pm – onwards.

TheFARM Digital, Suite 101, 4 – 14 Buckingham St Surry Hills

We’ve put an event up on the our.umbraco website (http://our.umbraco.org/events/sydney-umbraco-user-meet-up) so if you’re planning on coming then register your interest so we can ensure we’ve got the appropriate provisions (read: beer ;)).

We’ll see you there :)

Categories: Umbraco

TheFARM’s guide to Macros

by Aaron Powell 13. July 2010 14:46

When working with Macros in Umbraco there’s always the decision of what type to go with, are you going with XSLT, a .NET user control, a .NET class or maybe a DLR script?

Here’s a few guidelines which we use at TheFARM to do macros.

Please note – although Shannon and myself are both on the Umbraco core team this is the opinion of us and does not necessarily reflect that of the Umbraco core team.

Choosing the right type

Umbraco supports several different ways which you can create a macro:

  • XSLT
  • .NET User Control
  • .NET Custom Control
  • DLR languages such as IronPython or IronRuby


Choosing the right tool for the job comes down to what you're trying to do with your macro. Generally speaking you'll be writing a macro that is either a XSLT or a .NET User Control. We currently don't use the DLR languages so using IronPython or IronRuby as the macro type isn't done (but if you can put forth a good case as to why we should we're happy to hear it!) and it's not often that you're going to require a macro which loads up a .NET control which is just a class (such as a CompositeControl or WebControl).

In fact in all the years I’ve been working with Umbraco I’ve never used a .NET Custom Control. In fact I’ve hardly ever had a need to create custom .NET server controls in all the time which I have work with ASP.NET.

When to use XSLT

XSLT is a powerful but miss-understood language and using it in Umbraco can be a really good idea or a really bad idea. XSLT is great for interacting with the Umbraco content cache, as the cache stores all the data as XML, which obviously XML is great at working with. When working purely with the XML cache it can be really fast.
But XSLT can become slow if you start introducing .NET into the mix. This can be in the form of XSLT Extensions, or embedded C# code in your XSLT file.
Here's a few rules as to when XSLT is the probably right way to go:

  • Are you building a navigation system
    • Navigation systems such as breadcrumbs, top or page-based navigations should always be done in XSLT
  • Are you repeating content under the current node or something contextual from the current node
    • If you're building something like a news article listing outputting with XSLT is often faster as the data is contextual
  • Is the number of lines going to be small
    • XSLT can be hard to read (well, XPath really) and once you start getting long XSLT files with multiple templates it can be very hard to follow where you're going in the file

Here's a few rules as to when XSLT is probably not the right way to go:
  • Are you likely to debug
    • Yes you can debug XSLT. No it's not as nice an experience as debugging .NET
  • Do you have to write an XSLT extension
    • If you have to write an XSLT extension that means that you need some .NET which has functionality too complex to express in XSLT. Try not to mix technologies
  • Do you need to get nodes with a XPath selector like //node
    • If you're using //node then you're looking at the XML file as a whole, so chances are you don't really know where your data is. This means you're having to process the whole XML file to find what you want, resulting in potentially slow code and scalability problems


Essentially what it comes down to is that XSLT is great if you're working with transforming the data for just the data to display on the UI, remember XSLT stands for Extensible Stylesheet Language Template Transformations ;).

When to use .NET

.NET is what we're most familiar with, so coding .NET seems the right thing to do, but despite popular belief .NET isn't the be all and end all when it comes to programming. .NET has a lot of advantages over the other macro types, but it can also introduce its own problems particularly around performance.
Here's a few rules as to when .NET User Controls are probably the right way to go:

  • You're working with FADS
  • You're interacting with an external data source
    • UI should never directly access the data source
  • You have business rules around the data
    • Business rules don't have to be just external data reliant, they can be around the content too. Business rules can be generally expressed easier in .NET
  • You plan to unit test
    • This is kind of the same as the business rules point, but if you're writing business rules you should be unit testing them and unit testing XSLT is very hard

Here's a few rules as to when .NET User Controls are probably not the right way to go:
  • You need fine-grade control over the markup generated
    • Some .NET server controls have their own markup rules around them and outputting simple things can often be tricky (like menus or lists of data)

Macros vs Registered User Controls

When creating a .NET User Control you need to make a decision about whether you want to have it as a macro or whether you just register is as per normal in the Master Page. The general rule is that you shouldn't make something into a macro unless you need to pass data into it from the current Umbraco page, or you need to have the content editors place it anywhere they want.
Often a macro needs to have data passed into it from Umbraco, this could be a property which is set on the current page. This is an idea opportunity to use a Macro rather than just registering the user control as when Umbraco is processing the template and finding the macros it will assign any of these properties. This can save you from writing code to retrieve the value yourself.
The other reason is that you may want content editors to control where a macro is placed within a site. This can be handy for things such as Contact forms or news listings. By giving editors this freedom they can better control what pages do what in their site. But with great power comes great responsibility. If you're allowing macros to be inserts from the WYSIWYG editor (and this is applicable for XSLT macros as well as .NET) things can break so be sure to handle unexpected usage. A good rule of thumb is that a macro should only be allowable in the WYSIWYG editor if you know it can't break anything (design, functionality, etc).
Using a plain control registration though does have its own advantages. If you register the control yourself it will be faster as there is no dynamic creation of the user control instance from the Umbraco request, this is all handled via the ASP.NET runtime. It can also give you greater flexibility if you want to use features such as Dependency Injection, as you're running in the ASP.NET life cycle entirely, not being injected into the page from an outside source.
Our preference is to have controls registered in templates for .NET components.

 

Conclusion

Hopefully this simple set of guidelines will give you a bit of an insight into how we at TheFARM go about our Umbraco development.

Tags: ,
Categories: Umbraco | .Net

Snapshot CMS API

by Aaron Powell 24. June 2010 17:30

As we’ve been working on the API for Snapshot we realised that there’s a bunch of cool aspects to the API which we think that everyone can benefit from.

To this end we at TheFARM have decided that we’re going to give away the CMS API’s for Snapshot free!

What does this include?

What we’re providing is a set of API’s which can be used as replacements for some of the Umbraco API’s, most importantly the Media and NodeFactory API’s.

In Snapshot we’ve got quite a bit of caching built in for working with both media and node, along with some handy features around the creation of strongly typed representations of your objects, similar to LINQ to Umbraco, but not tied to Umbraco 4.1 and more focused on being used in the scope of NodeFactory.

What’s not included?

This is just the Snapshot API for working with the Umbraco XML cache, it does not include the Snapshot export engine, nor does it include the API for working in the published Snapshot environment (sorry, those aren’t going to be free!).

Why would I want to use it?

Well this is a good question, we’ve already got both Media and NodeFactory, why would you want to use something more custom for it?

Media caching is an obvious advantage, but most importantly the Snapshot API is designed with dependency injection as a forethought. This means that when you’re working on your applications in Umbraco you can have the Media or Node API’s injected using Autofac. This makes testable Umbraco development quite easy.

Lastly, since Snapshot is an abstraction away from the Umbraco API you can even write your own implementations which don’t require the Umbraoc XML file to read data, you could pull it in from any source.

Getting it

The pre-release of the Snapshot CMS API is available on the Snapshot page of FarmCode.org. Keep in mind that this is an early build of the Snapshot CMS API, and it is subject to change. For updated releases keep an eye on our blog.

Categories: Umbraco | Snapshot | .Net

Using an iPhone with the Visual Studio development server & Charles

by Aaron Powell 11. June 2010 06:26

Dave Ward did a good post recently on how to use the Visual Studio development server from a mobile devise such as an iPhone. But there’s a problem for us here, we use Charles which I have found to be a better than Fiddler (it’s also cross-platform so I can use it both on my Mac and Windows machines).

So after reading Dave’s post I decided to have a look at how to do it if you’re using Charles, and well it’s pretty darn simple.

I’d suggest that you read Dave’s post first as I’m going to assume that you have, I’m just going to point out what you need to do different for Charles.

Charles Configuration

The first thing you need to do is find out on what port Charles is running on, by default Charles is on port 8888, but you can find the settings under Proxy > Proxy Settings

charles-proxy-config

Next we need to configure the external access to the HTTP Proxy that Charles is running. This is something that Charles handles differently to Fiddler, it’s actually a lot more configurable as you can define individual IP’s or IP ranges for access.

To do this you need to navigate to Proxy > Access Control Settings

charles-access-control

Then you just need to click Add and enter the IP (or range) which you want to allow access to. I’ve just allowed access to the IP of my iPhone, which is 192.168.1.44.

Conclusion

The rest of Dave’s post is all you need to get this working, you connect to your computer from your external device in just the same way.

Hopefully this helps you out if you’re not a Fiddler user but want to be able to use a mobile device with Visual Studio’s development server.

Backing up Document Types

by Aaron Powell 11. June 2010 03:20

Something I’ve heard a number of people say is that they want a way in which they can store the DocumentType in their source control system.

This is obviously a bit of a problem since they are actually stored in the database, not on the file system. Hmmm…

Then yesterday I was talking to Tatham Oddie about it and how you could go about CI with Umbraco. Then after bouncing a few ideas of Shannon we had a great idea, that when you say a DocumentType it would just dump it to the file system. You can then check this file into your source control system and you have a backup.

Sounds pretty simple, and in fact, Umbraco has all the stuff you’d need for this, it’s just a matter of doing it. So while waiting for a rather large project to check out of source control I decided to just write it.

Please note, the following code is not tested, it’s just a POC, when I get some time I do plan on actually testing it :P

How do go about it

It’s actually quite simple, you just need to tie into the Umbraco event model for a DocumentType and use the built in XML export feature.

I’ve also done the code so you can either dump to a single file or to multiple files (depending which is easiest in your solution.

It doesn’t check the files out for you, so if you’re using something like TFS you’ll have a problem, but I have put in handlers for read-only files.

Also, there’s no error checking, like I said, this is POC code :P.

Code baby!

using System.Linq;
using System.IO;
using System.Web;
using System.Xml.Linq;
using umbraco;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;

namespace AaronPowell.Umbraco
{
    public class DocumentTypeSerializer : ApplicationBase
    {
        public DocumentTypeSerializer()
        {
            DocumentType.AfterSave += new DocumentType.SaveEventHandler(DocumentType_AfterSave);
            DocumentType.AfterDelete += new DocumentType.DeleteEventHandler(DocumentType_AfterDelete);
        }

        void DocumentType_AfterDelete(DocumentType sender, umbraco.cms.businesslogic.DeleteEventArgs e)
        {
            DumpDocumentTypes(false);
        }

        void DocumentType_AfterSave(DocumentType sender, umbraco.cms.businesslogic.SaveEventArgs e)
        {
            DumpDocumentTypes(false);
        }

        private static void DumpDocumentTypes(bool useSingleFile)
        {
            var allDocTypes = DocumentType.GetAllAsList();
			var storageFolder = GlobalSettings.StorageDirectory + "/";
			System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
			
			if(useSingleFile)
			{
				var xdoc = new XDocument(new XElement("DocumentTypes"));

				foreach (var dt in allDocTypes)
					xdoc.Root.Add(XElement.Parse(dt.ToXml(xmlDoc).InnerXml));

				var file = storageFolder + "DocumentTypes.config";
				var fileOnFileSystem = new FileInfo(HttpContext.Current.Server.MapPath(file));
				if (fileOnFileSystem.Exists)
				{
					if (fileOnFileSystem.Attributes == FileAttributes.ReadOnly)
						fileOnFileSystem.Attributes &= ~FileAttributes.ReadOnly;
						
					fileOnFileSystem.Delete();
				}

				xdoc.Save(fileOnFileSystem.FullName);
			}
			else
			{
				storageFolder += "DocumentTypes/";
				if(!Directory.Exists(storageFolder))
				{
					Directory.Create(storageFolder);
				}
				else
				{
					var di = new DirectoryInfo(storageFolder);
					var files = di.GetFiles();
					foreach(var file in files) 
					{
						if (file.Exists)
						{
							if (file.Attributes == FileAttributes.ReadOnly)
								file.Attributes &= ~FileAttributes.ReadOnly;
								
							file.Delete();
						}
					}
				}				
				
				foreach(var dt in allDocTypes) 
				{
					var xdoc = XDocument.Parse(dt.ToXml(xmlDoc).ToString());
					
					var file = storageFolder + dt.Alias + ".config";
					
					var fileOnFileSystem = new FileInfo(HttpContext.Current.Server.MapPath(file));
					if (fileOnFileSystem.Exists)
					{
						if (fileOnFileSystem.Attributes == FileAttributes.ReadOnly)
							fileOnFileSystem.Attributes &= ~FileAttributes.ReadOnly;
							
						fileOnFileSystem.Delete();
					}

					xdoc.Save(fileOnFileSystem.FullName);
				}
			}
        }
    }
}

I'll look at cleaning this up and testing it soon and releasing it as an actual Umbraco package, but in the mean time feel free to have a play around with it.

Categories: .Net | Umbraco

Why Snapshot?

by Aaron Powell 18. May 2010 02:53

In my previous post I introduced a new tool we’re working on here at TheFARM called Snapshot which is a content export engine for Umbraco.

When doing that post I knew that I was going to have to write this one anyway, people were inevitably going to ask “But why?”

I was hoping to have time to do it before anyone did, but alas I was wrong :P.

Here’s a few scenarios which Snapshot aims to be useful in.

The “We Don’t Need a CMS” client

We’ve all had these, it’s “just a small site”, “we don’t change content”, etc. But in the back of your mind you know they will change content and if it’s a static site you’ll have to get the devs to do it. Well with Snapshot you can create a CMS but the client doesn’t have to know.

Shared hosting, virtual folders and medium trust

Yes Umbraco can run in Shared Hosting, yes with 4.1 it can run in a virtual folder and medium trust. But 4.1 isn’t out yet. So you could look at the medium trust patch for 4.0.3, but you may not be comfortable with running a custom version of Umbraco, it does mean that upgrades are off the table.

Since Snapshot generates an ASP.NET site from within itself there’s no need to worry about Umbraco restrictions like that, it comes down to how you’ve coded your site.

Database-less websites

Not every website needs a database, but unfortunately Umbraco requires one even when you’re not editing content. You may not be aware but when you work with the Media API (either via the umbraco.library methods, or though the Media classes) you are going into the database. Yes there are projects which simulate a media cache you can even do this with Examine.
But you’ll still have to have your web server talking to a database.

Snapshot doesn’t require a database form the web server. We’ve got built-in Media caching, a Media API and even replace the umbraco.library XSLT methods so that the Media interaction in an XSLT doesn’t require a database!

Security

Umbraco is reasonably secure, but wherever you have a login you have a vulnerability, it’s just the way of the web. A Snapshot site has no login, has no database, thus it is more secure (yes yes, famous last words :P). With Snapshot you can put your CMS inside your DMZ and never have it expose itself to the outside world. You can then get Snapshot to generate a site from the CMS and you just deployed the generated files.

Speed

As fast as Umbraco is (and the backed just got faster) there’s always room for improvement. When writing Snapshot we came across several places which were not as good as they could have been within the Umbraco API. Take umbraco.library for example. You interact with static methods on the type in XSLT, but for every XSLT macro an instance of the class is created. This means that it can’t really have request-level caching of what you’re interacting with.
Or take NodeFactory, it doesn’t have caching of the node(s) you’ve tried to access; you get a new instance each time.

Now I’m not having a go at Umbraco’s API on this, just pointing out some facts.

In Snapshot we have much improved caching. Since we’re using Dependency Injection (via Autofac) we only create a single instance of our umbraco.library implementation, we also do the same for our ContentService (the NodeFactory replacement) and MediaService (media API replacement).
These have caching built into them as well, so you hardly ever create objects, you get back previously created ones.

In future posts we’ll look more into the new API’s and just how sexy they can be ;).

Because we can

Really, does there need to be any reason other than this! :P

 

 

Hopefully this gives some insight into what we’re trying to achieve and shows you how it could be viable in your scenarios. Keep in mind this isn’t all you can do with Snapshot, it’s just some of the most common reasons why.

Categories: .Net | Snapshot | Umbraco