Saturday, August 13, 2011

Office 365 development

I am a bit excited about office 365.

My company (Extelligent Design) is now a 365 partner, and we aim to support clients implementing it. We want to help people set it up, understand how to use it (my book may come in handy, seeing as the end user interface to the sharepoint online is the same as regular sharepoint) and customise it.

Customising is where the real challange begins. As you probably know, to develop for office 365, you can only use the sandbox - which limits what you can achieve in your customisations. I find that not being able to install application pages (pages that go to the _layouts folder) especially frustrating, and, like a fish out of the water for the first time in its life, I suddenly realise how much I tend to rely on having that resource.

Ok, maybe the fish analogy is a bit much. I can still write web parts, and features with event receivers - not to mention list and library event receivers as well! But this is when I hit another wall - a lot of code I wrote in the past relied on me being able to run sections of the code with elevated privilages. This is gone if you develop a sandbox solution, and is very frustrating. Some obvious things that people will want to do in their hosted sharepoint site rely on code being able to run as other users. Workflows are a great example - having a user with permissions on list A enter data into that list, and then the workflow enters data into another list to which the user does not have permissions. Oh, wait, workflows are also not allowed in hosted sharepoint, so replace the word "workflow" with "event receiver" in the sentance above and you still get the same functionality, and you still hit a wall with not being able to write code that implements that requirement.

PS - the security limitation is blocked for both usual methods of running in elevated privilages mode - you cannot use the security namespace (preveting you from using SPSecurity.RunWithElevatedPrivilages) and you cannot use the SPSite constructore - preventing you from loading a new SPSite object with the system user's token.

So where does this leave us? Not all is doom and gloom - we can still write web parts and event handlers and features - as long as they are not too complex. This is great for deploying look and feel, features that implement site templates and more. I see a lot of developers now focusing on creating turn-key solutions that implement industry-specific templates for sites. Small businesses have a lot in common, and they are the real target of office 365 - so anyone who comes up with a feature that creates lists and libraries that solve a common problem for a small business may hit a jackpot similar to the mobile phone app developers.

Finally, hosting companies that host office 365 may let you deploy code to the servers directly, allowing non-sandbox development to take place. I think this will be hardest to do, since those companies will be very afraid to change their environment from the microsoft default, but a strong partnership with such a company may be a great thing for a sharepoint developer. Come to think of it- if you ARE a hosting company that office 365 hosting and you want some value add to your clients - talk to me!

Office 2010 SP1

Ok, this may not be a sharepoint post, but...

If you are like me, you have "Virtual Clone Drive" installed, allowing you to mount ISO files as virtual CDs (other virutal CD software is available).

If you are like me, you do not have a physical CD drive in your laptop (having pulled it out and put a second SSD in instead).

If you are like me, you are running office 2010.

If you are like me, you always install whatever Windows Update tells you to install.

So...if you are like me, then you have been unable to install service pack 1 of Office 2010, failing (of all things) on installing a sharepoint client component.

The solution turns out to be simple - disable the virtual CD drive and then do the update. Some recommendations from Microsoft also suggests to put a DVD or CD in your physical CD drive while patching. Seems to be a bug in SP1.

Lucky for us sharepoint people that the SharePoint SP1 doesnt have all these annoying installation bugs. Oh, wait...

Thursday, March 31, 2011

A new methology for planning SharePoint Projects - how to win a tender

Today I want to discuss a methodology I have been working on for a while now.
For the last ten years, when developing a solution architecture for clients, I have always stuck to the KISS principle. If you are unfamiliar with that one, it means "Keep It Simple, Stupid!" - which is a good advice when you submit your solution to the review of your peers - other technical specialists and assorted IT professionals. A simple design usually wins through simplicity - it is easy to grasp, easy to implement and easy to maintain and expand on. By KISSing other technical people, you usually get the design you want approved. If you want to read more about KISS, see the wikipedia article about it: http://en.wikipedia.org/wiki/KISS_principle.

The problem I found with KISS is that when you pitch your design to other types of audience, for example upper management, it tends not to be accepted. Initially I thought it may be that management are less prone to like being called "stupid" (which is why I always phrased it "Keep it simple, silly!" - but that never improved things much), but the more I thought about it, I realised that those audiences just expect more out of a solution design than just simplicity.
In other words - KISSing the upper management is not enough.

This is why I decided to come up with my own new principle that extends KISS and adds the things that upper management really wants from a solution architect:
Keep It Simple, Stable, Available, Scalable and Secure.
By KISSASSing upper management, you are almost assured to get the project you are tendering for, or get your solution approved. I think KISSASS should be practiced - do not think you are ready to KISSASS today - it does require a lot of experience - which is why more experienced professionals may get projects over others - they are much better KISSASSers.

I intend to come up with some KISSASS best practices, KISSASS guidelines and perhaps some flow charts explaining how you should practice KISSASS in other aspects of life. If you have some ideas - please share them with me in the comments for this post.
But in the meanwhile - have a great April!

Monday, March 21, 2011

SharePoint 2010 developer training - thoughts

I spent the entire last week in Perth (Australia) delivering developer training to 5 students - and it was great. We covered the basic skills that every sharepoint developer must have, and we talked about what skills they should still learn after the course to enhance their skills. I feel this course (written by MVPs from all around the world, guided by my friend and fellow MVP Randy Williams from www.synergyonline.com, is the best course material available to train SharePoint developers.

Why? first, the labs are excellent - we only had one or two cases where the text in the lab (instructing the students) were slightly off. Synergy Online's quick reaction to fix and modify the labs to correct those typos is one of the reasons I love working with them.
Next, there is the chapters themselves. In 2006, a SharePoint development course could be completed in 3 days or so. Today, it is impossible to teach all aspects of SharePoint development in a five day course. Since students are working people, and cannot spend two weeks learning everything, prioritization is most important, and Synergy Online did a great job figuring out what most developers need to know, and then balancing it so that even within a specific topic, if the topic is too long to teach in 2-3 hours, the class teaches enough for the students to know how to begin and to know they should look into more details when they are back at work before starting a project using those skills.

The only recommendations that I have made to Synergy Online was to drop the Business Intelligence chapter, as while it is important for solution architects, it is less developer oriented, and a much more important chapter that should replace it would be custom field types. I also suggested that we include EditorParts in the web part chapter - at least one code sample so that students can carry on after the course knowing that this functionality is there.

My very favorite thing about training is to tell students about real life scenarios that I had with the technology. For example, in the chapter about querying sharepoint data (comparing SPQuery, SPLinq, and more), I can tell about projects that I had where I built web parts that aggregated sharepoint data, and even open the source code for a web part I have developed in the past and run through the code. This way the students get to see more complex examples of what they see in the lab, and I can even give "best practice" advice about things like error logging and error handling in web parts - as a side note.
I feel this adds so much value to the course...maybe I should be asking for tips at the end of the week?

Next month I am delivering the same course again, this time in Canberra (my home town). If you are seeking developer training and can take a week to be tought by myself - register now at the Dimension Data web site (make sure you choose "Canberra" as location - the other locations are not courses that I am teaching. If you cannot make it to Canberra, by all means register to one of the other ones - the other Syngery trainers are highly professional and knowlegeable - for example, Eric Cheng is a fantastic trainer and I enjoyed teaching with him in the past).

Sunday, January 23, 2011

Setting Target Audiences with code

If you search the web you will find many articles explaining how to set audiences on web parts (also on list items). Most are curious - explaining that you need to specify the IDs of the audiences, separated by colons, and then four semi colons to finish it.

Really? four semi colons at the end without any explanation why? I just had to investigate.
It turns out that the four semi colons have a use. The syntax of the "target to" audience field type is as follows:
[Guid Ids separated by comma];;[Active directory groups' LDAP paths separated by line breaks];;[SharePoint security group names separated by commas]

For example:
decd0c08-4649-4e61-a8d6-8fdf5e4017ad,decd0c08-4649-4e61-a8d6-8fdf5e4017ad;;CN=SecGroup,CN=Users,DC=Development,DC=Local
CN=samplegroup1,CN=Users,DC=Development,DC=Local
;;Admin Office, Authors

So if you only have global (user profile) audiences, the value will be guids followed by four semi colons (because the values of the other type of audiences are blank). If you only choose security\distribution lists then you will have two semi colons before them, and two after. If you chose only sharepoint groups you will get 4 semi colons before the group names.

To make things simpler for me, I wrote a small class to create a value for me, or to parse it out for me. Here is the code:

public class AudienceFieldValue
    {
        /// <summary>
        /// A list of the IDs of the sharepoint global audiences (the one defined in the user profile database, accessible with  Microsoft.Office.Server.Audience.AudienceManager
        /// </summary>
        public List<Guid> GlobalAudienceIds;
        /// <summary>
        /// A list of sharepoint security groups
        /// </summary>
        public List<string> SharePointSecurityGroupNames;
        /// <summary>
        /// A list of active directory LDAP paths (CN=SecGroup,CN=Users,DC=Development,DC=Local) pointing to security groups or distribution lists
        /// </summary>
        public List<string> DirectoryGroupsOrDistListLDAPPaths;

        public AudienceFieldValue()
        {
            GlobalAudienceIds = new List<Guid>();
            SharePointSecurityGroupNames = new List<string>();
            DirectoryGroupsOrDistListLDAPPaths = new List<string>();
        }
        /// <summary>
        /// Use this constructor if you have a value from an existing item and you want to parse it. 
        /// You can even join values from more than one item seperated by semi colons. 
        /// For example: AudienceFieldValue val = new AudienceFieldValue(item1["Target Audiences"].toString() +;"+ item1["Target Audiences"].toString())
        /// </summary>
        /// <param name="value">The value of a "audience target to" column from a SPListItem (or more than one, seperated by semi colons)</param>
        public AudienceFieldValue(string value) 
        {
            GlobalAudienceIds = new List<Guid>();
            SharePointSecurityGroupNames = new List<string>();
            DirectoryGroupsOrDistListLDAPPaths = new List<string>();
            string[] arrTargets = value.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            foreach (string audienceGroup in arrTargets)
            {
                if (audienceGroup.Contains("CN="))
                {
                    //it is one or more AD groups (CN) split by line breaks
                    string[] adgroups = audienceGroup.Split('\n');
                    foreach (string audienceCN in adgroups)
                    {
                        DirectoryGroupsOrDistListLDAPPaths.Add(audienceCN);
                    }
                }
                else
                {
                    string[] arrAudiences = audienceGroup.Split(',');
                    foreach (string audienceName in arrAudiences)
                    {
                        try
                        {
                            Guid g = new Guid(audienceName);
                            GlobalAudienceIds.Add(g);
                        }
                        catch (Exception ex)
                        {
                            //its not a guid - so it is a local sharepoint security group
                            SharePointSecurityGroupNames.Add(audienceName);
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Get the value that you can set to a list item's target audience field
        /// </summary>
        /// <returns>A string value containing all the IDs specified in a format that sharepoint understands</returns>
        public override string ToString()
        {
            if (GlobalAudienceIds.Count == 0 && SharePointSecurityGroupNames.Count == 0 && DirectoryGroupsOrDistListLDAPPaths.Count == 0)
            {
                return "";
            }
            else
            {
                StringBuilder result = new StringBuilder();
                //first add any global audiences
                foreach (Guid id in GlobalAudienceIds)
                {
                    if (result.Length > 0)
                        result.Append(",");
                    result.Append(id.ToString());
                }
                //must add two semi colons to seperate even if there is nothing after
                result.Append(";;");
                //add any directory group path, seperated by a line break
                bool addedDirGroup = false;
                foreach (string directoryGroupPath in DirectoryGroupsOrDistListLDAPPaths)
                {
                    if (addedDirGroup)
                        result.Append("\n");
                    result.Append(directoryGroupPath);
                    addedDirGroup = true;
                }
                //must add two semi colons to seperate even if there is nothing after
                result.Append(";;");
                //add any sharepoint group names seperated by commas
                bool addedSPGroup = false;
                foreach (string spGroupName in SharePointSecurityGroupNames)
                {
                    if (addedSPGroup)
                        result.Append(",");
                    result.Append(spGroupName);
                    addedSPGroup = true;
                }

                return result.ToString();
            }
        }
    }

Tuesday, January 18, 2011

Creating static and dynamin menus in the Site Actions menu

I am presenting tonight in the Canberra SharePoint User Group (and will show the code again in the upcoming Australian SharePoint Conference) about adding actions to the site actions menu. Instead of writing all I have to say, I will just share the source code with your - trusting that you will understand the difference between the two methods I am showing (static and dynamic). Enjoy!
Source code can be downloaded from:
MenuActions.zip

Friday, January 07, 2011

Adding Modules or Elements to a VS2010 SharePoint project

If you encounter the error "The Project Item "[Item Name]" cannot be deployed through a Feature with Farm scope." when you are deploying a Visual Studio 2010 SharePoint project, the problem may be that you have done what I have just done - try to force the wrong item type to deploy...
Let me explain: Lets say you want to add a custom action - something that can be deployed as a farm feature. But by mistake you added a project item of type "Module" instead of "Empty Element". You think to yourself - I can just clear the module, and add the custom action instead in the XML. The intellisense definitly supports it.
Well, it turns out that Visual Studio remembers that you added the XML as a module, and even if you edit out the module instructions, it will not let you deploy the feature as a farm feature, since modules are not supported at the farm scope.
The solution? delete your module, and add an empty element.

Wednesday, December 22, 2010

Controls not showing the value you choose after submitting

A quick one before I forget to post about it - common issue when developing a web control (mostly EditorParts these days, or web parts that do not have an ASCX) is that the user chooses values for the text boxes and dropdownd and other controls, click the submit button, but the value in the function that deals with the button click (or the ApplyChanges function in an editorpart) doesnt get the value the user chose. Instead, the value is the old value.
There are several possible things that can cause this (I covered some in a previous article about common mistakes) but one that is so obviously simple that I forget to make sure of is this:
Make sure "base.CreateChildControls();" is the first thing called in the "CreateChildControls" function.
If that doesnt work, read my previous article about the web part life cycle.

Sandbox solutions and the publishing image field type

I recently found out (to my horror) that sandbox solutions do not support (among many other things) the publishing field types. For example, if you want to use the "LinkFieldValue" class from the Microsoft.SharePoint.Publishing.Fields namespace in a sandbox solution, then you are out of luck. So far, fair enough - annoying, but fair. I figured that I can overcome this by not using that class to parse the details of the image from publishing pages, and I will write my own function that will tease out the image URL from the string that the field returns.

How naive am I!

When I tried running this: imageUrl = item[field.Internalname);

The code would time out, and not return anything. The entire page would just hang. No exception thrown or any reason given.

My solution was to use: imageUrl = item.GetFormattedValue(field.InternalName);

Apparently this gets the string value without trying to go through anything that is not supported in sandboxed solutions. Update: after a bit of more trial and error I found that the exception thrown (sometimes) is that the assembly (the publishing one) is not serialiazble:
'item[this.ImgFieldName]' threw an exception of type 'System.Runtime.Serialization.SerializationException'

Sunday, December 12, 2010

"Cannot complete this action." error when adding a field to a list (or a content type)

If you are trying to add a site column to a list, or a content type that has a site column to a list, and get the dreaded "Cannot complete this action.", then have a read below. Note - this is valid for both sharepoint 2007 and 2010.

To troubleshoot this, I turned off the custom errors to see the entire stack trace of the error. The error was thrown by the Microsoft.SharePoint.Library.SPRequest.AddField function. This lead me to read Eric's blog post about the error and he pointed me in the right direction.

It turns out that a lookup column that allows multiple selection is not supported as an indexed column. You'd expect SharePoint to validate that, and indeed you do not get that option when configuring such a column using the user interface.
However - sharepoint does not validate that when you create the site column using CAML (which is what Eric did) or using code (which is what I did).

Eric's blog post shows how you can recreate and resolve the problem if you are creating the column using XML. So just in case you are making the same mistake I was doing, this is how I resolved my issue - I just make sure none of my code-created lookup columns is both multi select AND indexed, as you can see in the code below.

SPFieldLookup newField = web.Fields.GetFieldByInternalName(newColumnInternalName) as SPFieldLookup;
newField.Title = "My Lookup";
newField.AllowMultipleValues = isMultiSelect;
newField.Group = "My Site Columns";
if (!isMultiSelect) newField.Indexed = true; else newField.Indexed = false;
newField.LookupField = myList.Fields["Title"].InternalName; newField.Update();