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

Formsy

by AnthonyDang 2. July 2010 03:19

"Formsy is an XML driven forms engine built with ASP.Net 3.5. Formsy is super easy to use, is completely extensible and generates clean markup for easy styling."

Formsy came about after my experiences with Umbraco Contour. I had to do some crazy code to get Contour to do things it was really not meant for. Getting Contour to regionalize form labels, validation messages, and validation regular expressions was the least crazy thing we needed to do. We made Contour do things that it was never ever meant to do.  

After the Contour experience I realized that I needed some practice with composite controls. So I sat at home and started to make a little forms engine. 

This project encompasses almost everything I have learned about .Net since I started at The Farm in 2007. On this my last day at Farm, I'm pleased to launch and share this open source project of mine to The Farm and to the open source community.

Check it out: http://formsy.codeplex.com

 

 

Tags:
Categories:

Examine demo site source code from CodeGarden 2010

by Shannon Deminick 1. July 2010 17:49

A few people were asking for the source code from my Examine presentation at CodeGarden, so here it is. I’m not going to go in to all of the details of this site or the Examine config as it’s pretty simple. However, i will give you a very quick run down of it and if you attended CodeGarden and my presentation, you’d probably already know this.

The Umbraco config for this demo site is simple: A search form, a couple of search result pages with different templates (results using XSLT extensions, results to query media using the FluentAPI, custom results using the FluentAPI). Then there’s the content: 5 very simple nodes consisting of a text field and a numeric field and a miniature blog with some posts and comments.

I’ve included all of the source files and a backup of the database that it was running on. The source files are left in the same state as we left the demo during the presentation. So to get it up and running, just restore the database to your MS SQL server, update your web.config, and put the project files into IIS (or open the solution in Visual Studio).

Download here

Categories: .Net | Examine | Umbraco

Examine slide deck for CodeGarden 2010

by Shannon Deminick 29. June 2010 16:55

A few people had asked during CodeGarden 2010 if I would post up the slide deck for my Examine presentation, so here it is. There’s not a heap of information in there since i think people would have soaked up most of the info during the examples and coding demos but it’s posted here regardless and hopefully it helps a few people.

I’ve included a PDF version (link at the bottom) and also the image version below (if you’re too lazy to download it :)

Slide2 Slide3 Slide4 Slide5 Slide6 Slide7 Slide8 Slide9 Slide10 Slide11 Slide12 Slide13 Slide14 Slide15

Download slide deck here

Categories: Examine | .Net | Umbraco

Multi-node tree picker source code from CodeGarden 2010

by Shannon Deminick 29. June 2010 14:54

If you missed out on CodeGarden 2010 this year then you definitely missed one heck of a conference! If you want to read more about what happened you can check out these posts here:

This post is about the data type I presented during the Umbraco package competition (which ended up winning too! :). It’s a multi-node picker with the full tree interface to allow you to select the nodes you want. It’s also sort-able with drag/drop functionality.

image

It’s a very simple control but has the capability to be made into a very awesome data type which is why I’m starting up a new CodePlex project called: Data Types for Umbraco (found here). I’m hoping that people in the Umbraco community will want to become developers on this project so a bunch of us can collaborate to create some seriously amazing data types for Umbraco 4.5. Please let me know if you want to contribute to this project as i think it could have huge potential. Instead of everyone creating their own closed source data types and developing them all in different ways, this would help unify and standardize the way we create data types.

So the first thing I will put up there is the full source for this data type as it was meant to be. However, I’m not going to get around to that today, so in the meantime I’ve put the source for this control right here. Remember, this is not really the best way to make this data type but it will give you a good indication of how to use the new JavaScript tree API and how to build a simple UserControl data type.

Enjoy!

ASCX Code

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TreePicker.ascx.cs"
    Inherits="ExamineDemo1.usercontrols.TreePicker" %>
<%@ Register TagPrefix="umb" TagName="tree" Src="~/umbraco/controls/Tree/TreeControl.ascx" %>
<script type="text/javascript">

//a reference to the hidden field
//storing the selected node data
var hiddenField = jQuery("#<%=PickedNodes.ClientID%>");

//create a sortable, drag/drop list and
//initialize the right panel with previously
//saved data.
jQuery(document).ready(function () {
    jQuery(".right").sortable({
        stop: function (event, ui) { StorePickedNodes(); }
    });
    jQuery(".item a.close").live("click", function () {
        jQuery(this).parent().remove();
    });

    //now rebuild the selected items
    try {
        var json = eval('(' + hiddenField.val() + ')');
        jQuery(".right").html(unescape(json.markup));
        StorePickedNodes();
    }
    catch (err) { };
});

//Add event handler to the tree API.
//Bind to the window load event as the document ready event
//is too early to query for the tree api object
jQuery(window).load(function () {
    //add a handler to the tree's nodeClicked event
    jQuery("#<%=TreePickerControl.ClientID%>")
        .UmbracoTreeAPI()
        .addEventHandler("nodeClicked", function (e, node) {
            AddToRight(node);
    });
});

function AddToRight(node) {
    //get the node id of the node selected
    var nodeId = jQuery(node).attr("id");
    //check if node id already exists in the right panel
    if (jQuery(".right").find("li[rel='" + nodeId + "']").length > 0) {
        return;
    }
    //create a copy of the node clicked on the tree
    var jNode = jQuery(node).clone().find("a:first")
    //remove un-needed attributes
    jNode.removeAttr("href")
        .removeAttr("umb:nodedata")
        .attr("href", "javascript:SyncItems(" + nodeId + ");");
    //build a DOM object to put in the right panel
    jQuery("<div class='item'><ul class='rightNode'>" +
            "<li rel='" + nodeId + "' class='closed'>" +
            "</li></ul><a class='close' href='javascript:void(0);'>[X]</a></div>")
        .appendTo(".right")
        .find(".closed").append(jNode);
    //now update the hidden field with the
    //node selection
    StorePickedNodes();
}

//A method to sync the left tree to the item
//selected in the right panel
function SyncItems(nodeId) {
    jQuery("#<%=TreePickerControl.ClientID%>")
        .UmbracoTreeAPI()
        .syncTree(nodeId.toString());
}

//were going to store both the node ids
//and the html markup to re-render the 
//right hand column as JSON to be put into
//the database.
function StorePickedNodes() {
    var val = "[";
    jQuery(".right .item ul.rightNode li").each(function () {
        val += jQuery(this).attr("rel") + ",";
    });
    if (val != "[") val = val.substr(0, val.length - 1);
    val += "]";
    //append the html markup
    var obj = "{ \"val\": " + val + ", \"markup\": \"" + escape(jQuery(".right").html()) + "\"}";
    hiddenField.val(obj);       
}

</script>

<%--Inline styles are dodgy, but simple for
this demonstration--%>

<style type="text/css">
.multiTreePicker .item ul.rightNode 
{
    float:left;
    margin:0;
    padding:0;    	
}
.multiTreePicker .item ul.rightNode li
{
    margin:0;
    padding: 0;
    list-style:none;
    font:icon;
    font-family:Arial,Lucida Grande;
    font-size:12px;
    min-height:20px;
}
.multiTreePicker .item ul.rightNode li a
{
    background-repeat:no-repeat !important;
    border:0 none;
    color:#2F2F2F;
    height:18px;
    line-height:18px;
    padding:0 0 0 18px;
    text-decoration:none;
}
.multiTreePicker .item a
{
    float:left;
}   
.multiTreePicker .item a.close 
{
    margin-left:5px;
}
.multiTreePicker .item
{
    cursor: pointer;
    width:100%;
    height:20px;
}
.multiTreePicker .left.propertypane
{
    width: 300px;
    float: left;
    clear:none;
    margin-right:10px;
}
.multiTreePicker .right.propertypane
{
    width: 300px;
    float: left;
    clear:right;
    padding:5px;
}
.multiTreePicker .header
{
    width:622px;
}
   
</style>

<div class="multiTreePicker">
    <div class="header propertypane">
        <div>Select items</div>
    </div>
    <div class="left propertypane">        
        <umb:tree runat="server" ID="TreePickerControl" 
            CssClass="myTreePicker" Mode="Standard" 
            DialogMode="id" ShowContextMenu="false" 
            IsDialog="true" TreeType="content" />
    </div>
    <div class="right propertypane">
    </div>
</div>

<asp:HiddenField runat="server" ID="PickedNodes" />

C# Code Behind


public partial class TreePicker : 
    System.Web.UI.UserControl, IUsercontrolDataEditor
{
        


    #region IUsercontrolDataEditor Members

    public object value
    {
        get
        {
            //put the node in umbraco
            return PickedNodes.Value;
        }
        set
        {
            //get from umbraco
            PickedNodes.Value = value.ToString();
        }
    }

    #endregion
}
Categories: .Net | Umbraco

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

Umbraco 4.1 Database Structure

by Shannon Deminick 15. June 2010 14:38

We’re finally shipping Umbraco 4.1 with all of the real database constraints, indexes and keys that it so much deserves! This issue was reported on over 3 years ago (http://umbraco.codeplex.com/workitem/8162) and it’s now a reality! So without further adieu, here’s the complete database diagram. If you stare at it long enough you might notice some constraints missing which may be true, but this is by design (I assure you :). I’ve attempted to compact the diagram to as small as possible but no matter which way you cut it, it’s a bit crazy. Enjoy! (click the image to see the full size)

 

image

Categories: Umbraco | .Net

100 Unit tests in Umbraco 4.1

by Shannon Deminick 14. June 2010 19:14

Well, hopefully there’ll be even more before the final release of 4.1 but as of tonight, there’s an even 100 written and passing! Most of these have to do with the data layer as we’ve implemented all of the proper database constraints, keys and indexes for 4.1! Here’s the full list of tests for those interested:

  • Application_Create_New_User_Assign_Application_And_Delete_User
  • Application_Make_New
  • Application_Make_New_Assign_User_And_Delete
  • ApplicationTree_Make_New_Then_Delete_App
  • DataTypeDefinition_Assign_Data_Type_To_Doc_Type_Then_Create_Doc_And_Set_Value
  • DataTypeDefinition_Assign_Data_Type_With_PreValues
  • DataTypeDefinition_Make_New
  • Dictionary_Attempt_Duplicate_Key
  • Dictionary_Change_Key
  • Dictionary_Contructor_Guid
  • Dictionary_Contructor_Key
  • Dictionary_Create_Add_Text_And_Delete
  • Dictionary_Delete_Parent_With_Children
  • Dictionary_Get_Top_Level_Items
  • Dictionary_IsTopMostItem
  • Dictionary_Parent_Child_Relationship
  • Dictionary_ToXml
  • Document_Assign_Domain
  • Document_Copy
  • Document_Copy_And_Relate
  • Document_Delete_All_Docs_By_Document_Type
  • Document_Delete_Heirarchy_Permanently
  • Document_Empty_Recycle_Bin
  • Document_Make_New
  • Document_Make_New_And_Publish
  • Document_Move
  • Document_Publish_Then_UnPublish
  • Document_Publish_With_Result
  • Document_Publish_With_Subs
  • Document_Regenerate_Previews
  • Document_RePublish_All
  • Document_Save_And_Publish_Then_Roll_Back
  • Document_To_Preview_Xml
  • Document_Undelete
  • DocumentType_Add_Properties_To_Tab_Then_Delete_It
  • DocumentType_Delete_Doc_Type_With_Content
  • DocumentType_Delete_Doc_Type_With_Content_And_Children_Of_Different_Doc_Types
  • DocumentType_Get_All
  • DocumentType_Has_Children
  • DocumentType_Make_New
  • IOHelper_MapPathTestVDirTraversal
  • Language_Delete_Default_Language
  • Language_Delete_With_Assigned_Dictionary_Items
  • Language_Delete_With_Assigned_Domain
  • Language_Get_By_Culture_Code
  • Language_GetAll
  • Language_Make_Duplicate
  • Language_Make_New
  • Language_To_Xml
  • Macro_Make_New
  • Macro_Make_New_Add_Property
  • Macro_Not_Found_Constructor
  • MacroProperty_Not_Found_Constructor
  • Media_Delete_All_Docs_By_Document_Type
  • Media_Delete_Heirarchy_Permanently
  • Media_Empty_Recycle_Bin
  • Media_Make_New
  • Media_Move
  • Media_Undelete
  • Media_Update_Data
  • MediaType_Add_Properties_To_Tab_Then_Delete_It
  • MediaType_Delete_Media_Type_With_Media_And_Children_Of_Diff_Media_Types
  • MediaType_Get_All
  • Member_Add_To_Group
  • Member_Make_New
  • MemberGroup_Add_Member_To_Group_And_Delete_Group
  • MemberGroup_Make_New
  • MembershipProvider_Create_User
  • MembershipProvider_Create_User_Assign_New_Role
  • MemberType_Delete_With_Assigned_Members
  • MemberType_Make_New
  • Notification_Assign_To_New_Node_Then_Delete_Node
  • Notification_Assign_To_New_User_Then_Delete_User
  • Notification_Make_New
  • Permission_Assign_To_New_Node_Then_Delete_Node
  • Permission_Assign_To_New_User_Then_Delete_User
  • Permission_Make_New
  • PropertyType_Make_New
  • Relation_Make_New
  • Relation_Relate_Docs_And_Delete_Parent
  • StyleSheet_Make_New
  • StyleSheet_Make_New_AddProperty
  • Tag_Add_Tags_To_Node
  • Tag_Make_New
  • Tag_Make_New_Assign_Node_Delete_Node
  • Task_Assign_To_New_Node_Delete_Node_And_Ensure_Tasks_Removed
  • Task_Make_New_And_Close
  • Task_Not_Found_Constructor
  • TaskType_Make_Duplicate
  • TaskType_Make_New_Assign_Tasks_And_Remove
  • TaskType_Not_Found_Constructor1
  • TaskType_Not_Found_Constructor2
  • Template_Assign_To_Document_And_Delete
  • Template_Make_New
  • Template_Make_New_With_Master_And_Remove_Heirarchy_And_Delete
  • User_Delete_Admin_Not_Allowed
  • User_Make_New_Duplicate_Login
  • User_Make_New_Override1
  • User_Make_New_Override2
  • User_Not_Found_Constructor
Categories: Umbraco | .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