Radi Atanassov

SharePoint MCM, MVP, MCT and owner of OneBit Software

Формите на списъци в SharePoint 2007 - как работят те – Втора Част

Този пост е продължение на предишния, в който разглеждам как можем да заменим “NewFormUrl”, “EditFormUrl” и “DisplayFormUrl” притежанията на Content Type обекти, с цел да се заменят формите, с които SharePoint показва данни за определен list item. Тука ще опиша как SharePoint използва Rendering Templates по време на зареждането на тези форми, и как можем да ги заменим с произволни, така че да добавиме функционалност.

Ето как работят Rendering Templates:

Всеки list item в SharePoint списък е под формата на определен Content Type. В повечето случай това е или “Item”, или “Document” Content Type, в зависимост от списъка, в който този list item се намира. Като разработчици, ние създаваме нови Content Types, които имат смисъл за съответния бизнес: Invoice, Quote, Proposal и т.н.

Всеки Content Type има свой “New”, “Edit” и “Display” форми за събиране и показване на своите данни. Всяка една от тези форми е дефинирана чрез “Form Rendering Template”, който съдържа HTML markup, или ASCX контролки.

Тези Rendering Templates са дефинирани в ASCX файлове в _CONTROLTEMPLATES папката (...12\TEMPLATE\CONTROLTEMPLATES). На повечето обекти в SharePoint са им дефинирани Rendering Templates в DefaultTemplates.ascx, там ще намерите HTML на много обекти като Toolbar, ListFieldIterator и т.н. Ето как е дефиниран DocumentLibraryForm:

<SharePoint:RenderingTemplate ID="DocumentLibraryForm" runat="server">
    <Template>
        <SharePoint:InformationBar runat="server"/>
        <wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" 
        RightButtonSeparator="" runat="server">
            <Template_RightButtons>
                <SharePoint:SaveButton runat="server"/>
                <SharePoint:GoBackButton runat="server"/>
            </Template_RightButtons>
        </wssuc:ToolBar>
        <SharePoint:FormToolBar runat="server"/>
        <SharePoint:FormComponent TemplateName="DocumentLibraryFormCore" 
        runat="server"/>
    </Template>
</SharePoint:RenderingTemplate>

Забележете, че този Rendering Template вика други “FormCompontent” обекти чрез “TemplateName” атрибута. Това е много сходно с PHP…

Нашата цел, е да заменим тези Rendering Templates, за да променим това, което крайните потребители виждат като работят с нашите Content Types.

Имаме 2 варианта:

Вариант 1: посочване на наши Rendering Templates в самата дефиниция на наш Content Type.

Дефиниции на Content Types ни дават тази възможност чрез XmlDocument елементи:

<!-- Document Content Type -->
<ContentType ID="0x0101006BD6DAD38F7947799A6F2EE72F5C3C24"
    Name="TemporaryContentType"
    Group="Custom Content Types"
    Description="">
    <FieldRefs>
        <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" />
    </FieldRefs>
    <XmlDocuments>
        <XmlDocument 
        NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
            <FormTemplates 
            xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
                <Display>MyCustomForm</Display>
                <Edit>MyCustomForm</Edit>
                <New>MyCustomForm</New>
            </FormTemplates>
        </XmlDocument>
    </XmlDocuments>
</ContentType>

 

С горният FormTemplates елемент (в XmlDocument) съм посочил на този Content Type да използва MyCustomForm за Display, New и Edit формите си.

За да създадем MyCustomForm като Rendering Template, трябва да си направим свой ASCX файл и да го сложим в CONTROLTEMPLATES папката. Това е хубаво да стане чрез Feature.

Ето примерен Rendering Template в свой ASCX файл. SharePoint ще разгледа и запомни всички RenderingTemplate обекти по време на зареждане на своя Application Pool, стига те да се намират в CONTROLTEMPLATES папката.

<SharePoint:RenderingTemplate ID="MyCustomForm" runat="server">
    <Template>
        Hello
    </Template>
</SharePoint:RenderingTemplate>

Можете да добавите HTML и свой ASCX контролки и напълно да промените това, което крайните потребители виждат и използват.

Ето какво всъщност става „зад сцената“:

В първата част описах как ListFormWebPart се добавя от платформата на ASPX страници, които по принцип SharePoint би използвал за да покаже свойте “New”, “Edit” и “Display” форми. Този Web Part върши цялата работа в намирането на точния Rendering Template за текущия Content Type, върху който крайният потребител иска да работи.

Ето малко код от Reflector… Това е TemplateName, притежанието на този ListFormWebPart, който проверява текущия Content Type и връща името на конфигурирания Rendering Template.

image

Това ни води до следващата опция:

Вариант 2: Да посочим името на Rendering Template чрез код.

Кода е много прост (: SPContentType обекта открива 3 притежания:

cType.NewFormTemplateName = “MyCustomForm”;
cType.EditFormTemplateName = “MyCustomForm”;;
cType.DisplayFormTemplateName = “MyCustomForm”;
cType.Update(true);

С това ще се постигне същото нещо като Опция 1, но няма нужда от декларативен CAML. Чрез cType.Update(true); изпращаме промените към дъщерни Content Types.

Успех.

SharePoint 2007 list forms under the hood – Part 2

In Part 1 of this series I looked at replacing the “NewFormUrl”, “EditFormUrl” and “DisplayFormUrl” properties of the Content Type object to change the forms that SharePoint uses to display list item information. In this second part I will discuss how we can replace the actual Rendering Templates to achieve the same goals.

This is how Rendering Templates work:

Each item in a SharePoint list is of a certain Content Type. In most cases items are either an “Item” Content Type or a “Document” Content Type depending on the Base Type of the list. Usually as developers and customisers we create additional Content Types that make sense to the business, for example Invoices, Quotes, Proposals, etc.

Each Content Type has “New”, “Edit” and “Display” forms for displaying and collecting data related to the content type item. Each one of these 3 forms has a “Form Rendering Template” that defines the HTML markup and controls that will render for the forms.

These Rendering Templates are defined in the _CONTROLTEMPLATES folder. Most other objects in SharePoint have Rendering Templates which are defined in DefaultTemplates.ascx, check out the Toolbar, ListFieldIterator, etc. The DocumentLibraryForm is defined in DefaultTemplates.ascx:

<SharePoint:RenderingTemplate ID="DocumentLibraryForm" runat="server">
    <Template>
        <SharePoint:InformationBar runat="server"/>
        <wssuc:ToolBar CssClass="ms-formtoolbar" id="toolBarTbltop" 
        RightButtonSeparator="" runat="server">
            <Template_RightButtons>
                <SharePoint:SaveButton runat="server"/>
                <SharePoint:GoBackButton runat="server"/>
            </Template_RightButtons>
        </wssuc:ToolBar>
        <SharePoint:FormToolBar runat="server"/>
        <SharePoint:FormComponent TemplateName="DocumentLibraryFormCore" 
        runat="server"/>
    </Template>
</SharePoint:RenderingTemplate>

 

This rendering template calls other “FormCompontent” objects and their Rendering Tempaltes – very similar to PHP…

Our goal is to replace these Rendering Templates with our own, so we modify what end users see in the New, Edit and Display scenarios.

We can “tell” Content Types to use our own Rendering Tempaltes by two ways: including XML in the Content Type Definition, or Using code.

Option 1:

Content Type definitions in features allow you to set XmlDocument elements:

<!-- Document Content Type -->
<ContentType ID="0x0101006BD6DAD38F7947799A6F2EE72F5C3C24"
    Name="TemporaryContentType"
    Group="Custom Content Types"
    Description="">
    <FieldRefs>
        <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" />
    </FieldRefs>
    <XmlDocuments>
        <XmlDocument 
        NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
            <FormTemplates 
            xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
                <Display>MyCustomForm</Display>
                <Edit>MyCustomForm</Edit>
                <New>MyCustomForm</New>
            </FormTemplates>
        </XmlDocument>
    </XmlDocuments>
</ContentType>

With the above FormTemplates element under XmlDocument I have specified that the custom content type should use the custom Rendering Template MyCustomForm for rendering the New, Edit and Display forms.

To create this Rendering Template, create your own ASCX file to store it, use a feature to deploy it to the CONTROLTEMPLATES folder, and then add the XmlDocuments element to your Content Type definition as demonstrated above. Here is a basic Rendering Template that should go in a custom ASCX file, you can name it however you wish, SharePoint will “crawl” the folder for all Rendering Templates during the Application Pool start-up sequence.

<SharePoint:RenderingTemplate ID="MyCustomForm" runat="server">
    <Template>
        Hello
    </Template>
</SharePoint:RenderingTemplate>

You can include your own custom ASCX controls in your rendering templates and completely change the SharePoint experience.

In part 1 I explained how the ListFormWebPart gets added to our form pages that get provisioned with the list. This web part is what actually does the work in both options. During load time it checks the Rendering Template name specified in the properties and builds its markup based on that template.

Here is a shot of the TemplateName property where the web part checks the current Content Type and gets its template names:

clip_image002

This leads us to our next option: changing the Template names in code.

Option 2: Using code to change a Content Types Rendering Template.

It’s quite simple - the SPContentType object exposes 3 properties so we can set the template names:

cType.NewFormTemplateName = “MyCustomForm”;
cType.EditFormTemplateName = “MyCustomForm”;;
cType.DisplayFormTemplateName = “MyCustomForm”;
cType.Update(true);

This essentially achieves the same as Option 1 above, but keep in mind that you should most likely push the changes down to child content types – call .Update(true); instead of .Update();

All the best.

Формите на списъци в SharePoint 2007 - как работят те

В този пост разглеждам как работят формите на списъци и Content Types в SharePoint 2007 (в случая WSS v3) и какви са ни възможностите за разширяване и разработка. В тази първа част ще обясня как да използваме наши си ASPX форми, а във втора – как да заменим стандартните Rendering Templates с нови. С втория пост ще сложа и линк към решение с пример за двата варианта.

Когато създадем списък в SharePoint, ние всъщност създаваме копие на базата на съществуващ шаблон – List Template. Тези шаблони са дефирнирани в Features, и всеки Feature с шаблон за списък ще съдържа дефиниращ файл: schema.xml. Този schema.xml може се разгледа за всеки списък, който WSS v3 ни предлага: разгледайте 12\TEMPLATE\FEATURES папката, там има Features като CustomList, ContactList, DocumentLibrary. Всичките имат schema.xml, който дефинира особеностите им.

В края на този XML (CAML) файл може да видим „Form” елемент, който дефинира ASPX страниците, които ще бъдат използвани за списъка, на който схемата принадлежи.

clip_image002

Когато се създава списък, платформата прави копие на файл-а дефиниран в “SetupPath” атрибута на “Form” елемента, и го качва в базата данни с посоченото име в “Url” атрибута. След като страницата е създадена (това е обикновен Web Part Page), SharePoint слага ListFormWebPart в посочения WebPartZoneID. “pages\form.aspx”, и подобни други ASPX страници се намират в 12\TEMPLATE\Pages, и там можете да сложите своите. Горният screenshot е от CustomList, но можете да разгледате и всички останали.

Интересното става, когато Content Types са добавени в списъци. Всеки Content Type може да има свои DisplayForm, EditForm или NewForm зададени, а всеки списък може да има множество Content Types. Съответно за създаването на различен вид съдържание в един списък можете да имате различни фо��ми за различните Content Types – това е много добра гъвкавост. От друга гледна точка, всеки Content Type може да има един и същ интерфейс (форма) навсякъде, когато е използван многократно.

Как работи всичкото това зад сцената?

В SharePoint SPContentType обектът ни позволява да му зададем EditFormUrl, DispFormUrl, и NewFormUrl (притежания):

cType.EditFormUrl = "_layouts/ourprojectfolder/customedit.aspx";

cType.NewFormUrl = "_layouts/ourprojectfolder/customnew.aspx";

cType.DisplayFormUrl = "_layouts/ourprojectfolder/customdisplay.aspx";

Ако тези са дефинирани от нас, SharePoint прави следното:

· С избирането на “New” бутона или съответните опции от менюто на всеки ред в списъка, SharePoint ни праща на EditForm.aspx, NewForm.aspx или DispForm.aspx според избора.

· Когато създава списъка, платформата конфигурира ListFormWebPart на всяка от тези ASPX страници (откопирани са от Pages\form.aspx). WebPartZoneID атрибута определя къде точно да се добави този Web Part. Тези страници не могат без този Web Part – SharePoint се оплаква ако се опитате да го насочите към страница без ListFormWebPart.

· Този ListFormWebPart проверява текущия SPControlMode (Edit, New, Display), и дали съответното притежание (EditFormUrl, DispFormUrl, или NewFormUrl) на текущия Content Type е зададен. Логиката може да бъде проследена ако разгледаме кода на този web part – в OnInit метода се прави проверка на притежанието this.FormPageUrl, което всъщност е obfuscated и не мога да ви го покажа.

clip_image003

Независимо дали е зададен от нас или не (EditFormUrl, DispFormUrl, или NewFormUrl), SharePoint ни праща към съответната страница - NewForm.aspx, EditForm.aspx или DispForm.aspx. На тези страници ListFormWebPart проверява текущия Content Type (всеки List Item в SharePoint има зададен Content Type) и ни препраща към съответната форма (ако тя е зададена от нас). this.FormPageUrl определя коя е тя на база на текущия SPControlMode (New, Edit, Display), но не мога да ви покажа кода на this.FormPageUrl защото е obfuscated. Така всъщност става конфигурирането на поризволни форми.

Проблемът на този дизайн, е че SharePoint първо ни праща към NewForm.aspx (или една от другите) и след малко код отново ни препраща чрез SPUtility.Redirect (което зад сцената е по-украсен Response.Redirect). Това са два post-back-a, и можете да ги проследите с Firebug или Fiddler, или който и да е друг HTTP listener. Освен, че препратките са две, NewForm.aspx е Web Part Page, а те не са хич леки.

Това, което все още не знам е как да конфигурираме EditFormUrl, NewFormUrl или DispFormUrl в схемата на ContentType елемента (чрез CAML). Примерът по-горе е чрез код и обектния модел, но той не е идеален за всички случаи. В WSS v3 “Form” елемента е “obsolete”, така че начина, по който го правихме в WSS v2 е вече невалиден. Втория ни вариант всъщност е да работим с XmlDocuments елемента в CAML – той ни позволява да сменим съответния Rendering Template на формата. Така става чрез CAML, но двата метода работят по коренно различен начин.

Във втората част на този пост ще разгледам как да заменим стандартните Rendering Templates с произволни, и съответно алтернативния вариант да разширяваме формите на списъци и Content Types.

Надявам се това ще помогне на някой.

SharePoint 2007 list forms under the hood

This post is the first out of a two-part series, where I look at the way SharePoint list forms work and how we can customise them. Part 1 explains how to use custom ASPX pages for list forms, and Part 2 goes through replacing SharePoint’s default rendering templates with custom ones. At the end of the activity I will provide a code sample with both.

When we provision a list in SharePoint we make an ‘instance’ of it based on a list template. Templates are defined in features, and each feature with a list template will hold a schema.xml file. You can view the schema.xml files of existing list templates in features that come with WSS – CustomList, ContactList, DocumentLibrary, etc. just check the 12\TEMPLATE\FEATURES folder.

At the bottom of the schema.xml files we can see a Form element that defines the ASPX files used during the provisioning of lists:

clip_image002

When a list is provisioned, the platform makes a copy of the physical file defined in the “SetupPath” attribute, stores it in the content DB and names it as specified in the “Url” attribute, and adds a ListFormWebPart to the WebPartZoneID specified. “pages\form.aspx” maps to 12\TEMPLATE\Pages\form.aspx and you could replace these with your own. I have demonstrated CustomList, but other lists and libraries use the rest of the files in there.

All this gets interesting when Content Types are added to lists. Each Content Type can have its _own_ DisplayForm, EditForm or NewForm – and in a single list you can have different forms for different Content Types - This means POWER! Furthermore, the same Content Types used across different lists can have consistent forms.

So how does it work?

The SPContentType class allows you to set the EditFormUrl, DispFormUrl, and NewFormUrl properties:

cType.EditFormUrl = "_layouts/ourprojectfolder/customedit.aspx";

cType.NewFormUrl = "_layouts/ourprojectfolder/customnew.aspx";

cType.DisplayFormUrl = "_layouts/ourprojectfolder/customdisplay.aspx";

If these are set, this is what happens under the hood:

· The “New” button on the toolbar or the Item Context menu takes the user to one of the forms: EditForm.aspx, NewForm.aspx or DispForm.aspx

· During provisioning, the ListFormWebPart is added to each of these forms. Check the WebPartZoneID property – this tells the platform where to add the web part. You can’t go without it – SharePoint complains.

· The ListFormWebPart checks the current SPControlMode (Edit, New, Display) and whether the respective property of the current item’s Content Type is null or empty. This can be seen by examining the source code of the web part with Reflector. It makes a check on this.FormPageUrl (which is obfuscated!):

clip_image003

So what we can see is the navigation items take us to either NewForm.aspx, EditForm.aspx or DispForm.aspx. The ListFormWebPart on that page does a check on the current Content Type and takes us to the current SPControlMode’s form. This is what “this.FormPageUrl” does behind the “obfuscated” scene.

An obvious drawback of such design is the fact that the user is first taken to NewForm.aspx (or any of the others), then an SPUtility.Redirect call is made (essentially Response.Redirect) which results in a second postback for the user. You can see the two requests with Firebug or any other HTTP listener. Apart from that there is some overhead – NewForm.aspx is a web part page (and they aren’t light).

What I currently don’t know is how to set the EditFormUrl, NewFormUrl and DispFormUrl properties in the ContentType definition schema. The “Forms” element is obsolete in WSS v3, so the only current way I know to change forms via CAML is to work with the XmlDocuments element. These two options work in a completely different manner. In Part 2 of this series I will demonstrate how you can set custom rendering templates in features with content types.

Hope this helps.