Wednesday, May 29, 2013

Visual Webpart – Programmatically Update Webpart Properties from the UserControl

As part of a requirement, I had to implement a custom visual webpart which work somewhat similar to the OOB Content Editor webpart, where it will render a given HTML markup. Purpose of this webpart was to create custom web forms on SharePoint publishing pages, using standard input fields.

In the edit mode of the webpart, textarea needs to be displayed to take HTML markup as the input and upon saving corresponding controls should be rendered. In addition, markup entered need to retain regardless of the browser session.

So my approach was to store text entered in textarea in a webpart property.

Edit mode of the webpart:


Once the page is saved, form will be rendered using the entered html markup. Textarea used to enter markup will be hidden:

In the UserControl of the visual webpart, clicking on ‘Update Control’ button will read the textarea text and set in the webpart property called FormControlsMarkup. Here I had to use SPWebPartManager class to save changes to the webpart.

Then in the next page load, I read above webpart property and render HTML markup in the control while hiding the textarea according to the control mode.

UserControl code:

using System;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace CustomWebparts.FormBuilder
{
 public partial class FormBuilderUserControl : UserControl
 {
  FormBuilder parentWebpart = null;
  protected void Page_Load(object sender, EventArgs e)
  {
   parentWebpart = (FormBuilder)this.Parent;

   // Create input controls from the markup saved in webpart property
   if (parentWebpart != null && !string.IsNullOrEmpty(parentWebpart.FormControlsMarkup))
   {
    pnlFormControls.Controls.Add(new LiteralControl(parentWebpart.FormControlsMarkup));
   }

   // In the edit mode, show markup editor text area
   if (SPContext.Current.FormContext.FormMode == SPControlMode.Edit || SPContext.Current.FormContext.FormMode == SPControlMode.New)
   {
    txtFormControlsMarkup.Text = parentWebpart.FormControlsMarkup;
    txtFormControlsMarkup.Visible = true;
    btnUpdateControl.Visible = true;
   }
   else
   {
    txtFormControlsMarkup.Visible = false;
    btnUpdateControl.Visible = false;
   }
  }

  protected void btnUpdateControl_Click(object sender, EventArgs e)
  {
   // Read markup from the textarea and update webpart property
   if (parentWebpart != null && !string.IsNullOrEmpty(txtFormControlsMarkup.Text))
   {
    parentWebpart.FormControlsMarkup = txtFormControlsMarkup.Text;

    SPWebPartManager currentWebPartManager = (SPWebPartManager)WebPartManager.GetCurrentWebPartManager(this.Page);
    Guid storageKey = currentWebPartManager.GetStorageKey(parentWebpart);
    currentWebPartManager.SaveChanges(storageKey);
   }
  }
 }
}

Webpart Markup:

<asp:Panel ID="pnlFormControls" runat="server" />

<asp:TextBox ID="txtFormControlsMarkup" runat="server" TextMode="MultiLine" Rows="10" Columns="50"></asp:TextBox>
<asp:Button ID="btnUpdateControl" runat="server" Text="Update Control"  onclick="btnUpdateControl_Click" />

Webpart Code:

using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;

namespace CustomWebparts.FormBuilder
{
 [ToolboxItemAttribute(false)]
 public class FormBuilder : WebPart
 {
  // Visual Studio might automatically update this path when you change the Visual Web Part project item.
  private const string _ascxPath = @"~/_CONTROLTEMPLATES/CustomWebparts/FormBuilder/FormBuilderUserControl.ascx";

  public string FormControlsMarkup { get; set; }

  protected override void CreateChildControls()
  {
   this.ChromeType = PartChromeType.None;
   Control control = Page.LoadControl(_ascxPath);
   Controls.Add(control);
  }
 }
}

Friday, May 10, 2013

Deployment – Copy Files to SharePoint Web Application Root using PowerShell

Here is a simple PowerShell script I wrote to automate copying certain configuration files to the root directory of SharePoint web application during the deployment. In my case I wanted to automate copying 2 files.
global.asax – with customized content
robots.txt - to instruct search engine robots

Following PowerShell script get the location of above files and website URL as parameters. Then it finds the virtual directory path of the website from IIS settings of the web application (default zone) to use as the destination. copy-item cmdlet copies files to the destination.

[string]$scriptsLocation = "C:\Projects\TestApp\ConfigFiles"
$webSiteUrl = "http://derdev02:40920/"

$robotsFilePath = "$scriptsLocation\robots.txt"
$globalAsaxPath = "$scriptsLocation\global.asax"

function UpdateConfigFiles([string]$siteUrl)
{
  write-host -f Yellow "Updating configuration files for " $siteUrl
  $SPSite = new-object Microsoft.SharePoint.SPSite($siteUrl)
  $WebApp = $SPSite.WebApplication

  #IisSettings in default zone
  $IISSettings = $WebApp.IisSettings[[Microsoft.SharePoint.Administration.SPUrlZone]::Default]
  $virtualDirectoryPath = $IISSettings.Path.FullName
  write-host "Virtual Directory path: " $virtualDirectoryPath

  $robotFileExists = test-path ($robotsFilePath)
  if ($robotFileExists)
  {   
    write-host "Copying file: " $robotsFilePath " to the destination: " $virtualDirectoryPath"\robots.txt"
    copy-item $robotsFilePath -destination "$virtualDirectoryPath\robots.txt" -Force
  }

  $globalAsaxFileExists = test-path ($globalAsaxPath)
  if ($globalAsaxFileExists)
  {   
    write-host "Copying file: " $globalAsaxPath " to the destination: " $virtualDirectoryPath"\global.asax"
    copy-item $globalAsaxPath -destination "$virtualDirectoryPath\global.asax" -Force
  }
}

UpdateConfigFiles($webSiteUrl)


Note: If the files already exist in the destination, they will be overwritten even if they are in read-only state, since –Force parameter is used with the copy-item cmdlet.

Tuesday, May 7, 2013

Overwrite or Hide OOB Markup Styles in SharePoint 2010 Ribbon

In a previous blog post I wrote how to provide customized styles for the SharePoint Rich Text Field, so that the “Styles” and “Markup Styles” dropdowns in the ribbon will contain brand compliant styles in addition to the OOB styles.

But there can be a requirement like we want to remove all or some of OOB styles and add custom styles. There are several ways we can achieve this task. One option is using jQuery we can catch the click event of the styles or markup styles dropdown and there we can hide the unwanted styles from the dropdown.

Approach explaining in this post is a completely CSS method. We simply find the id of each dropdown item and hide unwanted items using CSS.

Using developer tool bar of the browser, locate the markup style anchor tags and get the anchor tag ids. Ids are almost same but the number towards the end is incrementing. Here is how Styles drop down items look:


Here is how Markup Styles drop down items look:


We can hide items in Styles dropdown as below. Below code hide the first 2 OOB items in the dropdown.

/* ----- Hide OOB Ribbon Styles - Start ----- */
#Ribbon\.EditingTools\.CPEditTab\.Styles\.Styles\.Menu\.Styles
a[id="Ribbon.EditingTools.CPEditTab.Styles.Styles.Menu.Styles.TextStyle0-Menu"]
{
    display:none;
}
#Ribbon\.EditingTools\.CPEditTab\.Styles\.Styles\.Menu\.Styles
a[id="Ribbon.EditingTools.CPEditTab.Styles.Styles.Menu.Styles.TextStyle1-Menu"]
{
    display:none;
}
...
...
/* ----- Hide OOB Ribbon Styles - End ----- */


This is how we hide items in Markup Styles dropdown.

/* ----- Hide OOB Markup Styles - Start ----- */
#Ribbon\.EditingTools\.CPEditTab\.Paragraph\.ElementWithStyle\.Menu\.Styles
a[id="ElementWithStyle0-Menu"]
{
    display:none;
}
#Ribbon\.EditingTools\.CPEditTab\.Paragraph\.ElementWithStyle\.Menu\.Styles
a[id="ElementWithStyle1-Menu"]
{
    display:none;
}
...
...
/* ----- Hide OOB Markup Styles - End ----- */


Adding few custom Styles and Markup Styles in the ribbon.

/* ----- Styles - Start ------ */
.ms-rteStyle-BodyTextNormal {
  -ms-name: "Normal";
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
  color: #3e3e3e;
}

.ms-rteStyle-RegularArrowLink {
  -ms-name: "Regular Arrow Link";
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
  color: #1c3f95;
  background:url('/Style Library/Images/ar5.png') no-repeat;
  background-position:left center;
  padding-left: 15px;
}

.ms-rteStyle-RegularArrowLink a:hover {
  color: #236699;
  text-decoration: underline;
}

.ms-rteStyle-IntroductionParagraph {
  -ms-name: "Introduction Paragraph";
  font-family: Arial, Helvetica, sans-serif;
  font-size: 16px;
  line-height: 24px;
  color: #3e3e3e;
}
/* ----- Styles - End ------ */

/* ----- Markup Styles - Start ------ */
div.ms-rteElement-BodyParagraph {
  -ms-name: "Paragraph";
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
  color: #3e3e3e;
}

div.ms-rteElement-ThickHorizontalDivider {
  -ms-name: "Thick Horizontal Divider";
  border-bottom: 4px solid #f0f2f3;
  padding-bottom: 10px;
}
/* ----- Markup Styles - End ------ */


When hiding items from the styles dropdowns we can also use display name of the dropdown item as below. So the need to use developer tool bar to find out the ids can be avoided.

#Ribbon\.EditingTools\.CPEditTab\.Paragraph\.ElementWithStyle\.Menu\.Styles
a[title="Heading 1"]
{
    display:none;
}

But the drawback with this approach is, we cannot add a custom style with the same name as OOB item.