Geeks With Blogs

News

Hello,
My name is David Jacobus I am a former teacher of Computer Science and Mathematics, who is now working full time as a SharePoint Consultant! I have raced off-road motorcycles for 30+ years! I have fallen in love with Glamis and the Dune experience the last few years! My friends like to say: Jake, with age comes the cage! I suppose that is because at Glamis I use a Razor XP 900! Which has a full roll cage!


David Jacobus SharePoint Consultant

I was looking at codeplex and saw some tabbed web parts and a zone tab web part.  One was really good except it had display issues and the documentation was weak!    The issue of course is that a zone tabs JQuery UI solution needs a SharePoint page layout to set up the web parts zones on the layout!  In thinking about the solution I thought about the JQuery and JQuery UI JavaScript  Where should be load the scripts?  Master Page, Page Layout, Web Part, or  clever Delegate Control.  Nowadays we can be pretty sure that JQuery will already be loaded on the master page so I went with that solution.   The page layout will have the JQuery UI CSS and JQuery UI and our specific implementation JavaScript!   In Addition, I am using all the scripts loaded into the SharePoint site collection root vice a layouts folder! 

 

In Summarry, here is what we will do:

1.  Create an empty SharePoint project in Visual Studio.

2. Add Folders and Modules for housekeeping.

3. Get the JQuery UI artifacts, JavaScript, images and CSS.  Add these artifacts to the solution.

4. Create the Page Layout.

5. Build the test page in code!

6. add the code to codeplex

 

 

 

 

image

 

Above is the Zone Tabs Solution where I am using the web part title for the tabs text (JQUery Goodness).  Okay,  lets get to a complete solution with all the artifacts in the correct places and a working page with some built-in web parts to show how it works.

 

Create an empty SharePoint project in Visual Studio.  I am using VS2013, however, the same steps can be used in VS2010-VS2013.

 

image 

 

Where I am naming the project Demo.ZoneTabs,  Create it as a sandboxed solution.  The next steps are housekeeping  adding folders, etc..  Create a Folder :  Modules 

 

 

image

 

Right click on the modules folder and we will be adding modules:  CSS, Script, MasterPage, Images

 

image

 

Now that we have the three modules added we need to get the artifacts which will make this solution work.  Go to JQuery UI and download a themed Library for this demo I just used the default lightness theme.  You can roll your own to match your  sites theme.

image

this will download the JQuery UI widgets we need for this application in a zip file.  we will unzip this file on the desktop, so we can drag and drop it into our solution:  Open the downloaded zip file and your project from the documents folder:

 

image

Open up the CSS folder from the zip file mine has the entire folder ui-lightness and drag it to the css folder it is important to just leave everything in the folder as is.  Open the JS folder and drag the files to the Scripts Folder. In visual Studio click the show all files button on the tools above the solution explorer.  This will show the JavaScript and CSS Folder we just added.  Hold the control key and then right click and include in project! 

image

 

Now that we have the JQuery artifacts in our project we need to look at the elements file for these two modules. Lets look at the CSS Module elements file:

 

Delete the sample.txt file in each of the modules, we won’t need them.

 

image

what is of interest here is the Path and Url properties.  Path is the physical path in our solution and Url is the relative path from the site collection root in our site.  I will leave the CSS folder alone; However, if you thin it is possible that end users may need to edit these files then by all means change CSS to something like “Style Library/Demo/…”   The same idea goes for the JS files in the Scripts Module, I will put them in a Scripts folder at the site collection root.

image

 

Okay, enough housekeeping lets build our page layout.  Lets look what is required for tabs from the JQuery UI site:

   1:  <html lang="en">
   2:  <head>
   3:    <meta charset="utf-8" />
   4:    <title>jQuery UI Tabs - Default functionality</title>
   5:    <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
   6:    <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
   7:    <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
   8:    <link rel="stylesheet" href="/resources/demos/style.css" />
   9:    <script>
  10:    $(function() {
  11:      $( "#tabs" ).tabs();
  12:    });
  13:    </script>
  14:  </head>
  15:  <body>
  16:  <div id="tabs">
  17:    <ul>
  18:      <li><a href="#tabs-1">Nunc tincidunt</a></li>
  19:      <li><a href="#tabs-2">Proin dolor</a></li>
  20:      <li><a href="#tabs-3">Aenean lacinia</a></li>
  21:    </ul>
  22:    <div id="tabs-1">
  23:      <p>Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.</p>
  24:    </div>
  25:    <div id="tabs-2">
  26:      <p>Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.</p>
  27:    </div>
  28:    <div id="tabs-3">
  29:      <p>Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.</p>
  30:      <p>Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.</p>
  31:    </div>
  32:  </div>
  33:  </body>
  34:  </html>
  35:   
Okay we have the header with the JQuery UI being loaded.  a wrapper div called tabs a ul which are the tab names and links to the to nested divs within the tabs div.  We can add this same structure to a page  layout I am using 4 web part zones within the top of the page layout.  These 4 web part zones will drive the addition of web parts into our tabs solution.  I left the other zones there from a previous project.  In addition, look at the CSS Registration and Script Links,  These allow me to add the CSS and scripts relative to the site collection root.  I am not adding JQuery itself here as I think it belongs on the master page.   

 

 

   1:  <%@ Page language="C#"   Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full" %>
   2:  <%@ Import Namespace="Microsoft.SharePoint" %>
   3:  <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   4:  <%@ Register Tagprefix="SharePointWebControls" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   5:  <asp:Content ContentPlaceholderID="PlaceHolderAdditionalPageHead" runat="server">
   6:  <style type="text/css">
   7:  /* CSS layout */
   8:  body {
   9:      margin: 0;
  10:      padding: 0;
  11:  }
  12:  #masthead {
  13:  }
  14:  #top_nav {
  15:  }
  16:  #container {
  17:      width:100%;
  18:      display:table-row
  19:  }
  20:  #left_col {
  21:      width: 450px;
  22:      display:table-cell;
  23:  }
  24:  #page_content {
  25:      width: 450px;
  26:      display:table-cell;
  27:  }
  28:  #footer {
  29:      clear: both;
  30:  }
  31:  .wpZone{
  32:  vertical-align:top;
  33:  }
  34:  </style>
  35:   <SharePointWebControls:CssRegistration ID="CssRegistration2" name="<% $SPUrl:~sitecollection/css/ui-lightness/jquery-ui-1.10.3.custom.min.css %>"  After="corev4.css" runat="server"/>
  36:      <SharePoint:ScriptLink ID="Scriptlink4" Defer="true" Name="~sitecollection/Scripts/jquery-ui-1.10.3.custom.min.js" runat="server"/>
  37:      <SharePoint:ScriptLink ID="Scriptlink5" Defer="true" Name="~sitecollection/Scripts/WebPartTabs.js" runat="server"/>
  38:  </asp:Content>
  39:  <asp:Content ContentPlaceholderID="PlaceHolderPageTitle" runat="server">
  40:      <SharePointWebControls:FieldValue id="PageTitle" FieldName="Title" runat="server"/>
  41:  </asp:Content>
  42:  <asp:Content ContentPlaceholderID="PlaceHolderMain" runat="server">
  43:  <WebPartPages:SPProxyWebPartManager runat="server" id="spproxywebpartmanager"></WebPartPages:SPProxyWebPartManager>
  44:      
  45:  <div id="masthead">
  46:      <div id="tabs">
  47:    <ul>
  48:      <li><a   href="#WPZ1"><p id="wpzp1">One</p></a></li>
  49:      <li><a   href="#WPZ2"><p id="wpzp2">Two</p></a></li>
  50:      <li><a   href="#WPZ3"><p id="wpzp3">Three</p></a></li>
  51:      <li><a   href="#WPZ4"><p id="wpzp4">Four</p></a></li>
  52:    </ul>
  53:    <div id="WPZ1">
  54:  <WebPartPages:WebPartZone id="head1" CssClass="wpZone" runat="server" title="Head1"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  55:    </div>
  56:    <div id="WPZ2">
  57:  <WebPartPages:WebPartZone id="head2" CssClass="wpZone" runat="server" title="Head2"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  58:    </div>
  59:    <div id="WPZ3">
  60:  <WebPartPages:WebPartZone id="head3" CssClass="wpZone" runat="server" title="Head3"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  61:    </div>
  62:    <div id="WPZ4">
  63:  <WebPartPages:WebPartZone id="head4" CssClass="wpZone" runat="server" title="Head4"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  64:    </div>
  65:  </div>
  66:  </div>
  67:  <div id="top_nav">
  68:  <WebPartPages:WebPartZone id="nav" CssClass="wpZone" runat="server" title="UpperFullWidth"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  69:  </div>
  70:  <div id="container">
  71:      <div id="left_col">
  72:      <WebPartPages:WebPartZone id="left1" CssClass="wpZone" runat="server" title="MiddleLeftTop"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  73:      <WebPartPages:WebPartZone id="left2" CssClass="wpZone" runat="server" title="MiddleLeftBottom"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  74:      </div>
  75:      <div id="page_content">
  76:      <WebPartPages:WebPartZone id="content1" CssClass="wpZone"  runat="server" title="MiddleRightTop"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  77:      <WebPartPages:WebPartZone id="content2" CssClass="wpZone" runat="server" title="MiddleRightBottom"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  78:      </div>
  79:  </div>
  80:  <div class="footer">
  81:      <WebPartPages:WebPartZone id="footer" CssClass="wpZone" runat="server" title="footer"><ZoneTemplate></ZoneTemplate></WebPartPages:WebPartZone>
  82:  </div>
  83:  </asp:Content>

 

Okay, we have a page layout and we need to add it to our Master Page Module.   Click on the Master Page Module and add a new item, choose HTML page but rename it to TabsPageLayout.aspx before you save it.  Copy and paste the code from the link inside it (TabsPageLayout html)  Okay, we need to change the elements for the Master Page module to get the PageLayout into the correct place.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="MasterPage">
    <File Path="MasterPage\TabsPageLayout.aspx" Url="MasterPage/TabsPageLayout.aspx" />
  </Module>
</Elements>
 
to this:
 
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="MasterPage" Url="_catalogs/masterpage" Path="MasterPage/" RootWebOnly="TRUE">
    <File  Url="TabsPageLayout.aspx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE">
      <Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
      <Property Name="Title" Value="Tabs Layout" />
      <Property Name="PublishingPreviewImage" Value="~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/ThreeColumnLayout2.PNG, ~SiteCollection/_catalogs/masterpage/$Resources:core,Culture;/Preview Images/ThreeColumnLayout2.PNG" />
      <Property Name="PublishingAssociatedContentType" Value=";#Welcome Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF390064DEA0F50FC8C147B0B6EA0636C4A7D4;#" Type="string" />
    </File>
  </Module>
</Elements>

 

here we are telling SharePoint to put the page layout into the master page gallery and a preview image into the preview images folder.  We now need another module to place our images into our site.  Add another module to the modules folder and call it images.  Download this image and add it to the images module, change the elements file to put the image where it belongs in our SharePoint Site:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="Images">
    <File Path="Images\ThreeColumnLayout2.png" Url="Images/ThreeColumnLayout2.png" />
  </Module>
</Elements>

change it to this:

 

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="Images">
    <File Path="Images\ThreeColumnLayout2.png" Url="_catalogs/masterpage/en-us/Preview Images/ThreeColumnLayout2.png" />
  </Module>
</Elements>
Here we are putting the image into the catalogs master page preview folder.

Okay, we are now at the point where we need to add our custom JavaScript which you can infer from our page layout is called WebPartTabs.js here is the code which will make our tabs.  which is really quite simple!  Thanks, to JQuery!  Add a new item to the scripts module call it WebPartTabs.js and copy and paste the follwing code into it:

 

jQuery(document).ready(function ($) {
    
            
 var title1 = $("div#WPZ1 tr.ms-WPHeader td:nth-child(2) h3").text();
 var title2 = $("div#WPZ2 tr.ms-WPHeader td:nth-child(2) h3").text();
 var title3 = $("div#WPZ3 tr.ms-WPHeader td:nth-child(2) h3").text();
 var title4 = $("div#WPZ4 tr.ms-WPHeader td:nth-child(2) h3").text();
 $('#wpzp1').text(title1);
 $('#wpzp2').text(title2);
 $('#wpzp3').text(title3);
 $('#wpzp4').text(title4);

    
    if ($('#MSOLayout_InDesignMode').val() == 1) {

}
else
{


       $("#tabs").tabs({ active: 0 });
 }

});

What I am doing here is getting the text of the web parts title so we can name the tabs  instead of One—Four which is on the Page Layout!  Then if we are not in design mode we activate the tabs!   If we did nothing else at this point you could deploy the solution, build your tabs page and add web parts to it by hand as long as JQuery is on the master page.  However,  SharePoint publishing would come into play and we would see that the page layout is in draft mode and the user would have to check it in and approve it!  If you had read my previous blog about building pages in code, I didn’t mention it but the code I provided checked these items in!  I got this tidbit from another blog a few years ago and have forgot the authors name but whomever you are thank you!  so lets add this functionality now so our page etc. are provisioned checked in!  We need to add a feature receiver.  Our feature which was created when we added our first module by Visual Studio just need to be renamed to JQUeryTabs and then right click and add feature receiver!

image

 

image

Change the feature scope to site and rename the Feature title and Description .

 

image

add the following code at the bottom of the class:

 #region Checkin


        private void CheckinFiles(SPWeb web, Guid featureID)
        {

            // create a regular expression pattern for the manifest files

            string pattern = string.Format(@"^.+_{0}.xml$", featureID);

            Regex fileNameRE = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);



            // get the manifest files from the root folder of the site

            SPFile[] manifestFiles = web.RootFolder.Files.Cast<SPFile>().Where(f => fileNameRE.IsMatch(f.Name)).ToArray();

            try
            {

                // iterate the manifest files

                foreach (SPFile manifestFile in manifestFiles)
                {

                    // load the contents of the manifest file in an XDocument

                    MemoryStream mStream = new MemoryStream(manifestFile.OpenBinary());

                    StreamReader reader = new StreamReader(mStream, true);

                    XDocument manifestDoc = XDocument.Load(reader, LoadOptions.None);



                    // iterate over the 'Module' and 'File' elements in the XDocument, concatenating their Url attributes in a smart way so that we grab the site relative file Url-s

                    string[] fileUrls = manifestDoc.Root.Elements(WS + "Module")

                        .SelectMany(me => me.Elements(WS + "File"), (me, fe) => string.Join("/", new XAttribute[] { me.Attribute("Url"), fe.Attribute("Url") }.Select(attr => attr != null ? attr.Value : null).Where(val => !string.IsNullOrEmpty(val)).ToArray()))

                        .ToArray();



                    // iterate the file url-s

                    foreach (string fileUrl in fileUrls)
                    {

                        // get the file

                        SPFile file = web.GetFile(fileUrl);

                        // depending on the settings of the parent document library we may need to check in and/or (publish or approve) the file

                        if (file.Level == SPFileLevel.Checkout) file.CheckIn("", SPCheckinType.MajorCheckIn);

                        if (file.Level == SPFileLevel.Draft)
                        {

                            if (file.DocumentLibrary.EnableModeration) file.Approve("");

                            else file.Publish("");

                        }

                    }

                }

            }

            finally
            {

                // finally delete the manifest files from the site root folder

                foreach (SPFile manifestFile in manifestFiles) manifestFile.Delete();

            }

        }
        #endregion

 

Basically, we are going to read an XML file at the root of the site collection which lists all the files in the solution which need check-in.  how does this file get created?  By adding a module to those elements which need checked in.  In our case it is the Master Page Module.  Which now look like this:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="MasterPage">
    <File Path="MasterPage\TabsPageLayout.aspx" Url="MasterPage/TabsPageLayout.aspx" />
  </Module>
  <Module Name="CheckinModule">
    <File Path="MasterPage\Elements.xml" Url="Elements_$SharePoint.Feature.Id$.xml" Type="Ghostable" />

  </Module>
  
</Elements>

If we had anything going into the style library we would also add the check-in module here.  Now lets call our checkin code from our feature receiver:

 

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using System.Text.RegularExpressions;
using System.Linq;
using System.IO;
using System.Xml.Linq;
using Demo.ZoneTabs.Code;

namespace Demo.ZoneTabs.Features.JQueryTabs
{
    /// <summary>
    /// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
    /// </summary>
    /// <remarks>
    /// The GUID attached to this class may be used during packaging and should not be modified.
    /// </remarks>

    [Guid("e2e2146e-ed2b-449a-992b-61b6e4546954")]
    public class JQueryTabsEventReceiver : SPFeatureReceiver
    {
        // Uncomment the method below to handle the event raised after a feature has been activated.
readonly XNamespace WS = "http://schemas.microsoft.com/sharepoint/";
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {


            using (SPWeb spWeb = properties.GetWeb() as SPWeb)
            {

               

               

                //checkin masterpage and css
                CheckinFiles(spWeb, properties.Feature.DefinitionId);
                // SetDefaultPage(spWeb, spWeb.Url + "/pages/home.aspx");
                spWeb.Update();

            }
        }


        // Uncomment the method below to handle the event raised before a feature is deactivated.

        //public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        //{
        //}


        // Uncomment the method below to handle the event raised after a feature has been installed.

        //public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        //{
        //}


        // Uncomment the method below to handle the event raised before a feature is uninstalled.

        //public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        //{
        //}

        // Uncomment the method below to handle the event raised when a feature is upgrading.

        //public override void FeatureUpgrading(SPFeatureReceiverProperties properties, string upgradeActionName, System.Collections.Generic.IDictionary<string, string> parameters)
        //{
        //}

        #region Checkin


        private void CheckinFiles(SPWeb web, Guid featureID)
        {

            // create a regular expression pattern for the manifest files

            string pattern = string.Format(@"^.+_{0}.xml$", featureID);

            Regex fileNameRE = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);



            // get the manifest files from the root folder of the site

            SPFile[] manifestFiles = web.RootFolder.Files.Cast<SPFile>().Where(f => fileNameRE.IsMatch(f.Name)).ToArray();

            try
            {

                // iterate the manifest files

                foreach (SPFile manifestFile in manifestFiles)
                {

                    // load the contents of the manifest file in an XDocument

                    MemoryStream mStream = new MemoryStream(manifestFile.OpenBinary());

                    StreamReader reader = new StreamReader(mStream, true);

                    XDocument manifestDoc = XDocument.Load(reader, LoadOptions.None);



                    // iterate over the 'Module' and 'File' elements in the XDocument, concatenating their Url attributes in a smart way so that we grab the site relative file Url-s

                    string[] fileUrls = manifestDoc.Root.Elements(WS + "Module")

                        .SelectMany(me => me.Elements(WS + "File"), (me, fe) => string.Join("/", new XAttribute[] { me.Attribute("Url"), fe.Attribute("Url") }.Select(attr => attr != null ? attr.Value : null).Where(val => !string.IsNullOrEmpty(val)).ToArray()))

                        .ToArray();



                    // iterate the file url-s

                    foreach (string fileUrl in fileUrls)
                    {

                        // get the file

                        SPFile file = web.GetFile(fileUrl);

                        // depending on the settings of the parent document library we may need to check in and/or (publish or approve) the file

                        if (file.Level == SPFileLevel.Checkout) file.CheckIn("", SPCheckinType.MajorCheckIn);

                        if (file.Level == SPFileLevel.Draft)
                        {

                            if (file.DocumentLibrary.EnableModeration) file.Approve("");

                            else file.Publish("");

                        }

                    }

                }

            }

            finally
            {

                // finally delete the manifest files from the site root folder

                foreach (SPFile manifestFile in manifestFiles) manifestFile.Delete();

            }

        }
        #endregion
    }
}
This is the complete event receiver with an added extensions class in a folder called Code
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;

namespace Demo.ZoneTabs.Code
{
    public static class Extensions
    {

        /// <summary>
        /// Gets the web.
        /// </summary>
        /// <param name="properties">The properties.</param>
        /// <returns></returns>
        public static SPWeb GetWeb(this SPFeatureReceiverProperties properties)
        {
            SPWeb site;
            if (properties.Feature.Parent is SPWeb)
            {
                site = (SPWeb)properties.Feature.Parent;
            }
            else if (properties.Feature.Parent is SPSite)
            {
                site = ((SPSite)properties.Feature.Parent).RootWeb;
            }
            else
            {
                throw new Exception("Unable to retrieve SPWeb  this feature is not Site or Web-scoped.");
            }
            return site;
        }
    }
}

 

Okay, we can now check-in files which we create in our solution.   Let’s take this one step further and build our tabs page so we can test an use it when the feature is activated!  In my last blog post Click To View Entry Building Publishing Pages in code, I won’t rehash the blog but I will copy the code from the blog entry into this solution modified to fit our situation.  We need to add another feature we’ll call it Create Pages Feature and add an event receiver.

image

 

The Code For our Feature Receiver is as follows:

using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Navigation;
using Microsoft.SharePoint.Administration;
using PublishingSite = Microsoft.SharePoint.Publishing.PublishingSite;
using PublishingPage = Microsoft.SharePoint.Publishing.PublishingPage;
using SPListItem = Microsoft.SharePoint.SPListItem;
using SPFile = Microsoft.SharePoint.SPFile;
using SPModerationStatusType = Microsoft.SharePoint.SPModerationStatusType;
using PublishingWeb = Microsoft.SharePoint.Publishing.PublishingWeb;
using SPUser = Microsoft.SharePoint.SPUser;
using PageLayout = Microsoft.SharePoint.Publishing.PageLayout;
using PublishingPageCollection = Microsoft.SharePoint.Publishing.PublishingPageCollection;
using System.Reflection;
using System.Collections.Generic;
using System.Web.UI.WebControls.WebParts;
using Demo.ZoneTabs.Code;
namespace Demo.ZoneTabs.Features.CreatePages
{
    /// <summary>
    /// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
    /// </summary>
    /// <remarks>
    /// The GUID attached to this class may be used during packaging and should not be modified.
    /// </remarks>

    [Guid("7222392e-60bc-4a34-b842-e1d4cdadd7e5")]
    public class CreatePagesEventReceiver : SPFeatureReceiver
    {


        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {



            using (SPWeb spWeb = properties.GetWeb() as SPWeb)
            {


                //create the publishing pages
                CreatePublishingPage(spWeb, "Home.aspx", "TabsPageLayout.aspx", "Home");
                // CreatePublishingPage(currentWeb, "Dummy.aspx", "ThreeColumnLayout.aspx", "Dummy");
            }
        }


        private void CreatePublishingPage(SPWeb site, string pageName, string pageLayoutName, string title)
        {
            PublishingSite pubSiteCollection = new PublishingSite(site.Site);
            PublishingWeb pubSite = null;
            if (pubSiteCollection != null)
            {
                // Assign an object to the pubSite variable
                if (PublishingWeb.IsPublishingWeb(site))
                {
                    pubSite = PublishingWeb.GetPublishingWeb(site);
                }
            }
            // Search for the page layout for creating the new page
            PageLayout currentPageLayout = FindPageLayout(pubSiteCollection, pageLayoutName);
            // Check or the Page Layout could be found in the collection
            // if not (== null, return because the page has to be based on
            // an excisting Page Layout
            if (currentPageLayout == null)
            {
                return;
            }


            PublishingPageCollection pages = pubSite.GetPublishingPages();
            foreach (PublishingPage p in pages)
            {
                //The page allready exists
                if ((p.Name == pageName)) return;

            }



            PublishingPage newPage = pages.Add(pageName, currentPageLayout);
            newPage.Description = pageName.Replace(".aspx", "");
            // Here you can set some properties like:
            newPage.IncludeInCurrentNavigation = true;
            newPage.IncludeInGlobalNavigation = true;
            newPage.Title = title;

            //build the page 


            switch (pageName)
            {
                case "Home.aspx":
                    checkOut("Home.aspx", newPage);
                    BuildHomePage(site, newPage);
                    break;

                default:
                    break;
            }
            // newPage.Update();
            //Now we can checkin the newly created page to the “pages” library
            checkin(newPage, pubSite);


        }


        private static void BuildHomePage(SPWeb web, PublishingPage pubPage)
        {
            // build the pages
            // Get the web part manager for each page and do the same code as below (copy and paste, change to the web parts for the page)
            // Part  Description
            Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager mgr = web.GetLimitedWebPartManager(web.Url + "/Pages/Home.aspx", System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
            ContentEditorWebPart cewp1 = new ContentEditorWebPart() { Title = "The Best", ContentLink = web.Url + "/html/TheBest.Html"  };
            ContentEditorWebPart cewp2 = new ContentEditorWebPart() { Title = "The Worst", ContentLink = web.Url + "/html/TheWorst.Html" };
            ContentEditorWebPart cewp3 = new ContentEditorWebPart() { Title = "Links", ContentLink = web.Url + "/html/Links.Html" };
            ContentEditorWebPart cewp4 = new ContentEditorWebPart() { Title = "News", ContentLink = web.Url + "/html/News.Html" };

            // Add the web part to a pagelayout  Web Part Zone
            mgr.AddWebPart(cewp1, "head1", 1);
            mgr.AddWebPart(cewp2, "head2", 1);
            mgr.AddWebPart(cewp3, "head3", 1);
            mgr.AddWebPart(cewp4, "head4", 1);

            pubPage.Update();

        }

        /// <summary>
        /// Find a page layout in the layoutcollection based on the templatename
        /// </summary>
        /// <param name="pubSiteCollection">The toplevel publishing site</param>
        /// <param name="templateName">Name of the pagelayout</param>
        /// <returns>Pagelayout if excists or null</returns>
        private PageLayout FindPageLayout(PublishingSite pubSiteCollection, string templateName)
        {
            Microsoft.SharePoint.Publishing.PageLayoutCollection plCollection = pubSiteCollection.GetPageLayouts(true);
            foreach (Microsoft.SharePoint.Publishing.PageLayout layout in plCollection)
            {
                // String Comparison based on the Page Layout Name
                if (layout.Name.Equals(templateName, StringComparison.InvariantCultureIgnoreCase))
                {
                    return layout;
                }
            }
            return null;
        }


        private static void checkOut(string pagename, PublishingPage p)
        {
            if (p.Name.Equals(pagename, StringComparison.InvariantCultureIgnoreCase))
            {

                if (p.ListItem.File.CheckOutType == SPFile.SPCheckOutType.None)
                {
                    p.CheckOut();
                }

                if (p.ListItem.File.CheckOutType == SPFile.SPCheckOutType.Online)
                {
                    p.CheckIn("initial");
                    p.CheckOut();
                }
            }
        }
        private static void checkin(PublishingPage p, PublishingWeb pw)
        {
            SPFile publishFile = p.ListItem.File;

            if (publishFile.CheckOutType != SPFile.SPCheckOutType.None)
            {

                publishFile.CheckIn(

                "CheckedIn");

                publishFile.Publish(

                "published");
            }
            // In case of content approval, approve the file need to add
            //pulishing site 
            if (pw.PagesList.EnableModeration)
            {
                publishFile.Approve("Initial");
            }
            publishFile.Update();
        }

        public static void setWebPartProperties(System.Web.UI.WebControls.WebParts.WebPart webPart, string webPartName, Dictionary<string, string> props)
        {
            if (string.Equals(webPart.GetType().ToString(), webPartName))
            {
                PropertyInfo[] pinProperties = webPart.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);



                foreach (KeyValuePair<string, string> p in props)
                {
                    foreach (PropertyInfo pinProperty in pinProperties)
                    {

                        if (pinProperty.Name == p.Key)
                        {
                            pinProperty.SetValue(webPart, p.Value, null);
                            break;
                        }
                    }

                }
            }
        }
    }
}

 

 

I am using 4 Content Editor web parts on the page which are loading 4 html pages so we will need to add another module to deliver them to out site!  and just add some Latin text to each page.  I created some beautiful pages using this technique in the past!image

Build and deploy the solution and you should see this:

 

image

I blacked out the logo as the master page is for a client. The beauty of this way of adding Content Editor web parts is that you can use SharePoint Designer to edit these pages from the html folder with a WYSIWYG view!

In addition, when building the page, I had to make it a Farm Solution as The Limited Web Part Manager is not available in sandbox solutions!  I will post the code for this blog post to CodePlex as soon as I get my user name and password sorted out.

Remember, If you use this application you must use a master page with JQuery 1.6+ ! 

Posted on Sunday, November 17, 2013 8:36 PM SharePoint | Back to top


Comments on this post: JQuery Zone Tabs Web Part

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © David Jacobus | Powered by: GeeksWithBlogs.net