March 11, 2011 - 18:17, by Steven Van de Craen
Categories: Debugging, SharePoint 2007, SharePoint 2010
Each SharePoint List can be altered with a custom DispForm.aspx, EditForm.aspx and NewForm.aspx to display, edit or create list items and metadata. This post is about restoring them to a working point.
Botched Forms
So one of these forms are edited to a loss state or deleted altogether. If this is the case it will have some tangible effects on the List display:
The above screen shows a botched Display Form where the Item Url shows http://moss/?ID=1
Behind the scene
All three forms are based on the same template {SharePointRoot}\TEMPLATE\Pages\form.aspx and have a List Form Web Part in the “Main” WebPartZone with specific settings to connect to the list and which function they have. Also, the ID property of the List Form Web Part must match the value of the __WebPartId attribute.
SharePoint 2007
|
SharePoint 2010 |
|
|
Recovery
In a perfect world you can reset the edited form to the Site Definition or restore the it from the Recycle Bin, however this is often not the case.
Here’s a quick reference to the Microsoft.SharePoint.PAGETYPE enum:
Member name
|
Description
|
PAGE_INVALID
|
Not used. Value= -1.
|
PAGE_DEFAULTVIEW
|
Default view. Value=0.
|
PAGE_NORMALVIEW
|
Normal view. Value=1.
|
PAGE_DIALOGVIEW
|
File dialog box view. Value=2.
|
PAGE_VIEW
|
View, including both default view and normal view. Value=3.
|
PAGE_DISPLAYFORM
|
Display form for list items. Value=4.
|
PAGE_DISPLAYFORMDIALOG
|
Display form for a file dialog box. Value=5.
|
PAGE_EDITFORM
|
Edit form for list items. Value=6.
|
PAGE_EDITFORMDIALOG
|
Edit form for a file dialog box. Value=7.
|
PAGE_NEWFORM
|
New form for list items. Value=8.
|
PAGE_NEWFORMDIALOG
|
New form for a file dialog box. Value=9.
|
PAGE_SOLUTIONFORM
|
Solution form. Value=10.
|
PAGE_MAXITEMS
|
Not used. Value=11.
|
(http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.pagetype.aspx)
I’ve highlighted the three related to this matter (applies to both SharePoint 2007 and SharePoint 2010).
Manually
1. It’s easiest if you take a corresponding form from another SharePoint List in the site and export it (SharePoint Designer) or just copy it to a text editor. This way, most of the settings of the List Form Web Part are already correct
2. Generate a new GUID and fill that in for the __WebPartId and properties of the List Form Web Part markup. It has to be the same GUID but the formatting is different. See the above screens for samples
3. Update the property with the List ID you’re targeting
4. Verify all other properties of the List Form Web Part and save the file as either DispForm.aspx, EditForm.aspx or NewForm.aspx
5. Import (SharePoint Designer) the page (or paste contents from the text editor)
Scripted
I had a botched list in SharePoint 2007 and I wrote a Console Application for it. It takes the markup for a Form and I’ve replaced some values with placeholders already. It connects to the List and replaces the placeholders with real values, then it saves the file to SharePoint.
1 string listUrl = "http://moss/Lists/Sample List"; 2 using (SPSite site = new SPSite(listUrl)) 3 { 4 using (SPWeb web = site.OpenWeb()) 5 { 6 SPList list = web.GetList(listUrl); 7 RestoreListForm(list, PAGETYPE.PAGE_DISPLAYFORM); 8 //RestoreListForm(list, PAGETYPE.PAGE_EDITFORM); 9 //RestoreListForm(list, PAGETYPE.PAGE_NEWFORM); 10 } 11 }
1 private static void RestoreListForm(SPList list, PAGETYPE ptype) 2 { 3 SPWeb web = list.ParentWeb; 4 5 // Create Form File 6 string formFilename = null; 7 SPControlMode formMode = SPControlMode.Invalid; 8 SPFileCollection files = list.RootFolder.Files; 9 Guid wpId = Guid.NewGuid(); 10 CalcFormInfo(ptype, out formFilename, out formMode); 11 12 byte[] formContents = Encoding.ASCII.GetBytes(String.Format(pagecontent, list.Title, list.ID.ToString("B"), formMode, (int)ptype, wpId.ToString("B"), String.Concat("g_", wpId.ToString("D").Replace("-", "_")).ToLower())); 13 14 try 15 { 16 if (files[formFilename].Exists) 17 files[formFilename].Delete(); 18 } 19 catch { } 20 21 SPFile formFile = files.Add(formFilename, formContents, true); 22 } 23 24 private static void CalcFormInfo(PAGETYPE type, out string fileName, out SPControlMode mode) 25 { 26 switch (type) 27 { 28 case PAGETYPE.PAGE_DISPLAYFORM: 29 fileName = "DispForm.aspx"; 30 mode = SPControlMode.Display; 31 break; 32 case PAGETYPE.PAGE_EDITFORM: 33 fileName = "EditForm.aspx"; 34 mode = SPControlMode.Edit; 35 break; 36 case PAGETYPE.PAGE_NEWFORM: 37 fileName = "NewForm.aspx"; 38 mode = SPControlMode.New; 39 break; 40 default: 41 fileName = null; 42 mode = SPControlMode.Invalid; 43 break; 44 } 45 }
Find the complete code here: Program.cs. Note that there’s a different page markup and additional properties for SharePoint 2010 and the code needs to be adapted to that.
There’s no magic in the code above, just automating some of the manual steps in a quick fix script.
Conclusion
As a general practice, don’t directly modify the default forms, rather create copies or blank forms and update the List or Content Type Settings (through SharePoint Designer or programmatically) to point to your custom forms.
If you do have a botched list, hopefully the above information can be of help.
March 9, 2011 - 13:16, by Steven Van de Craen
Categories: InfoPath, Forms Server, SharePoint 2010
I’m currently testing our InfoPath Web Forms for an upcoming migration to InfoPath 2010 and SharePoint 2010 and have come up with a reproducible issue.
Issue
InfoPath Web Forms cannot use values from other Data Sources as parameters in a Submit Data Connection. When the form is submitted a warning “There has been an error while processing the form.” shows. From that point on the only working option is Exit Form.
- It does not affect data connections to XML files, only when the query data connections connects to a Web Service
- It does not affect the offline version of the form (InfoPath Filler 2010)
- Tested in both December 2010 Cumulative Update and February 2011 Cumulative Update for SharePoint 2010
Set up
- Create a new InfoPath Web Form
- Add a Query Data Connection to retrieve data from a Web Service
-
Add a Submit Data Connection to submit data to a Web Service. Configure at least 1 parameter (if more then one) to link to a value from the above Query Data Connection
- Configure the form to submit using the above connection
- Publish the form to SharePoint 2010 and open a new form in the browser
Workaround
Store the value of the query data connection in an intermediary field in the form (“Main” Data Connection). Configure the Submit Data Connection to use the intermediary field value as parameter for the Submit. You can store the value as late as possible using Rules in Submit Options or your own submit buttons.
March 8, 2011 - 10:28, by Steven Van de Craen
Categories: SharePoint 2007, SharePoint 2010, InfoPath, Forms Server
InfoPath Form Template URL
We have a solution that makes extensive use of online and offline InfoPath forms in a MOSS 2007 environment. An upgrade to SharePoint 2010 is due in a few months, including a redesign of Managed Paths and Site Collection ‘hierarchy’ having a direct effect on the URL of our Site Collection.
I thought this meant a lot of work for converting the thousands of InfoPath XML Forms by script, because they all contained an absolute URL to the InfoPath Form Template.
However as it seems after restoring the Site Collection all forms automagically had a corrected absolute URL to their template !!
xml version="1.0"?>
mso-infoPathSolution productVersion="12.0.0" PIVersion="1.0.0.0" href="https://public.contoso.com/sites/csapp/FormServerTemplates/LeaveRequest.xsn" name="urn:schemas-microsoft-com:office:infopath:LeaveRequest:-myXSD-2009-05-29T07-59-01" solutionVersion="1.0.0.422" ?>
mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<my:Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution"
xmlns:tns="http://public.contoso.com/webservices"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2009-05-29T07:59:01"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xml:lang="nl-be">
my:Data>
Default Alternate Access Mapping
What happens is that when you restore a Site Collection backup, it looks up the Web Application by the URL you specified as a parameter. From that Web Application, it takes the Alternate Mapping configured on the "Default” Zone and uses that URL to construct the absolute URL in the InfoPath XMLs.
Only at restore time
This mechanism only seems to trigger when the restore takes place. Changing the Default AAM afterwards to a different URL has no effect. You could temporarily change the AAM for the restore operation and then set it back if you want. Beats any script iterating and updating all Form XMLs any day.
Most of the times you expect the worst from SharePoint, but then it catches you by surprise and brings a huge smile to the face :)
March 6, 2011 - 07:41, by Steven Van de Craen
Categories: SharePoint 2007, SharePoint 2010, Sandbox Solutions
Property Bag
SPWeb exposes two ways of interacting with the property bag:
-
SPWeb.Properties exposes a Microsoft.SharePoint.Utilities.SPPropertyBag
-
SPWeb.AllProperties exposes a System.Collections.Hashtable
The former is considered legacy, also it stores the Key value as lowercase, while the latter keeps the casing of Key and Value intact.
Here’s a code sample:
1 string url = "http://intranet"; 2 3 using (SPSite site = new SPSite(url)) 4 { 5 SPWeb w = site.OpenWeb(); 6 w.AllProperties.Add("First Key", "First Value"); 7 w.Update(); 8 9 SPPropertyBag p = w.Properties; 10 p.Add("Second Key", "Second Value"); 11 p.Update(); 12 }
You can see the properties for a web using SharePoint Designer:
SharePoint 2007:
SharePoint 2010:
Case matters
When you configure a Search Center in the Site Collection Settings, a property named “SRCH_ENH_FTR_URL” stores the location to the Search Center. If this property isn’t entirely uppercase, it won’t be picked up correctly.
Sandbox Solutions
So what about Sandbox Solutions in SharePoint 2010 ?
- SPWeb.AllProperties can only be read from. Updating it doesn’t throw any exceptions but has no effects either.
- SPWeb.Properties cannot be used and throws a TypeLoadException:
March 5, 2011 - 12:40, by Steven Van de Craen
Categories: Content Types, SharePoint 2010
In my post yesterday I raised the question about Content Type syndication using the Content Type Hub mechanism, and how this would work together with Lookup Fields, since they don’t support crossing Site Collection boundaries.
Another question is in regard to the “challenges” of using OOXML (docx, dotx, …) templates with Content Types: Office 2007 document templates and Content Types in SharePoint – lessons learned. The main question was whether or not the Content Type Hub would improve the situation.
Configuration
Configure Content Type Syndication
You can follow this quick rundown on the Content Type Hub by Wictor Wilén:
Plan your SharePoint 2010 Content Type Hub carefully
I like the idea of creating a dedicated Site Collection for managing your Content Types.
The Hub: define Content Types and Publishing Settings
I’ve created a Content Type called “Specific Document X” with the following fields:
- a Lookup to the Tasks list
- an Enterprise Keywords field
- a Person field
- a text field called “Simple Field” with a default value CTHUB for the Hub
Next, go into the “Manage publishing for this content type” option of the Content Type settings and Publish the Content Type. See the aforementioned guide on how to make run the Content Type Subscriber Job immediately so you don’t have to wait for results.
The syndication occurs for all Site Collections in all Web Applications configured with the specific Managed Metadata Service.
The Subscriber
In the subscribing Site Collection the Content Type is added after the Subscriber Job has run. Most of the settings are locked down because they’re maintained in the Hub.
You can however go to Site Columns and modify the columns there. Not sure if this is supported
Experiences
Lookup Fields
Lookup Fields are copied if the Subscriber has the Lookup List defined as well. The fields are connected to the list on the Subscriber as expected, since the Site Collection boundary on Lookup Fields still exists.
If the Lookup List does not exist the Field will not be present on the Subscriber. The error log on the Hub will mention this:
Field default values
The “Simple Field” had a default value, which is of course pushed to all Subscribers. You can change the value on the Subscriber if you want, but if the Content Type is Republished in the Hub, the changes will be overwritten.
Document template “challenges”
Once a document is added to SharePoint, a ‘snapshot’ of the Content Type info is inserted as Custom XML Part (OOXML). This XML updates when the Content Type is changed, but it doesn’t when a referenced field changes (like the default value).
I had little hopes for improvement, as this is not really an issue of Content Types but more of the field update mechanism and/or OOXML inner workings.
A quick test with the “Simple Field” proved that this issue still exists in SharePoint 2010, even when changing the default value in the Hub itself, those changes aren’t pushed to the template. So it would definitely not be pushed to the Subscribers. The test with dotx rather than docx didn’t change the outcome.
So in these scenario’s a custom fix up script is still required to force the document template to resync it’s information from SharePoint.
Finally
I’m glad I got an answer to some questions I’ve been having with SharePoint 2010 and the Content Type Hub. I didn’t know what it would bring extra to the table in a SharePoint 2010 environment, but now I have a much clearer image. There’s a lot of potential in the Hub, but as always requires good planning and troubleshooting skills :)
March 4, 2011 - 17:23, by Steven Van de Craen
Categories: .NET, SharePoint 2007, SharePoint 2010, Custom Field Types
Custom Field Types are a rather advanced topic, but very powerful as well. They allow for real integration of custom components inside standard List and Library rendering. (See my other posts on Custom Field Types)
There are some things you can run into. Especially if you have custom Lookup Field Types like Cascading Lookup or Connected Lookup. Here’s what I’ve learned from a recent issue migration one from 2007 to 2010.
Custom Properties
Storing additional information in your Custom Field Types was always a real pain. The mechanism had serious flaws and it required some really dirty programming to work with. So instead I store my custom properties the same way SharePoint does. The involved API is internal but can be exposed through reflection.
I’m adding the this ‘reimplementation’ to every Custom Field Type of mine. When providing a null value to SetCustomProperty, the attribute is removed from the field XML.
1 #region Reimplementation of Get/Set Custom Property 2 public new void SetCustomProperty(string propertyName, object propertyValue) 3 { 4 Type type = typeof(SPField); 5 if (propertyValue != null) 6 { 7 MethodInfo set = type.GetMethod("SetFieldAttributeValue", BindingFlags.NonPublic | BindingFlags.Instance); 8 set.Invoke(this, new object[] { propertyName, propertyValue.ToString() }); 9 } 10 else 11 { 12 MethodInfo remove = type.GetMethod("RemoveFieldAttributeValue", BindingFlags.NonPublic | BindingFlags.Instance); 13 remove.Invoke(this, new object[] { propertyName }); 14 } 15 } 16 public new string GetCustomProperty(string propertyName) 17 { 18 Type type = typeof(SPField); 19 MethodInfo getField = type.GetMethod("GetFieldAttributeValue", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(String) }, null); 20 object o = getField.Invoke(this, new object[] { propertyName }); 21 return o as String; 22 } 23 #endregion
Now there’s also no more need to predefine custom properties in the FLDTYPES_*.xml either. This applies to any type of Custom Field Type.
AllowMultipleValues
You might see in some cases that when you set the value of SPFieldLookup.AllowMultipleValues to true, your custom field is reverted to a standard Lookup field.
If this is the case, you could override that Property and implement it accordingly.
1 private bool _allowMulti = false; 2 public override bool AllowMultipleValues 3 { 4 get 5 { 6 bool.TryParse(GetCustomProperty("AllowMultipleValues"), out _allowMulti); 7 return _allowMulti; 8 } 9 set 10 { 11 if (this.AllowMultipleValues != value) 12 { 13 _allowMulti = value; 14 SetCustomProperty("AllowMultipleValues", _allowMulti.ToString()); 15 if (value) 16 { 17 SetCustomProperty("Mult", "TRUE"); 18 SetCustomProperty("Sortable", "FALSE"); 19 } 20 else 21 { 22 SetCustomProperty("Mult", "FALSE"); 23 SetCustomProperty("Sortable", "TRUE"); 24 } 25 } 26 } 27 }
This applies to custom Multi Lookup Field Types.
Note: could not reproduce the issue with a new custom lookup field type in a clean SharePoint 2010 environment…
AllowBaseTypeRendering
We’ve seen this issue in a recent migration from SharePoint 2007 to SharePoint 2010 for a custom developed Connected Lookup Field (Single and Multi Valued). The field was ported from SharePoint 2007 with the following FLDTYPES_*.xml configuration:
The AllowBaseTypeRendering attribute will default to the rendering of the Base Type (in this case Lookup) when the Custom Field Type cannot be determined.
This had the weird effect that these fields would not render or store *any* value selected in a Multi Valued Field of this type. This behaviour might only be occuring when there’s a custom RenderPattern defined for the Custom Field Type.
In either way we had to remove the attribute for SharePoint 2010, since we really required the RenderPattern. Problem solved for new fields, but there was still an issue for existing fields (existing SharePoint 2007 content migrated to SharePoint 2010).
For those existing fields, we wrote a script that would remove the attribute causing the problem:
1 SPList list = web.GetList(listUrl); 2 SPField field = list.Fields["MyMULTI"]; 3 MyField clf = field as MyField; 4 clf.SetCustomProperty("BaseRenderingType", null); 5 clf.Update();
This is where the custom SetCustomProperty came in handy for removing the BaseRenderingType attribute (added because the Custom Field Type had the AllowBaseTypeRendering attribute).
In our case we knew exactly where the fields were that had our Custom Field Type, but it might be more difficult to write a generic script to find all fields based on the Custom Field Type…
Note: could not reproduce the issue with a new custom lookup field type in a clean SharePoint 2010 environment…
Enforce unique values
You can force unique values on fields in SharePoint 2010, however is is not compatible when storing multiple values.
If this is the case your custom “edit field control” should disable the option to “Enforce unique values” via JavaScript.
Oh, and for those wondering: you still can’t do cross site collection lookups with a lookup field. Which makes me wonder how that works in regard to the Content Type Hub.