Creating Custom Activity feed


Hi all,

SharePoint provide a excellent feature under my site which display the recent activities of the user performed recently using Recent Activities web part and for colleagues activities it provides My newsfeed web part. These web parts will show only those activities which users was configured under “Edit My Profile” page. Following is the screen shot of the Out of box activities feeds.

In order to create the custom activity feed, There are a sample console application provided by the microsoft SDK SharePoint Server 2010: Activity Feeds Console Application just to register the custom activity feed on the server.

Cation: There is no method implemented to delete custom activity once created. So make sure while deploying it on production that you must execute this code of creation once. Only one way to delete the activity feed is from database. For more information visit: Remove Method and Way to Remove Activity Feed

Now I am going to share the code which can be used as a console application as well as in a feature for create a custom activity feed as below:

var context = SPServiceContext.Current;
var pm = new UserProfileManager(context);
var currentUserProfile = pm.GetUserProfile(SPContext.Current.Site.OpenWeb().CurrentUser.LoginName);
var actMgr = new ActivityManager(currentUserProfile, context);
if (actMgr.PrepareToAllowSchemaChanges())
{
ActivityApplication actApplication;
if (actMgr.ActivityApplications["CustomActivity"] == null)
{
actApplication = actMgr.ActivityApplications.Create("CustomActivity");
actApplication.Commit();
actApplication.Refresh(false);
}
else
{
actApplication = actMgr.ActivityApplications["CustomActivity"];
}
ActivityType addDocumentsActivityType = actApplication.ActivityTypes["AddDocuments"];
if (addDocumentsActivityType == null)
{
addDocumentsActivityType = actApplication.ActivityTypes.Create("AddDocuments");
addDocumentsActivityType.ActivityTypeNameLocStringResourceFile = "ActivityResource";
addDocumentsActivityType.ActivityTypeNameLocStringName = "AddDocumentsActivityName";
addDocumentsActivityType.IsPublished = true;
addDocumentsActivityType.IsConsolidated = true;
addDocumentsActivityType.AllowRollup = true;
addDocumentsActivityType.Commit();
addDocumentsActivityType.Refresh(false);
}
ActivityTemplate addDocumentsActTemplate =
addDocumentsActivityType.ActivityTemplates[ActivityTemplatesCollection.CreateKey(false)];
if (addDocumentsActTemplate == null)
{
addDocumentsActTemplate = addDocumentsActivityType.ActivityTemplates.Create(false);
addDocumentsActTemplate.TitleFormatLocStringResourceFile = "ActivityResource";
addDocumentsActTemplate.TitleFormatLocStringName = "Activity_AddComments";
addDocumentsActTemplate.Commit();
addDocumentsActTemplate.Refresh(false);
}
}

Next is to create the resource file. Please follow the instruction given at: http://msdn.microsoft.com/en-us/library/xbx3z216.aspx

Now if you successfully created the activity feed then next step is to create the activity events. Here I am demonstrating the example of the add document event capture and then register it to my custom activity feed.

For this, I need to create a List item event receiver. I will capture the ItemAdding event of the document list and then create a event for the uploaded document.

var documentsListName = SPUtility.GetLocalizedString("$Resources:core,shareddocuments_Title;",
"core",
web.Language);
var pm = new UserProfileManager(SPServiceContext.GetContext(web.Site));
UserProfile currentUserProfile;
try
{
currentUserProfile = pm.GetUserProfile(userName);
PortalLog.LogString("Got user profile: {0}", userName);
SPSecurity.RunWithElevatedPrivileges(() => GrantPermissionsToUserProfileService(userName));
PortalLog.LogString("Permission Granted: {0}", userName);
var actMgr = new ActivityManager(currentUserProfile, SPServiceContext.GetContext(web.Site));
if (string.Equals(listTitle, documentsListName, StringComparison.CurrentCultureIgnoreCase))
{
long activityId =
actMgr.ActivityApplications["CustomActivity"].ActivityTypes["AddDocument"].ActivityTypeId;
if (activityId != 0)
CreateEvent(activityId, currentUserProfile, actMgr, "AddDocument", displayName, itemUrl,
string.Empty);
}
SPSecurity.RunWithElevatedPrivileges(() => RevokePermissionsToUserProfileService(userName));
}
catch (Exception ex)
{
PortalLog.LogString("Error for user: {0}, {1}, {2}", userName, ex.Message,
ex.InnerException);
return false;
private void CreateEvent(long activityId, UserProfile currentUserProfile, ActivityManager actMgr, string activityType, string linkTitle, string linkUrl, string value)
{
PortalLog.LogString("Creating event: {0}", activityType);
Entity publisher = new MinimalPerson(currentUserProfile).CreateEntity(actMgr);
ActivityEvent activityEvent = ActivityEvent.CreateActivityEvent(actMgr, activityId, publisher, publisher);
activityEvent.Name = activityType;
activityEvent.ItemPrivacy = (int)Privacy.Public;
activityEvent.Owner = publisher;
activityEvent.Publisher = publisher;
activityEvent.Value = value;
var link = new Link { Href = linkUrl, Name = linkTitle };
activityEvent.Link = link;
activityEvent.Commit();
PortalLog.LogString("Created event: {0}", activityType);
MulticastPublishedEvents(new List<ActivityEvent> { activityEvent }, actMgr);
}
private void MulticastPublishedEvents(List<ActivityEvent> activityEvents, ActivityManager actMgr)
{
if (activityEvents.Count == 0)
return;
List<long> publishers = activityEvents.Select(e => e.Owner.Id).Distinct().ToList();
Dictionary<long, MinimalPerson> owners;
Dictionary<long, List<MinimalPerson>> colleaguesOfOwners;
ActivityFeedGatherer.GetUsersColleaguesAndRights(actMgr, publishers, out owners, out colleaguesOfOwners);
Dictionary<long, List<ActivityEvent>> eventsPerOwner;
ActivityFeedGatherer.MulticastActivityEvents(actMgr, activityEvents, colleaguesOfOwners, out eventsPerOwner);
List<ActivityEvent> eventsToMulticast;
ActivityFeedGatherer.CollectActivityEventsToConsolidate(eventsPerOwner, out eventsToMulticast);
}
}

There are few more methods which are used to grant the permissions to the user so that they can create the event without any issue. But we must have to revoke that access for other unauthenticated changes.
private static void RevokePermissionsToUserProfileService(string accountName)
{
var upServiceproxy = SPFarm.Local.Services.Where(s => s.GetType().Name.Contains("UserProfileService")).FirstOrDefault();
if (upServiceproxy != null)
{
SPIisWebServiceApplication upServiceApp = upServiceproxy.Applications.OfType().FirstOrDefault();
if (upServiceApp != null)
{
var mgr = SPClaimProviderManager.Local;
var security = upServiceApp.GetAccessControl();
var claim = mgr.ConvertIdentifierToClaim(accountName, SPIdentifierTypes.WindowsSamAccountName);
security.RemoveAccessRule(new SPAclAccessRule(claim,
SPIisWebServiceApplicationRights
.FullControl));
upServiceApp.SetAccessControl(security);
var adminSecurity = upServiceApp.GetAdministrationAccessControl();
var adminClaim = mgr.ConvertIdentifierToClaim(accountName,
SPIdentifierTypes.WindowsSamAccountName);
adminSecurity.RemoveAccessRule(new SPAclAccessRule(adminClaim,
SPCentralAdministrationRights
.FullControl));
upServiceApp.SetAdministrationAccessControl(adminSecurity);
upServiceApp.Uncache();
upServiceproxy.Uncache();
}
}
}
private static void GrantPermissionsToUserProfileService(string accountName)
{
var upServiceproxy = SPFarm.Local.Services.Where(s => s.GetType().Name.Contains("UserProfileService")).FirstOrDefault();
if (upServiceproxy != null)
{
SPIisWebServiceApplication upServiceApp = upServiceproxy.Applications.OfType().FirstOrDefault();
if (upServiceApp != null)
{
var mgr = SPClaimProviderManager.Local;
var security = upServiceApp.GetAccessControl();
var claim = mgr.ConvertIdentifierToClaim(accountName, SPIdentifierTypes.WindowsSamAccountName);
security.AddAccessRule(new SPAclAccessRule(claim,
SPIisWebServiceApplicationRights
.FullControl));
upServiceApp.SetAccessControl(security);
var adminSecurity = upServiceApp.GetAdministrationAccessControl();
var adminClaim = mgr.ConvertIdentifierToClaim(accountName,
SPIdentifierTypes.WindowsSamAccountName);
adminSecurity.AddAccessRule(new SPAclAccessRule(adminClaim,
SPCentralAdministrationRights
.FullControl));
upServiceApp.SetAdministrationAccessControl(adminSecurity);
upServiceApp.Uncache();
upServiceproxy.Uncache();
}
}
}

For more reference :

http://msdn.microsoft.com/en-us/library/ff426884.aspx

http://msdn.microsoft.com/en-us/library/ff426883.aspx

http://msdn.microsoft.com/en-us/library/ff464387.aspx

Brands Must Move to Digital Experience

Extending the activity feed with enterprise content

Happy Sharepointing 🙂

Sharepoint silverlight World Analog Clock Webpart


Hi,

SharePoint 2010 has built-in support for Silverlight Web Parts, making it easy to get Silverlight applications up and running. But in real scenarios sometime requirement needs more customization then its hard to integrate the requirement with out of box WebPart. In this article I will explain how a SilverLight application can be integrated to the sharepoint webpart.

Requirements of WebPart:

Analog clock should be displayed to the home page of the site which can be personalized and customizable for the normal user. User can add as many as world clocks as he/she want and set the timezone and able to do basic configuration. following are the key points of the requirnments.

  • Can add multiple clocks to the WebPart.
  • Can display the time only
  • Select desired Time Zone from the list
  • Change clock title
  • Show/Hide seconds hand
  • Show/Hide milliseconds dial
  • Show/Hide tenth seconds dial

Explanation:

After goggling few I found Bamboo webpart which is pretty fine for me and fits my requirements but unfortunately got some error on test server related to safe control which is beyond my approach to fix. Finally I found a open source WebPart on codeplex which perfectly suits my requirement except adding multiple clocks. Then i have downloaded the code and did some of customization with the share point project as well as silver light project.

To add More clocks, I followed the given steps:

I modified the Sharepoint clock webpart class(ClockWebpart.cs) to create a new property called “Clocks”

#region Properties
protected ClockWebPart ParentWebPart
{
get { return this.Parent as ClockWebPart; }
}
protected List<EditorControls.ClockWebPart.EditorParts.Clock> Clocks
{
get
{
return Serialization.Deserialize(ParentWebPart.Clocks);
}
}
#endregion
private string _clocks =string.Empty;
[Personalizable(PersonalizationScope.Shared),
WebBrowsable(false)]
public string Clocks { get { return _clocks; } set { _clocks = value; } }
Next step is to modify the user control of the web part to pick the value from the user and then implement the Render method as follows:

protected override void Render(HtmlTextWriter writer)
{
if (Clocks.Count>0)
{
foreach (var clock in Clocks)
{
AddClock(clock);
}
}
base.Render(writer);
}
private void AddClock(EditorControls.ClockWebPart.EditorParts.Clock clock)
{
ClocckContainer.Controls.Add(new Literal { Text =
"<object data='data:application/x-silverlight-2,' type='application/x-silverlight-2' width='150px' height='150px'>" +
"<param name='source' value='~/_layouts/SharePoint.Silverlight.Clock/SilverlightClock_MVP.xap' />" +
"<param name='onError' value='onSilverlightError' />" +
"<param name='background' value='Transparent' /> " +
"<param name='windowless' value='true' />" +
"<param name='minRuntimeVersion' value='4.0.50826.0' />" +
"<param name='autoUpgrade' value='true' />" +
"<param name='initParams' value='" + GetInitParams(clock) + "' />" +
"<a href='http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0' style='text-decoration: none'>" +
"<img src='http://go.microsoft.com/fwlink/?LinkId=161376' alt='Get Microsoft Silverlight' style='border-style: none' /></a></object>"
});
}

Also, create a new class "Clock":

using System;
namespace SharePoint.Silverlight.Clock.EditorControls.ClockWebPart.EditorParts
{
[Serializable]
public class Clock
{
public string ClockTitle { get; set; }
public bool ShowDigitalClock { get; set; }
public bool ShowSecondHand { get; set; }
public bool ShowMilliHand { get; set; }
public bool ShowTenthsHand { get; set; }
public string ClockTimeZoneId { get; set; }
public string UtcOffsetTicks { get; set; }
internal Clock(string clockTitle, bool showDigitalClock, bool showSecondHand, bool showMilliHand, bool showTenthsHand, string clockTimeZoneId)
{
ClockTitle = clockTitle;
ShowDigitalClock = showDigitalClock;
ShowSecondHand = showSecondHand;
ShowMilliHand = showMilliHand;
ShowTenthsHand = showTenthsHand;
ClockTimeZoneId = clockTimeZoneId;
try
{
if (!string.Equals("0", clockTimeZoneId))
{
var tzId = TimeZoneInfo.FindSystemTimeZoneById(clockTimeZoneId);
UtcOffsetTicks = tzId.BaseUtcOffset.Ticks.ToString();
}
}
catch { }
}
public Clock()
{
}
}
}

Next, I modified the EditorBase class in two methods:

public override bool ApplyChanges()
{
ClockWebPart wp = this.WebPartToEdit as ClockWebPart;
EditorPartMain partMain = this.FindControl(ID + "EditorPartMain") as EditorPartMain;
wp.Clocks = partMain.AddedClocks;
return true;
}
public override void SyncChanges()
{
EnsureChildControls();
ClockWebPart wp = this.WebPartToEdit as ClockWebPart;
EditorPartMain partMain = this.FindControl(ID + "EditorPartMain") as EditorPartMain;
partMain.AddedClocks = wp.Clocks;
partMain.Initialize();
}

With some modifications in the silverlight like height and width adjustments. This code will provide functionality to add more clocks to the webpart.

Note: For beginners of the silverlight use following trik to scale down the clock:

<Grid.RenderTransform>
<ScaleTransform x:Name=”CanvasScale” ScaleX=”.5″ ScaleY=”.5″></ScaleTransform>
</Grid.RenderTransform>

Happy sharepointing

Custom Site Map Web Part using dynamic Asp Menu


Hi,

Site Map is a very important feature for the users to have a high level view of all sites and even for cross site navigation. I have implement recently a very basic Site map webpart which iterate through all the site collections with in a web application and display them as a tree view like structure.

I am sharing the code which basically contains a visual web part:

For Ascx file:

<asp:Menu ID=”SiteMapMenu” runat=”server” StaticDisplayLevels=”3″ Font-Size=”16px” Font-Bold=”true” DynamicMenuItemStyle-ItemSpacing=”5px” DynamicHorizontalOffset=”10″ >
<StaticSelectedStyle ForeColor=”#A5C8E8″ BackColor=”#A5C8E” />
<StaticMenuItemStyle HorizontalPadding=”20px” VerticalPadding=”3px” />
<DynamicHoverStyle BackColor=”#A5C8E8″ />
<DynamicMenuStyle BackColor=”#A5C8E8″ BorderStyle=”Solid” BorderWidth=”1px” BorderColor=”#4F86BC” />
<DynamicSelectedStyle BackColor=”#A5C8E8″ />
<DynamicMenuItemStyle HorizontalPadding=”5px” VerticalPadding=”2px” />
<StaticHoverStyle BackColor=”#A5C8E8″ />
</asp:Menu>

For code behind of the in CreateChildControls

protected override void CreateChildControls()
{
var currentUser = SPContext.Current.Web.CurrentUser.LoginName;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
foreach (SPSite sc in SPContext.Current.Site.WebApplication.Sites)
{
var rootWeb = sc.RootWeb;
if (string.Equals(rootWeb.WebTemplate.ToUpper(), "SRCHCEN"))
continue;

var siteMapMenu = new Menu();
ConfigureMenu(ref siteMapMenu);

var siteCollectionNode = new MenuItem(rootWeb.Title, rootWeb.Title, "", rootWeb.Url);
if (rootWeb.DoesUserHavePermissions(currentUser, SPBasePermissions.Open))
{
var webCollection = rootWeb.GetSubwebsForCurrentUser();

foreach (SPWeb web in webCollection)
{
try
{
IterateSubWebs(web, currentUser, ref siteCollectionNode);
}
finally
{
if (web != null)
web.Dispose();
}

}
siteMapMenu.Items.Add(siteCollectionNode);
}
var menuDiv = new HtmlGenericControl("div") {ID = rootWeb.Title + "Div"};
menuDiv.Attributes["style"] = "float: left; padding-right:10px";
menuDiv.Controls.Add(siteMapMenu);
Controls.Add(menuDiv);
}
});
}

 private static void ConfigureMenu(ref Menu siteMapMenu)
{
// move the styles to css
siteMapMenu.StaticDisplayLevels = 3;
siteMapMenu.Font.Size = FontUnit.Point(11);
siteMapMenu.Font.Bold = true;
siteMapMenu.DynamicMenuItemStyle.ItemSpacing = Unit.Pixel(5);
siteMapMenu.DynamicHorizontalOffset = 10;
siteMapMenu.StaticSelectedStyle.ForeColor = ColorTranslator.FromHtml("#E0E0E0");
siteMapMenu.StaticSelectedStyle.BackColor = ColorTranslator.FromHtml("#E0E0E0");
siteMapMenu.StaticSelectedStyle.HorizontalPadding = Unit.Pixel(20);
siteMapMenu.StaticSelectedStyle.VerticalPadding = Unit.Pixel(3);
siteMapMenu.DynamicHoverStyle.BackColor = ColorTranslator.FromHtml("#E0E0E0");
siteMapMenu.DynamicMenuStyle.BackColor = ColorTranslator.FromHtml("#E0E0E0");
siteMapMenu.DynamicMenuStyle.BorderStyle = BorderStyle.Solid;
siteMapMenu.DynamicMenuStyle.ForeColor = ColorTranslator.FromHtml("#E0E0E0");
siteMapMenu.DynamicMenuStyle.BorderWidth = Unit.Pixel(1);
siteMapMenu.DynamicMenuStyle.BorderColor = ColorTranslator.FromHtml("#0C1F50");
siteMapMenu.DynamicSelectedStyle.BorderColor = ColorTranslator.FromHtml("#E0E0E0");
siteMapMenu.DynamicMenuStyle.HorizontalPadding = Unit.Pixel(10);
siteMapMenu.DynamicSelectedStyle.HorizontalPadding = Unit.Pixel(5);
siteMapMenu.DynamicSelectedStyle.VerticalPadding = Unit.Pixel(2);
siteMapMenu.StaticHoverStyle.BackColor = ColorTranslator.FromHtml("#E0E0E0");
siteMapMenu.StaticHoverStyle.BorderStyle = BorderStyle.Solid;
siteMapMenu.StaticHoverStyle.BorderWidth = Unit.Pixel(1);
siteMapMenu.StaticHoverStyle.BorderColor = ColorTranslator.FromHtml("#0C1F50");

}

private static void IterateSubWebs(SPWeb web, string user, ref MenuItem node)
{
var chieldNode = new MenuItem(web.Title, web.Title, "", web.Url);
if (web.DoesUserHavePermissions(user, SPBasePermissions.Open))
{
foreach (SPWeb subWeb in web.GetSubwebsForCurrentUser())
{
IterateSubWebs(subWeb, user, ref chieldNode);
}
node.ChildItems.Add(chieldNode);
}
}

Using the above code simply we can iterate through all the site collections and then sub sites of each site.

Happy SharePointing…:)

Custom Cross site collection Navigation using user control


Hi,

Sometimes the out of box navigation does not provides us the type of navigation wants on the whole application. To make such custom navigation which is consistent throughout many site collection it is batter to create a custom user control and simply register it in to the master page. Applying the same master page on every site collection will do the job.

Follow below steps to create the user control:

1) Right click on the project and the click on Add New Item.

2)Then add a User Control. Name it as per the convention.

3)Under the ascx file create any type of control you wana use, I prefer asp menu:

<div >
<asp:Menu ID=”MyTopMenu” Orientation=”Horizontal” runat=”server”>
<StaticMenuItemStyle CssClass=”topNavStaticMenuItem”/>
<StaticSelectedStyle CssClass=”topNavSelectedStaticMenuItem”></StaticSelectedStyle>
</asp:Menu></div>

4) Now next step is to populate the menu with the tabs:

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
var homeLinkTitle = “Home”;
var homeLinkUrl = “https://mohitvash.wordpress.com&#8221;;

ApoteoseTopMenu.Items.Add(new MenuItem(homeLinkTitle) { NavigateUrl = homeLinkUrl, Selected = true });
ApoteoseTopMenu.Items.Add(new MenuItem(“Google”) { NavigateUrl = “www.google.com”, Selected = false });
}
}

5) Now next step is to add this control to the master page:

<%@ Register TagPrefix=”MyTopNavControl” TagName=”CurrentPageControl” src=”~/_ControlTemplates/MyTopNavControl.ascx” %>

also add below line at the required placeholder.

<MyTopNavControl:CurrentPageControl ID=”topNav” runat=”server”></MyTopNavControl:CurrentPageControl>

6) Build and deploy the application and enjoy the consistent navigation 🙂

Happy Sharepointing….

Add a webpart to page using Onet/Feature


Hi,

Custom home page is quite common requirement for any development project. This post helps you if you need to add out of box web part to page. This post contains good details how to deploy custom page.

The AllUsersWebPart tag allow us to insert a custom/Out of box web part and It also contains few attributes which are used to place web part. Two important attributes are WebPartOrder and WebPartZone attributes. The content of the tag is the dwp (or webpart) of any web part. The tag cloud dwp code example is given below:

<AllUsersWebPart WebPartZoneID="Right" WebPartOrder="3">           <![CDATA[<WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">                       <Title>Tag Cloud</Title>                       <Description>Displays the most popular subjects being tagged inside your organization.</Description>                       <FrameType>TitleBarOnly</FrameType>                       <ChromeType>None</ChromeType>                       <AllowRemove>true</AllowRemove>                       <AllowMinimize>true</AllowMinimize>                       <Assembly>Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>                      <TypeName>Microsoft.SharePoint.Portal.WebControls.TagCloudWebPart</TypeName>                     </WebPart>         ]]>         </AllUsersWebPart> 

For .webpart, following

<AllUsersWebPart WebPartZoneID="MiddleLeftZone" WebPartOrder="1">     <![CDATA[                                                     <webParts>         <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">           <metaData>             <type name="MyClass, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b9d1137704f880" />             <importErrorMessage>No se puede importar este elemento Web.</importErrorMessage>           </metaData>           <data>             <properties>               <property name="AllowClose" type="bool">False</property>               <property name="AllowMinimize" type="bool">False</property>               <property name="AllowConnect" type="bool">True</property>               <property name="ChromeType" type="chrometype">None</property>               <property name="Hidden" type="bool">False</property>               <property name="AllowEdit" type="bool">False</property>               <property name="AllowZoneChange" type="bool">False</property>               <property name="MissingAssembly" type="string">No se puede importar este elemento Web.</property>               <property name="ChromeState" type="chromestate">Normal</property>             </properties>           </data>         </webPart>       </webParts>     ]]>   </AllUsersWebPart> 

AllUserWebPart tag can be used in onet.xml under “File” tag. If we are adding the custom page using the feature then also the same way we can define this tag value to the File tag.

Thanks for your time :)…

Set Home page of sharepoint sites


Hi,

According to the client’s requirement, every site must be set as a custom home page other then “Pages/Default.aspx”. I have added a site definition using CKSDev tool and add this page using Onet.xml.

Next step is to create a feature at web level which should contains following code:

SPFolder rootFolder = properties.Web.RootFolder;
rootFolder.WelcomePage = "default.aspx";
rootFolder.Update();

This code will also work for any site template.