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