I'm very fortunate to have been the opportunity to talk at SharePoint Saturday. This year I'm going slightly more technical than last year, and hopefully get in a few demo's as well. My topic, "Practical SharePoint 2013", will deal with specific issues that I had come across the past few months, and hopefully will give a few people in the audience ideas in how to tackle problems they face themselves.
I'll be uploading my slide-deck and any examples here, so please feel free to download them, when they become available.
Happy coding, and see you at SPSCPT14!!
The Chronicle's of DamnedDutch
Thursday, September 11, 2014
Tuesday, September 18, 2012
Setting the Value for a SharePoint DateTimeField not working?
Some day's I stand amazed by the simplicity and rich features the SharePoint API gives to the developer. But some days (they seem to pop up more and more these days), I can only shake my head at the pure stupidity it seems from the Microsoft SharePoint team.
Problem:
Take the DateTimeField. I'm busy rending controls (from a list) in order to build a custom edit form. Everything seems to render perfectly, including the DateTimeField. Normally you'd do something like this :
Solution :
I've ended up creating a DateTimeControl, whenever the DateTimeField pops up. My code ended up looking something like this:
Not the perfect solution, but it works. In my next post I'll reveal the entire control that will render the Taxonomy controls as well. Happy programming!
Important and Useful Links:
Problem:
Take the DateTimeField. I'm busy rending controls (from a list) in order to build a custom edit form. Everything seems to render perfectly, including the DateTimeField. Normally you'd do something like this :
private void CreateControl(SPList list, string internalName, object val) { SPField field = list.Fields.GetField(internalName); if (field == null) return; BaseFieldControl fieldRenderingControl = field.FieldRenderingControl; fieldRenderingControl.ID = "fld_" + field.Id.ToString().Replace("-", "_"); fieldRenderingControl.ControlMode = SPControlMode.New; fieldRenderingControl.ListId = list.ID; fieldRenderingControl.FieldName = field.InternalName; fieldRenderingControl.RenderContext = SPContext.GetContext(HttpContext.Current, list.DefaultView.ID, list.ID, adapter.Web); // Per example only. Set the Value, cast to type if necessary. fieldRenderingControl.Value = val; this.Controls.Add(fieldRenderingControl); }This renders basic controls (excluding the Taxonomy and Custom FieldType controls) in a fairly straight forward and easy way. The problem comes in when you try and set the Value of a DateTimeField control. You'll end up with "an Object reference not set" error. I haven't had the time to fire up Reflector, so I'm not exactly sure what's happening here.
Solution :
I've ended up creating a DateTimeControl, whenever the DateTimeField pops up. My code ended up looking something like this:
private void CreateControl(SPList list, string internalName, object val) { SPField field = list.Fields.GetField(internalName); if (field == null) return; if (field.FieldRenderingControl.GetType().Equals(typeof(DateTimeField))) { DateTimeControl dateTimeControl = new DateTimeControl(); SPFieldDateTime sp = (SPFieldDateTime)field; if (sp.DisplayFormat == SPDateTimeFieldFormatType.DateOnly) { dateTimeControl.DateOnly = true; } dateTimeControl.ID = "fld_" + field.Id.ToString().Replace("-", "_"); // Set the value dateTimeControl.SelectedDate = (DateTime)val; this.Controls.Add(dateTimeControl); } else { BaseFieldControl fieldRenderingControl = field.FieldRenderingControl; fieldRenderingControl.ID = "fld_" + field.Id.ToString().Replace("-", "_"); fieldRenderingControl.ControlMode = SPControlMode.New; fieldRenderingControl.ListId = list.ID; fieldRenderingControl.FieldName = field.InternalName; fieldRenderingControl.RenderContext = SPContext.GetContext(HttpContext.Current, list.DefaultView.ID, list.ID, adapter.Web); // Per example only. Set the Value, cast to type if necessary. fieldRenderingControl.Value = val; this.Controls.Add(fieldRenderingControl); } }
Important and Useful Links:
- Do not use DateTimeField when displaying the date of your field by entwicklungsgedanken
- Making Use of the SharePoint Field Type Editing Controls by Sike Mullivan
- DateTimeControl Control by Karine Bosch
Wednesday, August 29, 2012
SharePoint 2010 CodePlex Projects
The following SharePoint 2010 CodePlex projects have helped me creating wonderful solutions for my clients. Here are the ones that stood out for me (so far):
SharePoint Manager
Overview:
The SharePoint Manager is a SharePoint object model explorer. It enables you to browse every site on the local farm and view every property.
Comments:
First and foremost! Indispensable, and a must have for every SharePoint developer and administrator.
SharePoint Developer Tools for Visual Studio 2010
and
CKS: Development Tools Edition
Overview:
This project extends the Visual Studio 2010 SharePoint Project system with advanced templates and tools.
Comments:
Attended an SharePoint evangelism by Wouter van Vugt a couple of years ago, and have been a keen user of CodeCounsel's Visual Studio tools ever since. Highly recommended!
SharePoint 2010 Fluent Ribbon API
Overview:
An API that lets you create Ribbon buttons for application pages and even contextual ribbon buttons for WebParts.
Comments:
Wow, if you're involved with custom application building for SharePoint as much as I am, this was a true blessing in disguise. Very easy to understand, and simple to use.
Documentation :
http://markeev.com/sharepoint/ribbon/
http://amarkeev.wordpress.com/2012/01/06/sharepoint-ribbon-togglebutton/
MiniCalendar Web Part
Overview:
A small web part to display links to events stored in a list (or document library) in a mini calendar (in month view mode).
Comments:
Really simple little webpart. Gave us a great head-start in creating a mini calendar that we could use in a masterpage.
SharePoint Property Bag Settings
Overview:
The Property Bag Settings can store any metadata as Key-Value pairs. This SharePoint administrative application page(s) provides a hierarchical configuration manager that can safely store and retrieve configuration settings at Farm, Web, Site Collection, Site and List level.
Comments:
Simple and clean interface lets you easily manage the Property Bag. Becomes very useful once you start building custom applications within the SharePoint platform.
SharePoint Batch Edit
Overview:
A Bulk an Batch updating for list and document properties. The ribbon button makes it possible to update multiple items with a single click, supporting the common used column types like Managed Metadata, Enterprise Keyword, People Picker and many more.
Comments:
As the description says on the site, "wonder why this wasn't-in-the box". I've extended mine with ability to select a content type, if the list has more than one.
Starter Master Pages for SharePoint
Overview:
Starter Master Pages for SharePoint are a clean, commented starting point for creating your own SharePoint 2010 branding.
Comments:
It's done by Randy Drisgill... Need we say more?
SharePoint Manager
Overview:
The SharePoint Manager is a SharePoint object model explorer. It enables you to browse every site on the local farm and view every property.
Comments:
First and foremost! Indispensable, and a must have for every SharePoint developer and administrator.
SharePoint Developer Tools for Visual Studio 2010
and
CKS: Development Tools Edition
Overview:
This project extends the Visual Studio 2010 SharePoint Project system with advanced templates and tools.
Comments:
Attended an SharePoint evangelism by Wouter van Vugt a couple of years ago, and have been a keen user of CodeCounsel's Visual Studio tools ever since. Highly recommended!
SharePoint 2010 Fluent Ribbon API
Overview:
An API that lets you create Ribbon buttons for application pages and even contextual ribbon buttons for WebParts.
Comments:
Wow, if you're involved with custom application building for SharePoint as much as I am, this was a true blessing in disguise. Very easy to understand, and simple to use.
Documentation :
http://markeev.com/sharepoint/ribbon/
http://amarkeev.wordpress.com/2012/01/06/sharepoint-ribbon-togglebutton/
MiniCalendar Web Part
Overview:
A small web part to display links to events stored in a list (or document library) in a mini calendar (in month view mode).
Comments:
Really simple little webpart. Gave us a great head-start in creating a mini calendar that we could use in a masterpage.
SharePoint Property Bag Settings
Overview:
The Property Bag Settings can store any metadata as Key-Value pairs. This SharePoint administrative application page(s) provides a hierarchical configuration manager that can safely store and retrieve configuration settings at Farm, Web, Site Collection, Site and List level.
Comments:
Simple and clean interface lets you easily manage the Property Bag. Becomes very useful once you start building custom applications within the SharePoint platform.
SharePoint Batch Edit
Overview:
A Bulk an Batch updating for list and document properties. The ribbon button makes it possible to update multiple items with a single click, supporting the common used column types like Managed Metadata, Enterprise Keyword, People Picker and many more.
Comments:
As the description says on the site, "wonder why this wasn't-in-the box". I've extended mine with ability to select a content type, if the list has more than one.
Starter Master Pages for SharePoint
Overview:
Starter Master Pages for SharePoint are a clean, commented starting point for creating your own SharePoint 2010 branding.
Comments:
It's done by Randy Drisgill... Need we say more?
Tuesday, August 28, 2012
Programmatically create a SharePoint XsltListViewWebPart, with Cross-Site support
Since I've found a few very helpful pieces of code this week, including the SharePoint 2010 Batch Property Edit control, that my friends of Tam Tam generously uploaded to CodePlex, I though that I'll share the XsltListViewWebPart that I've recently built for a client of ours.
After Googling for days it seemed, I couldn't find a good example of a programmatically created XsltListViewWebPart. Below I'll share a few thoughts on what I did to get the control up and running. I've also added the functionality to display a "Add new item" link, the same way the OOTB SharePoint List WebPart does.
Although by no means perfect, I hope this does help someone in desperate need of answers. Remember, this is not a copy-paste exercise, but a gentle nudge in the right direction!
Public and Private properties
Nothing too special here. Give the user the ability to select a Web, List and View combination, plus the option to show or hide the "Add new item" link.
TIP: I've tried setting the XSL Link on a OOTB SharePoint list web part, but it doesn't seem to work!? Is it just me or does anyone else have issues with this? By rendering this custom XsltListViewWebPart and setting its XslLink property I could get the XslLink rendering working. But not the OOTB list web part?
UPDATE (01/10/2012) : The problem (bug if you will) with using the XSL Link on a List Part, is better explained over at Glyn Clough's blog. Just follow the these two links.
Overrides
The only real override to take note of, the the CreateChildControls method. Here I use a few helper functions to render all the plumbing necessary for the SP.UI.ModelDialog. This is needed to display the "Add new item" dialog. I've used a series of fixed values, but obviously you can make them all dynamic and have the user set them in your EditorPart.
Helper functions
I've included the SP.UI.ModelDialog helper functions. They're all but perfect (but works!), so feel free to dice and slice them according to your own needs. Again I've included them just to give you an idea of what I've done.
Private methods
So here comes the juicy bits. First its important to notice that the CreateXsltListViewer method runs with an elevated privilaged SPWeb and UnsafeUpdates set to true. I've created a series of extension methods to handle this for me. (If your'e a bit confused about this, see the helpful links section at the bottom of this page for more information.)
In order to create and render a cross-site list, you'll simply need to include the WebId, ListName, ListId and ViewGuid properties. Personally thought it might be a bit more complex, but that was it...
Since the toolbar itself becomes way too much of a problem in its complexity, I've decided not to display it. All the elements inside the list can still be served by its dropdown menu, but unfortunately not the Ribbon. If you're in dire need of this functionality, happy hunting, and do drop me a line if you've figured it out! ;-)
Conclusion
So there it is. Hope this will give you some ideas on your own version of a custom XsltListViewWebPart, that supports cross-site lookups, and have the ability to have custom XSL applied to it (see below).
Important and Useful Links:
Nothing too special here. Give the user the ability to select a Web, List and View combination, plus the option to show or hide the "Add new item" link.
// User can set the WebId public string WebId { get; set; } // User can set the ListId public string ListId { get; set; } // User can set the ViewId public string ViewId { get; set; } // User can set a custom XslFile path public string XslLink { get; set; } // User can choose to display the "Add item" link public bool HideAddLink { get; set; } // Check if all requirements are met to display the "Add item" link private bool HasValidButtonProperties { get { return base.HasWebIdAndListIdAndViewId && !this.HideAddLink; } } // Unique key generation private string OpenScriptedDialog { get { return string.Format("OpenXsltListViewerControl{0}", this.ClientID); } } // Use panel/updatepanel to add the XsltListViewWebPart protected UpdatePanel updatePanel; protected Panel panel;
OOTB not working? Aaaaaaargh! |
- Overriding the presentation of an XSLT List View Web Part
- XLV Bug: XSL Link Property and the ECB Menu
The only real override to take note of, the the CreateChildControls method. Here I use a few helper functions to render all the plumbing necessary for the SP.UI.ModelDialog. This is needed to display the "Add new item" dialog. I've used a series of fixed values, but obviously you can make them all dynamic and have the user set them in your EditorPart.
protected override void OnInit(EventArgs e) { // Init update panel updatePanel = new UpdatePanel(); updatePanel.ChildrenAsTriggers = true; updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional; updatePanel.ID = string.Format("updatePanel_{0}", this.ClientID); // Init panel panel = new Panel(); base.OnInit(e); } protected override void CreateChildControls() { // WebPart base ensures selected variables base.CreateChildControls(); // Register Scripts StringBuilder sb = new StringBuilder(); if (HasValidButtonProperties) { // Use helper function to inject javascript for the SP.UI.ModalDialog add functionality sb.Append(SPUtilities.GetModalDialogScript(this.ListId + "/NewForm.aspx", "New Item", 625, 525, this.OpenScriptedDialog)); } // Helper function to register script base.RegisterScript(base.UniqueScriptsKey, sb.ToString()); // Add the control to the controls collection, not the panel, // this will ensure that the "Add link" is shown at the bottom of the control. if (HasValidButtonProperties) { Use a helper function to render the "Add link" this.Controls.Add(new LiteralControl(SPUtilities.GetModalDialogAddButton(this.OpenScriptedDialog))); } } protected override void OnLoad(EventArgs e) { RefreshXsltListViewer(); base.OnLoad(e); }
I've included the SP.UI.ModelDialog helper functions. They're all but perfect (but works!), so feel free to dice and slice them according to your own needs. Again I've included them just to give you an idea of what I've done.
// Method injects javascript to handle the SP.UI.ModalDialog functions public static string GetModalDialogScript(string url, string title, int width, int height, string functionName) { var sb = new StringBuilder(); var _width = width > 0 ? width : 625; var _height = height > 0 ? height : 325; sb.Append(" "); return sb.ToString(); } // Method renders the 'Add new item link' public static string GetModalDialogAddButton(string addLinkKey) { return GetModalDialogAddButton(addLinkKey, string.Empty); } // Method renders the 'Add new item link' public static string GetModalDialogAddButton(string addLinkKey, string addLinkText) { if (string.IsNullOrEmpty(addLinkKey)) return string.Empty; if (string.IsNullOrEmpty(addLinkText)) addLinkText = "Add new item"; StringBuilder sb = new StringBuilder(); sb.Append("<table width='100%'>"); sb.Append(" <tr>"); sb.Append(" <td class='ms-partline'>"); sb.Append(" <img height='1' width='1' alt='' src='/_layouts/images/blank.gif'>"); sb.Append(" </td>"); sb.Append(" </tr>"); sb.Append(" <tr>"); sb.Append(" <td style='padding-bottom: 3px' class='ms-addnew'>"); sb.Append(" <span class='s4-clust' style='height:10px;width:10px;position:relative;display:inline-block;overflow:hidden;'>"); sb.Append(" <img style='left:-0px !important;top:-128px !important;position:absolute;' alt='' src='/_layouts/images/fgimg.png'>"); sb.AppendFormat(" </span> <a href='javascript:{0}()'>{1}</a>", addLinkKey, addLinkText); sb.Append(" </td>"); sb.Append(" </tr>"); sb.Append("</table>"); return sb.ToString(); }
So here comes the juicy bits. First its important to notice that the CreateXsltListViewer method runs with an elevated privilaged SPWeb and UnsafeUpdates set to true. I've created a series of extension methods to handle this for me. (If your'e a bit confused about this, see the helpful links section at the bottom of this page for more information.)
private void RefreshXsltListViewer() { // Run the CreateXsltListViewer method with an // elveated privilaged web, that also caters for // unsafe-updates. SPWeb web = SPContext.Current.Site.OpenWeb(WebId); web.UnsafeUpdate(CreateXsltListViewer); } private void CreateXsltListViewer(SPWeb web) { // Use helper function to get the SPList SPList list = SPUtilities.GetSPList(ListId); // Use list acquired from AllowUnsafeUpdates web SPList viewList = web.Lists[list.ID]; // Create the control XsltListViewWebPart xsltControl = CreateXsltListViewerInstance(viewList, ViewId); // Add control to the update panel if (xsltControl != null) { panel.Controls.Add(xsltControl); } else { panel.Controls.Add(new LiteralControl("There was an error creating the XsltListViewer.")); } } private XsltListViewWebPart CreateXsltListViewerInstance(SPList list, string viewName) { XsltListViewWebPart xsltListViewWebPart = new XsltListViewWebPart(); // First check if view acutally exists if (list.Views.Exists(viewName)) { SPView defaultView = list.Views[viewName]; xsltListViewWebPart.ID = "wpListView"; // We're displaying this WebPart inside a WebPart. Disable the // XsltListViewWebPart's own Title and ChromeType. xsltListViewWebPart.Title = string.Empty; xsltListViewWebPart.ChromeType = System.Web.UI.WebControls.WebParts.PartChromeType.None; // Set all these properties to allow for Cross-Site lookups xsltListViewWebPart.WebId = list.ParentWeb.ID; xsltListViewWebPart.ListName = list.ID.ToString("B").ToUpper(); xsltListViewWebPart.ListId = list.ID; xsltListViewWebPart.ViewGuid = defaultView.ID.ToString("B").ToUpper(); // If required, set the XSL file link if (!string.IsNullOrEmpty(XslLink)) { xsltListViewWebPart.XslLink = this.XslLink; } defaultView.Update(); // I never show the toolbar SetToolbarType(xsltListViewWebPart, "None"); } return xsltListViewWebPart; } public void SetToolbarType(XsltListViewWebPart lvwp, string viewType) { try { MethodInfo ensureViewMethod = lvwp.GetType().GetMethod("EnsureView", BindingFlags.Instance | BindingFlags.NonPublic); object[] ensureViewParams = { }; ensureViewMethod.Invoke(lvwp, ensureViewParams); FieldInfo viewFieldInfo = lvwp.GetType().GetField("view", BindingFlags.NonPublic | BindingFlags.Instance); SPView view = viewFieldInfo.GetValue(lvwp) as SPView; Type[] toolbarMethodParamTypes = { Type.GetType("System.String") }; MethodInfo setToolbarTypeMethod = view.GetType().GetMethod("SetToolbarType", BindingFlags.Instance | BindingFlags.NonPublic, null, toolbarMethodParamTypes, null); object[] setToolbarParam = { viewType }; //set the type here setToolbarTypeMethod.Invoke(view, setToolbarParam); view.Update(); } catch { } }
So there it is. Hope this will give you some ideas on your own version of a custom XsltListViewWebPart, that supports cross-site lookups, and have the ability to have custom XSL applied to it (see below).
Important and Useful Links:
- Elegant SPSite Elevation by Keith Dahlby
- Best practices for RunWithElevatedPrivileges on StackOverflow
- XSLTListViewWebpart in Sharepoint 2010 Uncovered by Sunil Singhal
- Cross-Site (Web) XsltListViewWebPart by Thomas Zepeda McMillan
- Programmatically enable Ajax Auto Refresh on a SharePoint XsltListViewWebPart by Brian Cartmel
Friday, August 24, 2012
401 Error (Unauthorized) When Accessing SharePoint Web Services
We've created application helper classes that accesses all SharePoint lists via the integrated web services (/_vti_bin/*.asmx). One of the miserable errors we sometimes get when moving through the deployment stages (Development, QA then Production) is that, even with administrative privileges, we still get a 401 Unauthorized error.
Our code goes something like this:
The way we fix this is by either specifying the host names or disabling the loopback check and EACH of the application servers. See this post for more details.
Important and Useful Links:
Our code goes something like this:
protected WebServiceSPLists.Lists _lists; const string CClassName = "DataContextSharePointList"; private string GetListNameByTitle(Connection connection, string listTitle) { const string CProcName = "GetListNameByTitle"; string response = ""; XmlNode listsNode; XmlNode node; try { this.InitConnection(connection); _lists.Credentials = ((Connection)connection).Credentials; _lists.Url = ((Connection)connection).SiteUrl + "/_vti_bin/lists.asmx"; Trace.Write(CClassName, CProcName, "GetListCollection"); listsNode = _lists.GetListCollection(); // <-- 401 error occurs here Trace.Write(CClassName, CProcName, "GetListCollection OK"); // Code omitted // } catch (Exception ex) { Trace.Write(CClassName, CProcName, "Error: " + ex.Message); } }
The way we fix this is by either specifying the host names or disabling the loopback check and EACH of the application servers. See this post for more details.
Important and Useful Links:
- You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version by Microsoft Support
- How to resolve (401) Unauthorized error for SharePoint web service call from Infopath Webbased form? by Harry Chen
Subscribe to:
Posts (Atom)