Home | Blog | Screencasts | Projects
# Sunday, April 26, 2009

We needed the basic ability to redirect specific users to a different page. I first took a look at Adam Buenz excellent Redirection web part but we thought that it might be a little bit complex for our content authors to use, so I put together a really simple web part that gives the user the ability to select the users or groups via the standard SharePoint people picker interface.

 

image

 

The idea is that you can select a number of groups or users to apply the redirection to, this could be as broad as all authenticated users or refined to a single user. Of course you need a way edit the web part, so another people picker dialog allows the user to select users or groups that this redirect does not apply to. If they match the exclusion, the web part will present them with the text ‘This account was excluded from redirection to <url>’. This exclusion group is normally the content authors.

 

image

 

It is possible to put a number of these web parts on the same page, so if for example you wanted to redirect to a number of different sites based on AD group or user accounts, this should work fine.

 

First step, as always is to add the solution to SharePoint, once the solution is deployed, be sure to activate the ‘Redirector web part’ feature at the site level:

 

image

 

Once that is done, it’s just a matter of selecting the ‘Redirect web part’ from the web part gallery:

 

image

 

Hopefully you’ll find some use for it. The source code can be downloaded here and the WSP can be found here.

 

The code for this web part is fairly straight forward, I have a tool part that uses the SharePoint  PeopleEditor control. This control has a property to return the selection of groups or accounts as a comma separated string. With this string I can then do a search to see if the current user is in the group like:

        private bool IsUserIn(string commaSeperatedList)
        {
            bool isMatch = false;

            if (commaSeperatedList != string.Empty)
            {
                foreach (string group in commaSeperatedList.Split(','))
                {
                    if (Page.User.IsInRole(group) || Page.User.Identity.Name == group)
                    {
                        isMatch = true;
                    }
                }
            }
            return isMatch;
        }

 

So the web parts main logic looks like:

 

            bool redirectMatch = false;
            bool exclusionMatch = false;

            redirectMatch = IsUserIn(selectedADVal);
            exclusionMatch = IsUserIn(excludeADVal);
                   

            if (redirectMatch == true && exclusionMatch == false)
            {
                Page.Response.Redirect( redirectUrl);
            }
            else
           
{
                if (redirectMatch)
                {
                    outputCtrl.Text = "This account was excluded from redirection to " + redirectUrl;
                }
            }

Sunday, April 26, 2009 12:23:41 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | MOSS | Sharepoint
# Saturday, April 25, 2009

CRM 4 has lots of excellent customization options, one of which is the auto-numbering of entities:

 

image

 

The Settings – Administration section allows you to setup some options for auto-numbering your entities, you can choose the prefix, and the suffix length:

 

image

 

This is pretty powerful, but how would approach this problem if you needed your entities to have a more complex numbering scheme? Being a developer, everything looks like a development task to me. So I would start with a plugin that implements the IPlugin interface:

 

       public class AutoNumber : IPlugin

 

Then we need to implement the Execute method such as:

 

        public void Execute(IPluginExecutionContext context)
        {
           
            if (context.InputParameters.Properties.Contains("Target"))
            {
                DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];

                if (entity != null)
                {
                    if (!entity.Properties.Contains("AttributeName"))
                    {
                        entity.Properties.Add(new StringProperty("AttributeName", GetAutoNumber()));

                    }
                }
            }
        }

 

So the above code will check to make sure our input properties contains the ‘Target’ parameter, this will be the entity that is affected by the plugin. From here we can cast it to a DynamicEntity  which will make it easier to work with. Once we have an instance of DynamicEntity the whole task of updating and attributes becomes trivial, the above code just creates a new property and calls some method that will generate the complex AutoNumber.

 

Once the plugin has been developed, the next important step is to register the plugin. The best tool for the job is probably the CRM Plugin Registration Tool:

 

PlugIn_thumb

 

The first step is to register the plugin assembly:

 

image

 

This will prompt you with a form that allows you to select the assembly and the location it will be stored i.e. Database, file system or GAC.

 

Once the assembly has been registered we can register a ‘new step’, this is done from the same menu as the assembly registration.

You will be prompted to fill out the following form:

 

image

 

For our plugin we will choose the following options:

Message: Create  (since we are doing an auto number, it makes sense to only run the plugin at the create stage)

Primary Entity:  Your choice

Plugin: Your assembly that was loaded at the previous step

Post Stage: we want to run it after the entity has been created, otherwise the built in CRM auto numbering will overwrite our value.

Synchronous: lets fire the event synchronously, no reason not to, the end user will be presented with a consistent interface this way.

Step Deployment: your choice

Triggering Pipeline: Parent

 

As you can see its all fairly simple and straight forward, now you can have an auto number that be what ever you need, it might be simple like some combination of a financial year or complex where a lookup to some external system needs to take place, but at the end of the day at least there is a nice mechanism in place to help you out.

Saturday, April 25, 2009 6:50:35 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
code | CRM

By default when you edit the tool part settings of a SharePoint web part and you click on the ‘…’ if the property is a string, you will get the default property builder:

 

image

 

Clicking the above ‘…’ will launch the default string property builder:

Standard SharePoint Property Text Builder

 

This popup is actually the ‘zoombldr.aspx’ page located in the _layouts virtual directory.

 

There are a few problems with this builder, well it’s not so much the builder, the string property’s of the web part are serialised in a manner that strips out carriage returns. So if you were to spend time formatting the contents of this dialog, it will get wiped out when you save the contents. This is most apparent when you edit the XSLT of the core search results web part.

 

How can you override this property?

 

The key is to make use of the HtmlDesigner attribute on your web part property. There are two ways to make use of the attribute, the first way is to explicitly define the page which you want to load (i.e. change ‘url to page’):

 

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
        System.Web.UI.WebControls.WebParts.WebDisplayName("Template"),
        System.Web.UI.WebControls.WebParts.WebDescription(""),
        System.Web.UI.WebControls.WebParts.Personalizable(
        System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
        System.ComponentModel.Category("Settings"),
        System.ComponentModel.DefaultValue("")
        ]
        [HtmlDesignerAttribute("url to page", DialogFeatures = "dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")]
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }

 

Notice that you could also modify the parameters that get passed into the JavaScript popup window creation script, in the case above I have made the popup window larger than the default size. The problem with the above approach is that the URL that gets passed as the first parameter to the HtmlDesignerAttribute must be a constant value, since it’s used in the attribute declaration. However Microsoft have provided us with a nice way to change this behaviour.

 

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
        System.Web.UI.WebControls.WebParts.WebDisplayName("Template"),
        System.Web.UI.WebControls.WebParts.WebDescription(""),
        System.Web.UI.WebControls.WebParts.Personalizable(
        System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
        System.ComponentModel.Category("Settings"),
        System.ComponentModel.DefaultValue("")
        ]
        [HtmlDesignerAttribute(BrowserBuilderType.Dynamic, DialogFeatures = dialogHeight:500px;dialogWidth:650px;help:no;status:no;resizable:yes")]
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }

 

I’ve now added the BrowserBuilderType.Dynamic option. To get this to work, we now need to override the GetCustomBuilder method of your web part:

 

         protected override string GetCustomBuilder(stringpropertyName)
        {
            if(propertyName == "CustomProp")
            {
                return"url.aspx?"+ "some custom params";
            }
            return base.GetCustomBuilder(propertyName);
        }

 

Now every property that has BrowserBuilderType.Dynamic passed into the HtmlDesignerAttribute will get passed into the GetCustomBuilder method, this gives you the chance to create a URL that passes parameters to your custom builder page.

 

Ok, so now you got the web part covered, what about the JavaScript that needs to run on your custom property builder page?

 

The best thing to do is to look at the current zoomdldr.aspx page, when the page loads you can get the current arguments from:

 

window.dialogArguments

 

Then to save the arguments, save the string value back to:

 

window.returnValue

 

 

Now that you’ve got a fair idea about creating custom property builders, you can create builders that are specific to you needs, below is a screenshot of a property builder form that lets the user enter some c# code that gets compiled an injected into the web part, the need was to provide a nice interface that the end user (i.e. a programmer) could use. The property builder uses the EditArea control to format the code nicely. The user can then click the compile button which will do a compilation of the code and report any errors. I’ve also added some JavaScript code to get around the removal of the carriage return characters, which was a major pain point for us.

 

Custom Property Builder

 

 

This approach will give you the power to create property builders that more closely suit your needs and goals.

Saturday, April 25, 2009 12:44:51 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | Sharepoint | Tip
# Tuesday, April 21, 2009

There is always lots of criticism about SharePoint from developers, things like bloated pages etc. But there is lots of good stuff in SharePoint, so I thought it might be fun to come up with a list of things that a typical asp.net application might be able to look at SharePoint for guidance.

 

1. Search - Have a good search story

We now have a whole generation that have grown up using a search engine, they expect a search box that they can type free form data, so by far the biggest feature that ASP.NET applications could learn a lot is the search capability of SharePoint. The good news is that you could use a product like search server express to crawl parts of your application and then use the search server web services like I did with my ASP.NET MVC search engine example.

 

2. Extensibility – Make your system extensible where it makes sense

SharePoint has a number of extensibility points like Features, Site Definitions, Web Parts which all work in different ways to allow fine grade control over the system. The model that SharePoint provides works well for the tasks that it is trying to achieve, your ASP.NET application may benefit from a plugin model that a framework like MEF provides, where crazy things like plugins for plugins are supported. Maybe you want to expose a RESTful service via ADO.NET Data Services, in any case extensibility has proved for many web applications (twitter, facebook) to be an important consideration.

 

3. Workflow - Hosting done right

Workflow has become a hot topic in recent years, lots of tasks are well suited to be modelled with a workflow, I’m sure your ASP.NET application has some actions that need some auditing or approval before action can be taken. SharePoint does a particularly good job at hosting Window Workflow Foundation, even if we exclude the building of custom workflow’s on the fly in SharePoint Designer, a great deal can be taken away from the way SharePoint has implemented WorkFlow, it’s made it simple for most users to understand.

 

4. Well designed API  

Well not everyone would agree with the ‘well designed’ bit, but for the most part you can achieve almost all the administrative activities from the API. Although a well built ASP.NET application should in theory have loosely coupled components that should facilitate an API, in practice most ASP.NET applications have code thrown all over the place, with event handlers performing data access etc. Ideally the well designed API should fit with the Extensibility point from number two, it’s no good having an extensible system if the API is unusable.

 

5. Background Processes – How do you handle them?

One thing that SharePoint does particularly well is the background processing of tasks like Active Directory imports, site cleanup processes and scheduled emails. The API from the above point extends to the scheduling plugin’s as well, the main point here is that SharePoint runs these tasks as a windows service, this is in contrast to applications like DotNetNuke which do background and scheduled processing on the application OnStart event. Both are valid approaches given certain circumstances, my point is that the difference between a professional product like SharePoint and a custom ASP.NET application is the little house keeping items, SharePoint tries to take care of it all, so should your ASP.NET application.

 

6. Integration with the Microsoft Software Ecosystem

SharePoint being a Microsoft product plays nice with other Microsoft systems, your ASP.NET application can do the same, things like using Exchange for email events to support incoming email. Making use of active directory groups for authentication and authorisation or even for organisation charts and manager notifications, this integration is what really differentiates Microsoft’s products and makes people feel more comfortable. Does your ASP.NET application provide custom WMI performance counters for it’s operations?

Tuesday, April 21, 2009 10:48:44 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
Misc
# Sunday, April 19, 2009

A little while ago, I was working with a customer who wanted to have the ability to write some annotations on a graph that was presented in a SharePoint site. The idea was that the graph was quite complex and it would help the end user understand it more if it was annotated by a domain expert.

So I put together a little proof of concept web part that makes use of all that JQuery goodness. I figured that it didn’t matter if you were annotating complex graphs, or tagging someone’s face like the way flickr and facebook does, the concept was the same:

 

image

 

image

 

image

 

So like I said the code is proof of concept, not production ready, but I thought I’d post it here anyway, if only to inspire ideas.

I’ve stored the data in a list with the schema of

 

Title:  default setting (single line of text)

JSON: single line of text

 

Or:

image

 

The data will look like this once some annotations have been added:

 

image

 

So I’ve just stored the JSON serialised object as is. The web part will iterate over each item on that list and pump that JSON into some JavaScript that is written to the page, so when the page load’s it will insatiate a JavaScript array of annotations and then render them. Which looks something like:

 

notes = [{ "x1": "11", "y1": "28", "height": "91", "width": "73", "note": "This is bill", "imgID": "0"}, { "x1": "86", "y1": "54", "height": "81", "width": "95", "note": "This is Kate", "imgID": "0"}];

 

You might ask why I have the imgID property, this is used if more than one image on the page is annotated.

 

To have a play around with the web part, you can download the WSP here, you just need to deploy the solution in the normal manner (stsadm –o addsolution –filename <path to httpcode.imageannotate.wsp>

 

Next step is to activate the Image Annotation web part feature on the site:

 

image

 

Add the web part as normal from the web part menu:

 

image

 

Once the web part is on the page, you need to set List setting to the SharePoint list that will store the JSON data (this list must conform to the schema above):

 

image

 

 

The client ID is the most technical part, this is a selector that gets used directly by JQuery, so any valid JQuery selector will work. A couple of examples:

This will find the default image on the homepage of a standard collabration portal:

img[src*='/PublishingImages/newsarticleimage.jpg']

 

All the images produced by a Dundas chart:

img[src*='SharePointChartAxd.axd']

 

Hopefully you get the idea.

 

I haven’t added any way to delete the annotation from the image itself, rather you need to go to the custom list and delete the item from there. The web part is smart enough to not allow users who can’t add items to the list. Credit should also go to odyniec.net for the imgAreaSelect JQuery plugin which I’ve made use of.

The web part injects lots of JavaScript into the page, so the code might look a little messy because of this, but the basic premise is pretty straight forward.

 

The source code can be found here. It includes a HttpHandler (ImageAnnotate.ashx) for handling all the ajax calls from the client side script. Remember this is just some sample code, use it at your own risk.

Sunday, April 19, 2009 6:49:54 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | JQuery | Sharepoint
Statistics
Total Posts: 190
This Year: 3
This Month: 0
This Week: 0
Comments: 38