Now that we have our frame in place, let's build a page decorated with an instance of our web part to cover it. There's a bit of drama surrounding when (and how) pages should be provisioned during a deployment. I feel that they could be considered part of the data (since they are, after all, items in a list under the Publishing paradigm) and should be the responsibility of the data creator.
However, pages are also absolutely structural, since wireframes and mockups almost always have designated web parts in designated areas of the home page, landing pages, and interior pages. So perhaps structure should create these, and leave the content pages for the data creator? And, of course, no SharePoint deployment book can exist without making mention of the AllUsersWebPart, no matter how many claims it makes to be for developers only!
So where does this thing fit in? If you ask me, not at all. I've only had to deal with it for one project, and that's because I was out of town during the architectural discussions. (I'm thrilled to report that my teammate who made that decision, while we were logging hour 67 on the final week of the project, apologized profusely for choosing it.)
What happened is that we built a beautiful structure feature that provisioned our entire site sans pages. Then the data creator built out all of our pages, which were based off of layouts with AllUsersWebParts scattered about them. To make a long story short, this was all deployed to production with a typo that flipped the positions of some of the web parts (which, in his defense, had similar names) on hundreds of pages.
So we ended having to write almost the same code to fix it as we would have had to write to deploy it cleanly in the first place. But robustness against typos isn't the only claim of an All Code deployment, especially with pages, since Microsoft has given us tools like the AllUsersWebPart and SharePoint Designer for this very purpose.
The thing I hate about all this XML-driven page deployment stuff is the whole "ghosted" (in 2007) or "customized" (in 2010) state of pages. It's too ethereal. Pages actually live in _layouts, but have fake "pointer" SPFiles in the master page gallery? If you ask me, the only markup that should not be in a library backed by the content database is that which is contained in an ASCX user control or an ASPX application page. These are for administrative / reusable logic; everything else should fall under the publishing paradigm.
I much prefer the aforementioned rigidity of All Code deployments that allow us to programmatically create pages and web parts, set their properties with hard references to our constants, and have everything live happily in galleries or page libraries. After adding the constants...
Code Listing 32: Constants.cs
- #region Pages
- public static class Pages
- public const string RollupArticleTitle = "Rollup Articles";
- public const string RollupArticleFileName = "RollupArticles.aspx";
...here's how easily you can provision a publishing page with a single line of code:
Code Listing 33: Structure.EventReceiver.cs
- //create page
- PublishingPage page = root.CreatePage(pageLayout.Item, Constants.Pages.RollupArticleFileName, Constants.Pages.RollupArticleTitle, true);
CreatePage creates a page in the extended SPWeb's Pages library with the specified layout, name, and url (file name). It wil get its content type from the page layout's associated content type. The final Boolean determines if this new page should be the home page of the site. After this method runs, whenever the web that is represented by the "root" object above is navigated to, the user will be redirected to /Pages/RollupArticles.aspx.
I end up overloading this method a lot to pass metadata to the page (which will implicitly populate site columns on the default content type associated with the Pages library). If the library has multiple content types, you can tell the page which one to "be" by setting the value of the appropriate SPContentTypeId in the page's listitem's "ContentTypeId" field.
Where does the page layout come from? You can either use an out-of-the-box publishing layout, or create your own in Visual Studio. The latter is an advanced topic, and will be covered in the last chapter of this book. This all might seem like more work up front compared to the AllUsersWebPart, but remember the mantra of this book: doing the diligence beforehand will make your deployments (and inevitable redeployments) trivial.
Add the Web Part to the Page
Finally, let's get our web part out there! Provisioning web parts is really more so the second step of creating a page than its own process. Rarely will we be writing deployment code to add web parts to a page that we didn't previously create ourselves. This one-two punch code can be placed in either your structure feature or your data creator; the logic is the same for either use case.
Code Listing 34: Constants.cs
- #region Web Part Zones
- public static class WebPartZones
- public const string Top = "Top";
And the code:
Code Listing 35: Structure.EventReceiver.cs
- //create webpart
- ShowStuffFromList webPart = new ShowStuffFromList ()
- //set properties
- Title = "All Code Rules!"
- //add webpart to page
- root.AddWebPartsToPageAndPublish(page, Constants.WebPartZones.Top, webPart);
AddWebPartsToPageAndPublish adds a params listing of web parts (defined as a class that inherits from Microsoft.SharePoint.WebPartPages.WebPart) and adds them to the specified zone of the specified page in the extended SPWeb's Pages library. The "AndPublish" portion of the method name means that in addition to the manipulation of the page's content, the method does the proper handling of checking in and out, publishing, and approving, depending on the library's settings.