<CharlieDigital/> Programming, Politics, and uhh…pineapples

30Dec/09Off

C# and ASP.NET Syntax Highlighting in Trac

Posted by Charles Chen

Well, spent the good amount of time trying to figure this out. See the configuration info below from my trac.ini file.

[mimeviewer]
max_preview_size = 262144
mime_map = text/x-dylan:dylan,text/x-idl:ice,text/x-ada:ads:adb,
php_path = php
pygments_default_style = trac
pygments_modes = text/x-csharp:csharp:7,text/plain:aspx-cs:7
tab_width = 4
treat_as_binary = application/octet-stream,application/pdf,application/postscript,application/rtf

Oh yeah, it helps if you actually install Pygments, too.

Filed under: Dev, Self Note No Comments
22Dec/09Off

SharePoint Design Patterns: Entry 2.5

Posted by Charles Chen

In the previous entry, we looked at how we can model a SharePoint list item using a more domain specific model to simplify programmatic access to the list item thus reducing otherwise error prone data access code and making the overall framework easier to use. Again: the idea is to promote reuse and decrease complexity through domain specific code that abstracts the underlying SharePoint object models, making it easier for a team to build functionality on top of this framework.

One interesting point is that if you're already building your fields and content types using features XML, the work required to generate the domain specific wrappers can be simplified dramatically using automation. In a sense, a content type is basically a class (this is generally how I map them in my domain design); why double your effort and write both the content types and the classes?

So how do we go about this?

(Side note: this isn't so much a "design pattern" as it is an "implementation pattern")

As an example, here is a simple XML file which defines a set of fields and content types which use those fields:

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Field DisplayName="Model Code"
    Name="Model_Code"
    StaticName="Model_Code"
    ID="{F0000000-0000-0000-0000-000000000001}"
    Type="Integer"
    SourceID="http://schemas.someusedcarinventory.com"
    Group="My Custom Columns"/>
  <Field DisplayName="VIN"
    Name="VIN"
    StaticName="VIN"
    ID="{F0000000-0000-0000-0000-000000000002}"
    Type="Text"
    SourceID="http://schemas.someusedcarinventory.com"
    Group="My Custom Columns"/>
  <Field DisplayName="Make"
    Name="Make"
    StaticName="Make"
    ID="{F0000000-0000-0000-0000-00000000003}"
    Type="Text"
    SourceID="http://schemas.someusedcarinventory.com"
    Group="My Custom Columns"/>
  <ContentType Name="Vehicle"
    ID="0x0100FC000000000000000000000000000001"
    Description="Used car inventory"
    Group="My Custom Content Types" >
    <FieldRefs>
      <FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType" />
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" 
        Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
      <FieldRef ID="{F0000000-0000-0000-0000-000000000001}" Name="Model_Code"/>
      <FieldRef ID="{F0000000-0000-0000-0000-000000000002}" Name="VIN"/>
      <FieldRef ID="{F0000000-0000-0000-0000-000000000003}" Name="Make"/>
    </FieldRefs>
  </ContentType>  
  <Field DisplayName="Dealership Code"
    Name="Dealership_Code"
    StaticName="Dealership_Code"
    ID="{F0000000-0000-0000-0000-00000000004}"
    Type="Integer"
    SourceID="http://schemas.someusedcarinventory.com"
    Group="My Custom Columns"/>
  <Field DisplayName="Dealership Fax Number"
    Name="Dealership_Fax_Number"
    StaticName="Dealership_Fax_Number"
    ID="{F0000000-0000-0000-0000-00000000005}"
    Type="Text"
    SourceID="http://schemas.someusedcarinventory.com"
    Group="My Custom Columns"/>
  <ContentType Name="Dealership"
    ID="0x0100FC000000000000000000000000000002"
    Description="Dealerships"
    Group="My Custom Content Types" >
    <FieldRefs>
      <FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType" />
      <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" 
        Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
      <FieldRef ID="{F0000000-0000-0000-0000-000000000004}" Name="Dealership_Code"/>
      <FieldRef ID="{F0000000-0000-0000-0000-000000000005}" Name="Dealership_Fax_Number"/>
    </FieldRefs>
  </ContentType>    
</Elements>

When approaching this problem, I considered three ways of handling the class file generation:

  1. Use an object model and StringTemplate to create .cs files. This invovled writing POCO classes (or generating them from the schema) which I could deserialize the XML to and then passing those objects to a template instance. This seemed like too much work, given that I really didn't feel like maintaining all of that code as well. Plus, while StringTemplate isn't - by any sense of the imagination - hard, it is a non-standard syntax that someone would have to learn to maintain and/or extend the conversion.
  2. Use an XDocument and CodeDom to create .cs files. This seemed like even more work! While it's framework supported, I feel like this solution would be hard to extend and maintain for most developers.
  3. Use an XSL transform to create .cs files. This seemed to be the most natural solution given that the source file is already in XML format and a the target content structure is far from complex (the basic class file is fairly simple). Plus, while XSLT isn't trivial, it's not that hard either (and the syntax is "standard").

One of the cool features of XSL 2.0 is the xsl:result-document element which allows you to create multiple documents from one source document. Only one problem: .NET's XSLT engine doesn't implement XSLT 2.0! What a bummer; it seemed like if I wanted to get this to work and generate multiple output files, it was going to take some work in code or find an XSLT 2.0 capable processor.

Enter Saxon, which provides an XSLT 2.0 processor for .NET. The following code takes the XML above and uses the xsl:result-document to create two class files, one for each content type:

using System;
using System.IO;
using Saxon.Api;
 

namespace FeatureToClass
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Uri xmlFile = new Uri(
                @"C:\Users\Charles\Desktop\elements.xml");

            // Create a Processor instance.  
            Processor p = new Processor();

            // Load the source document.  
            XdmNode node = p.NewDocumentBuilder().Build(xmlFile);

            using (Stream stream = File.OpenRead("core-transform.xslt"))
            {
                // Create a transformer for the stylesheet.  
                XsltTransformer transformer = 
                    p.NewXsltCompiler().Compile(stream).Load();

                // Set the root node of the source document
                // to be the initial context node.  
                transformer.InitialContextNode = node;

                // BaseOutputUri is only necessary for xsl:result-document.  
                transformer.BaseOutputUri = xmlFile;

                transformer.SetParameter(
                    new QName("ct", "http://www.customtransform.com", "namespace"), 
                    new XdmAtomicValue("My.Custom.Package"));

                // Create a serializer.  
                Serializer serializer = new Serializer();
                transformer.Run(serializer);
            }
        }
    }
}

The code above is a simple console program that takes a (hardcoded) path to a source XML file (a SharePoint elements.xml file) and (hardcoded) namespace and loads an XSL file to transform the XML to C# class files.

Here's the transform (it's a bit messy for output formatting reasons, so you're best off copying it into an XML aware text editor to get a better view):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stylesheet [
    <!ENTITY space "<xsl:text> </xsl:text>">
    <!ENTITY cr "<xsl:text>
</xsl:text>">
]>
 

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:sp="http://schemas.microsoft.com/sharepoint/"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:functx="http://www.functx.com"
  xmlns:ct="http://www.customtransform.com"
  version="2.0">
    <xsl:param name="ct:namespace"/>
    <xsl:output method="text"/>

    <xsl:template match="/">
        <xsl:for-each select="//sp:ContentType">
            <xsl:variable name="classname" select="replace(@Name, ' ', '')"/>
            <xsl:variable name="filename" select="concat($classname,'.cs')" />
            <xsl:value-of select="$filename" />
            <!-- Creating  -->
            <xsl:result-document href="{$filename}">
using System;

    namespace <xsl:value-of select="$ct:namespace"/>
    {
        public partial class <xsl:value-of select="$classname"/>
        {
            private string _contentTypeId;
            
            public string ContentTypeId {
                get { return _contentTypeId; }
                set { _contentTypeId = value; }
            }

            public <xsl:value-of select="$classname"/>()
            {
                _contentType = "<xsl:value-of select='@Name'/>";
                _contentTypeId = "<xsl:value-of select='@ID'/>";
            }

            <xsl:apply-templates/>
    }
}
            </xsl:result-document>
        </xsl:for-each>
    </xsl:template>

    <!--///
        Templates
    ///-->
    <xsl:template match="sp:FieldRef">
        <xsl:variable name="fieldname" select="functx:lower-first(replace(@Name, '_', ''))"/>
        <xsl:variable name="fieldid" select="@ID"/>
        <xsl:variable name="dotnettype">
            <xsl:choose>
                <xsl:when test="//sp:Field[(@ID = $fieldid) and (@Type = 'Integer')]">int</xsl:when>
                <xsl:otherwise>string</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        private <xsl:value-of select="$dotnettype"/> _<xsl:value-of select="$fieldname" />;
        <xsl:call-template name="attribute"><xsl:with-param name="fieldid" select="$fieldid"/></xsl:call-template>
        public <xsl:value-of select="$dotnettype"/>&space;<xsl:value-of select="replace(@Name, '_', '')"/>
        {
            get { return _<xsl:value-of select="$fieldname" />; }
            set { _<xsl:value-of select="$fieldname" /> = value; }
        }
    </xsl:template>

    <xsl:template name="attribute">
        <xsl:param name="fieldid"/>
    <xsl:if test="exists(//sp:Field[@ID = $fieldid]/@ID)">[Caml(Id="<xsl:value-of select='//sp:Field[@ID = $fieldid]/@ID'/>", StaticName="<xsl:value-of select='//sp:Field[@ID = $fieldid]/@StaticName'/>", Type="<xsl:value-of select='//sp:Field[@ID = $fieldid]/@Type'/>")]</xsl:if></xsl:template>

    <!--///
        Custom functions
    ///-->
    <xsl:function
        name="functx:lower-first" as="xs:string"
        xmlns:functx="http://www.functx.com" >
        <xsl:param name="arg" as="xs:string"/>
        <xsl:sequence select="concat(lower-case(substring($arg,1,1)), substring($arg,2))"/>
    </xsl:function>
</xsl:stylesheet>

I borrowed one function from the FunctX library to create the camelCased field names. The XSL probably isn't nearly as clean or optimized as it should be (my XSL is admittedly a bit rusty), but it gets the job done. Here's one of the two classes (and class files) which get generated at the source directory of the input XML file:

using System;
 

namespace My.Custom.Package 
{
    public partial class Dealership
    {
        private string _contentTypeId;
        public string ContentTypeId {
            get { return _contentTypeId; }
            set { _contentTypeId = value; }
        }   

        public Dealership()
        {
            _contentType = "Dealership";
            _contentTypeId = "0x0100FC000000000000000000000000000002";
        }

        private string _contentType;
        public string ContentType
        {
            get { return _contentType; }
            set { _contentType = value; }
        }      

        private string _title;
        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }

        private int _dealershipCode;
        [Caml(Id="{F0000000-0000-0000-0000-000000000003}", 
            StaticName="Dealership_Code", Type="Integer")]
        public int DealershipCode
        {
            get { return _dealershipCode; }
            set { _dealershipCode = value; }
        }

        private string _dealershipFaxNumber;
        [Caml(Id="{F0000000-0000-0000-0000-000000000004}", 
            StaticName="Dealership_Fax_Number", Type="Text")]
        public string DealershipFaxNumber
        {
            get { return _dealershipFaxNumber; }
            set { _dealershipFaxNumber = value; }
        }
    }
}

Awesome! It's amazing how little code was required to get this basic scenario working.

Now we have a single source which defines our SharePoint artifacts and our code artifacts; I love it. Write your fields and content types in your feature and you get class files for free! You'll note that I've added a simple CamlAttribute where applicable. This will prove handy when it comes time to automate construction of the object instance from a SharePoint list item, which we'll look at next time (for the time being, feel free to modify the XSL and remove the line for it or write an implementation of CamlAttribute).

Again, to reiterate: the goal is make it easy to build applications on top of a SharePoint deployment by adding a layer of domain specific APIs and objects so that a team can be productive while reducing duplication and the ramp up time required to understand the business domain.

Other points for improvement and enhancement (look for these in a future installment):

  1. Parameterize the program.
  2. Consider making it a Visual Studio add-in or a custom tool.
  3. Make it go the other way; in other words: generate the content type and field XML from class files (which would be cool, too).

But even as it is, it's incredibly useful. On the next installment, we'll see how we can build more intelligence into the model and make it more useful.

Filed under: .Net, Dev, SharePoint No Comments
7Dec/09Off

Host Headers, SharePoint (2010), and Access Denied (401)

Posted by Charles Chen

I've been working on setting up a VM to play around with SharePoint 2010 and kept running into a weird issue where I would be prompted for my credentials repeatedly and I was denied access to the site entirely, even when using an administrator account.


I had set up my SharePoint web application using a host header; for example, beta.dev.com.


Meanwhile, I was able to access the same site from my host environment without any issue.  What gives?  Turns out that there is a slight registry tweak you have to make when working with host headers and IIS.


Phil Harding has a good writeup with links to the Microsoft KBs and additional details.

4Dec/09Off

More WebSequenceDiagrams.com Awesomeness

Posted by Charles Chen

As I've been working with a client which has demanded rigorous sequence diagrams as deliverables for the design phase of the project, I've started to use WebSequenceDiagrams.com more and more.

I've blogged about it previously, but I've only come to truly appreciate it after having to use Visio for a few days before I convinced the client that I could deliver the content faster and in an easier to maintain format (well, text) using WSD.

What Visio Gets Wrong


I ran out of connection points. Yes, it's possible; for a long activation sequence, you can actually run out of connection points. One could argue that this calls for a refactoring of the diagram in the first place, but then I would say that you've never tried to actually refactor a Visio diagram...I still find it hard to believe: the activation box ran out of connection points; I wouldn't have believed it if I didn't see it myself:

I kept having to resize the page. By default, you can't really fit much on the page. But as I started to build my sequence up, I found that I had to keep toggling around with the paper size just so that I would have a grid. This was annoying since it also involved then zooming out so that I could select everything and reposition it to the top left corner of the page then zooming back in. You'd think that Visio would be smart enough to do this, but it isn't...

I kept having to move elements around. Want to add a new step? What about introducing a new actor in between two existing actors? Prepare for some carpal tunnel my friend. There's simply no easy way to do it aside from zooming out, grabbing everything to the right and shifting it around while counting gridlines and getting your result some 10-20 clicks later. What's worse is that you end up having to scroll around horizontally (reordering actors) and vertically (reordering steps) while dragging a bunch of stuff around.

I kept having to fix connection lines. This was absolutely mindboggling: if I extended an activation, it would cause the first connection on the activation to jump, which would then require me to manually drag the connection back to where it belonged. I probably spent a good 10% of my time simply fixing these connection points as a adjusted activations:

This is an incomprehensible design flaw; I have no idea how people work around this in Visio since I adjust activations multiple times as I'm working through a diagram.

There was no representation of alternate paths or optional steps. I ended up having to draw a rectangle and manually managing the size of it as I changed steps and added more steps. What made this even more annoying was that having the rectangle, even though I sent it to the back, then made it difficult to select elements that were enclosed by the rectangle like a message line or a connection point or a note; I'd end up selecting this stupid rectangle instead.

I had to keep managing the location of notes. The notes don't seem to anchor to anything and it's not clear to me how that's supposed to work. That meant that I had to keep moving notes around as I changed activations and modified connection points.

The lifelines didn't synchronize. For the life of me, I couldn't figure out how to get the lifelines to synchronize in length so I didn't have to manually go back and drag each one down to the same length. You can't actually CTRL+click two lifelines, as one would think you'd be able to do, and drag them both to extend them simultaneously. I mean, this seamed like pretty basic stuff to me.

So yeah, all in all, I'm not sure why anyone would want to subject themselves to the pain of creating sequence diagrams in Visio. Maybe if it's a final product and you don't plan on touching it ever again and you've already done most of the work on paper or something, but it's a terrible tool if you're just trying to think an idea out and see it visually.

What WebSequenceDiagrams.com Gets Right


I can't speak to the console program or the DLL (yet), but I decided that the only way that I could do this right was to do it in WSD first and then just use Visio to render the final output. While doing it in the browser is fine, I found it much easier to do it in EditPlus, my text editor of choice.

The first step was to create a syntax and auto-complete file so that it was a little more user-friendly.

Here's my .stx syntax file:

#TITLE=WSD
 

#DELIMITER=,(){}[]+*%/="'~!&|<?:;.
#QUOTATION1="
#LINECOMMENT=note
#CASE=y

#KEYWORD=Activate
activate

#KEYWORD=Deactivate
deactivate
destroy

#KEYWORD=Alias
as
participant

#KEYWORD=Notes
note
over
right
left

#KEYWORD=Control
alt
else
end
opt

#KEYWORD=Newline
\n

#KEYWORD=Transition
->
-->
#

And my .acp auto-complete file:

#TITLE=WSD
 

#CASE=y

#T=act
activate ^!
#T=de
deactivate ^!
#T=pa
participant ^!
#T=alt
alt ^!

else

end
#T=opt
opt ^!

end
#

What I get from this is:

I know what you're thinking: Chuck, that looks like code! By golly, it does! And -- at least to me -- that's the beauty; all of a sudden, a frustrating, time consuming, mouse-centric activity becomes a keyboard-centric, coding-like activity. Moving objects around becomes a matter of moving lines of text. Reordering actors involves moving your participant declarations around. Notes stay in their context if you add a step since everything gets pushed down. There's no manual resizing of anything. There's no fixing connection points. There's no stupid. It actually makes working with sequence diagrams, beyond just whiteboarding, much more useful and much more productive as it lets you kind of think out the code by actually writing pseudocode.

I highly recommend downloading EditPlus (you can keep using it for free, perpetually, if you're a cheap bastard or pony up the $20 for such an awesome editor). For me, EditPlus is a perfect pairing for WSD due to the easy to create language syntax/autocomplete files and the handy split-document feature so you can easily reference your participants at the top.

Simply create a new file type and add the .stx and .acp files I defined:

Now the downside of this whole affair is that you have an extra step of having to copy out the text and pasting it into the browser, but even with that extra little bit of annoyance, the time and frustration saved over working with Visio is more than worth it.

The next step, once I get my hands on the command line tool, is to hook it up to the external tools feature of EditPlus for a quick hit. I'm also considering writing an integration for Visual Studio for custom rendering within VS or at least something quick-and-dirty like an add-in.

One additional note is that WSD has an HTML/Javascript API whereby you can render a diagram inline with your HTML by simply using a set of <pre></pre> tags and a reference to a Javascript file.

What's cool about this is that now you can use the standard CTRL+B/CTRL+E shortcut keys to preview without a copy/paste step! For free! That's pretty awesome.

Of course, the downside is that using this method, there is a limit to the size of the sequence that you can send up as well as the fact that you need some additional hijinks to make the syntax highlighting work (I gave up on that part :-D) . But if were doing it in the browser to begin with or using notepad and you don't care for the syntax highlighting, then this is a huge upgrade.

Conclusion: stop using Visio :-D Now I'm just looking forward to WebERDiagrams.com, WebStateMachineDiagram.com, and....well, you get the idea.

My EditPlus .stx and .acp files for anyone that wants 'em: wsd-files.zip (.52 KB)

Filed under: Awesome, DevTools No Comments