zevenseas


 

Publishing Page creation bug

Overview

This post describes an issue with creating pages in a publishing environment using the UI.

 

Cause

Given the following scenario :

  • RootWeb with some PageLayouts.
  • Several subsites with unique permissions
    • User A has contribute permissions on a subsite and has read/limited access on the Portal

User A tries to create a new page based on a Page Layout which is a Site ContentType and not yet ‘installed’ on the Pages Library. During creation the user gets the following error:

 

Object reference not set to an instance of an object. at Microsoft.SharePoint.Publishing.PublishingPage.SetContentType(SPContentType listContentType) at Microsoft.SharePoint.Publishing.PublishingPageCollection.
<>c__DisplayClass5.<Add>b__0() at Microsoft.Office.Server.Diagnostics.FirstChanceHandler.ExceptionFilter(Boolean fRethrowException, TryBlock tryBlock, FilterBlock filter, CatchBlock catchBlock, FinallyBlock finallyBlock) at Microsoft.Office.Server.Diagnostics.ULS.SendWatsonOnExceptionTag(ULSTagID tagID, ULSCat categoryID, String output, Boolean fRethrowException, TryBlock tryBlock, CatchBlock catchBlock, FinallyBlock finallyBlock) at Microsoft.SharePoint.Publishing.PublishingPageCollection.Add(String name, PageLayout layout) …

 

If the user tries to create the page again (by pressing F5 in the error screen), the page is successfully created.

If the PageLayout is already installed on the Pages Library there is no error at all and the page is created immediately. 


So it seems that the error is raised when SharePoint tries to add the ContentType of the PageLayout during the creation of the page. I’ve tried to mimic these behavior using a console application with the following code :

static void Main(string[] args)
{
    using (SPSite site = new SPSite("http://test/a/c/d/e"))
    {   
        using (SPWeb web = site.OpenWeb())
        {
            SPContentType cType = web.Site.RootWeb.ContentTypes["Link"];
            SPList list = web.Lists["Shared Documents"];             
            SetContentType(cType, list);
        }
    }
}
public static void SetContentType(SPContentType contentType, SPList list)
{
    list.ContentTypes.Add(contentType);
    list.Update();
}

The code breaks on the .Add of the ContentType, prompting me with the following error “Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))”  but when executing the code again I get the following message “A duplicate name "Contact" was found.” Indicating the ContentType is installed the first time when it threw the Access Denied exception. That confirms that when an user tries to create a page based on a PageLayout that was not already installed on the Pages Library the first time, the PageLayout is correctly installed the second time and thus the page get’s created.

 

When I change the permissions of User A to Full Control I get no error at all and everything works smoothly.

 

So what actually is happening, is that the PublishingPageCollection.Add method tries to add the ContentType using the current context and does not check if the user has sufficient permissions to add the ContentType to the Pages Library.

 

Googling on the subject gave me this blogpost by Bernd who also came across this weird behavior..  

 

Solution

 

Preventive  

A solution can be to add all the (relevant) PageLayouts to each Pages Library using a featurereceiver and/or a stapler to prevent the action (the add action of the ContentType by SharePoint during the creation of the Page) from happening.

 

Reactive  

A console application / timerjob / page with longoperation method that loops through every publishingWeb and adds the PageLayouts from the publishingSite to the Pages Library.

OperationsPage & ApplicationsManagementPage

One of the new things I learned when I created the TimerJobOverview solution was to make use of the OperationsPage class.. remember that I talked about creating ApplicationPages and that you should inherit from the LayoutsPageBase or UnsecuredLayoutsPageBase? Well in the case you are developing application pages that will only be available in the Central Admin webapp you have two more choices to choose from :

  • OperationsPage
  • ApplicationsManagementPage

I googled on this pages and found the following interesting blogpost from Paul Shkurikhin called : Creating and customizing SharePoint 2007/MOSS Central Administration Application Pages

“.. Both OperationsPage and ApplicationsManagementPage classes overrides just one PageToRedirectOnCancel property from GlobalAdminPageBase…”

I was wondering when I first created my Application Pages how pages in the Application Management and Operations tab knew where to redirect to when you clicked on Cancel and this explains why.. ;)
Next to that you also get some nice properties you can set as well:

  • AccessibleBySharePointAdminGroup
  • DefaultContentDatabaseName
  • PageToRedirectOnMissingPageParameter
  • RequiredPageParameters

And these methods

  • RedirectToApplicationManagementPage
  • RedirectToOperationsPage
  • RedirectToErrorPage

So if you are going to create Application Pages that will reside within ApplicationManagement or the Operations tab I should recommend on using these pages and make use of the cool properties and methods that are in there!

 

Technorati Tags:

Want to configure Timer Jobs ? ;)

As you might have noticed I’ve been blogging a lot about Application Pages and TimerJob related stuff in the last few months and this post is about those two same things! So apologies for that.. But you might get a smile on your face when you see the following screenshot:

timerjobconfigurator

What have I done and what do you see?

Well, I’ve created yet another application page (YAAP, cool acronym ;) that displays all the current timer job definitions and furthermore it showes for each timer job it’s status. So basically I’ve combined the Timer Job Status page and the Timer Job definitions page. But that wasn’t my main concern to build these pages.. the main concern was to have some more insight in the current defined jobs. Things like, when are they scheduled exactly (when is ‘hourly’ or ‘daily’) and give the administrators the choice to disable and/or delete timerjobs or change the schedule of each one.

Why did I add the option of mass deletion of timerjobs? I’ve seen some farms that when they are using Content Deployment, this service creates a lot of OneTime scheduled timerjobs. When these fail, they are not being deleted properly so it could be that the Timer Job definitions list can be flooded with those OneTime scheduled jobs. So you might want to remove these timerjobs to clean up the definitions list eh? Especially if the Timer service get’s restarted (after a WSP installation for example) then all those one timed jobs are being executed again and will fail again and will stay in the list again..

Why did I add the option of changing the schedule of timerjobs? I’ve seen some farms that had custom timerjobs installed on them but no interface for administrators to change the schedule or to disable the timerjob at all (other then deactivating the feature which came with the solution).

timerjobedit

I also wanted to incorporate a feature that would let you create a new OneTime schedule to kick off a timerjob. Since you cannot change the schedule of a existing timerjob to OneTime schedule (you receive a nice error that tells you, you can’t change the schedule:), I have to create a new instance of that particular timerjob and give that one the OneTime schedule to run. And that ‘creating a new instance of that particular job’ is still a work in progress :)

<Update>

It’s going to be on Codeplex.. but currently I’m getting this message on Codeplex : “Unable to save the release. The TFS server is not available”.. so .. sorry ;)

Meanwhile I’ve uploaded the installer here for you to download and play around with ;)

Get it from CodePlex right here !

</Update>

 

Technorati Tags:

New MOSS SP2 bug .. continued and problem solved!

Remember that I blogged about problem that I had (New MOSS SP2 bug) where suddenly my SharePoint environment died and was giving me the error in the event viewer :

The schema version (3.1.10.0) of the database SharePoint_AdminContent_5e188b87-c7a8-4e0a-a488-b21a4a89e941 
on mosswin2k3 is not consistent with the expected database schema version (3.1.3.0) on mosswin2k3.  
Connections to this database from this server have been blocked to avoid data loss.  
Upgrade the web front end or the content database to ensure that these versions match.

Well.. I had pinpointed the problem to the fact I was using a function of WSPBuilder called “Copy to GAC”. Now this function copies all the .dll’s from the debug/release folder of your project into the GAC. I worked on a solution which I had worked on my local machine before I took into my VPC to develop on. During that time I had copied all the SharePoint dll’s locally in my project (eg. ‘Copy Local’ of the references were set to True) so I could develop against the SharePoint API.

So you can guess what happens if the “Copy to GAC” function copies old versions of the SharePoint .dll’s in the GAC right? ;) Yes, SharePoint was now dealing with old dll’s in the GAC while the databases were running on a higher version. When I copied the .dll’s back from the ISAPI folder in the 12 hive into the GAC, everything worked again! w00h00w!

Concluding that the schema version error was absolutely right and it made sense that the PSConfig could not find the new types that were introduced with SP2 and thus failed. So I wasn’t a bug but merely a stupid action on my account where I didn’t pay attention to the output window of the WSPBuilder of which .dll’s were copied during the “Copy to GAC” function.

 

Want to thanks Neil Hodgkinson as well for his valuable time in supporting me with this issue ;)

Technorati Tags:

Item-level permissions for Document Libraries

Do you want the same item-level settings on document libraries like there is on lists?.. Confused what I’m talking about? Well it’s basically having this on document libraries as well:

Chakkaradeep wanted it as well and created a solution called the “ILP:Item level permission library”, head over to his blogpost Item Level Permission for Document Libraries-BETA Release, download it, use it and give your feedback!

Great stuff Chak! ;)

Re-using SharePoint controls

This post is going to be a summary of some of some the SharePoint controls I regularly use in my solutions. Cool thing about re-using the controls that are already out there is, that saves time in re-inventing the wheel and you don’t have to worry about keeping the look&feel consistent ;)

First, check out the following blog posts for more information on re-using the out-of-the-box controls of SharePoint

 

Now some controls are already covered by the links that are stated above, so this is really just a quick summary on the things I like to use most ;)

I’ve given each control a table, in the first row there is a screenshot (if applicable) how to control looks like when it’s rendered by SharePoint,
in the second row there is the declarative way of using the control,
in the third row there is (also, if applicable) some codebehind logic on how to  use the control and it’s value.

WebApplicationSelector 

webapplicationselector
<SharePoint:WebApplicationSelector ID="webApplicationSelector" runat="server" />
protected WebApplicationSelector webApplicationSelector;
..
SPWebApplication webApplication = webApplicationSelector.CurrentItem;

 

SiteAdministrationSelector

siteadministrationSelector
<SharePoint:SiteAdministrationSelector runat=server ID="siteadminselector" /> 
protected SiteAdministrationSelector siteAdministrationSelector;
..
SPSiteAdministration siteAdmin = siteAdministrationSelector.CurrentItem;

 

SchedulePicker , for more info on this one and when & why you should use this please read “Using the SchedulePicker” ;)

schedulepicker (2)

<%@ Register TagPrefix="wssuc" TagName="SchedulePicker"

src="~/_controltemplates/SchedulePicker.ascx" %>

<wssuc:SchedulePicker id="schedulePicker"Hourly="True"

Weekly="True" Monthly="True" Enabled="True" EnableStateView="True" runat="server"/>

protected SchedulePicker schedulePicker;
..
//Getting a schedule from the picker
SPSchedule schedule = schedulePicker.Schedule;
//Setting the schedule of the picker
schedulePicker.ScheduleString = schedule.ToString();

 

SPDatePickerControl 

spDatePickerControl
<script type="text/javascript" 
src="/_layouts/datepicker.js"></script>
<SharePoint:SPDatePickerControl id="datePickerControl" 
   runat="server"/>

 

ButtonSection

buttonsection
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" 
src="~/_controltemplates/ButtonSection.ascx" %>
<wssuc:ButtonSection runat="server">
    <Template_Buttons>
        <asp:Button runat="server" class="ms-ButtonHeightWidth" 
        OnClick="OnClickOK" Text="<%$Resources:wss,multipages_okbutton_text%>" 
        id="btnOk" accesskey="<%$Resources:wss,okbutton_accesskey%>"/>
    </Template_Buttons>
</wssuc:ButtonSection> 
When making use of the LayoutsPageBase in your codebehind, you can define the URL of the Cancel button by using this snippet:
public override string PageToRedirectOnCancel
{
    get
    {
        //return base.PageToRedirectOnCancel;
        return "/_layouts/settings.aspx";
    }
}

 

InputFormSection & InputFormControl

inputformsectionandcontrol
<%@ Register TagPrefix="wssuc" 
TagName="InputFormSection" 
src="~/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" 
TagName="InputFormControl" 
src="~/_controltemplates/InputFormControl.ascx" %>
<wssuc:InputFormSection Title="InputFormSection"
    Description="InputFormSection Description" runat="server">		
    <template_inputformcontrols>            
        <wssuc:InputFormControl runat="server" LabelText="InputFormControl Text">
            <Template_Control>
                <%--Put a control here--%>
                </Template_Control>
        </wssuc:InputFormControl> 
    </template_inputformcontrols> 
</wssuc:InputFormSection> 

 

InputFormTextBox, notice there are two InputFormTextBoxes shown here. One with the RichTexT property set to true and the other set to false. So with a simple property change you can get the nicely and shiny FullHTML textbox.

inputformtextbox1

<SharePoint:InputFormTextBox ID="ApproveMailSubject" RichText="false"

runat="server" Width="100%"/>

<SharePoint:InputFormTextBox ID="ApproveMailContent" RichText="true" 
	RichTextMode="FullHtml" 
	runat="server" 
	TextMode="MultiLine" 
Rows="20"/>

 

InputFormRequiredFieldValidator

inputformrequiredvalidator
<wssawc:InputFormTextBox ID="uxFirstName" RichText="false" 
    TextMode="Singleline" runat="server" Width="100%"/>	
<wssawc:InputFormRequiredFieldValidator ID="uxFirstNameValidator"
    ControlToValidate="uxFirstName" Text="Error" runat="server"
    ErrorMessage ="First name is required" 
    EnableClientScript="true" Display="Dynamic">
</wssawc:InputFormRequiredFieldValidator>
 

InputFormRangeValidator

inputformrangevalidator
<wssawc:InputFormRangeValidator 
    ID="inputFormRangeVal" 
    Type="Integer" MinimumValue="1" MaximumValue="30"
    ControlToValidate="uxNumberOfDays"
    ErrorMessage="Value must between 1 and 30" 
    Runat="server" />

 

ToolBar & ToolBarButton

toolbar
<%@ Register TagPrefix="wssuc" TagName="ToolBar" 
src="~/_controltemplates/ToolBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" 
src="~/_controltemplates/ToolBarButton.ascx" %>
<wssuc:ToolBar id="Toolbar" runat="server">
	<template_buttons>
		<wssuc:ToolBarButton runat="server"
			id="ViewSites"
			Text="View Sites"
			ToolTip=""
			OnClick="ViewSites_Link"
			ImageUrl="/_layouts/images/newitem.gif"
			Padding="2px"
			AccessKey="V" />			
	</template_buttons>
	<template_rightbuttons>

<SharePoint:WebApplicationSelector id="Selector"

runat="server"/>

	</template_rightbuttons>
</wssuc:ToolBar>
To use the ToolBar in your WebPart you can load the UserControls and reference them using this:

protected ToolBarButton ToolBarButton;
protected ToolBar ToolBar;
..
//Adding a ToolBarButton
ToolBarButton = 
    (ToolBarButton)Page.LoadControl("/_controltemplates/ToolBarButton.ascx");
ToolBarButton.Click += new EventHandler(ToolBarButton_Click);
ToolBarButton.Text = "Complete selected tasks";
ToolBarButton.ImageUrl = "/_layouts/images/CheckNames.gif";
//Adding the ToolBar
ToolBar = 
    (ToolBar)Page.LoadControl("/_controltemplates/ToolBar.ascx");
ToolBar.Buttons.Controls.Add(ToolBarButton);
this.Controls.Add(ToolBar);


 
 
 

© 2009 Community Kit For SharePoint