Attachment A: Complete Code for the Class: CloneAndAssignAction
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions;
using Microsoft.SharePoint.Publishing.WebControls;
namespace SamplePublishingActions
{
[Microsoft.SharePoint.Security.SharePointPermission(System.Security.Permissions.SecurityAction.LinkDemand, ObjectModel = true)]
[System.Web.AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = System.Web.AspNetHostingPermissionLevel.Minimal)]
public sealed class CloneAndAssignAction : ConsoleAction
{
private const string PostBackEventArgument = "cloneAndAssign";
private const string HiddenFieldName = "cloneAndAssignInfo";
private const string TitleField = "Title";
internal const char SeparatorCharacter = ':';
public CloneAndAssignAction() : base()
{
this.DisplayText = "Clone and Assign";
}
#region public overridden properties
public override AuthoringStates RequiredStates
{
get
{
return AuthoringStates.IsDocLibListItemTrue |
AuthoringStates.InWebPartDesignModeFalse |
AuthoringStates.IsPublishingPageTrue |
AuthoringStates.SaveConflictExistsFalse;
}
}
[Microsoft.SharePoint.Security.SharePointPermission(System.Security.Permissions.SecurityAction.Demand, ObjectModel = true)]
public override void RaisePostBackEvent(string eventArgument)
{
if (eventArgument == CloneAndAssignAction.PostBackEventArgument)
{
string formFieldValue = System.Web.HttpContext.Current.Request.Form.Get(HiddenFieldName);
string[] dialogValues = formFieldValue.Split(SeparatorCharacter);
string name = dialogValues[0] + ".aspx";
string assignTo = dialogValues[1];
//string name = "newpage" + DateTime.Now.Ticks.ToString() + ".aspx";
PublishingPage thisPage = PublishingPage.GetPublishingPage(SPContext.Current.ListItem);
PageLayout layout = null;
if (thisPage != null)
{
layout = thisPage.Layout;
}
// create a new list item using the page name supplied in the dialog
// our new page will be in this current web
SPWeb currentWeb = SPContext.Current.Web;
PublishingWeb web = PublishingWeb.GetPublishingWeb(currentWeb);
PublishingPage newPage = null;
try
{
PublishingPageCollection subset = web.GetPublishingPages(1);
newPage = subset.Add(name, layout);
SPListItem currentItem = thisPage.ListItem;
SPListItem newItem = newPage.ListItem;
// copy everything from this current item into the new item
foreach (SPField field in currentItem.Fields)
{
if (newItem.Fields[field.Id] == null)
{
newItem.Fields.Add(field);
}
}
foreach (object key in currentItem.Properties.Keys)
{
if (newItem.Properties[key] == null)
{
newItem.Properties.Add(key, currentItem.Properties[key]);
}
else
{
newItem.Properties[key] = currentItem.Properties[key];
}
}
foreach (object obj in SPContext.Current.FormContext.FieldControlCollection)
{
BaseFieldControl fc = obj as BaseFieldControl;
if (fc != null)
{
newItem[fc.FieldName] = currentItem[fc.FieldName];
}
}
SPLimitedWebPartManager newItemManager = newItem.File.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
SPLimitedWebPartManager currentItemManager = currentItem.File.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
foreach (WebPart part in currentItemManager.WebParts)
{
newItemManager.AddWebPart(part, part.ZoneID, part.ZoneIndex);
}
newItem[TitleField] = "Copy of " + currentItem[TitleField];
newPage.Update();
newPage.ListItem.File.CheckIn(String.Empty);
}
catch (SPException e)
{
this.ShowError(e, new ConsoleError(e.Message));
}
SPSite currentSite = currentWeb.Site;
// send mail to the email address specified in the dialog
if (currentSite.WebApplication.OutboundMailServiceInstance != null)
{
string fullUrl = currentSite.MakeFullUrl(currentWeb.GetFile(newPage.Url).ServerRelativeUrl);
SPUtility.SendEmail(currentWeb, false, false, assignTo,
"Page " + name + " has been assigned to you.",
"A new page, " + name + ", has been created by " + currentWeb.CurrentUser.Name +
" and assigned to you. You can view this page at " + fullUrl + ".");
}
else
{
this.ShowError(new SPException(), new ConsoleError("The new file was created, but email was not sent because outgoing email is not configured for this server."));
return;
}
}
}
public override SPBasePermissions UserRights
{
get { return SPBasePermissions.AddListItems; }
}
public override string NavigateUrl
{
get
{
// we'll pop up a small dialog box that allows the user to input a page
// name and the email address of the person to assign the page to.
Page.ClientScript.RegisterHiddenField(HiddenFieldName, String.Empty);
string cloneUrl = SPContext.Current.Web.ServerRelativeUrl.TrimEnd('/') + "/_layouts/CloneAssignDialog.aspx";
Page.ClientScript.RegisterClientScriptBlock(typeof(CloneAndAssignAction), "cloneAssignScript", @"
var cloneAssignReturnValue = null;
function GetCloneAndAssignCallback(ret){cloneAssignReturnValue = ret;}
function GetCloneAndAssignInfo(){
window.commonShowModalDialog('" + cloneUrl + @"', 'dialogHeight: 230px; status: no; scroll: no; help: no', GetCloneAndAssignCallback);
document.getElementById('" + HiddenFieldName + @"').value = cloneAssignReturnValue;
return (cloneAssignReturnValue != null);
}", true);
return "javascript:"
+ "if (GetCloneAndAssignInfo()) {"
+ Page.ClientScript.GetPostBackEventReference(this, CloneAndAssignAction.PostBackEventArgument)
+ "} else { return false; }";
}
}
#endregion
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
public class CloneAndAssignPage : Microsoft.SharePoint.WebControls.UnsecuredLayoutsPageBase
{
protected Microsoft.SharePoint.WebControls.PeopleEditor emailPicker;
protected System.Web.UI.WebControls.TextBox namePicker;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Page.ClientScript.RegisterClientScriptBlock(typeof(CloneAndAssignAction), "validateAndReturn", @"
var sepChar = '" + CloneAndAssignAction.SeparatorCharacter + @"';
function ValidateAndReturn()
{"
// make sure there are no instances of the separation
// character in either field, then pack them together
// and return them to the calling window.
+ @"
var pageNameForVal = document.getElementById('pageName').value;
var emailAddressForVal = document.getElementById('emailAddress').value;
if (pageNameForVal.indexOf(sepChar, 0) == -1 && emailAddressForVal.indexOf(sepChar, 0) == -1)
{
window.returnValue = document.getElementById('pageName').value +
sepChar +
document.getElementById('emailAddress').value;
window.close();
}
else
{
alert('The character \'' + sepChar + '\' can not appear in the page name or email address.');
}
}
", true);
//((DialogMaster)Page.Master).OkButton.OnClientClick = "ValidateAndReturn()";
((DialogMaster)Page.Master).OkButton.Focus();
}
protected override void OnPreRender(EventArgs e)
{
string emailName = String.Empty;
string pageName = String.Empty;
if (this.emailPicker.IsValid)
{
if (emailPicker.ResolvedEntities.Count > 0)
{
PickerEntity entity = emailPicker.ResolvedEntities[0] as PickerEntity;
if (entity != null)
{
object address = entity.EntityData["Email"];
emailName = address != null ? address.ToString() : String.Empty;
Page.ClientScript.RegisterClientScriptBlock(typeof(CloneAndAssignAction), "invokeReturn", "ValidateAndReturn()", true);
}
}
}
Page.ClientScript.RegisterHiddenField("pageName", this.namePicker.Text);
Page.ClientScript.RegisterHiddenField("emailAddress", emailName);
base.OnPreRender(e);
}
}
}
Attachment B: Complete Code for Class: RecentlyEditedItemsNode
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions;
using Microsoft.SharePoint.Publishing.WebControls;
namespace SamplePublishingActions
{
[Microsoft.SharePoint.Security.SharePointPermission(System.Security.Permissions.SecurityAction.LinkDemand, ObjectModel = true)]
public sealed class RecentlyEditedItemsNode : ConsoleNode
{
private const string CookieName = "RecentlyEditedItemsCookie";
private const char UrlSeparator = ':'; // this will never be in a URL returned by ServerRelativeUrl
private const int MaxRecentUrls = 5;
public RecentlyEditedItemsNode() : base()
{
this.initNode();
}
public RecentlyEditedItemsNode(ConsoleNode node) : base(node)
{
this.initNode();
}
private void initNode()
{
if (SPContext.Current.FormContext.FormMode == Microsoft.SharePoint.WebControls.SPControlMode.Edit)
{
// We're in the process of editing this current page. Let's add its URL to the list of
// recently edited pages.
System.Web.HttpContext currentContext = System.Web.HttpContext.Current;
string[] recentPageUrls = RecentPageUrls(currentContext);
// start out our list of recently visited URLs with the current one
string outputRecentPages = SPContext.Current.File.ServerRelativeUrl;
// then add the remaining recent URLs (that don't match the current one)
int count = 1;
foreach (string url in recentPageUrls)
{
if (count < MaxRecentUrls)
{
if (url != SPContext.Current.File.ServerRelativeUrl)
{
outputRecentPages += UrlSeparator.ToString() + url;
count++;
}
}
else
{
break;
}
}
currentContext.Response.Cookies.Add(new System.Web.HttpCookie(CookieName, outputRecentPages));
currentContext.Response.Cookies[CookieName].Expires = DateTime.Now.AddYears(1);
currentContext.Items[CookieName] = outputRecentPages;
}
}
public override AuthoringStates RequiredStates
{
get
{
return AuthoringStates.EmptyMask; // we want this item always to appear in the console
}
}
private void EnsureChildNodes()
{
base.ChildConsoleNodes.Clear();
System.Web.HttpContext currentContext = System.Web.HttpContext.Current;
string[] recentPageUrls = RecentPageUrls(currentContext);
// In order for this list to display correctly, what we do is add each recent URL
// as a child node of this node. In the browser, they will appear as a submenu
// with this node as the root.
foreach (string url in recentPageUrls)
{
if (!String.IsNullOrEmpty(url))
{
this.AddNewNode(url, url);
}
}
}
private void AddNewNode(string name, string url)
{
ConsoleNode recentEditNode = new ConsoleNode(this);
recentEditNode.Text = name;
recentEditNode.NavigateUrl = url;
base.ChildConsoleNodes.Add(recentEditNode);
}
private string[] RecentPageUrls(HttpContext currentContext)
{
string recentPages = String.Empty;
System.Web.HttpCookie requestCookie = currentContext.Request.Cookies[CookieName];
if (requestCookie != null)
{
recentPages = requestCookie.Value;
}
return recentPages.Split(UrlSeparator);
}
public override ConsoleNodeCollection ChildConsoleNodes
{
get
{
this.EnsureChildNodes();
return base.ChildConsoleNodes;
}
}
// If there are no recent URLs to display, then this node will be disabled
public override bool IsCurrentlyEnabled(AuthoringStates currentState)
{
this.EnsureChildNodes();
return (this.HasChildNodes && base.IsCurrentlyEnabled(currentState));
}
}
}
Attachment C: Complete ASPX code listing for the custom Dialog.
<%@ Assembly Name="RecentlyEditedItemsAction, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d3bfa9e538ba8cdc"%>
<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Page Inherits="SamplePublishingActions.CloneAndAssignPage" Language="C#" MasterPageFile="~/_layouts/dialog.master" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
Clone Current Page
Clone Current Page
Please provide the following information:
Page Name:
.aspx
Assign to:
Attachment D: Complete listing of the CustomEditingMenu.xml file