May 2008 - Posts
The Content Query Web Part (CQWP) in MOSS is one of the popular out-of-the-box Publishing components which is used in content management sites, allowing you to get results from different sources.
But sometimes out of the box functionality is not enough to meet your requirements, and you need to customize CQWP. There are several resources describing how to do this - 1, 2, 3, 4
The most important features which are missed in CQWP are:
- overriding query to use custom CAML
- enabling web-part connections
To implement these functionality you need to subclass ContentByQueryWebPart class and override several methods.
Below I will describe what exactly need to be done to achieve desired behaviour.
Query Override
To have custom CAML query in your Web part you need to override CreateChildControls base method. You can find samples how to do this in the references above, but it's done so implicitly, that you can easily spend hours to find out why you code doesn't work and gives you different errors
The crucial part is in the way sending the query to the base class within overrided CreateChildControls
1: protected override void CreateChildControls()
2: { 3: this.QueryOverride = customQueryString;
4:
5: base.CreateChildControls();
6:
7: QueryOverride = string.Empty;
8:
9: CommonViewFields = string.Empty;
10: }
First, you need to set your custom query to the "this.QueryOverride" to inform base class about query - line 3
Second, call base CreateChildControls() allowing your query being executed - line 5
Third and last - CLEAN query and view fields properties - line 7,9. Without this step you will get errors when open your Web Part in edit mode
Enabling Connection
The Content Query WP doesn't accept any connections by default. You need to implement this too. Everything you need to to is just add ConnectionConsumer attribute. But trick in setting the right attribute which is used in Parameter dialog box, when you connect two web-parts.
1: [ConnectionConsumer("Another WebPart", "IFilterValues", AllowsMultipleConnections = true)] 2: public void SetConnectionInterface(IFilterValues filterProvider)
3: { 4: if (filterProvider != null)
5: { 6: // save provider with values
7: providerValues = filterProvider.ParameterValues;
8: _filterProviders.Add(filterProvider); // variable declaration is List<IFilterValues> _filterProviders = new List<IFilterValues>();
9: List<ConsumerParameter> parameters = new List<ConsumerParameter>();
10:
11: // add params
12: parameters.Add(new ConsumerParameter("param1", 13: ConsumerParameterCapabilities.SupportsSingleValue |
14: ConsumerParameterCapabilities.SupportsEmptyValue));
15: parameters.Add(new ConsumerParameter("param2", 16: ConsumerParameterCapabilities.SupportsMultipleValues |
17: ConsumerParameterCapabilities.SupportsAllValue |
18: ConsumerParameterCapabilities.SupportsEmptyValue));
19:
20: filterProvider.SetConsumerParameters(new ReadOnlyCollection<ConsumerParameter>(parameters));
21:
22: }
23: }
Method name can be any, but pay attention which provider you are using . WebPart providers send data via different provider interfaces, for example when you are working with Filtering web parts (like "Current User Filter" web part) then you need to "listen" your incoming connections via IFilterValues (thx Mutaz for this findings), for other WB it could be IWebPartField or IWebPartRow.
If you are expecting to get data from WP via unsupported provider you connection link will be dimmed
Practical Sample
@d2design kindly asked to provide practical sample, for example how to use current user as a keyword :)
Actually, it's good example which shows how use both features I described.
To do this you need to have:
- ShareServices (SSP) with UserProfile, where current userName is stored
- "Current User Filter" WebPart, to get the userName from SSP. It returns you logged user name by default (not necessary actually, as well as SSP, because you could resolve you user name via standard asp.net User class when construct custom query)
- Enable WebPart connections as I described above. You need to use IFilterValue interface for "Current User Filter" and store your incoming user name in variable.
- Override query to include your userName in resulted custom query.
TIPS:
- When you are quering content which is not published and approved (like pages) you wont be able to see that content on the page in final view. You data will be available in page edit mode only, because they are treated as "draft" data.
- <OrderBy>CAML tag is not parsed by CQWP, you need to remove it from your query and set the related order field of CQWP class
Mirror: Subclassing Content Query Web Part to override CAML query
Content Deployment Job is a nice feature of MOSS 2007 to secure your main site from unauthorized and accidental changes and have approved content being deployed via standard MOSS functionality.
But Content Deployment Jobs are a bit tricky :(
There are some areas which I'd like to highlight in the current post, which helps you to save hours in configuring Content Deployments Path and Jobs
Requirements
To have successful contend deployment you need to prepare your sites correctly. There is a great post from Stefan Gossner who provided detailed explanations to all pre-requirement.
Just to summarize, you need:
-
Destination Site without any template pages (don't use "Blank" template even)
-
Deploy features to Source and Destination sites
-
Activate features ONLY on Destination site
Incremental Deployment
When you set deployment path and created your first job you need to do full deployment ("Deploy all content" option) first, and after it's done set the incremental deployment.
Your incremental job could fail with the next exception
"Violation of PRIMARY KEY constraint 'PK__#ExportObjects____50430BFC'. Cannot insert duplicate key in object 'dbo.#ExportObjects'."
The reason is that SP1 for MOSS changed the document versioning system, but no changes where done to Content Deployment Job functionality. To solve this issue you may need to install the following hotfix http://support.microsoft.com/kb/950279 or disable minor versioning for specific lists (google to find details about this)
Lookup fields
The biggest trick in Content Deployment Jobs relates to Lookup fields deployed via Feature. I think that the following exception will be familiar for those who tried run Content Deployment Jobs for Site with Lookup Fields feature
The element 'FieldTemplate' in namespace 'urn:deployment-manifest-schema' has invalid child element 'Field' in namespace 'http://schemas.microsoft.com/sharepoint/'. List of possible elements expected: 'Field' in namespace 'urn:deployment-manifest-schema'. at System.Xml.Schema.XmlSchemaValidator.SendValidationEvent(ValidationEventHandler eventHandler, Object sender, XmlSchemaValidationException e, XmlSeverityType severity) at System.Xml.Schema.XmlSchemaValidator.ElementValidationError(XmlQualifiedName name, ValidationState context, ValidationEventHandler eventHandler, Object sender, String sourceUri, Int32 lineNo, Int32 linePos, Boolean getParticles) at System.Xml.Schema.XmlSchemaValidator.ValidateElementContext(XmlQualifiedName elementName, Boolean& invalidElementInContext) at System.Xml.Schema.XmlSchemaValidator.ValidateElement(String localName, String namespaceUri, XmlSchemaInfo schemaInfo, String xsiType, String xsiNil, String xsiSchemaLocation, String xsiNoNamespaceSchemaLocation) at System.Xml.XsdValidatingReader.ProcessElementEvent() at System.Xml.XsdValidatingReader.ProcessReaderEvent() at System.Xml.XsdValidatingReader.Read() at Microsoft.SharePoint.Deployment.XmlFormatter.ParseObject(Type objectType, Boolean isChildObject) at Microsoft.SharePoint.Deployment.XmlFormatter.DeserializeObject(Type objectType, Boolean isChildObject, DeploymentObject envelope) at Microsoft.SharePoint.Deployment.XmlFormatter.Deserialize(Stream serializationStream) at Microsoft.SharePoint.Deployment.ObjectSerializer.Deserialize(Stream serializationStream) at Microsoft.SharePoint.Deployment.ImportObjectManager.ProcessObject(XmlReader xmlReader) at Microsoft.SharePoint.Deployment.SPImport.DeserializeObjects() at Microsoft.SharePoint.Deployment.SPImport.Run()
5/22/2008 11:14 AM Content deployment job 'Remote import job for job with sourceID = 0ffd575d-df2f-474f-83b6-f3454f2e7d0d' failed.The exception thrown was 'System.Xml.Schema.XmlSchemaValidationException' : 'The element 'FieldTemplate' in namespace 'urn:deployment-manifest-schema' has invalid child element 'Field' in namespace 'http://schemas.microsoft.com/sharepoint/'. List of possible elements expected: 'Field' in namespace 'urn:deployment-manifest-schema'.'
The problems is exactly in deployment scripts of Lookup fields. There are several samples how to deploy lookup fields via feature (http://www.sharepointnutsandbolts.com/2007/04/feature-to-create-lookup-fields-on.html) but all of them are not suitable for the Content Deployment Jobs. Because you will get the exception which I posted above.
The only way to find out why this happens is to use 3rd party tools (SharepointSpy or FieldsExplorer) to compare the SchemaXML of Lookup Fields after deployment via FeatureReceiver. So, after scrutinizing this issue I found out that deploying Lookup Fields via FeatureReceiver differs from Lookup fields create via Central Admin Web site. FeatureReceiver deployment adds additional attribute "xmlns="http://schemas.microsoft.com/sharepoint/" to the Lookup SchemaXML which breaks the Content Deployment Job.
So, the solution is simple - verify the published schema in FeatureReceiver before calling Update() method for Lookup Field and remove this attribute.
Mirror: MOSS Content Deployment Jobs - Lookup Fields and requirements
SharePoint content types is very popular thing, and used in almost all Sharepoint projects. Deployment of content types via feature is described in almost every book about SharePoint, but non of them describe how to do in correctly :) This happens mostly because the real nature of deployment content types reveals in Web Content Managed (WCM) projects, where you are working with content types inherited from publishing types. And there is no Sharepoint WCM book yet.
So, what's exactly specific in publishing content types for WCM? It's the nature on publishing types and using content types in publishing lists, such as "Pages" for example.

If to follow the standard practices in deployment of content types via feature, but inherited from any "Publishing" types and then add your custom content type to specific list (let's consider "Pages" list) you will find a bit strange results.
Adding new custom publishing content type pages will fail in that scenario - you will be redirected to "UploadPage.aspx" page, not to "CreatePage.aspx" page where you usually create new pages for your custom publishing content types.
The problem in that, if you create the content type inherited from publishing types, and don't specify the "CreatePage.aspx" template pages explicitly then you will loose the ability to create new pages for your content types in list toolbar menu (like on the screen above).
Actually it depends how you are adding new content type page - via Site Action menu, or via List toolbar menu. I realized that adding new page for custom publishing type fails from list toolbar menu (in default screnario), and works if you add it via Site Action menu.
The workaround is next - add the reference to "CreatePage.aspx" template explicitly using the <DocumentTemplate> tag inside the <ContentType> feature tag. The code-snipped is following
<ContentType
ID="0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900<GUID>"
Name="my Publishing Content Type"
Group="my Publishing Content Type"
Description="Content Type inherited from Publishing Page"
>
<FieldRefs>
<FieldRef ID="{<GUID>}" DisplayName="Title" Name="Title" Sealed="TRUE" />
<FieldRef ID="{<GUID>}" DisplayName="Description" Name="Comments" />
</FieldRefs>
<DocumentTemplate TargetName ="/_layouts/CreatePage.aspx"/>
</ContentType>
When you deploy this content type via feature and add this content type to the list, "CreatePage.aspx" become available and you could create your publishing content via list toolbar menu. The work is done :)
Another trick is how to make your publishing content type available in specific list during feature deployment. For this reason you need to use following tag inside feature deployment xml
<ContentTypeBinding
ContentTypeId="<LIST ID where your content type should be added>"
ListUrl="$Resources:cmscore,List_Pages_UrlName;"
/>
Mirror: Publishing Content Types and missed "CreatePage.aspx" page in WCM sites
I used to use a couple of tools in WSS/MOSS development which covered 90% of all functionality. These tools are
- Sharepoint Spy
- WSS Extensions for Visual Studio 2005
but when I switched on Windows 2008 Server + Visual Studio 2008 I noted that these tools are not supported there. Sharepoint Spy can't be installed on Windows 2008 (actually I can, but u need to use ORCA and change MSI); WSS Extensions works with VS 2005 only, and you can't use them with TFS builds.
Well... there is another reality, where you can survive without these tools :)
There I'd like to post the list of other tools which help you to substitute the Sharepoint Spy and WSS Extensions
I welcome if you post in comments other tools which help you in Sharepoint development, deployment and debugging
Mirror: Sharepoint Tools Overview for Windows 2008 and Visual Studio 2008
Using CSS in SharePoint differs a little bit from the standard approach we use in ASP.NET applications - some styles doesn't work as they expected to work.
The major problem is that SharePoint doesn't understand some CSS applied on ASP.NET сontrols. I stumbled over several cases. There is a simple rule how to avoid this - just use the SharePoint controls instead of ASP.NET once. In this cases CSS will be applied correctly
The nastiest thing I remember about CSS and Sharepoint is using asp:Menu control :) This control is very dodgy under normal asp.net sites, but it's double dodgy under Sharepoint.
The situation is that - you have page area height equal 100% of client browser height. It means that if you change the size of your browser window page layout height is changing too. On this page we have navigation which is implemented via <asp:Menu> control. So far so good, but there is a small issue. If you navigate on menu and dynamic sub-items are shown your page height changes and you noticing that vertical scroll bar is shown. WTF you could think :) Why this scroll bar appears?! This never happens in ASP.NET sites.
The answer is - CSS "min-height" property. This property sets the minimum height of your page when scroll bar appears. So, what you do is just edit your page and add the .min-height: 2000 for the page's root element and test the page in browser.
Unfortunately, situation the same - scroll bar appears when your menu items are shown. You can use IE Dev Toolbar of other tool to debug DOM model in browser, but changing this property doesn't help at all.
The issue is exactly in <asp:Menu> control. I dunno why but it breaks the page CSS style of min-height (I'd pleased if someone explain me WHY and HOW this happens?!). To fix this you need to use <Sharepoint:AspMenu> control instead of <asp:Menu> and min-height is set correctly in this case - no scroll bar when you navigate on menu.
Mirror: CSS and ASP.NET Menu in Sharepoint
When you are engaged in SharePoint development under Windows Server 2008 you are limited in 3rd party tools, because not all tools are working there, for example you can't use Sharepoint Spy - it requests Windows Server 2003 only (actually you can run Sharepoint Spy by modifying MSI and removing Windows version check)
There are some other tools which support Windows 2008, one of this tools is "OCDExportList", as you could imply from its name it allows you to export the List Definitions.
OCDExportList is just STSADM extension which gives you list's schema xml . But, if you gonna use this schema xml in your feature deployment, you need to make some changes.
By default "OCDExportList" exports list with all views, like "AllItems.aspx" and etc, but actually in 90% cases you need to use standard views, and don't deploy custom ones. So, to deploy list and use standard views the following changes of list schema xml should be applied
- Add SetupPath="pages\form.aspx" for each <Form> in <MetaData>/<Forms>
- Find <View> ulr="AllItems.asxp" and change attributes
- add SetupPath="pages\viewpage.aspx"
- change "DisplayName" value on "$Resources:core,objectiv_schema_mwsidcamlidC24;"
After that, the deployed list will use standard views
Mirror: Export List Definition with OCDExportList and reusing standard Views
Going to attend the ReMIX 08 in Sydney. You can catch me in "Expert Stand" section during breaks.
Mirror: Attend ReMIX 08 in Sydney - Experts stand