Fair warning: The setup for this question is long, so be patient and stay with me.
I have two features in my solution package. The first is a set of site fields and content types; let's call this one Feature A. Among the fields are a field of type "TaxonomyFieldType" and an associate field of type "Note" (an explanation of the note field).
<Elements ...>
<Field ID="{956a1078-ec35-4c04-83c4-0a3742119496}"
Name="TaxonomyTextField"
Type="Note" DisplayName="Tags_0"
ShowInViewForms="FALSE"
Required="FALSE"
Group="MyGroup"
Hidden="TRUE"/>
<Field ID="{92BC866B-0415-45F0-B431-D4DF69C421CC}"
Name="Tags"
DisplayName="Custom Tags"
Type="TaxonomyFieldType"
ShowField="Term1033"
Required="FALSE"
Group="MyGroup"
>
<Customization>
<ArrayOfProperty>
<Property>
<Name>IsPathRendered</Name>
<Value xmlns:q7="http://www.w3.org/2001/XMLSchema" p4:t开发者_StackOverflowype="q7:boolean" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">true</Value>
</Property>
<Property>
<Name>TextField</Name>
<Value xmlns:q6="http://www.w3.org/2001/XMLSchema" p4:type="q6:string" xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{956a1078-ec35-4c04-83c4-0a3742119496}</Value>
</Property>
</ArrayOfProperty>
</Customization>
</Field>
</Elements>
and
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<!-- Parent ContentType: Item (0x01) -->
<ContentType ID="0x0100b61c774f4c0e4a89bf230cbb44cd4f75"
Name="MyContent"
Group="MyGroup"
Description="Description of My Content Type"
Inherits="FALSE"
Overwrite="TRUE"
Version="0">
<FieldRefs>
<FieldRef ID="{52578fc3-1f01-4f4d-b016-94ccbcf428cf}" DisplayName="Comments" Name="Comments" Required="FALSE"/>
<FieldRef ID="{956a1078-ec35-4c04-83c4-0a3742119496}" Name="TimeTrackerTaxonomyTextField"/>
<FieldRef ID="{92BC866B-0415-45F0-B431-D4DF69C421CC}" DisplayName="Tags" Name="Tags" Required="FALSE"/>
</FieldRefs>
</ContentType>
</Elements>
In the feature receiver for the first feature (let's call it the Feature A), I programmatically retrieve this TaxonomyField
and
ensure that it is configured to retrieve terms out of a predetermined term set:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb web = GetWebObj(properties.Feature.Parent);
Guid fieldId = new Guid("92BC866B-0415-45F0-B431-D4DF69C421CC");
TaxonomyField field = web.Fields[fieldId] as TaxonomyField;
string groupName = properties.Feature.Properties["TaxonomyGroupName"].Value;
string termSetName = properties.Feature.Properties["TermSetName"].Value;
DiagnosticService logger = DiagnosticService.Local;
TermSet set = null;
TaxonomySession session = new TaxonomySession(web.Site);
TermSetCollection termSets = session.GetTermSets(termSetName, System.Threading.Thread.CurrentThread.CurrentUICulture.LCID);
if (termSets == null || termSets.Count == 0)
{
logger.WriteTrace(1, logger[CategoryId.Deployment], TraceSeverity.Medium,
"Activity Tags term set not found. Ensuring '{0}' group and '{1}' term set.", groupName, termSetName);
// create the term set in the default store
var store = session.DefaultSiteCollectionTermStore;
var group = store.EnsureGroup(groupName);
set = group.EnsureTermSet(termSetName);
store.CommitAll();
logger.WriteTrace(1, logger[CategoryId.Provisioning], TraceSeverity.Verbose, "created taxonomy group '{0}' and term set '{1}'", group, set);
}
else
{
logger.WriteTrace(1, logger[CategoryId.Deployment], TraceSeverity.Verbose, "term set found.");
// need to make sure we grab the one in the right group, or it might be someone else's term set.
foreach (var termSet in termSets)
{
if (String.Equals(termSet.Group.Name,groupName))
{
if (set == null)
{
set = termSet;
}
else
{
logger.WriteTrace(1, logger[CategoryId.Deployment], TraceSeverity.Unexpected,
"Multiple term sets named '{0}' found in more than one taxonomy group.", termSetName);
throw new SPException(String.Format("Multiple term sets named '{0}' found in more than one taxonomy group. "+
"Was there a previous installation that was not removed properly?", termSetName));
}
}
}
if (set == null)
{
// term set found, but in an unrecognized group. leave it alone and do like above
logger.WriteTrace(1, logger[CategoryId.Deployment], TraceSeverity.Verbose,
"Term set '{0}' found, but in unrecognized group. Provisioning new group and term set as configured.", termSetName);
var store = session.DefaultSiteCollectionTermStore;
var group = store.EnsureGroup(groupName);
set = group.EnsureTermSet(termSetName);
store.CommitAll();
logger.WriteTrace(1, logger[CategoryId.Provisioning], TraceSeverity.Verbose, "created taxonomy group '{0}' and term set '{1}'", group, set);
}
}
// set termSets to the newly created term set
field.SspId = set.TermStore.Id;
field.TermSetId = set.Id;
field.TargetTemplate = String.Empty;
field.AnchorId = Guid.Empty;
field.Open = true;
field.AllowMultipleValues = true;
field.Update();
}
The second feature contains list templates and instances, one of which uses the above content type; let's call this feature Feature B.
Here's the list schema for the list that blows up when provisioned (ListInstance
element not shown):
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:ows="Microsoft SharePoint" Title="My List" FolderCreation="FALSE" Direction="$Resources:Direction;" Url="Lists/MyList" BaseType="0" xmlns="http://schemas.microsoft.com/sharepoint/">
<MetaData>
<ContentTypes>
<ContentTypeRef ID="0x0100b61c774f4c0e4a89bf230cbb44cd4f75"></ContentTypeRef>
</ContentTypes>
<Fields>
<Field ID="{956a1078-ec35-4c04-83c4-0a3742119496}" Name="TaxonomyTextField" Type="Note"/>
<Field ID="{92bc866b-0415-45f0-b431-d4df69c421cc}" Name="Tags" Type="TaxonomyFieldType"/>
<Field ID="{52578FC3-1F01-4f4d-B016-94CCBCF428CF}" Name="_Comments" Type="Note"/>
</Fields>
<Views>
<View BaseViewID="1" Type="HTML" WebPartZoneID="Main" DisplayName="$Resources:core,objectiv_schema_mwsidcamlidC24;" DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/generic.png" Url="AllItems.aspx">
<Toolbar Type="Standard" />
<XslLink Default="TRUE">main.xsl</XslLink>
<RowLimit Paged="TRUE">30</RowLimit>
<ViewFields>
<!-- <FieldRef Name="Tags"></FieldRef> -->
<FieldRef Name="_Comments"></FieldRef>
</ViewFields>
<Query>
<OrderBy>
<FieldRef Name="ID">
</FieldRef>
</OrderBy>
</Query>
<ParameterBindings>
<ParameterBinding Name="NoAnnouncements" Location="Resource(wss,noXinviewofY_LIST)" />
<ParameterBinding Name="NoAnnouncementsHowTo" Location="Resource(wss,noXinviewofY_DEFAULT)" />
</ParameterBindings>
</View>
</Views>
<Forms>
<Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="EditForm" Url="EditForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
<Form Type="NewForm" Url="NewForm.aspx" SetupPath="pages\form.aspx" WebPartZoneID="Main" />
</Forms>
</MetaData>
</List>
After the solution deploys, I am able to activate Feature A without issue. The site columns and content types are created. When I attempt to activate Feature B, the feature activation call stack blows up and results in an error page with the following stack trace:
[COMException (0x80004005): Cannot complete this action. Please try again.] Microsoft.SharePoint.Library.SPRequestInternalClass.UpdateField(String bstrUrl, String bstrListName, String bstrXML) +0 Microsoft.SharePoint.Library.SPRequest.UpdateField(String bstrUrl, String bstrListName, String bstrXML) +134 [SPException: Cannot complete this action. Please try again.] Microsoft.SharePoint.Administration.SPElementDefinitionCollection.ProvisionListInstances(SPFeaturePropertyCollection props, SPSite site, SPWeb web, Boolean fForce) +23649702 Microsoft.SharePoint.Administration.SPElementDefinitionCollection.ProvisionElements(SPFeaturePropertyCollection props, SPWebApplication webapp, SPSite site, SPWeb web, Boolean fForce) +197 Microsoft.SharePoint.SPFeature.Activate(SPSite siteParent, SPWeb webParent, SPFeaturePropertyCollection props, Boolean fForce) +25437263 Microsoft.SharePoint.SPFeatureCollection.AddInternal(SPFeatureDefinition featdef, Version version, SPFeaturePropertyCollection properties, Boolean force, Boolean fMarkOnly) +27496735 Microsoft.SharePoint.SPFeatureCollection.AddInternalWithName(Guid featureId, String featureName, Version version, SPFeaturePropertyCollection properties, Boolean force, Boolean fMarkOnly, SPFeatureDefinitionScope featdefScope) +150 Microsoft.SharePoint.SPFeatureCollection.Add(Guid featureId, Boolean force, SPFeatureDefinitionScope featdefScope) +83 Microsoft.SharePoint.WebControls.FeatureActivator.ActivateFeature(Guid featid, SPFeatureDefinitionScope featdefScope) +699 Microsoft.SharePoint.WebControls.FeatureActivatorItem.BtnActivateFeature_Click(Object objSender, EventArgs evtargs) +140 System.Web.UI.WebControls.Button.OnClick(EventArgs e) +115 System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +140 System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +29 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2981
I'm fairly certain that there's something wrong with the way I'm configuring the TaxonomyField
in Feature A; its association with a list instance at provisioning time is the cause of the error (I've determined this by commenting out pieces and deploying over and over). There seems to be very little documentation, or even blogger experience, with TaxonomyFields
and provisioning them in list instances, so I'm a bit at a loss. Does someone have any idea what's going wrong?
I followed How to provision SharePoint 2010 Managed Metadata columns by Wictor Wilén and was able to get something similar working (be sure to also make the modification from this comment).
I ended up opening a support incident with microsoft to get this figured out. Eventually, their service rep tracked it down to setting the property DisallowContentTypes="FALSE"
on the list template and EnableContentTypes="TRUE"
on the list schema. That solved my provisioning issue.
However, I still have an issue with being able to actually create items on the newly provisioned lists, having to do with the hidden text field that must accompany the taxonomy field (grrr). I have provisioned a note field in the site, and I have referenced it in my list template, and I have set the TextField property to the ID of this note field in both the site column definition and in the field definition.
Wictor makes mention of that (if I recall from reading his post), but there's more here: http://www.sharepointconfig.com/2011/03/the-complete-guide-to-provisioning-sharepoint-2010-managed-metadata-fields/
I am currently stuck on the exception that's thrown at item creation time, saying:
Failed to get value of the "Tags" column from the "Managed Metadata" field type control. See details in log. Exception message: Invalid field name. {956a1078-ec35-4c04-83c4-0a3742119496} http://server/sites/mysite /sites/mysite/Lists/Entries
I have been getting the same error when activating a sandboxed feature that contains ListInstance elements for a sandboxed custom list template that contains a custom ContentTypeRef. The list is created, but the feature errors out on creation. Further, the list contains a auto-generated Content Type rather than the one specified in the list definition. If you keep attempting to activate the feature until all lists are created, the feature will finally activate.
Further, I have noticed that I cannot update sandboxed custom field properties from SandBoxed solutions in MOSS 2010. I get the same type of error indicating that it cannot complete the action when SPListItem.UpdateField is called on a custom field defined via XML in a sandboxed solution.
I am now concluding that sandboxed field updates are not supported in sandboxed solutions for MOSS 2010.
Instead of programmatically updating fields from sandboxed custom content types, you should completely define the field completely in the XML field definition and list template field XML element.
The way to get your list to use your custom content type and activate without causing the "Cannot Complete This Action" error is as follows:
1) Use a Default ContentTypeRef in your list definition, instead of the custom one you created.
https://msdn.microsoft.com/en-us/library/office/ms452896(v=office.14).aspx
For example: 0x01 Default Item Content Type
0x0101 Default Document Content Type
In the List Schema.xml file, if your Content Type is based off the Default Item Content Type, you'd change it to:
<ContentTypes>
<ContentTypeRef ID="0x01"></ContentTypeRef>
</ContentTypes>
2) Add a Feature Activated Event Receiver to the feature that runs code to configure the list's content type.
I wrote a function that basically sets the Content Type for list and gets rid of any other content types associated with the list. Your feature activation event receiver can run this function and set the content types for your lists to the one it should be. The function below assumes your content types are uniquely named. You can add a check for the content type group name, as well, if needed.
public static string ConfigureCustomListForCustomContentType(SPWeb web, string strListName, string strCustomContentTypeName)
{
StringBuilder sbOutput = new StringBuilder();
try
{
SPList customlist = web.Lists[strListName];
SPContentType CustomContentType = null;
//Validate Content Types
//1) Find the Content Type in the Content Type list
foreach (SPContentType spct in web.Site.RootWeb.ContentTypes)
{
if (spct.Name == strCustomContentTypeName)
{
CustomContentType = spct;
break;
}
}
if (CustomContentType == null)
{
sbOutput.Append("<div class='error'>Unable to find custom content type named " + strCustomContentTypeName +".</div>");
return sbOutput.ToString();
}
sbOutput.Append("Found content Type "+CustomContentType.Name+"...<br />");
Boolean bFoundContentType = false;
customlist.ContentTypesEnabled = true;
List<SPContentTypeId> RemoveContentTypeList = new List<SPContentTypeId>();
//Remove all other content types
foreach (SPContentType spct in customlist.ContentTypes)
{
if (spct.Name == strCustomContentTypeName)
{
bFoundContentType = true;
}
else
{
RemoveContentTypeList.Add(spct.Id);
}
}
if (!bFoundContentType)
{
sbOutput.Append("Adding [" + strCustomContentTypeName + "] to List " + customlist.Title + "<br />");
customlist.ContentTypes.Add(CustomContentType);
}
else
{
sbOutput.Append("[" + strCustomContentTypeName + "] already in List " + customlist.Title + ".<br />");
}
for (int i = 0; i < RemoveContentTypeList.Count; i++)
{
sbOutput.Append("Removing extra content type: " + customlist.ContentTypes[RemoveContentTypeList[i]].Name + "<br />");
customlist.ContentTypes[RemoveContentTypeList[i]].Delete();
}
}
catch (Exception ex)
{
sbOutput.Append("<div class='error'>Error occurred configuring "+strListName+": " + ex.ToString() + "<br /></div>");
}
return sbOutput.ToString();
}
That should allow you to get your list instantiated and set the content type to your custom content type.
If you list is based off the Event Type 0x0102, then the above function may not work without causing an error in a sandboxed solution.
For the Event Type, I used the default Event Content Type and ran code to customize the list (adding columns) as needed.
精彩评论